CEJson: https://ardjson.codeplex.com This now includes the Arduino apps covered in this blog.
Previous:Ardjson-Part 3b: cURL CRUD Examples
Additional: Ardjson-4b- Arduino Networking
The ToDoItems Microsoft Azure Mobile Services sample app has been examined in detail in the previous blogs in this series. It is a simple Universal App that records things "To Do" in a table on the service. The app lists all currently todo tasks, not yet tagged as complete. The user can add new tasks. When an item is complete the item is checked and is no longer listed in the app. The UI actions in the app are:
The Universal app runs on Desktop Windows 8.1, Windows Phone 8.1 and Windows RT. In Parts 3 of this blog, it was demonstrated as to how to action these functions via a command line, using cURL.exe.
In this blog we will action those same actions from an Arduino device.
In parts 1 and 2 of this blog series, the TodoItems app was extended into a telemetry app that can record values of sensors. A later blog in this series will cover using an Arduino device to to interact with the Telemetry table in the Mobil Service in the same way. The final blog in this series will then use sensor values from actual sensors connected to an Arduino device
This code is three programs; one for each of the ToDo actions as list above. The structure of these programs is:
The send_request and read_response functions are specific to each of the three apps. The other code is common to all three. This code common is listed in two parts:
Placed at the top of source file
#include <Ethernet.h> #include <SPI.h> // Ethernet shield MAC address (sticker in the back) // Note: Will use DHCP byte mac = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // Azure Mobile Service address // You can find this in your Azure Mobile Service dashboard const char *server = "<Insert service name>.azure-mobile.net"; // Azure Mobile Service table name const char *table_name = "todoItem"; // Azure Mobile Service Application Key // You can find this key in the 'Manage Keys' menu on the Azure Mobile Service dashboard const char *ams_key = "<Insert App Key>"; //Used to store the send and response strings char jsonBuffer[640]; //The tenth line of the response contains the Json string #define RESPONSE_JSON_DATA_LINENNO 10
Placed at bottom of source code (below send_request and read_response functions:
// Wait for response void wait_response() { while (!client.available()) { if (!client.connected()) { return; } } } // Close the connection void end_request() { client.stop(); } // Arduino Setup void setup() { Serial.begin(9600); while (!Serial) { ; // Wait for serial port to connect. } Serial.println("Starting Ethernet"); if (Ethernet.begin(mac) == 0) { Serial.println("**** Ethernet failed ****"); for (;;) ; } Serial.println("Ethernet started"); // Give the Ethernet shield a second to initialize: delay(1000); } // Arduino Loop void loop() { send_request(); wait_response(); read_response(); end_request(); //Only run once while(1) ; }
The send_request and read_response functions, specific to each app follows:
send_request( ) ::
// Send an HTTP GET request to the Azure Mobile Service data API void send_request() { Serial.println("\nconnecting..."); if (client.connect(server, 80)) { Serial.print("sending "); // GET URI sprintf(jsonBuffer, "GET /tables/%s?$filter=(complete+eq+false) HTTP/1.1", table_name); Serial.println(jsonBuffer); client.println(jsonBuffer); // Host header sprintf(jsonBuffer, "Host: %s", server); client.println(jsonBuffer); // Azure Mobile Services application key sprintf(jsonBuffer, "X-ZUMO-APPLICATION: %s", ams_key); client.println(jsonBuffer); // JSON content type client.println("Content-Type: application/json"); // POST body sprintf(jsonBuffer, "", ""); // Content length client.print("Content-Length: "); client.println(strlen(jsonBuffer)); // End of headers client.println(); // Request body client.println(jsonBuffer); } else { Serial. println("connection failed"); } }
read_response( ):
// Read the response and dump to serial void read_response() { int jsonStringLength; int jsonBufferCntr=0; int numline=RESPONSE_JSON_DATA_LINENNO; //Ignore the response except for the 10th line while (client.available()) { char c = client.read(); if (c == '\n') { numline -=1; } else { if (numline == 0 ) { //Capture the 10th line in the response //To do: Could be more deterministic about this: // Expect certain content, checks and balances etc. jsonBuffer[jsonBufferCntr++] = c; jsonBuffer[jsonBufferCntr] = '\0'; } } } Serial.println("Received:"); Serial.println(jsonBuffer); Serial.println(""); }
Output:
See the Arduino project in CEJson JSonToDoGetIncomplete for an implementation of this.
send_request:
// Send an HTTP POST request to the Azure Mobile Service data API void send_request() { Serial.println("\nconnecting..."); if (client.connect(server, 80)) { Serial.print("sending "); Serial.println(value); // POST URI sprintf(buffer, "POST /tables/%s HTTP/1.1", table_name); client.println(buffer); // Host header sprintf(buffer, "Host: %s", server); client.println(buffer); // Azure Mobile Services application key sprintf(buffer, "X-ZUMO-APPLICATION: %s", ams_key); client.println(buffer); // JSON content type client.println("Content-Type: application/json"); // POST body sprintf(buffer, "{\"text\":\"NewToDoTask1\",\"complete\":false); // Content length client.print("Content-Length: "); client.println(strlen(buffer)); // End of headers client.println(); // Request body client.println(buffer); } else { Serial.println("connection failed"); } }
read_response:
// Read the response and dump to serial void read_response() { bool print = true; while (client.available()) { char c = client.read(); // Print only until the first carriage return if (c == '\n') print = false; if (print) Serial.print(c); } }
Starting Ethernet ipconfig: 192.168.0.103 255.255.255.0 192.168.0.1 192.168.0.1 Ethernet started Looping connecting... sending BookDentist POST /tables/todoItem HTTP/1.1 Host: sportronicsdj.azure-mobile.net X-ZUMO-APPLICATION: NtcMLPQtuAqWtvXOwrZVQtqHevNUnN27 Content-Type: application/json Content-Length: 39 {"text":"BookDentist","complete":false} HTTP/1.1 201 Created
See the Arduino project in CEJson JSonPostNewToDoItem for an implementation of this.
HTTP PUT, PATCH and DELETE require a record id, which with toDoItem table is a GUID. Filters cannot be used for these actions.
This means that the following code must be modified to include a specific record’s GUID before it is run.
Hint: Amore complex example would use an index and search for that record’s GUID amongst the JSon string return from [1] above, and use that.
#define GIUD=<insert> const char * guid = GUID; /* ** Send an HTTP PATCH request to the Azure Mobile Service data API */ void send_request() { Serial.println("\nconnecting..."); if (client.connect(server, 80)) { Serial.print("sending "); Serial.println(guid); // POST URI sprintf(buffer, "PATCH /tables/%s/%s HTTP/1.1", table_name,guid); client.println(buffer); Serial.println(buffer); // Host header sprintf(buffer, "Host: %s", server); client.println(buffer); Serial.println(buffer); // Azure Mobile Services application key sprintf(buffer, "X-ZUMO-APPLICATION: %s", ams_key); client.println(buffer); Serial.println(buffer); // JSON content type client.println("Content-Type: application/json"); Serial.println("Content-Type: application/json"); // POST body sprintf(buffer, "{\"complete\":true}"); // Content length client.print("Content-Length: "); client.println(strlen(buffer)); Serial.print("Content-Length: "); Serial.println(strlen(buffer)); // End of headers client.println(); Serial.println(); // Request body client.println(buffer); Serial.println(buffer); } else { Serial.println("connection failed"); } }
connecting... sending 4742AE79-628A-4FF5-84A9-B0B468263936 PATCH /tables/todoItem/4742AE79-628A-4FF5-84A9-B0B468263936 HTTP/1.1 Host: sportronicsdj.azure-mobile.net X-ZUMO-APPLICATION: NtcMLPQtuAqWtvXOwrZVQtqHevNUnN27 Content-Type: application/json Content-Length: 17 {"complete":true} HTTP/1.1 200 OK
See the Arduino project in CEJson JSonCompleteToDoItem for an implementation of this.
The first program, that GETS the active tasks, extracts the JSon string from the HTTP Response. No attempt was made to interpret the string and so it looks like a blob! The next blog introduces a simple JSon parser.
Next: CEJSon-5: A Simple JSon Parser