Introduction: Smart Alarm Clock Using Magicbit(Arduino)

This tutorial shows how to make a smart alarm clock using OLED display in Magicbit dev board without using any RTC module.

Supplies

  • Magicbit
  • USB-A to Micro-USB Cable

Step 1: Story

In this tutorial we will learn about how to make a smart Alarm clock using Magicbit.

Step 2: HARDWARE SETUP

Plug your Magicbit to computer using USB cable.

Step 3: SOFTWARE SETUP

Open your Arduino IDE and setup the board with Arduino IDE. The following link refers how to do that. So we recommend you to that first go to link and getting familiar with Magic bit.

https://magicbit-arduino.readthedocs.io/en/latest/...

Now select correct board type and port. In this case the board type is Magicbit. The libraries are already installed when the in Magicbit libraries.

Step 4: Theory and Methodology

If you look at the first video, you can see the display have 2 screens.

  • clock screen which showing time detail
  • alarm screen which showing alarm details

For toggle between these two screen we used any push button of two in Magicbit. These buttons are connected to 35(left button) and 34(right button) pins of the ESP32 in Magicbit. To show the time and other details we used builtin OLED display in magicbit.

Lets talk about how does these graphic screens are working.

The clock screen has analog clock, digital clock, date, month and year texts.

For creating analog clock we use some graphics functions which are available in graphics library called Adafriut GFX. By using circle function and line function we create analog clock face. Simple geometrical functions called sin and cos are use to the position of the clock hands. So we only input the angle which correspond to time for for rotate hands. for that we first converts the time to angle as follows.

  • angle of minute hand=minutes*(360/60)
  • angle of hours hand=hours*(360/12)

The angle measured with respect to line between center of the clock face and number 12 in clock face.Using sin and cos functions we can calculate the x and y coordinates of the ends of hour and minutes lines. The below picture describe how it is doing.

According coordinates we print hour and minute hand by draw lines. There is also have text print function in Adafruit GFX library. It helps to print other details (date, month and time show in digits) on display. You can change the analog clock position and text positions by changing parameters in the code.

As like as clock screen we used text print function in the Adafruit GFX library for print numbers on OLED display at appropriate places.

Step 5: Getting the Local Time

The most important part of the clock is how we get the local time accurately. For that purpose you can use external RTC clock module or inbuilt RC clock in ESP32 in Magicbit. In this project we used second method. In this method we use NTP (network time protocall ) client for gets the local time from internet. For access internet we used inbuilt WIFI facility in the ESP32. Therefor at the first stage we use WIFI for access the internet by providing SSID and password. Then we should configure the gmtOffset and daylightOffset in variables in seconds. The values of these variables are differ from region to region in the world. gmtOffset means number of seconds you differ from the GMT..For most ares daylightOffset is 3600. You can found it in the internet. After we got the current local time we not longer used WIFI. Because then we calculate local time from inbuilt RC clock in ESP32. This is done by using time.h library. There is simple example in Arduino( Arduino>Examples> ESP32> Time>simpletime) for you to learn about how this works further. Also these links are you can use for further knowledge about NTP client.

After getting the local time correctly, we change our time showing texts and angle according to that time information in every loop.

Step 6: Setting Up the Alarm

By clicking the left and right buttons you can change the alarm date and time selection. Make sure turn off the alarm when you changing the alarm date and time. After set up the date and time turn on the alarm. Because if the alarm is on and when alarm time is equal to your current time while you setting up it, the alarm buzzer will ring. In the main loop always checks the current local time and alarm information are equal. If those are equal, then buzzer and built in green LED in Magicbit will work during one minute.

Step 7: Setting Up the Buzzer

We use PWM pulse to create the buzzer sound by using analogCwrite() function in the code. Because of the all library functions are in ESP32 is valid for Magicbit. You can change the beep sound of the buzzer from change it's frequency and PWM value in the code.

https://techtutorialsx.com/2017/06/15/esp32-arduin...

This page describes about how buzzer works with ESP32.

Step 8: Setting Up Buttons

For changing the all states we used two built in push buttons in Magicbit. Main loop always check the state of two buttons. Because they pulled up internally, there normal state is high signal. So you can see the digital read of those pins are 1. At the default stage the display shows clock interface. At that time, when any of the two button is pressed, then it change screen to alarm screen. Also we count the time in seconds from the last time when button is pressed. If that count is larger than some predefined duration then the display will show the clock screen.

The code is written by using basic functions for beginners. So the code is simple to understand and you can learn the method how it works by referring the code.

Step 9: Troubleshooting

Some times the clock is start bit later or it don't display the graphics properly. Following tips help to solve situation.

  • Make sure you gave the right SSID and password
  • Change the NTP server (you can find many servers from internet which relates to your region).
  • Changes the internet connection.(mobile hotspot can also possible).

Also you can troubleshoot everything by using the serial monitor. In addition to the OLED display serial monitor shows time information.

Step 10: Arduino Code

//libraries for OLED display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4

#include <WiFi.h>//wifi library for connect
#include "time.h"//library for use RC clock 

//define input and output pin names
#define RightButton 34
#define LeftButton 35
#define GreenLED 16
#define Buzzer 25


int preTime = 0;
int counts = 0;
int currentTime = 0;
struct tm timeinfo;


const char* ssid       = "YOUR SSID";//wifi details
const char* password   = "YOUR PASSWORD";

int alarmDateTime[5] = {1, 1, 2020, 0, 0};//alarm varibles
int dateIndex = 0;
int timeIndex = 0;
int selectIndex = -1;
bool buzzerOn = 0;
int rect[6][4] = {{5, 0, 118, 16}, {1, 22, 30, 22}, {37, 22, 30, 22}, {73, 22, 55, 22}, {31, 44, 30, 20}, {67, 44, 30, 20}};//selection rectangle

const char* ntpServer = "asia.pool.ntp.org";//server detais
const long  gmtOffset_sec = 19800;
const int   daylightOffset_sec = 0;

Adafruit_SSD1306 display(128, 64);//OLED size define

byte clockCenterY = (display.height() + 16) / 2;//analog clock face details
byte clockCenterX = (display.height() - 16) / 2;
byte clockRadius = 23;

bool state = 0;//screen on or off

boolean Alarm = 0;//alarm current state
String alarmState = "Alarm ON";//alarm on or off

//varibles stored time data
char dayName[10];
char daynumber[3];
char month[10];
char year[5];
char hours[3];
char minutes[3];
char monthnumber[3];
char seconds[3];

//button variables
bool RightState = 1;
bool LeftState = 1;

//buzzer variables
int channel = 0;
int Frequency = 2000;
int PWM = 200;
int resolution = 8;

void setup() {
  //set input and ouputs
  pinMode(RightButton, INPUT);
  pinMode(LeftButton, INPUT);
  pinMode(GreenLED, OUTPUT);
  pinMode(Buzzer, OUTPUT);


  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);//intialize display
  display.display();
  delay(3000);
  display.clearDisplay();
  ledcSetup(0, Frequency, resolution);//configure pwm parameters
  ledcAttachPin(Buzzer, 0);

  Serial.begin(115200);//intilize serial communication

  //connect to WiFi
  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" CONNECTED");

  //init and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  getTime();
  //disconnect WiFi as it's no longer needed
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);

  display.clearDisplay();
}


void loop() {

  getTime();//get current time

  //store right and left pushbutton states
  RightState = digitalRead(RightButton);
  LeftState = digitalRead(LeftButton);

  //chaeck the buttouns are pressed
  if (RightState == 0 || LeftState == 0) {
    ledcWrite(0, 200);//when button is pressed the buzzer emit beep sound
    delay(100);

    if (state == 0) {//change to alarm screen frame
      state = 1;//change state to alarm state
      RightState = LeftState = 1;//we only need change sceern
    }
    counts = 0;//reset counter
  }

  if (state == 1 && (counts) < 5) {//if in alarm screen and no timeout
    calculateAlarm();//calculate time values of alarm informaton
    showAlarm();//show values

  }
  else {//if in clock screen
    state = 0;
    display.clearDisplay();
    clockFace();//analog clock face
    printLocalTime();//print time in clock face and print other details
  }
  onAlarm();//compare alarm timewith local time and turn on the alarm
  delay(100);//delay for alarm on and off
}



void clockFace() { //caalog clock face
  display.drawCircle(clockCenterX, clockCenterY, clockRadius, WHITE);//print watch circle
  for (int digit = 0; digit < 12; digit++) { //print watch digits
    float digitAngle = digit * 30.0 * PI / 180.0;
    display.drawLine(clockCenterX + (clockRadius - 3)*sin(digitAngle), clockCenterY - (clockRadius - 3)*cos(digitAngle), clockCenterX + (clockRadius)*sin(digitAngle), clockCenterY - (clockRadius)*cos(digitAngle), WHITE);
  }
  display.fillCircle(clockCenterX, clockCenterY, 2, WHITE);//print watch center
}

void calculateAlarm() { //set alarm date and time
  display.clearDisplay();
  if (LeftState == 0) { //left button to change selction
    selectIndex++;
  }
  if (selectIndex >= 5) { //only have 5 selctions.so reset to -1(-1= is alarn on or off)
    selectIndex = -1;
  }
  dateAndTimeSelection(selectIndex);//change selction
}

void dateAndTimeSelection(int index) {
  if (index == -1) { //alarm on or off
    if (RightState == 0) { //togle between on and off alarm
      if (alarmState == "Alarm ON") {
        alarmState = "Alarm OFF";
      }
      else {
        alarmState = "Alarm ON";
      }
    }
  }
  else {
    if (RightState == 0) { //in other selctions icrement the relate date or time in array
      alarmDateTime[index] = alarmDateTime[index] + 1; //index is the selection
    }
  }
  int compare[4] = {12, 2030, 23, 59}; //upper limits of the dates and years
  int comparemonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; //upper limi of the months
  int resetValue[4] = {1, 2020, 0, 0}; //starting values

  for (int i = 1; i < 5; i++) { //reset vlaues if dates or years are larger than their limis

    if (alarmDateTime[i] > compare[i - 1]) {
      alarmDateTime[i] = resetValue[i - 1];
    }
  }
  if (alarmDateTime[0] > comparemonth[alarmDateTime[1] - 1]) { //reset vlaues if months are larger than their limits
    alarmDateTime[0] = 1;
  }



}

void showAlarm() { //print alarm details
  String alarmDateTime0 = String(alarmDateTime[0]); //convert stings to show
  String alarmDateTime1 = String(alarmDateTime[1]);
  String alarmDateTime2 = String(alarmDateTime[2]);
  String alarmDateTime3 = String(alarmDateTime[3]);
  String alarmDateTime4 = String(alarmDateTime[4]);

  //if values have one 1 digita the add "0" to them.
  if (alarmDateTime[0] < 10) {
    alarmDateTime0 = "0" + String(alarmDateTime[0]);
  }
  if (alarmDateTime[1] < 10) {
    alarmDateTime1 = "0" + String(alarmDateTime[1]);
  }
  if (alarmDateTime[3] < 10) {
    alarmDateTime3 = "0" + String(alarmDateTime[3]);
  }
  if (alarmDateTime[4] < 10) {
    alarmDateTime4 = "0" + String(alarmDateTime[4]);
  }

  //select position and print the details
  display.setTextSize(2);      // Normal 1:1 pixel scale
  display.setTextColor(WHITE); // Draw white text
  display.setCursor(15, 0);
  display.println(alarmState);
  display.setCursor(5, 25);
  display.println(alarmDateTime0 + ":" + alarmDateTime1 + ":" + alarmDateTime2);
  display.setCursor(35, 48);
  display.println(alarmDateTime3 + ":" + alarmDateTime4);
  display.drawRect(rect[selectIndex + 1][0], rect[selectIndex + 1][1], rect[selectIndex + 1][2], rect[selectIndex + 1][3], WHITE);
  display.display();

}

void onAlarm() { //turn on and of the alarm
  if ((alarmState == "Alarm ON") && (String(daynumber).toInt() == alarmDateTime[0]) && (String(monthnumber).toInt() == alarmDateTime[1]) && (String(year).toInt() == alarmDateTime[2]) && (String(hours).toInt() == alarmDateTime[3]) && (String(minutes).toInt() == alarmDateTime[4]) )
    //comapare with local time
  {
    ledcWrite(0, PWM * buzzerOn);
    digitalWrite(GreenLED, 1 * buzzerOn);
    buzzerOn = !buzzerOn; //turn on and off buzzer
    Alarm = 1;
  }
  else {
    ledcWrite(0, 0); //if not on alarm
    Alarm = 0;
    digitalWrite(16, LOW);
  }
}

void printLocalTime()
{
  Date(dayName, daynumber, month, year);
  int minuteAngle = String(minutes).toInt() * 6;
  int hourAngle = (String(hours).toInt() + (minuteAngle / 360.0)) * 30.0;
  Minute(minuteAngle);
  Hour(hourAngle);
  digitShow(String(hours) + ":" + String(minutes));
  display.display();

}
void Hour(float hourAngle) {
  //calculate angles arrow head
  float delta = (PI * hourAngle / 180.0);
  float delta1 = (PI * (hourAngle - 10) / 180.0);
  float delta2 = (PI * (hourAngle + 10) / 180.0);
  int trangleX0 = clockCenterX + (clockRadius - 10) * sin(delta);
  int trangleX1 = clockCenterX + (clockRadius - 15) * sin(delta1);
  int trangleX2 = clockCenterX + (clockRadius - 15) * sin(delta2);
  int trangleY0 = clockCenterY - (clockRadius - 10) * cos(delta);
  int trangleY1 = clockCenterY - (clockRadius - 15) * cos(delta1);
  int trangleY2 = clockCenterY - (clockRadius - 15) * cos(delta2);
  display.drawLine(clockCenterX, clockCenterY, clockCenterX + (clockRadius - 10)*sin(delta), clockCenterY - (clockRadius - 10)*cos(delta), WHITE); //print hour hand
  display.fillTriangle(trangleX0, trangleY0, trangleX1, trangleY1, trangleX2, trangleY2, WHITE); //print arrow head

}
void Minute(float minuteAngle) {
  float delta = (PI * minuteAngle / 180.0); //calculate angle of minute hand(minute hand dont have arrow head)
  display.drawLine(clockCenterX, clockCenterY, clockCenterX + (clockRadius - 5)*sin(delta), clockCenterY - (clockRadius - 5)*cos(delta), WHITE); //print minute hand
}

void digitShow(String Time) { //print time in digits
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(57, 30);
  display.print(Time);
}
void Date(String dateName, String date, String month, String year) { //print date/month/year
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);
  display.print(dateName + " ");
  display.print(date);
  display.print(" / ");
  display.print(month);
  display.print(" / ");
  display.println(year);
}
void getTime() {
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }
  //get time informations seperatly
  strftime(dayName, 4, "%A", &timeinfo);
  strftime(daynumber, 3, "%d", &timeinfo);
  strftime(month, 4, "%B", &timeinfo);
  strftime(year, 5, "%Y", &timeinfo);
  strftime(hours, 3, "%H", &timeinfo);
  strftime(minutes, 3, "%M", &timeinfo);
  strftime(monthnumber, 3, "%m", &timeinfo);
  strftime(seconds, 3, "%S", &timeinfo);
  currentTime = String(seconds).toInt(); //conevert string to intiger
  if (currentTime != preTime) {
    counts++;
  }
  preTime = currentTime;
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");//print time in serial monitor

}