Skill Level | Area of Focus | Operating System | Software Tools |
---|---|---|---|
Beginner | Embedded, IoT | RTOS | QCA 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.
Objective
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
Materials Required / Parts List / Tools
The contents of the QCA4020 Development kit, as seen above, include:
- QCA4020 development board
- USB to Micro USB cables
- Power supply
- Jumpers
- Setup Guide
Additional Resources
- QCA4020 Development Kit Users Guide
- Moddable SDK
- Getting started with QCA4020 development using the Moddable SDK
- Moddable SDK httpgetjson example app
Build / Assembly Instructions
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
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 andmcconfig
command line host tool built as part of the Moddable SDK setup described above. In themcconfig
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.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 thehttpgetjson
app:cd $MODDABLE/build/devices/qca4020/xsProj/build/gcc APP_NAME=httpgetjson DEBUG=1 make
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
Open another terminal window and launch the
xsbug
debugger andserial2xsbug
tool. Both thexsbug
debugger andserial2xsbug
tool were built as part of the Moddable SDK setup. Thexsbug
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 thexsbug
window.
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 typec
to continue three times to launch thehttpgetjson
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 thexsbug
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 callingstrcpy()
andstrcat()
. - JavaScript automatically frees the memory after the request has completed. No need to call
malloc()
orfree()
. 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
- Moddable SDK: Networking reference.
- Moddable Blog Post: Web Services Series Part 1: Working with REST APIs
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.
Contributors
Name | Title/Company | |
---|---|---|
Brian Friedkin | [email protected] | Principal Engineer - Moddable Tech, Inc. |