QCA4020 HTTP client in JavaScript using the Moddable SDK

Skill LevelArea of FocusOperating SystemSoftware Tools
BeginnerEmbedded, IoTRTOSQCA 402x Wi-Fi/BLE/ZigBee

This project introduces you to Moddable SDK development in JavaScript on the QCA4020 System-On-Chip (SoC) Product Development Kit. The Moddable SDK is a combination of development tools and runtime software to create applications for embedded devices. The QCA4020 IoT solution offers Wi-Fi, Bluetooth low energy (BLE), and 802.15.4 capable radios in a single-chip package. This project showcases the httpgetjson example app, a JavaScript app from the Moddable SDK that issues an HTTP request to the OpenWeatherMap REST API and parses the JSON response.

The "httpgetjson" application issues an HTTP request to the OpenWeatherMap REST API to retrieve the current weather in Menlo Park, CA. The response is delivered in JSON format, parsed into a JavaScript object and output to the Moddable xsbug JavaScript source-level debugger. In this project you will learn:

  • How to use the Moddable SDK build tools to build and run applications for the QCA4020 SoC Product Development Kit
  • How the httpgetjson application leverages modules from the Moddable SDK to connect to the network over Wi-Fi and issue HTTP requests
  • How to use xsbug, the JavaScript source-level debugger from the Moddable SDK
  • How developers benefit from writing application code in JavaScript to build a web service client compared to native C/C++ code

The contents of the QCA4020 Development kit, as seen above, include:

Additional Resources

Follow the one-time setup instructions in the Moddable SDK Getting started with QCA4020 development document to complete the following steps:

  • Linux Host Environment Setup. Moddable supports FreeRTOS on the QCA4020 Customer Development Board (CDB) using a Linux host build platform.
  • QCA4020 SDK Setup, including FreeRTOS, QuRT, and OpenOCD
  • ARM Toolchain Setup
  • Moddable Host Tools Build

Once you have completed the setup instructions above, you are ready to build the httpgetjson example app, following the instructions in the next section.

Usage Instructions

In this section we learn how to build and run the Moddable httpgetjson example app on the QCA4020 CDB.

Building the httpgetjson app

  1. Open a terminal window on the Linux build host, navigate to the httpgetjson example app directory, and build the app:

    cd $MODDABLE/examples/network/http/httpgetjson
    mcconfig -d -m -p qca4020/cdb ssid=<your SSID> password=<your password>
    

    The $MODDABLE environment variable was initialized and mcconfig command line host tool built as part of the Moddable SDK setup described above. In the mcconfig command line you specify the Wi-Fi credentials corresponding to the Wi-Fi access point you'd like to CDB to connect to. The Moddable runtime automatically connects the CDB to this access point before running the app.

    The mcconfig tool builds the Moddable XS runtime, application, modules, and any assets into an archive that will be included in the final build.

  2. Open another terminal window, navigate to the project build directory, and build the QCA4020 launcher application. The launcher application includes the archive built above and QCA4020 libraries. The make command is prefixed by the environment variables required to build a debug version of the httpgetjson app:

    cd $MODDABLE/build/devices/qca4020/xsProj/build/gcc
    APP_NAME=httpgetjson DEBUG=1 make
    
  3. Navigate to the project build directory and flash the built application to the device:

    cd $MODDABLE/build/devices/qca4020/xsProj/build/gcc
    sh flash_openocd.sh
    
  4. Open another terminal window and launch the xsbug debugger and serial2xsbug tool. Both the xsbug debugger and serial2xsbug tool were built as part of the Moddable SDK setup. The xsbug debugger is used to debug the JavaScript portion of the application and monitor resources.

    xsbug &
    serial2xsbug /dev/ttyUSB1 115200 8N1
    

    The application will output the HTTP response data to the xsbug console pane. The image below shows the xsbug window.

  1. Navigate to the project build directory and run gdb to launch the app:

    cd $MODDABLE/build/devices/qca4020/xsProj/build/gcc
    arm-none-eabi-gdb -x v2/quartzcdb.gdbinit
    

    At the (gdb) prompt type c to continue three times to launch the httpgetjson app. You can also set breakpoints, view source and variables, etc... The application will connect to the specified Wi-Fi access point and issue the HTTP request. The HTTP response data is traced to the xsbug console. The INSTRUMENTS pane displays real-time application runtime status and feedback.

Note that all Moddable applications are built and run following the same basic steps outlined above.

Application Walk-Through

In this section we take a closer look at how the httpgetjson JavaScript application uses the Moddable SDK Request object to issue the HTTP request and how the JSON response is parsed into a JavaScript object. We also cover some of the advantages using JavaScript compared to native C/C++ code.

Here is the complete httpgetjson application:

import {Request} from "http"

const APPID = "94de4cda19a2ba07d3fa6450eb80f091";
const zip = "94025";
const country = "us";

let request = new Request({
    host: "api.openweathermap.org",
    path: `/data/2.5/weather?zip=${zip},${country}&appid=${APPID}&units=imperial`,
    response: String
});

request.callback = function(message, value)
{
    if (Request.responseComplete === message) {
        value = JSON.parse(value, ["main", "name", "temp", "weather"]);
        trace(`The temperature in ${value.name} is ${value.main.temp} F.\n`);
        trace(`The weather condition is ${value.weather[0].main}.\n`);
    }
}

Importing modules

The Moddable SDK includes a large collection of modern class-based JavaScript modules for use by application developers. The HTTP Request class implements a client for making HTTP requests and is imported by the application module:

import {Request} from "http"

API parameters

The OpenWeatherMap REST API encodes the application ID, zip code, and country request parameters into HTTP request path URI. The httpgetjson application defines the parameters as constants. You can modify the zip and country constants to request the current weather for a different region:

const APPID = "94de4cda19a2ba07d3fa6450eb80f091";
const zip = "94025";
const country = "us";

HTTP request

The application instantiates a Request object instance, passing a dictionary of properties to configure the request:

let request = new Request({
    host: "api.openweathermap.org",
    path: `/data/2.5/weather?zip=${zip},${country}&appid=${APPID}&units=imperial`,
    response: String
});

The host and path properties define the URI domain and the path respectively. The path property includes the request parameters. In this example we use a JavaScript template literal to build the path. The template literal substitutes ${zip}, ${country} and ${APPID} by the associated constants, resulting in the following path string:

/data/2.5/weather?zip=94025,us&appid=94de4cda19a2ba07d3fa6450eb80f091&units=imperial

The response property corresponds to the type of object to use for the HTTP response delivered to the callback (see below) when the request is complete. In this example, we request that the response is delivered as a JavaScript String object.

Using a dictionary of properties along with JavaScript template literals greatly simplifies the code required to build and issue an HTTP request, compared to native C code:

  • JavaScript automatically allocates the memory required to contain the URI components. No need to pre-calculate buffer lengths by calling strlen() on each substring or build up strings by calling strcpy() and strcat().
  • JavaScript automatically frees the memory after the request has completed. No need to call malloc() or free(). No risk of memory buffer overrun, which could lead to security vulnerabilities.
  • Additional HTTP request properties are specified, e.g. HTTP headers, body, etc... by adding associated properties to the dictionary. No need for a fixed parameter list or data structure with optional parameters set to NULL.
  • The HTTP Request class is the only module required to issue a HTTP request to a web service. No additional networking, HTTP or Wi-Fi libraries are needed.

The HTTP request is issued immediately and asynchronously after the Request is instantiated.

HTTP response

The HTTP class object uses a callback function to receive status information, including the HTTP response. The application is responsible for providing the callback function. The message callback parameter defines the type of information received. This example only processes the Request.responseComplete message, sent when the HTTP request completes successfully and a response is delivered to the client:

request.callback = function(message, value)
{
    if (Request.responseComplete === message) {
        value = JSON.parse(value, ["main", "name", "temp", "weather"]);
    }
}

Applications can monitor the HTTP request progress by processing additional messages. For example, the Request.headersComplete message is delivered when all the HTTP headers have been received by the client. For additional details, please refer to the network reference document and http.js module source code.

JSON parsing

The OpenWeatherMap API returns the HTTP response in JSON format by default. JSON is a text format that describes name/value data pairs and data formats. Our application requests that the response is delivered as a String object. Hence the HTTP response is delivered as JSON text in a JavaScript String in the callback value parameter. The complete response text received by the application callback looks like this:

{
    "coord":{
        "lon":-122.19,"lat":37.44
    },
    "weather":[
        {
            "id":802,
            "main":"Clouds",
            "description":"scattered clouds",
            "icon":"03d"
        }
    ],
    "base":"stations",
    "main":{
        "temp":73.02,
        "pressure":1017,
        "humidity":60,
        "temp_min":64,
        "temp_max":79
    },
    "visibility":16093,
    "wind":{
        "speed":19.46,
        "deg":350
    },
    "clouds":{
        "all":40
    },
    "dt":1562019103,
    "sys":{
        "type":1,
        "id":5310,
        "message":0.0109,
        "country":"US",
        "sunrise":1561985482,
        "sunset":1562038413
    },
    "timezone":-25200,
    "id":0,
    "name":"Menlo Park",
    "cod":200
}

While some applications might be interested in many of the JSON properties returned by the HTTP request, our application is only interested in the city, temperature and current condition. Further, parsing all the unnecessary properties into a JavaScript object requires additional memory. Because applications frequently only need to parse a handful of JSON keys, the XS JavaScript engine extends the JSON.parse() function to allow callers to specify which keys to parse. Our application only needs four of the JSON key/value pairs. We pass the needed keys as a second Array parameter to JSON.parse:

value = JSON.parse(value, ["main", "name", "temp", "weather"]);

The JavaScript built-in JSON object contains methods to parse a JSON String into a JavaScript object and to convert a JavaScript object into a JSON String. Our application calls JSON.parse() to parse the HTTP response String into a JavaScript object. The resulting JavaScript object contains only the keys specified. By adding a call to JSON.stringify(), we can examine the JavaScript object as a string:

value = JSON.parse(value, ["main", "name", "temp", "weather"]);
trace("Parsed JSON:\n");
trace(JSON.stringify(value) + "\n");
trace(`The temperature in ${value.name} is ${value.main.temp} F.\n`);
trace(`The weather condition is ${value.weather[0].main}.\n`);

The application output looks like this:

Parsed JSON:
{"weather":[{"main":"Clear"}],"main":{"temp":72.72},"name":"Menlo Park"}
The temperature in Menlo Park is 72.5 F.
The weather condition is Clear.

Using the JSON parsing built into the JavaScript language substantially reduces the amount of application code required to process an HTTP response. Our application parses the response into a JavaScript object in one line of code. To compare, a native code application would need to parse the response text using C library functions, e.g. strtok(), atoi() etc... and buffer intermediate results or use a JSON API/library that requires multiple API calls to traverse the JSON "tree" to find the key/value pairs of interest.

Congratulations! By following the steps outlined in this project, you've built, deployed and run a JavaScript app on the QCA4020/CDB that issues an HTTP request and processes the response. You've also learned how quick and efficient embedded development can be in JavaScript using the Moddable SDK runtime software and development tools.

Further Reading

Availability

The Moddable SDK with Qualcomm QCA4020 support is available immediately as part of the Moddable SDK repository on GitHub. The Moddable SDK is available for use with either a FOSS (Free and Open Source Software) license or a commercial software license. Additional information on both licenses is provided on the Moddable website.

NameEmailTitle/Company
Brian Friedkinbrian@moddable.techPrincipal Engineer - Moddable Tech, Inc.