Welcome to Hackster!
Hackster is a community dedicated to learning hardware, from beginner to pro. Join us, it's free!
mboretto
Published © MIT

Oplà Alarm clock

Transform your Oplà kit in a night table alarm

AdvancedFull instructions provided2,824
Oplà Alarm clock

Things used in this project

Hardware components

Arduino Oplà IoT Kit
Arduino Oplà IoT Kit
×1

Software apps and online services

Arduino IoT Cloud
Arduino IoT Cloud

Story

Read more

Code

opla_alarm.ino

Arduino
#include "ScreenController.hpp"

#include "arduino_secrets.h"
#include "thingProperties.h"

MKRIoTCarrier carrier;
RTCZero rtclock;
const int GMT = +1;
ScreenController screen(&carrier, &rtclock, GMT);

// Check the connection every 15 seconds
const long interval = 15 * 1000;
unsigned long previous_millis = 0;
bool wifi_first_connection = true;

const long ntp_interval = 60000 * 5; //5 minutes

unsigned long ntp_previous_millis = 0;

const long date_interval = 1000; //every second
unsigned long date_previous_millis = 0;

bool is_ntp_time_sync = false;

void setup() {
  /* Initialize serial and wait up to 5 seconds for port to open */
  Serial.begin(115200);
  for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime > 5000);) {
    continue;
  }

  /* This function takes care of connecting your sketch variables to the ArduinoIoTCloud object */
  initProperties();

  /* Initialize Arduino IoT Cloud library */
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);

  //setDebugMessageLevel(DBG_INFO);
  setDebugMessageLevel(DBG_DEBUG);
  ArduinoCloud.printDebugInfo();
  // ArduinoCloud.updateInternalTimezoneInfo();

  rtclock.begin(true); //resetTime = true
  //Serial.println(TOUCH.getSensorsSensitivity());
  CARRIER_CASE = true;
  carrier.begin();
 
  TOUCH.setSensorsSensitivity(100);
  Serial.print("Button sensitivity:");
  Serial.println(TOUCH.getSensorsSensitivity());
}

unsigned long prev_sound = 0;
volatile bool play_alarm = false;

void loop() {
  ArduinoCloud.update();
  const unsigned long current_millis = millis();

  if ((current_millis - date_previous_millis) >= date_interval) {
    date_previous_millis = current_millis;  
    screen.render();
  }
    
  if (play_alarm) {
    if ((current_millis - prev_sound > 1000)) {
      prev_sound = current_millis;
      tone(carrier.Buzzer.getPin(), 500, 500);// async
    }
  }
  //Serial.println(TOUCH.getSensorsSensitivity());
  carrier.Buttons.update();
  if (carrier.Buttons.onTouchUp(TOUCH0) or
      carrier.Buttons.onTouchUp(TOUCH1) or
      carrier.Buttons.onTouchUp(TOUCH2) or
      carrier.Buttons.onTouchUp(TOUCH3) or
      carrier.Buttons.onTouchUp(TOUCH4)
     ) {
    Serial.println("Touched Down a button");
    play_alarm = false;
  }

  // Checking the network status every X seconds
  if (current_millis - previous_millis >= interval or wifi_first_connection) {
    previous_millis = current_millis;
    if (ArduinoCloud.connected() != 0) {
      // Wifi Ok
      screen.is_wifi_connected = true;

      // I'm sure that the WIFI is active I sync the time with NTP Protocol
      if (current_millis - ntp_previous_millis >= ntp_interval or not is_ntp_time_sync) {
        uint32_t epoch = 0;
        uint8_t attempts = 0;
                
        while (epoch == 0) {
          epoch = ArduinoCloud.getInternalTime();
          // Limit
          if (++attempts > 4) {
            break;
          }
        }
        if (epoch == 0) {
          Serial.println("NTP unreachable!");
        } else {
          screen.is_time_set = true;
          if (is_ntp_time_sync) {
            // Checking if the internal RTC is still sync
            uint32_t rtc_epoch = rtclock.getEpoch();
            if (rtc_epoch != epoch) {
              Serial.print("Internal epoch NOT sync anymore: rtc_epoch != epoch ");
              Serial.print(rtc_epoch);
              Serial.print(" != ");
              Serial.println(epoch);
              is_ntp_time_sync = false;
            } else {
              Serial.print("Internal epoch still sync at ");
              printRTCInternalDate();
            }
          }
          // First epoch set or out of sync..
          if (not is_ntp_time_sync) {
            rtclock.setEpoch(epoch);
            is_ntp_time_sync = true; //TODO check if the time is still on sync!
            Serial.print("Epoch set: ");
            Serial.println(epoch);
            Serial.print("Internal RTC time: ");
            printRTCInternalDate();
          }
          ntp_previous_millis = current_millis; // Force NTP check every n minutes
        }
      }
    } else {
      screen.is_wifi_connected = false;
    }
  }
}

void setAlarm() {
  rtclock.disableAlarm();
  rtclock.detachInterrupt();  
  if (switchAlarm) {
    uint alarm_time_second = alarmTime;
    uint raw_minutes = alarm_time_second / 60;
    uint8_t clock_hour = raw_minutes / 60 - GMT;
    uint8_t clock_minute = raw_minutes % 60;
    if (clock_hour >= 24) {
      clock_hour -= 24;
    } else if (clock_hour < 0) {
      clock_hour += 24;
    }
     
    rtclock.setAlarmTime(clock_hour, clock_minute, 0);
    rtclock.enableAlarm(rtclock.MATCH_HHMMSS);
    
    rtclock.attachInterrupt(alarmMatch);
    Serial.println("Alarm set");
    return;
  }
  Serial.println("Alarm UNset");
}

void alarmMatch() {
  Serial.println("Interrupt triggered");
  play_alarm = true;
}

void onAlarmTimeChange() {
   setAlarm();
   Serial.print("Alarm changed: ");
   Serial.println(alarmTime);
//   uint raw_minutes = alarmTime / 60;
//   uint clock_hour = raw_minutes / 60;
//   uint clock_minute = raw_minutes % 60;
//   Serial.print(clock_hour);
//   Serial.print(":");
//   Serial.println(clock_minute);
   screen.printAlarmTime(alarmTime);
}

void onSwitchAlarmChange() {
  setAlarm();
  Serial.print("Switching alarm status to: ");
  Serial.print(switchAlarm);
  screen.printAlarm(switchAlarm);
}

void printRTCInternalDate() {
  Serial.print(rtclock.getDay());
  Serial.print("/");
  Serial.print(rtclock.getMonth());
  Serial.print("/");
  Serial.print(rtclock.getYear());
  Serial.print(" ");
  Serial.print(rtclock.getHours() + GMT);
  Serial.print(":");
  Serial.print(rtclock.getMinutes());
  Serial.print(":");
  Serial.print(rtclock.getSeconds());
  Serial.println(" ");
}

ScreenController.hpp

C/C++
#ifndef SCREEN_CONTROLLER
#define SCREEN_CONTROLLER

#include <RTCZero.h>
#include <Arduino_MKRIoTCarrier.h>

#include "images.h"

class ScreenController {
  public:
    int GMT; //change this to adapt it to your time zone
    bool is_wifi_connected;
    bool is_time_set;

  ScreenController(MKRIoTCarrier* carrier, RTCZero* rtc, int gmt):
    GMT(gmt),
    m_carrier(carrier),
    m_rtc(rtc),
    is_first_time_render(true),
    current_color(ST77XX_WHITE),
    screen_status_second(0),
    is_wifi_connected(false), // allows to do the first render
    screen_status_is_wifi_connected(true),
    is_time_set(false) {
  }

  bool isFirstRender() const {
    return is_first_time_render;
  }


  void off() const  {
    m_carrier->display.enableDisplay(false);
  }

  void on() const {
    m_carrier->display.enableDisplay(true);
  }
  void cleanScreen() const {
    m_carrier->display.fillScreen(ST77XX_BLACK); //black background
  }

  void render() {
    on();
    if (is_time_set) {
      if (isFirstRender()) {
        initRender();
      }
      uint16_t second = m_rtc->getSeconds();

      //printint everything until 0
      if (second < (screen_status_second + 1)) {
        for (uint16_t this_second = (screen_status_second + 1); this_second <= 59; this_second++) {
          printBorderSeconds(this_second);
        }
        printBorderSeconds(0);
        printTime();
        screen_status_second = 0;
      }
      for (uint16_t this_second = (screen_status_second + 1); this_second <= second; this_second++) {
        if (printBorderSeconds(this_second)) {
          printTime();
        }
      }
      screen_status_second = second;
    }

    drawWifiIcon();

    if (screen_status_is_wifi_connected and not is_time_set) {
      printDateUnavailable();
    } else if (screen_status_is_wifi_connected and is_time_set) {
      cleanMessage();
    }
  }

  void cleanMessage() {
     printMessage("                ");
  }

  void printDateUnavailable() {
    printMessage("Fetching date..");
  }

  void drawWifiIcon() {
    if (screen_status_is_wifi_connected == is_wifi_connected) {
      // the screen already reflects the status no need to update.
      return;
    }
    screen_status_is_wifi_connected = is_wifi_connected;

    cleanMessage();
    m_carrier->display.drawBitmap(110, 200, clean, 15, 15, ST77XX_BLACK);
    if (is_wifi_connected) {
      m_carrier->display.drawBitmap(110, 200, wifi, 15, 15, ST77XX_WHITE);
      return;
    }
    printConnectingWiFI();
    m_carrier->display.drawBitmap(110, 200, nowifi, 15, 15, ST77XX_RED);
  }


  void printAlarm(bool alarm_is_enabled) {
    if (alarm_is_enabled) {
        m_carrier->display.drawBitmap(110, 30, alarmon, 15, 15, ST77XX_RED);
        return;
    }
    m_carrier->display.drawBitmap(110, 30, clean, 15, 15, ST77XX_BLACK);
  }

  void printAlarmTime(uint alarm_time_second) {
    uint raw_minutes = alarm_time_second / 60;
    uint clock_hour = raw_minutes / 60;
    uint clock_minute = raw_minutes % 60;
    m_carrier->display.setTextColor(ST77XX_RED, ST77XX_BLACK);
    m_carrier->display.setCursor(87, 55); //sets position for printing (x and y)
    m_carrier->display.setTextSize(2);
    if (clock_hour < 10) {
      m_carrier->display.print(" ");
    }
    m_carrier->display.print(clock_hour);
    m_carrier->display.print(":");
    if (clock_minute < 10) {
      m_carrier->display.print("0");
    }
    m_carrier->display.print(clock_minute);
  }

  private:
  bool is_first_time_render;
  bool screen_status_is_wifi_connected;
  uint16_t current_color;
  uint16_t screen_status_second;
  //uint32_t alarm_time_second;

  MKRIoTCarrier* m_carrier;
  RTCZero* m_rtc;

  void printMessage(String message) {
    m_carrier->display.setTextColor(ST77XX_WHITE, ST77XX_BLACK); //white text
    m_carrier->display.setCursor(75, 155); //sets position for printing (x and y)
    m_carrier->display.setTextSize(1);
    m_carrier->display.print(message);
  }


  void printConnectingWiFI() {
    printMessage("  Connecting..");
  }

  void initRender() {
    printTime();
    is_first_time_render = false;
  }

  bool printBorderSeconds(uint16_t second) {
    const float pi = 3.14;
    const float pi_mezzi = pi / 2;
    //Serial.print("printing: ");
    //Serial.println(second);
    const float step = 2 * pi / 60;
    const float start = (second - 1) * step - pi_mezzi;
    const float stop = second * step - pi_mezzi;

    //for (float rad = start; rad < stop; rad = rad + 0.1) {
    //for (float rad = start; rad < stop; rad = rad + 0.05) {
    for (float rad = start; rad < stop; rad = rad + 0.01) {
      for (uint16_t radius = 110; radius <= 117; radius++) {
        const uint16_t x = 120 + radius * cos(rad);
        const uint16_t y = 120 + radius * sin(rad);
        //Serial.print(x);
        //Serial.print(" ");
        //Serial.println(y);
        m_carrier->display.drawPixel(x, y, current_color);
      }
    }
    if (second == 0) {
      if (current_color == ST77XX_WHITE) {
        current_color = ST77XX_BLACK;
      } else {
        current_color = ST77XX_WHITE;
      }
      return true;
   }
   return false;
  }

  void printTime() {
    m_carrier->display.setTextColor(ST77XX_WHITE, ST77XX_BLACK); //white text black Background
    m_carrier->display.setTextSize(6); //medium sized text

    m_carrier->display.setCursor(30, 100); //sets position for printing (x and y)
    uint16_t hour = m_rtc->getHours() + GMT;
    if (hour > 23) {
      // Deal with impossible hours intoduced by GTM conversion
      m_carrier->display.print(hour - 24);
    } else {
      if (hour < 10) {
        m_carrier->display.print(" ");
      }
       m_carrier->display.print(hour);
    }
    m_carrier->display.print(":");
    if (m_rtc->getMinutes() < 10) {
      m_carrier->display.print("0");
    }
    m_carrier->display.print(m_rtc->getMinutes());
  }
};
#endif

images.h

C/C++
//https://www.mischianti.org/images-to-byte-array-online-converter-cpp-arduino/
const unsigned char wifi [] PROGMEM = {
  // 'wifi, 15x15px
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x38, 0x38, 0xe0, 0x0e, 0x4f, 0xe4, 0x3c, 0x78, 
  0x10, 0x10, 0x07, 0xc0, 0x04, 0x40, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x01, 0x00
};

const unsigned char nowifi [] PROGMEM = {
  // 'nowifi, 15x15px
  0x03, 0x80, 0x03, 0x80, 0x03, 0x80, 0x0f, 0xe0, 0x3b, 0xb8, 0xe1, 0x0e, 0x4d, 0x64, 0x19, 0x30, 
  0x11, 0x10, 0x07, 0xc0, 0x05, 0x40, 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x01, 0x00
};


const unsigned char clean [] PROGMEM = {
  // 'clean, 15x15px
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};

const unsigned char alarmon [] PROGMEM = {
	// 'alarmon, 15x15px
	0x00, 0x00, 0x30, 0x18, 0x69, 0x2c, 0x59, 0x34, 0xb6, 0xda, 0xa8, 0x2a, 0xa8, 0x2a, 0xa8, 0x2a, 
	0x08, 0x20, 0x08, 0x20, 0x10, 0x10, 0x30, 0x18, 0x1f, 0xf0, 0x03, 0x80, 0x00, 0x00
};

arduino_secrets.h

C/C++
#include <Arduino_ConnectionHandler.h>

#define THING_ID "CHANGEME"
#define BOARD_ID "CHANGEME"

/* MKR1000, MKR WiFi 1010 */
#if defined(BOARD_HAS_WIFI)
  #define SECRET_SSID "CHANGEME"
  #define SECRET_PASS "CHANGEME"
#endif

thingProperties.h

C/C++
#include <ArduinoIoTCloud.h>
#include <Arduino_ConnectionHandler.h>

#include "arduino_secrets.h"

void onSwitchAlarmChange();
void onAlarmTimeChange();

bool switchAlarm;
CloudTime alarmTime;

void initProperties() {

  ArduinoCloud.setThingId(THING_ID);
#if defined(BOARD_HAS_WIFI) || defined(BOARD_HAS_GSM) || defined(BOARD_HAS_NB)
  ArduinoCloud.addProperty(switchAlarm, READWRITE, ON_CHANGE, onSwitchAlarmChange);
  ArduinoCloud.addProperty(alarmTime, READWRITE, ON_CHANGE, onAlarmTimeChange);
#endif
}

#if defined(BOARD_HAS_WIFI)
  WiFiConnectionHandler ArduinoIoTPreferredConnection(SECRET_SSID, SECRET_PASS);
#endif

Credits

mboretto
0 projects • 2 followers
Contact

Comments

Please log in or sign up to comment.