Web-Based console for Raspberry Pi: JSON data

Posted by:

|

On:

|

In this Raspberry Pi project, JSON data is utilized as an interchange format between the front-end HTML/JavaScript and the back-end Python programs. In this post, let’s delve into the topic of JSON data, specifically focusing on its role in this project and providing examples of how it is used in the programs.

JSON (JavaScript Object Notation) is a lightweight data interchange format that is easy for humans to read and write and easy for machines to parse and generate. It is widely used for transmitting structured data between a server and a client or between different parts of an application, enabling the seamless transfer of information and allowing both sides to understand and work with the shared data.

Here are some key features and characteristics of JSON:

  1. Syntax: JSON data is represented using a simple and intuitive syntax. Data is plain text organized in key-value pairs, where the key is a string enclosed in single or double quotation marks and the value can be a string, number, boolean, null, array, or another JSON object. Multiple key-value pairs are separated by commas, and the entire JSON data is enclosed in curly braces { }. In a JSON file, spaces, tabs, or line breaks added within the file do not affect the structure or content of the JSON object. These characters can be included to enhance readability and make the object easier to comprehend. As an illustration, in our project, we store configuration data in with JSON data format called json.conf using the following syntax:
{'name': 'MyApp2', 'login_id': 'admin', 'login_pw': 'password', 'LED_pin_arr': [{'name': 'RED', 'pin': 13}, {'name': 'GREEN', 'pin': 14}]} 

This configuration data can be downloaded from the following link:

https://github.com/FelixT123/CS-Student/blob/main/web-based%20console/conf.json

Now, let’s examine this JSON data. It follows a two-level structure where a JSON object is nested within another JSON object. The first level is represented by:

{
  'key1': 'data1',
  'key2': 'data2',
  ...
  'key_n': 'data_n'
}

If one of the data values is itself a JSON object, it is enclosed within square brackets [ ] first, and then the JSON object structure is added again:

{
  'key1': 'data1',
  'key2': [ {'nested_key1': 'nested_data1'}, {'nested_key2': 'nested_data2'}, ... ],
  ...
  'key_n': 'data_n'
}

This structure allows for organizing and representing complex data relationships within a JSON data format.

2. Data Types: JSON supports several data types:

  • String: A sequence of characters enclosed in single/double quotation marks.
  • Number: A numeric value, which can be an integer or a floating-point number.
  • Boolean: Either true or false.
  • Null: Represents an empty or null value.
  • Array: An ordered collection of values enclosed in square brackets ([ ]). The values can be of any JSON data type.
  • Object: An unordered collection of key-value pairs enclosed in curly braces ({ }). The keys are strings, and the values can be of any JSON data type.

3. Human Readability: JSON is designed to be easy for humans to read and write. The syntax is straightforward and resembles the syntax used in many programming languages.

4. Language Independence: JSON is a language-independent format, meaning it can be used with any programming language that has JSON parsing and serialization libraries available.

5. Interchangeability: JSON data can be interchanged between different systems, platforms, and programming languages. It is widely used in web services and APIs for data exchange.

6. Standardization: JSON is a standardized format defined by the ECMA-404 and RFC 8259 specifications. This ensures consistent handling and interpretation of JSON data across different implementations.

JSON has become the de facto standard for data interchange due to its simplicity, readability, and wide support in various programming languages and frameworks. It is commonly used in web development, mobile app development, and data exchange between different systems and services.

In both the HTML and Python programs, there are three areas where JSON data is utilized. The following code snippets will demonstrate how to manipulate the JSON data in these areas.

(1) Initialization during program starts

When the backend server, “server.py,” is executed, it reads the configuration JSON file, “json.conf,” using the provided code.

    file = open('conf.json', 'r', encoding='utf8')
    json_data = file.read()
    # Parse the JSON string into a Python object
    conf = json.loads(json_data)

Consequently, the variable conf holds the JSON object with contents

conf={'name': 'MyApp2', 'login_id': 'admin', 'login_pw': 'password', 'LED_pin_arr': [{'name': 'RED', 'pin': 13}, {'name': 'GREEN', 'pin': 14}]}

Then the data of each key is retrieved using the following method

name = conf['name']     ; name = "MyApp2"
LED_pin_arr = conf['LED_pin_arr']    ; LED_pin_arr=[{'name': 'RED', 'pin': 13}, {'name': 'GREEN', 'pin': 14}]

(2) Dash board webpage

When a URL request is received from front-end ajax or other handlers within server.py, this server python program calls the IndexHandler class.

class IndexHandler(BaseHandler):
    def get(self):
        # name and LED_pin_arr are parameters pass to index.html 
        self.render('index.html', name=name, LED_pin_arr=LED_pin_arr)

In the code snippet self.render('index.html', name=name, LED_pin_arr=LED_pin_arr), the self.render() method is used to provide the “index.html” template in a folder “template” with data (name and LED_pin_arr) and render it as a response to the client’s request.

(3) Configuration webpage

This configuration interface at the front end is commonly used in IP-based products. It involves two data flows:

  1. The back-end server uses the template, together with the JSON data and generating an HTML file to be sent back to the client.
  2. The front-end webpage can modify the JSON data and send it back to the server. This data flow enables the front-end to update and overwrite the json.conf configuration file stored on the SD card, reflecting the changes made through the interface.

These two data flows facilitate seamless communication and interaction between the back-end server and the front-end interface, allowing for real-time updates and configuration adjustments.

When a URL request is received from front-end ajax or other handlers within server.py, this server python program calls the ConfigHandler class.

class ConfigHandler(BaseHandler):
    def get(self):
        # name and conf are parameters pass to config.html 
        self.render('config.html',name=name,json_data=conf)   

In the provided code snippet self.render('config.html', name=name, json_data=conf), the self.render() method is utilized to supply the “config.html” template with data, specifically the variables name and json_data. This process prepares the template for rendering and generating an HTML response to be sent back to the client.

<input type="text" id="app_name" value="{{ json_data['name'] }}" class="underline-input">
<input type="text" id="user" value="{{ json_data['login_id'] }}" class="underline-input">
<input type="password" id="password" value="{{ json_data['login_pw'] }}" class="underline-input">

Within the “config.html” template file, you employ template tags and expressions to retrieve the provided JSON data and generate the final HTML file. This rendered HTML file is then transmitted to the front-end browser for display.

{% if len(json_data['LED_pin_arr']) > 0 %}      {% for id, LED in enumerate(json_data['LED_pin_arr']) %}<div class="mdl-cell mdl-cell--8-col mdl-cell--5-col-tablet mdl-cell--2-col-phone"> LED{{id+1}} name </div>   
 <input type="text" id="LED{{id+1}}name" value="{{LED['name']}}" class="underline-input">
 <div class="mdl-cell mdl-cell--8-col mdl-cell--5-col-tablet mdl-cell--2-col-phone"> LED{{id+1}} pin </div>
  <input type="text" id="LED{{id+1}}pin" value="{{LED['pin']}}" class="underline-input"> 
{% end %}
{% end %}

After all configuration data are modified, a javascript function is called to construct the JSON data

        function saveConfig() {
            // Assuming the HTML form has input fields with IDs "setting1Input" and "setting2Input"
            var setting1Value = document.getElementById("app_name").value;
            var setting2Value = document.getElementById("user").value;
            var setting3Value = document.getElementById("password").value;
            var setting4Value = document.getElementById("LED1name").value;
            var setting5Value = document.getElementById("LED1pin").value;
            var setting6Value = document.getElementById("LED2name").value;
            var setting7Value = document.getElementById("LED2pin").value;
            var ledPinArr = [
                {
                    name: setting4Value,
                    pin: parseInt(setting5Value)
                },
                {
                    name: setting6Value,
                    pin: parseInt(setting7Value)
                }
            ];

            // Create an object to hold the edited data
            var editedData = {
                name: setting1Value,
                login_id: setting2Value,
                login_pw: setting3Value,
                LED_pin_arr: ledPinArr
            };

            // Convert the edited data to JSON
            var jsonData = JSON.stringify(editedData);

The JSON data is then sent back to server using ajax

$.ajax({
      url: "/saveConfig",
      type: "POST",
      data: jsonData,
      contentType: "application/json",
      success: function(abc) {
                // Update the success message element with the response from the server
                   var successMessageElement = document.getElementById("success-message");
                   successMessageElement.textContent = abc;

                },
                error: function(error) {
                    console.error("Error saving configuration:", error);
			
                }
        });

When server.py receives the HTTP post request, it will execute ConfigHandler post function and use following codes to get the updated JSON data and write it to file.

class ConfigHandler(BaseHandler):
    def post(self):
        data = json.loads(self.request.body)
        # Save the JSON data to a file
        with open('conf.json', 'w') as file:
            json.dump(data, file)
        self.write('Data saved successfully')

In conclusion, the utilization of JSON as the intermediate format enables smooth data exchange between the front-end and back-end components of the project. This approach ensures compatibility and simplifies the process. JSON’s structured and flexible nature facilitates efficient data representation, manipulation, and communication across various programming languages and systems.

Posted by

in