Weather Audio Notifier
Create an auditory weather notification project.
Introduction
This tutorial demonstrates how to use the Arduino Zero and the WiFi Shield 101 to act as a web client and parse Json formatted text. Json is well-known data-interchange format which is often used for communication between a server and client, and is particularly useful owing to its easy-to-read format and simplicity to parse and generate. In this example, weather information from openweathermap.org is used to display the current weather information. This is then periodically compared with weather information from the following hours.
Goals
- About Json.
 - Use weather data to create an audio notification when the weather changes.
 
Hardware & Software Needed
- Arduino Zero Board
 - Arduino WiFi Shield 101
 - Arduino IDE (online or offline).
 - ArduinoJson Library
 - Piezo
 - Jumper wires
 
The Circuit
The red wire of the piezo is connected to digital pin 8, and the black wire to ground. Optionally, the audio can be improved by using preloaded .wav files instead of the
tone() function, in which case the circuit from this audio player example can be substituted (with the addition of the WiFi Shield 101).
  
    
    
In the image above, the Arduino Zero board would be stacked below the WiFi Shield 101.
Installing Libraries
The ArduinoJson library can installed from Arduino IDE's library manager. To do this, open the Arduino IDE, go to Tools-> Manage Libraries.. There you can search ArduinoJson and install the library shown. The 'more info' link will take you to the GitHub page which includes all the documentation for the library. For a more detailed explanation on installing and importing libraries see this tutorial.
Parsing a Json
In this tutorial we use openweathermap.org to provide the Json information. An example of the raw information used can be found here, this is the API from openweathermap and the city can be changed in the URL.
Using the Json below as an example, we can see that it contains a lot of information, including the city, coordinates, rain, temperature, wind speeds, etc.
1{"city":{"id":3165524,"name":"Torino","coord":{"lon":7.68682,"lat":45.070492},"country":"IT","population":0,"sys":{"population":0}},"cod":"200","message":0.0066,"cnt":2,"list":[{"dt":1442404800,"main":{"temp":22.11,"temp_min":18.61,"temp_max":22.11,"pressure":989.45,"sea_level":1023.92,"grnd_level":989.45,"humidity":89,"temp_kf":3.5},"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"clouds":{"all":80},"wind":{"speed":1.89,"deg":17.5001},"rain":{"3h":0.095},"sys":{"pod":"d"},"dt_txt":"2015-09-16 12:00:00"},{"dt":1442415600,"main":{"temp":22.93,"temp_min":19.62,"temp_max":22.93,"pressure":988.09,"sea_level":1022.61,"grnd_level":988.09,"humidity":79,"temp_kf":3.3},"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"clouds":{"all":92},"wind":{"speed":2.01,"deg":29.502},"rain":{},"sys":{"pod":"d"},"dt_txt":"2015-09-16 15:00:00"}]}It is useful to view this information as a tree using an online Json formatter so that we can easily see which of the nested objects/arrays contains the information in which we are interested. The following image shows the tree view of the previous Json. It is easy to see that inside the root object 'JSON', there is an object called 'city' and an array called 'list' which contains two objects; '0' and '1'. the '0' array contains current weather information and '1' contains later weather information.
If we then open further the 'main' and 'weather' fields, we can see that main contains various information and that 'weather' contains a further object called [0]. The information accessed in this example is 'temp' and 'humidity' which are inside 'main', and 'description' which is inside '0' inside 'weather', all of which are found in both [0] and [1] from the 'list' array.
The part of the code that deals with parsing the information from the Json is seen in the following block. From the tree view, we can see that there is a root object which represents the entire Json and corresponds to the the 'root'
JsonObject in the code. From there, we can access the list array with 'root["list"]'. It can be seen that inside the list there are two objects; [0] and [1], which correspond to 'now' and 'later' JsonObjects. Inside each of the objects now and later, there is an object called main and and an array called weather , inside which there is an object called [0] which contains the description. We are interested in the information inside 'main' and 'weather -> [0]' for both now and later, specifically, temperature, humidity and description. Therefore we can use the following code to access this information and store it either as a String or as a float. We can also access the city name directly from the root:Note: Arduino Json library provides a syntax which allows you to navigate the tree starting from a JsonObject and searching the values by their labels.
1JsonArray& list = root["list"];2
3JsonObject& now = list[0];4
5JsonObject& later = list[1];6
7String city = root["city"]["name"];8float tempNow = now["main"]["temp"];9float humidityNow = now["main"]["humidity"];10
11String weatherNow = now["weather"][0]["description"];12
13float tempLater = later["main"]["temp"];14float humidityLater = later["main"]["humidity"];15
16String weatherLater = later["weather"][0]["description"];Code
The basic concept of this program is that it parses six fields from the Json; humidity, temperature and description for both now and later and compares them at an interval of 10 minutes (can be changed to a shorter period for testing). At the beginning of the sketch, you must manually enter the network name of your Wireless network, the password for this network and the city name and country code without spaces, for example: "NewYork,US".
1char ssid[] = "ssid";             //  your network SSID (name)2char pass[] = "password";         // your network password (use for WPA, or use as key for WEP)3int keyIndex = 0;                 // your network key Index number (needed only for WEP)4
5String nameOfCity = "Turin,IT";   // your city of interest here in format "city,countrycode"The melodies which will be played (for either a positive or negative weather change) are instantiated in the following block where the notes and note durations are defined for later:
1int posMelody[] = {330,415,494,659};     //E3,G#3,B3,E42int negMelody[] = {392,370,349,330};     //G3,F#3,F3,E33int noteDurations[] = {4, 4, 4, 8};      //Will correspond to note lengths 8th,8th,8th,4thA statement to check if 10 minutes have passed is executed inside the loop, and if so a http request is made.
1if (millis() - lastConnectionTime > postingInterval) {2
3    // note the time that the connection was made:4
5    lastConnectionTime = millis();6
7    httpRequest();8
9  }After that, there is a check to see whether there are incoming bytes available, which will only happen once every 10 minutes after the http request. Since it is known that a Json message is a nest of curly brackets and that for each open bracket there must be a close bracket, we can deduce that the message starts at the first curly bracket in the stream and ends when the number of open brackets - close brackets = 0. Therefore, the following code waits for the first curly bracket and then sets a variable
startJson to 1, indicating that the message has started, and increments a variable endResponse which decrements each time the incoming byte is a close bracket. Then, if startJson is true, i.e the message has started, the incoming byte is appended to the string text. If endResponse = 0 indicating that there were an equal number of close and open brackets, then the message is over, providing that it started (startJson = 1). When both these conditions are met, then the Json is ready to be parsed and the string is sent to the parseJson() function.1if (client.available()) {2
3    c = client.read();4
5    // json contains equal number of open and close curly brackets, therefore by counting6
7    // the open and close occurrences, we can determine when a json is completely received8
9    // endResponse == 0 means equal number of open and close curly brackets reached10
11    if (endResponse == 0 && startJson == true) {12
13      parseJson(text.c_str());  // parse c string text in parseJson function14
15      text = "";                // clear text string for the next time16
17      startJson = false;        // set startJson to false to indicate that a new message has not yet started18
19    }20
21    if (c == '{') {22
23      startJson = true;         // set startJson true to indicate json message has started24
25      endResponse++;26
27    }28
29    if (c == '}') {30
31      endResponse--;32
33    }34
35    if (startJson == true) {36
37      text += c;38
39    }40
41  }The function
printDiffFloat() compares the two values of the now and later floats and if there is a difference and if so, the difference is printed on the serial monitor. If there is no change, the return; exits the function and nothing is printed nor played.1void printDiffFloat(float now, float later, String parameter, String unit) {2
3  String change;4
5  if (now > later) {        //if parameter is higher now than later6
7    change = "drop from ";8
9  }10
11  else if (now < later) {   //else if parameter is higher later than now12
13    change = "rise from ";14
15  }16
17  else {                    //else there is no difference18
19    return;                 //exit function printDiffFloat20
21  }22
23  Serial.print("UPDATE: The " + parameter + "will " + change); //print change24
25  Serial.print(now);26
27  Serial.print(unit + " to ");28
29  Serial.print(later);30
31  Serial.println(unit + "!");32}The function
printDiffString() checks for keywords inside the now and later strings. If the index of a word such as "rain" is not found because it does not exist inside the string, then the value of the int is set to -1. We can then check to see if the word was not in the string of current data but does exist in the string for the future data (int != -1), and if so we can send a notification, either the positive or negative short melody depending on the change and the information of the change is printed in the serial monitor. Note that the search for clear is in a different statement than for words rain, snow and hail so that a positive notification is sounded instead of negative.1void printDiffString(String now, String later, String weatherType) {2
3  int indexNow = now.indexOf(weatherType);4
5  int indexLater = later.indexOf(weatherType);6
7  //for all types of weather except for clear skies, if the current weather does not contain the weather type and the later message does, send notification8
9  if (weatherType != "clear") {10
11    if (indexNow == -1 && indexLater != -1) {12
13      Serial.println("Oh no! It is going to " + weatherType + " later! Predicted " + later);14
15      for (int thisNote = 0; thisNote < 4; thisNote++) {16
17        int noteDuration = 1000 / noteDurations[thisNote];18
19        tone(8, negMelody[thisNote], noteDuration);      //play negative melody through piezo20
21      }22
23    }24
25  }26
27  //for clear skies, if the current weather does not contain the word clear and the later message does, send notification that it will be sunny later28
29  else {30
31    if (indexNow == -1 && indexLater != -1) {32
33      Serial.println("It is going to be sunny later! Predicted " + later);34
35      for (int thisNote = 0; thisNote < 4; thisNote++) {36
37        int noteDuration = 1000 / noteDurations[thisNote];38
39        tone(8, posMelody[thisNote], noteDuration);      //play positive melody through piezo40
41      }42
43    }44
45  }46}The full sketch can be seen below:
1/*2
3Weather Audio Notifier4
5Hardware Required:6
7* Arduino Zero Board8
9* Arduino WIFI Shield 10110
11
12* Piezo13
14Software Required:15
16* ArduinoJson Library17
18 created Sept 201519
20 by Helena Bisby <support@arduino.cc>21
22This example code is in the public domain23
24http://arduino.cc/en/Tutorial/WeatherAudioNotifier25
26
27
28*/29
30#include <SPI.h>31#include <WiFi101.h>32#include <ArduinoJson.h>33
34#define JSON_BUFF_DIMENSION 250035#include "arduino_secrets.h"36///////please enter your sensitive data in the Secret tab/arduino_secrets.h37char ssid[] = SECRET_SSID;        // your network SSID (name)38char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)39int keyIndex = 0;            // your network key Index number (needed only for WEP)40
41String nameOfCity = "Turin,IT";   // your city of interest here in format "city,countrycode"42
43String text;44int endResponse = 0;45boolean startJson = false;46int posMelody[] = {330, 415, 494, 659};  //E3,G#3,B3,E447int negMelody[] = {392, 370, 349, 330};  //G3,F#3,F3,E348int noteDurations[] = {4, 4, 4, 8};      //Will correspond to note lengths 8th,8th,8th,4th49int status = WL_IDLE_STATUS;50
51const char server[] = "api.openweathermap.org";    // name address for openweathermap (using DNS)52
53WiFiClient client;54unsigned long lastConnectionTime = 10 * 60 * 1000;     // last time you connected to the server, in milliseconds55
56const unsigned long postingInterval = 10 * 60 * 1000;  // posting interval of 10 minutes  (10L * 1000L; 10 seconds delay for testing)57
58void setup() {59
60  //Initialize serial and wait for port to open:61
62  Serial.begin(9600);63
64  text.reserve(JSON_BUFF_DIMENSION);65
66  // check for the presence of the shield:67
68  if (WiFi.status() == WL_NO_SHIELD) {69
70    Serial.println("WiFi shield not present");71
72    // don't continue:73
74    while (true);75
76  }77
78  // attempt to connect to Wifi network:79
80  while ( status != WL_CONNECTED) {81
82    Serial.print("Attempting to connect to SSID: ");83
84    Serial.println(ssid);85
86    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:87
88    status = WiFi.begin(ssid, pass);89
90    // wait 10 seconds for connection:91
92    delay(10000);93
94  }95
96  // you're connected now, so print out the status:97
98  printWifiStatus();99}100
101void loop() {102
103  // if ten minutes have passed since your last connection,104
105  // then connect again and send data:106
107  if (millis() - lastConnectionTime > postingInterval) {108
109    // note the time that the connection was made:110
111    lastConnectionTime = millis();112
113    httpRequest();114
115  }116
117  char c = 0;118
119  if (client.available()) {120
121    c = client.read();122
123    // json contains equal number of open and close curly brackets, therefore by counting124
125    // the open and close occurrences, we can determine when a json is completely received126
127
128
129    // endResponse == 0 means equal number of open and close curly brackets reached130
131    if (endResponse == 0 && startJson == true) {132
133      parseJson(text.c_str());  // parse c string text in parseJson function134
135      text = "";                // clear text string for the next time136
137      startJson = false;        // set startJson to false to indicate that a new message has not yet started138
139    }140
141    if (c == '{') {142
143      startJson = true;         // set startJson true to indicate json message has started144
145      endResponse++;146
147    }148
149    if (c == '}') {150
151      endResponse--;152
153    }154
155    if (startJson == true) {156
157      text += c;158
159    }160
161  }162}163void parseJson(const char * jsonString) {164
165  StaticJsonBuffer<4000> jsonBuffer;166
167  // FIND FIELDS IN JSON TREE168
169  JsonObject& root = jsonBuffer.parseObject(jsonString);170
171  if (!root.success()) {172
173    Serial.println("parseObject() failed");174
175    return;176
177  }178
179  JsonArray& list = root["list"];180
181  JsonObject& now = list[0];182
183  JsonObject& later = list[1];184
185  String city = root["city"]["name"];186
187  float tempNow = now["main"]["temp"];188
189  float humidityNow = now["main"]["humidity"];190
191  String weatherNow = now["weather"][0]["description"];192
193  float tempLater = later["main"]["temp"];194
195  float humidityLater = later["main"]["humidity"];196
197  String weatherLater = later["weather"][0]["description"];198
199  printDiffFloat(tempNow, tempLater, "temperature", "*C");200
201  printDiffString(weatherNow, weatherLater, "rain");202
203  printDiffString(weatherNow, weatherLater, "snow");204
205  printDiffString(weatherNow, weatherLater, "hail");206
207  printDiffString(weatherNow, weatherLater, "clear");208
209  printDiffFloat(humidityNow, humidityLater, "humidity", "%");210
211  Serial.println();212
213}214
215// this method makes a HTTP connection to the server:216void httpRequest() {217
218  // close any connection before send a new request.219
220  // This will free the socket on the WiFi shield221
222  client.stop();223
224  // if there's a successful connection:225
226  if (client.connect(server, 80)) {227
228    // Serial.println("connecting...");229
230    // send the HTTP PUT request:231
232    client.println("GET /data/2.5/forecast?q=" + nameOfCity + "&mode=json&units=metric&cnt=2 HTTP/1.1");233
234    client.println("Host: api.openweathermap.org");235
236    client.println("User-Agent: ArduinoWiFi/1.1");237
238    client.println("Connection: close");239
240    client.println();241
242  }243
244  else {245
246    // if you couldn't make a connection:247
248    Serial.println("connection failed");249
250  }251}252
253void printDiffString(String now, String later, String weatherType) {254
255  int indexNow = now.indexOf(weatherType);256
257  int indexLater = later.indexOf(weatherType);258
259  // for all types of weather except for clear skies, if the current weather does not contain the weather type and the later message does, send notification260
261  if (weatherType != "clear") {262
263    if (indexNow == -1 && indexLater != -1) {264
265      Serial.println("Oh no! It is going to " + weatherType + " later! Predicted " + later);266
267      for (int thisNote = 0; thisNote < 4; thisNote++) {268
269        int noteDuration = 1000 / noteDurations[thisNote];270
271        tone(8, negMelody[thisNote], noteDuration);      // play negative melody through piezo272
273      }274
275    }276
277  }278
279  // for clear skies, if the current weather does not contain the word clear and the later message does, send notification that it will be sunny later280
281  else {282
283    if (indexNow == -1 && indexLater != -1) {284
285      Serial.println("It is going to be sunny later! Predicted " + later);286
287      for (int thisNote = 0; thisNote < 4; thisNote++) {288
289        int noteDuration = 1000 / noteDurations[thisNote];290
291        tone(8, posMelody[thisNote], noteDuration);      // play positive melody through piezo292
293      }294
295    }296
297  }298}299
300void printDiffFloat(float now, float later, String parameter, String unit) {301
302  String change;303
304  if (now > later) {305
306    change = "drop from ";307
308  }309
310  else if (now < later) {311
312    change = "rise from ";313
314  }315
316  else {317
318    return;319
320  }321
322  Serial.print("UPDATE: The " + parameter + " will " + change);323
324  Serial.print(now);325
326  Serial.print(unit + " to ");327
328  Serial.print(later);329
330  Serial.println(unit + "!");331}332
333void printWifiStatus() {334
335  // print the SSID of the network you're attached to:336
337  Serial.print("SSID: ");338
339  Serial.println(WiFi.SSID());340
341  // print your WiFi shield's IP address:342
343  IPAddress ip = WiFi.localIP();344
345  Serial.print("IP Address: ");346
347  Serial.println(ip);348
349  // print the received signal strength:350
351  long rssi = WiFi.RSSI();352
353  Serial.print("signal strength (RSSI):");354
355  Serial.print(rssi);356
357  Serial.println(" dBm");358}Testing It Out
After you have uploaded the code, if there is a change in weather conditions of the selected city of interest defined in
String nameOfCity = "cityname,countrycode"; an update of the changes is written to the serial monitor and the piezo will generate an audio notification depending on the result of the weather change.Troubleshoot
If the code is not working, there are some common issues we can troubleshoot:
- You have not installed the ArduinoJson library.
 - You have entered the incorrect 
orssid
of your network.pass 
Conclusion
In this example, we have learned how to create a weather notification project that notifies you when weather conditions change! All of this is possible because of Json and the ArduinoJson library. Now that you have finished this tutorial, you can start to use Json for other cool applications and projects.
Suggested changes
The content on docs.arduino.cc is facilitated through a public GitHub repository. You can read more on how to contribute in the contribution policy.
License
The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.