.

ESP8266 reads Google Calendar

own workWith Google holding much of the treasure trove of our data, it would be nice to be able to dig into that from an embedded platform such as the ESP8266.

This is not exactly the first approach of pulling data from Google Calendar, but it may be the first that directly connects to Google. Other approaches rely on intermediary systems such as Temboo [1], a raspberry pi [2], or Edison [3].  The only other approach that connected directly to Google uses the iCal interface [4].  The latter approach requires parsing through lots of data.

This program calls the Google Script API, that in returns the relevant calendar event.

Google Apps Script reading your Google Calendar

We use a Google script pull events from your Google calendar.  Security relies on a hard to guess URL, but could be tightened using OAuth.

Go to Google Apps Scripts and create the script listed below.  Substitute 'your.email@gmail.com' with yours.  The script listed below uses the CalendarApp class to retrieve your calendar for the next week.  The script can be found under examples in the GitHub repository.

function doGet(e) {
  var cal = CalendarApp.getCalendarById('your.email@gmail.com');
  if (cal == undefined) {
    return ContentService.createTextOutput('no access to calendar');
  }

  const now = new Date();
  var start = new Date(); start.setHours(0, 0, 0); // start at midnight
  const oneday = 24*3600000; // [msec]
  const stop = new Date(start.getTime() + 7 * oneday);
 
  var events = cal.getEvents(start, stop);
  var str = '';
  for (var ii = 0; ii < events.length; ii++) {
    var event=events[ii]; 
    var myStatus = event.getMyStatus(); 
    switch(myStatus) {
      case CalendarApp.GuestStatus.OWNER:
      case CalendarApp.GuestStatus.YES:
      case CalendarApp.GuestStatus.MAYBE:
        str += event.getStartTime() + '\t' +
               event.isAllDayEvent() + '\t' +
               event.getPopupReminders()[0] + '\t' +
               event.getTitle() +'\n';
        break;
      default:
        break;
    }
  }
  return ContentService.createTextOutput(str);
}

To execute this script we need to deploy it as a web app, and provide the permissions.

  1. Publish > Deploy as web app.  Project version = new.  Execute the app as me.  Access = anyone, even anonymous. OK.
  2. Copy the script URL that includes the script id, we’ll need it later.
  3. When you run the script for the first time (Run > doGet), it will ask you to grand access to your calendar.

Paste the script URL in a new tab of your browser.  It should now return your calendar events for the next week.  That means your halfway there.

The next section describes how to execute this script from the ESP8266.

ESP8266 executing your Google Apps Script

The script URL will refer to “https://script.google.com”.  This implies that the ESP8266 needs to do HTTP protocol over Transport Layer Security (TLS) . The WiFiClientSecure library by Ivan Grokhotkov, implements support for TLS 1.0/1.1.

For security reasons, content returned by the Google Content service isn’t served from the host “script.google.com”, but instead redirected to a one-time URL at “script.googleusercontent.com”.  This means that the ESP8266 must be able to follow redirects.

To access the script URL, we the ESP8266 needs to send the text below over TLS

GET /macros/s/<script-id>/exec HTTP/1.1
Host: script.google.com
User-Agent: ESP8266
Connection: close  

Google will reply with HTTP error code 302 Moved Temporarily, and the response header will include the real location

HTTP/1.1 302 Moved Temporarily
Location: https://script.googleusercontent.com/macros/<random url>

The ESP8266 then needs to make a second request over TLS to this location

GET /macros/<random url> HTTP/1.1
Host: script.googleusercontent.com
User-Agent: ESP8266
Connection: close 

The response body will contain the output from the script.

This is implemented in the WiFiClientSecureRedirect similar to what was suggested by Sujay Phadke.  The method proposed here uses an asynchronous  approach.  This allows other tasks to proceed while calendar events are retrieved, a process that can take up to about 10 seconds.

Synchronous example

Even though the class is supports asynchronous access, it can be used in synchronous programs as well as shown in the simplified code below.

void loop() {
	WiFiClientSecureRedirect client;
	do {
		if (client.connect(dstHost, dstPort) != 1) {  // send connect request
			break;
		}
		while (!client.connected()) {  // wait until connected
			client.tick();
		}
		if (client.request(dstPath, dstHost, 2000, dstFingerprint, redirFingerprint) != 0) { // send alarm request
			break;
		}
		while (!client.response()) {  // wait until host responded
			client.tick();
		}
		while (client.available()) {  // read and print until end of data
			String line = client.readStringUntil('\n');
			Serial.println(line);
		}
		client.stop();
	} while (0);
}

Asynchronous example

To take advantage of the asynchronous nature of this class, your loop() function needs to support this.  For an example, take a look the GoogleCalendar.ino example.  The asynchronous loop() code is listed below.

void loop() {
	client.tick();
	state_t prevState;
	do {
		prevState = state;
		uint32_t const timeout = eventTimeouts[state];
		if (timeout && millis() - beginWait >= timeout) {
			DPRINT(__func__); DPRINT(": timeout in state "); DPRINTLN(state);
			client.stop();
			state = CONNECT;
			beginWait = millis();
		}

		bool error = false;
		switch (state) {
			case CONNECT:
				if (!(error = connect())) {
					state = WAIT4CONNECT;
				}
				break;
			case WAIT4CONNECT:
				if (client.connected()) {
					if (!(error = sendAlarmRequest())) {
						state = WAIT4RESPONSE;
					}
				}
				break;
			case WAIT4RESPONSE:
				if (client.response()) {
					if (!(error = receiveAlarmResponse())) {
						state = CONNECT;
					}
				}
				break;
			case COUNT:
				break; // to please the compiler
		}
		if (error) {
			DPRINT(__func__); DPRINT(": error in state "); DPRINTLN(state);
			state = CONNECT;
			client.stop();
		}
		if (state != prevState) {
			beginWait = millis();
		}

	} while (state != prevState && eventTimeouts[state]);
}

Enjoy!

Coert Vonk

Coert Vonk

Independent Firmware Engineer at Los Altos, CA
Welcome to the things that I couldn’t find.This blog shares some of the notes that I took while deep diving into various fields.Many such endeavors were triggered by curious inquiries from students. Even though the notes often cover a broader area, the key goal is to help the them adopt, flourish and inspire them to invent new technology.
Coert Vonk

Latest posts by Coert Vonk (see all)

2 comments to ESP8266 reads Google Calendar

  • Hi Mr. Vonk
    Thank you for sharing your code.
    We are making a small smart farm and want to control pump and valves with google calendar and Arduino.
    Your codes help us a lot.

  • Hello all,
    Thanks for the mention Coert. I am the author of the HTTPSRedirect library, which can be used with ESP8266 to directly connect to Google services. In case you have any questions, please check out the github page and open a new “issue” there. I have also launched version 2 of the library. Please see the readme for the details and use this only in future projects. Thanks.

    github: http://bit.ly/1SopEug

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

  

  

  

Protected with IP Blacklist CloudIP Blacklist Cloud