By our entrepreneur in residence, Sander Vonk
There is no better feeling than going to bed at night and not having to set an alarm
Note to self: when using ESP-IDF, use esp-idf-ssd1306 as the driver for the OLED display.
ESP8266 based alarm clock. This project gets the time and your next alarm in Google Calendar from the Internet and displays this information. The alarm will go off and the board will beep and vibrate.

The software is written using the Arduino IDE with the ESP8266 board package. The main components are time synchronization ….
The code can be found at
Remember to update the wireless settings (_wifi
in alarm_clock.ino
); install your Google script and update dstPath inGoogleCalEvent.cpp
accordingly.
Libraries required
- WiFiClientSecureRedirect (now obsolete)
- Adafruit_GFX
- Adafruit_SSD1306
Google Script
We use a Google script to determine the next alarm time based on your Google calendar. For instructions of how to enter the google scripts refer to ESP8266 reads Google Calendar. The code below can be found at GitHub in the extra
directory.
Since this writing, the ESP8266 support package switched from AxTLS
to BearSSL
. This code still uses the AxTLS
variant.
/* Fetch first event of the day to set alarm clock * Platform: Google WebApp Script * (c) Copyright 2016, Coert Vonk, Sander Vonk */ function _alarmTime(event) { if (event == undefined ) { return undefined; } // https://code.google.com/p/google-apps-script-issues/issues/detail?id=4433 var reminders = event.getPopupReminders(); // known issue: method getPopupReminders() doesn't return anything if there is only one reminder so try setting two reminders for the same timed if (reminders.length == 0) { reminder = 0; } var longestReminder = 0; for (var ii=0; ii < reminders.length; ii++) { if (reminders[ii] > longestReminder) { longestReminder = reminders[ii]; } } date = new Date(event.getStartTime().getTime()-longestReminder*60*1000); return date; } function _findFirstAlarm(events) { var firstAlarmTime = undefined; var firstAlarmIdx = undefined; for (var ii = 0; ii < events.length; ii++) { var event=events[ii]; switch(event.getMyStatus()) { case CalendarApp.GuestStatus.OWNER: case CalendarApp.GuestStatus.YES: case CalendarApp.GuestStatus.MAYBE: var alarmTime = _alarmTime(event); if (firstAlarmTime == undefined || alarmTime < firstAlarmTime) { firstAlarmIdx = ii; firstAlarmTime = alarmTime; } break; default: break; } } if (firstAlarmIdx == undefined) { return undefined; } return events[firstAlarmIdx]; } function doGet(e) { // open calendar var cal = CalendarApp.getCalendarById('your.e.mail@gmail.com'); if (cal == undefined) { return ContentService.createTextOutput("no access to calendar"); } // find the first event today that I'm participating in, and that is not an all day event. const now = new Date(); var todayStart = new Date(); todayStart.setHours(0, 0, 0); // start at midnight this day const oneday = 24*3600000; // [msec] const todayStop = new Date(todayStart.getTime() + oneday - 1); var eventsToday = cal.getEvents(todayStart, todayStop); var firstAlarmToday = _findFirstAlarm(eventsToday); // find the first event tomorrow that I'm participating in, and that is not an all day event. const tomorrowStart = new Date(todayStart.getTime() + oneday); const tomorrowStop = new Date(tomorrowStart.getTime() + oneday - 1); var eventsTomorrow = cal.getEvents(tomorrowStart, tomorrowStop); var firstAlarmTomorrow = _findFirstAlarm(eventsTomorrow); // select the alarm event const event = firstAlarmToday != undefined && _alarmTime(firstAlarmToday) > now ? firstAlarmToday : firstAlarmTomorrow; // print event details that should trigger alarm var str = ''; if ( event != undefined ) { const date = event.getStartTime(); const alarm = _alarmTime(event); const startMinutes = date.getHours()*60 + date.getMinutes(); const alarmMinutes = alarm.getHours()*60 + alarm.getMinutes(); str += alarmMinutes + '\n' + // alarm [minutes since midnight] startMinutes + '\n' + // start time of event in [minutes since midnight] event.getTitle() +'\n'; // event title } Logger.log("<REPLY>" + str + "</REPLY>"); return ContentService.createTextOutput(str); }
Google Calendar (github)
Schematic
The Huzzah Feather does not output enough current to supply the haptic motor and the buzzer so we used a transistor to amplify the current. Resistor (R2) allows the buzzer to discharge. The Diode (D1) before the haptic element provides a clear path for the induced current to flow. Both the haptic and buzzer are connected to 5 volts so they can produce more vibration and sound. Teh photo transistor (Q1) and resistor (R4) form a voltage divider that feeds the analog input (Adc). These circuits are built on the Feather Proto board.
The Oled screen is stacked on top of the Huzzah Feather and uses the input from I2C.
The project is powered with a micro USB cable or Optional Li-Poly batteries.
Parts
Picture | Link | Price |
![]() |
ESP8266 Huzzah Feather | $15.95 |
![]() |
OLED Feather | $14.95 |
![]() |
FeatherWing Proto | $4.95 |
![]() |
Feather Stacking Headers | $1.25 |
![]() |
Haptic | $1.95 |
![]() |
Buzzer | $0.95 |
![]() |
Photo Transistor | $0.95 |
![]() |
1k resistor 3 needed $0.10 each | $0.30 |
![]() |
1.5k resistor | $0.10 |
![]() |
NPN Transistor | $0.19 |
Diode | $0.03 | |
Optional batteries: | (Not included in Total Price):
|
|
Total Price: | $41.38 |