Wifi101 Google Calendar Example

This example shows you how to make repeated HTTP requests using a WiFi shield 101.

Google Calendar Actions Planner

This example shows you how to make repeated HTTP requests using a WiFi shield 101. It connects to a given Google Calendar. The content of the page is downloaded and parsed in order to extract commands from the event title and planning actions to be executed at a given time.

This example is written for a network using WPA encryption. For WEP or WPA, change the Wifi101.begin() call accordingly.

I this particular example if an event called LED1 is added to the specified calendar, the built-in led on pin 13 will be on for the from-to time specified in the event.

Hardware Required

  • Arduino WiFi Shield 101

  • Arduino Zero board

Circuit

The circuit simply consist of an Arduino WiFi shield stacked in top of an Arduino Zero Board.

ArduinoWiFi101 front 450

Code

First of all to have a working code you need to follow these steps:

Create a Calendar

To create a Calendar account, go to Google Calendar and log in using your Google account email and password. (If you don't have a Google account, click the Create an account now link.)

Find Your Calendar Feed URL

There are a variety of different URLs you can use to request feeds from a calendar, but the simplest feed URL to use is that of the calendar's read-only so-called magic cookie private feed, because that URL doesn't require authentication.

To find your calendar's magic cookie feed URL:

  • In the list of calendars on the left side of the page, find the calendar you want to interact with. You can create a new calendar for this purpose if you want to.

  • Click the arrow button to the right of the calendar. Select "Calendar settings" from the drop-down menu.

calendarSettings
  • Scroll down to the Private Address section. There are two buttons: select the XML button. The feed URL appears.
calendarPrivateURL
  • Copy the URL. This is the URL of your calendar's read-only magic cookie private feed.

The feed URL has the following form:

http://www.google.com/calendar/feeds/userID/private-magicCookie/basic

  • Copy and paste the part of the URL /calendar/feeds/userID/private-magicCookie/basic in the magicCookie variable of the sketch.

Connect to Your Wi-Fi Network

  • Write in the ssid variable your network name.

  • Write in the pass variable your network password.

Write months name in your language

Please note that the calendar works downloading data in your language, so for the date parsing the name of the months must be written in your language in the variable months in the following way:

1String months[12] = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"}; // put the months name in your language here please

XML Data Received

If you look at the data received using the magic cookie link (using for example Postman or a cURL command) you obtain something like:

XMLdata

From these data we can extract the following information:

  • 1) At maximum 25 events will be showed. This is not a modifiable parameter. 25 events centred along the today date will be downloaded in any case both in the past and in the future;

  • 2) The name of the downloaded calendar (not used in the code);

  • 3) The name of the event: each event has as boundary the string

    <title type='html'>
    on its left and
    </title>
    on its right. These two strings are used in the event extraction parsing;

  • 4) The time in which the event is valid: each event that lasts a single day has a boundary

    <summary type='html'>
    xxx: on its left where xxx it's the word When in the language of the calendar and the string &.... on its right. The character : and & are so used to extract the event duration;

  • 5) The name of the event's author (not used in the code).

Code Explanation

The sketch is based on the Client Repeating and WiFiUdpNtpClient example. In particular the http request is repeated with a repeating time specified by two variables:

  • int refreshTime that specifies the time quantity.

  • char refreshType that specifies if the refresh must be executed every refreshTime hours (refreshType = 'h' ) or every refreshTime minutes (refreshType = 'm' );

For what concern the WiFiUdpNtpClient example, it is used in order to get the UNIX epoch and using the time.h and RTCZero library, configure the built-in RTC in the Arduino Zero board.

readLinuxEpochUsingNetworkTimeProtocol: This functions asks for the UNIX time and returns the obtained epoch.

setRealTimeClock(uint32_t epoch): This function uses the parameter epoch to set the Arduino Zero's RTC. In order to set the hours in the right way the variable const int GMT must be set according to your time zone.

checkCalendarRefresh(int howMany, char when): This functions polls the Arduino Zero's RTC and checks if it's time to make the http request needed to refresh the downloaded calendar information according to the parameters.

extractEvents(): This function is used to remove all the useless information from the downloaded XML content. Every time an event content is detected the function interpretEvent(String event) is called.

interpretEvent(String event): This function is used event title and the event duration in order to understand if the event is a command and if has to be executed calling the function 'extractHowLong(String whenFromTo, String command).

1/*
2
3 Google Calendar Actions Planner
4
5 This sketch connects to Gogole Calendar, makes a HTTP request and downloads the day events.Comparing the actual time with the one of the events actions can be programmed.
6
7 using an Arduino Wifi shield 101 and Arduino Zero.
8
9 created 08 Sept 2015
10
11 by Arturo Guadalupi <a.guadalupi@arduino.cc>
12
13 https://www.arduino.cc/en/Tutorial/GoogleCalendarWiFi101
14
15 This code is in the public domain.
16
17 Information about google APIs and hnow to make HTTP requests can be found at : http://www.udel.edu/CIS/software/dist/google/calendar/java.client/gdata/doc/calendar.html#Feeds
18
19*/
20
21#include <SPI.h>
22#include <WiFi101.h>
23#include <WiFiUdp.h>
24#include <RTCZero.h>
25#include <time.h>
26
27// Create an rtc object
28
29RTCZero rtc;
30
31#include "arduino_secrets.h"
32///////please enter your sensitive data in the Secret tab/arduino_secrets.h
33char ssid[] = SECRET_SSID; // your network SSID (name)
34char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)
35int keyIndex = 0; // your network key Index number (needed only for WEP)
36
37int status = WL_IDLE_STATUS;
38
39// Initialize the Wifi client library
40
41WiFiClient client;
42
43// server address:
44char server[] = "www.google.com";
45
46// google magic cookie: replace your calendar private address here
47char magicCookie[] = "magicCookie";
48
49// Used for NTP
50unsigned int localPort = 2390; // local port to listen for UDP packets
51
52IPAddress timeServer(129, 6, 15, 28); // time.nist.gov NTP server
53
54const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
55byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
56
57WiFiUDP Udp; // A UDP instance to let us send and receive packets over UDP
58
59// Data used for time
60
61const int refreshTime = 1;
62
63String months[12] = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"}; // put the months name in your language here please
64
65const char refreshType = 'm'; // refresh every refreshTime minutes
66//const char refreshType = 'h'; // refresh every refreshTime hours
67
68const int GMT = 2; //change this to adapt it to your time zone
69int lastHours, lastMinutes, lastSeconds;
70
71// Commands that can be interpreted*/
72char cmd1[] = "LED1";
73
74// string used for the commands parsing
75
76String clientBufferString = "";
77
78void setup() {
79
80 // Initialize serial and wait for port to open:
81
82 Serial.begin(115200);
83
84 while (!Serial);// wait for serial port to connect. Needed for Leonardo only
85
86 // check for the presence of the shield:
87
88 if (WiFi.status() == WL_NO_SHIELD) {
89
90 Serial.println("WiFi shield not present");
91
92 // don't continue:
93
94 while (true);
95
96 }
97
98 // attempt to connect to Wifi network:
99
100 while ( status != WL_CONNECTED) {
101
102 Serial.print("Attempting to connect to SSID: ");
103
104 Serial.println(ssid);
105
106 // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
107
108 status = WiFi.begin(ssid, pass);
109
110 // wait 10 seconds for connection:
111
112 delay(10000);
113
114 }
115
116 // you're connected now, so print out the status:
117
118 printWifiStatus();
119
120 rtc.begin(); // start the RTC in 24 hours mode
121
122 unsigned long epoch = readLinuxEpochUsingNetworkTimeProtocol();
123
124 setRealTimeClock(epoch);
125
126 // print the current time reading values from the RTC
127
128 printTime(rtc.getHours(), rtc.getMinutes(), rtc.getSeconds());
129
130 // ask for data to google calendar
131
132 httpRequest();
133}
134
135void loop() {
136
137 int i = 0;
138
139 bool start = false;
140
141 clientBufferString = "";
142
143 checkCalendarRefresh(refreshTime, refreshType);
144
145 if (client.available())
146
147 {
148
149 while (client.available())
150
151 {
152
153 char c = client.read();
154
155 if (c == '<') //useful data starts from '<'
156
157 start = true;
158
159 if (start && isPrintable(c)) // if useful data start and significant data is received
160
161 clientBufferString += c; // add it to the buffer
162
163 }
164
165 extractEvents();
166
167 }
168}
169
170unsigned long readLinuxEpochUsingNetworkTimeProtocol()
171{
172
173 unsigned long epoch;
174
175 Udp.begin(localPort);
176
177 sendNTPpacket(timeServer); // send an NTP packet to a time server
178
179 // wait to see if a reply is available
180
181 delay(1000);
182
183 if ( Udp.parsePacket() ) {
184
185 Serial.println("NTP time received");
186
187 // We've received a packet, read the data from it
188
189 Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
190
191 //the timestamp starts at byte 40 of the received packet and is four bytes,
192
193 // or two words, long. First, esxtract the two words:
194
195 unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
196
197 unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
198
199 // combine the four bytes (two words) into a long integer
200
201 // this is NTP time (seconds since Jan 1 1900):
202
203 unsigned long secsSince1900 = highWord << 16 | lowWord;
204
205 // now convert NTP time into everyday time:
206
207 // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
208
209 const unsigned long seventyYears = 2208988800UL;
210
211 // subtract seventy years:
212
213 epoch = secsSince1900 - seventyYears;
214
215 }
216
217 Udp.stop();
218
219 return epoch;
220}
221
222// this function computes the current date and time to set the RTC using the time.h library
223void setRealTimeClock(uint32_t epoch)
224{
225
226 time_t rawtime;
227
228 struct tm * timeinfo;
229
230 rawtime = (time_t) epoch;
231
232 timeinfo = localtime (&rawtime);
233
234 Serial.println ("Current local time and date");
235
236 Serial.print(asctime(timeinfo));
237
238 rtc.setSeconds(timeinfo->tm_sec);
239
240 rtc.setMinutes(timeinfo->tm_min);
241
242 rtc.setHours((timeinfo->tm_hour) + GMT);
243
244 rtc.setDay(timeinfo->tm_mday);
245
246 rtc.setMonth((timeinfo->tm_mon) + 1); //tm_mon months since January - [ 0 to 11 ]
247
248 rtc.setYear((timeinfo->tm_year) - 100); //tm_year years since 1900 and format is yy
249}
250
251// this function checks if the calendar has to be refreshed
252void checkCalendarRefresh(int howMany, char when)
253{
254
255 if (when == 'h')
256
257 {
258
259 if (rtc.getHours() >= (lastHours + howMany))
260
261 refresh();
262
263 }
264
265 else if (when == 'm')
266
267 {
268
269 if (rtc.getMinutes() >= (lastMinutes + howMany) || (lastMinutes + howMany >= 60))
270
271 {
272
273 refresh();
274
275 }
276
277 delay(1000);
278
279 // print the current time
280
281 printTime(rtc.getHours(), rtc.getMinutes(), rtc.getSeconds());
282
283 }
284}
285
286// this function refreshes the calendar
287void refresh()
288{
289
290 lastHours = rtc.getHours();
291
292 lastMinutes = rtc.getMinutes();
293
294 lastSeconds = rtc.getSeconds();
295
296 printTime(lastHours, lastMinutes, lastSeconds);
297
298 httpRequest();
299}
300
301/* this function makes a HTTP connection to the server: */
302void httpRequest()
303{
304
305 // close any connection before send a new request.
306
307 // This will free the socket on the WiFi shield
308
309 client.stop();
310
311 // if there's a successful connection:
312
313 if (client.connect(server, 80)) {
314
315 Serial.println("connecting...");
316
317 // send the HTTP PUT request:
318
319 client.print("GET ");
320
321 client.print(magicCookie);
322
323 client.println(" HTTP/1.0");
324
325 client.println("Connection: close");
326
327 client.println();
328
329 }
330
331 else {
332
333 // if you couldn't make a connection:
334
335 Serial.println("connection failed");
336
337 }
338}
339
340// this function is used to parse the different events
341void extractEvents()
342{
343
344 // first consider all the available events and trim them
345
346 unsigned int index1 = clientBufferString.indexOf("<title type='html'>");
347
348 unsigned index2 = clientBufferString.lastIndexOf(";");
349
350 clientBufferString = clientBufferString.substring(index1, index2); //remove uneseful information
351
352 int i = 1;
353
354 while (clientBufferString != "")
355
356 {
357
358 index1 = clientBufferString.indexOf("<title type='html'>");
359
360 if (i != 1) //if is not the first event remove other useless data
361
362 {
363
364 clientBufferString.remove(0, index1);
365
366 index1 = clientBufferString.indexOf("<title type='html'>");
367
368 }
369
370 index2 = clientBufferString.indexOf("&");
371
372 String event = clientBufferString.substring(index1, index2);
373
374 if (event != "")
375
376 {
377
378 /*Serial.print("EVENT #");
379
380 Serial.print(i);
381
382 Serial.print(": ");
383
384 Serial.println(event);*/ // uncomment to debug
385
386 interpretEvent(event);
387
388 i++;
389
390 }
391
392 clientBufferString.remove(0, event.length());
393
394 }
395
396 /*Serial.println("TOTAL # EVENTS:");
397
398 Serial.println(i);*/ // uncomment to debug
399}
400
401// this function is used to interpret the commands in the event title
402void interpretEvent(String event)
403{
404
405 int index1 = event.indexOf("<title type='html'>") + strlen("<title type='html'>");
406
407 int index2 = event.indexOf("</title>");
408
409 String title = event.substring(index1, index2);
410
411 index2 = event.indexOf(":");// remove useless data
412
413 event.remove(0, index2 + strlen(": ddd "));// for the next step: after the when there is ": ddd " where "ddd" indicate 3 letter of the day of the weeek
414
415 Serial.println("Title:");
416
417 Serial.println(title);
418
419 Serial.println();
420
421 Serial.println("When and from to:");
422
423 Serial.println(event);
424
425 Serial.println();
426
427 extractHowLong(event, title);
428}
429
430// this function executes the command from to
431void extractHowLong(String whenFromTo, String command)
432{
433
434 char duratio[whenFromTo.length()];
435
436 sprintf(duratio, whenFromTo.c_str());
437
438 int day, month, year, fromHours, fromMinutes, toHours, toMinutes;
439
440 char monthString[4];
441
442 sscanf(duratio, "%d %s %d %d:%d%*s%d:%d", &day, monthString, &year, &fromHours, &fromMinutes, &toHours, &toMinutes);
443
444 month = monthToInt(monthString);
445
446 //Serial.println(day);
447
448 //Serial.println(monthString);
449
450 //Serial.println(year);
451
452 //Serial.println(fromHours);
453
454 //Serial.println(fromMinutes);
455
456 //Serial.println(toHours);
457
458 //Serial.println(toMinutes); //uncomment to debug
459
460 if (rtc.getYear() == year - 2000) // if the year is correct
461
462 {
463
464 /*Serial.print("Valid year: ");
465
466 Serial.println(year - 2000);*/
467
468 if (rtc.getMonth() == month) // if the month is correct
469
470 {
471
472 /*Serial.print("Valid month: ");
473
474 Serial.println(year - 2000);*/
475
476 if (rtc.getDay() == day) // if the day is correct
477
478 {
479
480 /*Serial.print("Valid day: ");
481
482 Serial.println(day);*/
483
484 if (((rtc.getHours() >= fromHours) && (rtc.getMinutes() >= fromMinutes)) || ((toHours >= rtc.getHours()) && (toMinutes > rtc.getMinutes()))) // if the execution time is valid
485
486 {
487
488 decodeCommand(command, true);
489
490 }
491
492 else
493
494 {
495
496 decodeCommand(command, false);
497
498 }
499
500 }
501
502 }
503
504 }
505}
506
507// this function converts the month string in the corresponding number
508int monthToInt(String month)
509{
510
511 for (int i = 0; i < 12; i++)
512
513 if (month == months[i])
514
515 return i + 1;
516}
517
518// this function dedoded the different commands
519void decodeCommand(String command, bool validTime)
520{
521
522 if (command == cmd1)
523
524 {
525
526 Serial.println();
527
528 Serial.print("Known command detected: ");
529
530 Serial.println(cmd1);
531
532 if (validTime)
533
534 {
535
536 Serial.println();
537
538 Serial.println("Valid time");
539
540 pinMode(13, OUTPUT);
541
542 digitalWrite(13, HIGH);
543
544 Serial.println();
545
546 }
547
548 else
549
550 {
551
552 Serial.println();
553
554 Serial.println("Invalid time");
555
556 pinMode(13, INPUT);
557
558 }
559
560 }
561}
562
563// this function prints the current time
564void printTime(int hours, int minutes, int seconds)
565{
566
567 // print the hour, minute and second:
568
569 Serial.print(rtc.getDay());
570
571 Serial.print("/");
572
573 Serial.print(rtc.getMonth());
574
575 Serial.print("/");
576
577 Serial.print(rtc.getYear());
578
579 Serial.print(" ");
580
581 Serial.print(hours); // print the hour
582
583 Serial.print(':');
584
585 if (minutes < 10 ) {
586
587 // In the first 10 minutes of each hour, we'll want a leading '0'
588
589 Serial.print('0');
590
591 }
592
593 Serial.print(minutes); // print the minute
594
595 Serial.print(':');
596
597 if (seconds < 10 ) {
598
599 // In the first 10 seconds of each minute, we'll want a leading '0'
600
601 Serial.print('0');
602
603 }
604
605 Serial.println(seconds); // print the second
606}
607// send an NTP request to the time server at the given address
608unsigned long sendNTPpacket(IPAddress & address)
609{
610
611 //Serial.println("1");
612
613 // set all bytes in the buffer to 0
614
615 memset(packetBuffer, 0, NTP_PACKET_SIZE);
616
617 // Initialize values needed to form NTP request
618
619 // (see URL above for details on the packets)
620
621 //Serial.println("2");
622
623 packetBuffer[0] = 0b11100011; // LI, Version, Mode
624
625 packetBuffer[1] = 0; // Stratum, or type of clock
626
627 packetBuffer[2] = 6; // Polling Interval
628
629 packetBuffer[3] = 0xEC; // Peer Clock Precision
630
631 // 8 bytes of zero for Root Delay & Root Dispersion
632
633 packetBuffer[12] = 49;
634
635 packetBuffer[13] = 0x4E;
636
637 packetBuffer[14] = 49;
638
639 packetBuffer[15] = 52;
640
641 //Serial.println("3");
642
643 // all NTP fields have been given values, now
644
645 // you can send a packet requesting a timestamp:
646
647 Udp.beginPacket(address, 123); //NTP requests are to port 123
648
649 Udp.write(packetBuffer, NTP_PACKET_SIZE);
650
651 Udp.endPacket();
652}
653
654void printWifiStatus() {
655
656 // print the SSID of the network you're attached to:
657
658 Serial.print("SSID: ");
659
660 Serial.println(WiFi.SSID());
661
662 // print your WiFi shield's IP address:
663
664 IPAddress ip = WiFi.localIP();
665
666 Serial.print("IP Address: ");
667
668 Serial.println(ip);
669
670 // print the received signal strength:
671
672 long rssi = WiFi.RSSI();
673
674 Serial.print("signal strength (RSSI):");
675
676 Serial.print(rssi);
677
678 Serial.println(" dBm");
679}

Suggest changes

The content on docs.arduino.cc is facilitated through a public GitHub repository. If you see anything wrong, you can edit this page here.

License

The Arduino documentation is licensed under the Creative Commons Attribution-Share Alike 4.0 license.