Brian Wente
Published © GPL3+

Countdown Calendars

Display the number of days until the next event from your Google hosted calendars.

EasyFull instructions provided1 hour2,062
Countdown Calendars

Things used in this project

Hardware components

NodeMCU ESP8266 Breakout Board
NodeMCU ESP8266 Breakout Board
Close enough to the one I used... Makerfocus 2pcs ESP8266 Module ESP-12E from Amazon
×1
I2c OLED Module 128X64
×1
Solderless Breadboard Half Size
Solderless Breadboard Half Size
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Software apps and online services

Google Apps Script - Calendars

Story

Read more

Schematics

Wiring

super simple wiring layout
Oled for esp8266 bb jea7mtdypd

Code

getCalendarEvent.gs

JavaScript
Google script used to prepare data for arduino.
function doGet(e) {
  
  // name of you calendar
  var cal = CalendarApp.getCalendarsByName('Countdown')[0];

  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() + 365 * oneday);
  
  var events = cal.getEvents(start, stop);
  var str = '';

    var event=events[0];
    var startDate=event.getStartTime();
  
  var daysTil = Math.ceil(((startDate - now) / oneday));

  //Logger.log(daysTil);

  str += event.getTitle() + '\t' + daysTil +'\n';

  return ContentService.createTextOutput(str);
}

getNewMoon.gs

JavaScript
Goole script similar to the get event script.
function doGet(e) {
  
  var cal = CalendarApp.getCalendarsByName('Phases of the Moon')[0];

  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() + 30 * oneday);
    
  var events = cal.getEvents(start, stop, {search: 'Full moon'});
  var str = '';

    var event=events[0];
    var startDate=event.getStartTime();
  
  var daysTil = Math.ceil(((startDate - now) / oneday));
  var moonStart = event.getTitle().slice(10);

  // Logger.log(daysTil);
  // Logger.log(moonStart);
  

  str += moonStart + '\t' + daysTil +'\n';

  return ContentService.createTextOutput(str);
}

GetCalendars

Arduino
kinda of a mess, needs a bit of optimization
#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

U8G2_SSD1306_128X64_NONAME_1_SW_I2C u8g2(U8G2_R0, D2, D1, U8X8_PIN_NONE);   // All Boards without Reset of the Display


#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino

//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>          //https://github.com/tzapu/WiFiManager

#include "HTTPSRedirect.h"

//Connection Settings
const char* host = "script.google.com";
const char* googleRedirHost = "script.googleusercontent.com";
const int httpsPort = 443;

const String delimeter = "|";
String eventTitle = "";
String eventCount = "";
String eventTitle2 = "";
String eventCount2 = "";
String eventTime2 = "";

//


static const unsigned char fullmoon_xbm[] U8X8_PROGMEM = {
  0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x80, 0x83, 0x70, 0x00, 0x00, 
  0x00, 0x60, 0x81, 0xC0, 0x01, 0x00, 0x00, 0x98, 0x00, 0x21, 0x06, 0x00, 
  0x00, 0x46, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x21, 0x50, 0x81, 0x26, 0x00, 
  0x80, 0x00, 0x48, 0x9B, 0x4F, 0x00, 0x40, 0x12, 0xA0, 0x00, 0xFF, 0x00, 
  0x20, 0x04, 0xE9, 0x2A, 0x86, 0x01, 0x10, 0x12, 0xF2, 0x11, 0x40, 0x02, 
  0x10, 0x08, 0xD2, 0xF7, 0xC2, 0x02, 0x88, 0x85, 0xE9, 0xFE, 0x79, 0x05, 
  0x88, 0x22, 0xC8, 0x77, 0xFA, 0x04, 0x84, 0x44, 0xFA, 0xFF, 0xFF, 0x08, 
  0xE4, 0x9B, 0x08, 0xFF, 0xFD, 0x08, 0xC6, 0x0D, 0x3C, 0x37, 0x74, 0x11, 
  0xA6, 0x0D, 0x80, 0x2E, 0x30, 0x15, 0xC2, 0xA3, 0x38, 0xFC, 0x00, 0x11, 
  0xE5, 0x09, 0x0A, 0xC8, 0x00, 0x29, 0xC9, 0x55, 0x33, 0x00, 0x40, 0x33, 
  0xB1, 0x8B, 0x02, 0x00, 0x08, 0x24, 0xEB, 0x17, 0x01, 0x00, 0x50, 0x20, 
  0x83, 0x46, 0x2B, 0x00, 0x00, 0x30, 0x6F, 0xA1, 0x51, 0x00, 0x00, 0x20, 
  0xF7, 0xC0, 0x06, 0x00, 0x10, 0x20, 0x8D, 0x10, 0x02, 0x00, 0x00, 0x20, 
  0xBF, 0x8A, 0x80, 0x00, 0x00, 0x30, 0x3F, 0x10, 0x16, 0x00, 0x00, 0x30, 
  0xBE, 0x48, 0x18, 0x00, 0x00, 0x10, 0xFE, 0x11, 0xDB, 0x00, 0x00, 0x10, 
  0x7E, 0x9A, 0x74, 0x00, 0x08, 0x10, 0xFC, 0xCF, 0x0F, 0x00, 0x00, 0x08, 
  0xEC, 0xBF, 0xFF, 0x00, 0x02, 0x08, 0xF8, 0xFF, 0xAF, 0x41, 0x00, 0x04, 
  0xE8, 0xBF, 0xE1, 0x00, 0x00, 0x04, 0xD0, 0xEF, 0xC1, 0x00, 0x04, 0x02, 
  0x90, 0x5B, 0x5D, 0x00, 0x00, 0x03, 0x60, 0x8F, 0x17, 0x02, 0x00, 0x01, 
  0x40, 0xE8, 0x77, 0x00, 0x80, 0x00, 0x80, 0x50, 0x77, 0x01, 0x40, 0x00, 
  0x00, 0x01, 0x05, 0x00, 0x20, 0x00, 0x00, 0x0E, 0x04, 0x04, 0x18, 0x00, 
  0x00, 0x18, 0x00, 0x08, 0x06, 0x00, 0x00, 0x60, 0x40, 0x80, 0x01, 0x00, 
  0x00, 0x80, 0x33, 0x72, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 
  };





unsigned long previousMillis = 0;
const long interval = 3600000; 

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.flush();
  
  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;
  //reset settings - for testing
  //wifiManager.resetSettings();

  //sets timeout until configuration portal gets turned off
  //useful to make it all retry or go to sleep
  //in seconds
  wifiManager.setTimeout(180);
  
  //fetches ssid and pass and tries to connect
  //if it does not connect it starts an access point with the specified name
  //here  "AutoConnectAP"
  //and goes into a blocking loop awaiting configuration
  if(!wifiManager.autoConnect("AutoConnectAP")) {
    Serial.println("failed to connect and hit timeout");
    delay(3000);
    //reset and try again, or maybe put it to deep sleep
    ESP.reset();
    delay(5000);
  } 

  //if you get here you have connected to the WiFi
  Serial.println("connected... :)");
  u8g2.begin();
  // init calendars
  //replace with you gscript ids
  getCalendar1("BHfycWje68avEHz6n09mOPsMWTq_Y7JHuGBNIC320jAmm2aYiE");
  getCalendar2("MKfl1QedMs5004Z35Fer9HxgPDfuSHP8olhkJJ8yICn3H8H_Fn");

}


void loop() {

  u8g2.firstPage();
  do {
    calendar1();
  } while ( u8g2.nextPage() );
  delay(10000);
 
  u8g2.firstPage();
  do {  
    calendar2();    
  } while( u8g2.nextPage() ); 
 delay(10000); 

 // update calendar each hour
 unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
      getCalendar1("BHfycWje68avEHz6n09mOPsMWTq_Y7JHuGBNIC320jAmm2aYiE");
      getCalendar2("MKfl1QedMs5004Z35Fer9HxgPDfuSHP8olhkJJ8yICn3H8H_Fn");
  }
}


//Get calendar entries from google

String fetchDataFromGoogle(String GScriptId) {
  String url = String("/macros/s/") + GScriptId + "/exec";
  HTTPSRedirect client(httpsPort);
  Serial.print("Connecting to ");
  Serial.println(host);

  if (!client.connected())
    client.connect(host, httpsPort);

  String data = client.getData(url, host, googleRedirHost);

// Serial.println(data);
return(data);
}

String getValue(String data, char separator, int index)
{
    int found = 0;
    int strIndex[] = { 0, -1 };
    int maxIndex = data.length() - 1;

    for (int i = 0; i <= maxIndex && found <= index; i++) {
        if (data.charAt(i) == separator || i == maxIndex) {
            found++;
            strIndex[0] = strIndex[1] + 1;
            strIndex[1] = (i == maxIndex) ? i+1 : i;
        }
    }
    return found > index ? data.substring(strIndex[0], strIndex[1]) : "";
}

void calendar1(){
      u8g2.setFont(u8g2_font_helvB08_tr);
u8g2.setCursor(0, 14);
u8g2.print(eventTitle);
if (eventCount == "today"){
   u8g2.setFont(u8g2_font_logisoso24_tr);
   u8g2.setCursor(20, 44);
}else{
   u8g2.setFont(u8g2_font_logisoso46_tr);
   u8g2.setCursor(0, 64);
}
u8g2.print(eventCount);
}

void getCalendar1(String GScriptId){
String data = fetchDataFromGoogle(GScriptId);
// Serial.println(data);
eventTitle = getValue(data, '\t', 0);
eventCount = getValue(data, '\t', 1);
if (eventCount.toInt()>0) {
 eventCount = String(eventCount + "d"); 
} else {
  eventCount = "today";
}

    Serial.println(eventTitle);
    Serial.println(eventCount);
  
}

void calendar2(){
      u8g2.setFont(u8g2_font_helvB08_tr);
      u8g2.setFontMode(1);
u8g2.setCursor(0, 14);
u8g2.print(eventTitle2);sub

if (eventCount2 == "today"){
   u8g2.setFont(u8g2_font_logisoso24_tr);
   u8g2.setCursor(20, 44);
}else{
   u8g2.setFont(u8g2_font_logisoso24_tr);
   u8g2.setCursor(54, 44);
}
   u8g2.print(eventCount2);
   u8g2.setFont(u8g2_font_logisoso16_tr);
   u8g2.setCursor(54, 64);
   u8g2.print("DAYS");
   u8g2.drawXBM(0, 18, 46, 46, fullmoon_xbm);
}

void getCalendar2(String GScriptId){

String data = fetchDataFromGoogle(GScriptId);
// Serial.println(data);
eventTime2 = getValue(data, '\t', 0);
eventCount2 = getValue(data, '\t', 1);
if (eventCount.toInt()>9) {
 eventCount2 = String("0" + eventCount2); 
}
eventTitle2 = "New Moon @ " + eventTime2;
    Serial.println(eventTitle2);
    Serial.println(eventCount2);
}

Credits

Brian Wente

Brian Wente

2 projects • 6 followers

Comments