Daniel Harel
Published © GPL3+

All In One Clock 2.0

Alarm clock, full weather info, time, timer, and more all in one clock

IntermediateFull instructions provided2 hours317
All In One Clock 2.0

Things used in this project

Hardware components

3.5 tft display arduino
×1
Arduino Mega 2560
Arduino Mega 2560
×1
Arduino Nano RP2040 Connect
Arduino Nano RP2040 Connect
×1
Buzzer
Buzzer
×1

Software apps and online services

Arduino IDE
Arduino IDE
Arduino IoT Cloud
Arduino IoT Cloud

Hand tools and fabrication machines

Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
10 Pc. Jumper Wire Kit, 5 cm Long
10 Pc. Jumper Wire Kit, 5 cm Long
Soldering iron (generic)
Soldering iron (generic)
3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Box

Box Cover

Schematics

Schematics

Code

Arduino Mega Code

Arduino
// Include necessary libraries
#include <Adafruit_GFX.h>
#include <MCUFRIEND_kbv.h>
#include <TouchScreen.h>
#include <FreeDefaultFonts.h>
#include "./fonts/FreeSans20pt7b.h"
#include "./fonts/FreeSans15pt7b.h"
#include "./fonts/FreeSans10pt7b.h"
#include "./fonts/FreeSans5pt7b.h"
#include "./fonts/digital20pt7b.h"
#include <SoftwareSerial.h>
#include "icons.h"
#include <ArduinoJson.h>
#include <Wire.h>
#include <SPI.h>
#define USE_SDFAT
#include <SdFat.h> 

// Configure the SPI and SD card
SoftSpiDriver<12, 11, 13> softSpi;
SdFat SD;
#define SD_CS SdSpiConfig(10, DEDICATED_SPI, SD_SCK_MHZ(0), &softSpi)
#define PALETTEDEPTH 8 // support 256-colour Palette

// Initialize variables
int temperature;
int humidity;
int pressure;
int feelsLike;
int UV;
float windSpeed;

bool reloadTemp = false;
bool reloadForecast = false;

int hours;
int minutes;
int month;
int year;
int dayMonth;
int dayWeek = 1;

int oldMin;
int oldDayMonth;

int imageNumber = 113;
String timeOfDayForImageToday = "day";

int todayMax;
int todayMin;
int todayRain;
int todayIcon;

int tommorowMax;
int tommorowMin;
int tommorowRain;
int tommorowIcon;

int tommorow1Max;
int tommorow1Min;
int tommorow1Rain;
int tommorow1Icon;

int alarmHours = 20;
int alarmMinutes = 0;

int oldDayMM;

#define MINPRESSURE 200
#define MAXPRESSURE 1000

int Orientation = 1;

const int XP = 9, XM = A3, YP = A2, YM = 8; // 320x480 ID=0x9488
const int TS_LEFT = 120, TS_RT = 900, TS_TOP = 49, TS_BOT = 949;
MCUFRIEND_kbv tft;
TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

Adafruit_GFX_Button temp_button, alarm_button, back_button, forecast_button, hour_up, minutes_up, dismissButton, timer_button, timerMinuteAdd_button, timerSecAdd_button, timerSecMinus_button, timerMinuteMinus_button, timerStart_button, timerClear_Button, timer5_button, timer10_button, timer15_button;

// Set up screens
enum Screen
{
  MAIN,
  ALARM,
  TEMP,
  FORECAST,
  ALARM_ACTIVE,
  TIMER
};

Screen currentScreen = MAIN;

// Set up colors
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define GRAY 0x8410

bool canBeOnDelay = true;
int buzzer = 44;
bool alarmScreenShown = false;
bool timerScreenShown = false;
bool reloadTempImage = false;
bool wasScreenBack = true;

unsigned long previousMillisAlarm = 0;
unsigned long previousMillisTimer = 0;
unsigned long previousMillis = 0;
const long interval = 1000;
const long intervalAlarm = 2500;

bool soundOn = false;

int pixel_x, pixel_y;

// Get touch coordinates
bool Touch_getXY(void)
{
  TSPoint tp = ts.getPoint();
  pinMode(YP, OUTPUT);
  pinMode(XM, OUTPUT);
  digitalWrite(YP, HIGH);
  digitalWrite(XM, HIGH);
  bool pressed = (tp.z > MINPRESSURE && tp.z < MAXPRESSURE);
  if (pressed)
  {
    pixel_x = map(tp.y, TS_TOP, TS_BOT, 0, tft.width());
    pixel_y = map(tp.x, TS_RT, TS_LEFT, 0, tft.height());
  }
  delay(10);
  return pressed;
}

void receiveEvent(int numBytes);

// Define timer states
enum TimerState
{
  TIMER_STOPPED,
  TIMER_RUNNING,
  TIMER_FINISHED
};

struct Timer
{
  int minutes;
  int seconds;
  TimerState state;
  unsigned long previousMillis;
  const unsigned long interval = 1000;

  Timer() : minutes(0), seconds(0), state(TIMER_STOPPED), previousMillis(0) {}

  void update()
  {
    if (state == TIMER_RUNNING)
    {
      unsigned long currentMillis = millis();
      if (currentMillis - previousMillis >= interval)
      {
        previousMillis = currentMillis;
        if (seconds == 0)
        {
          if (minutes > 0)
          {
            minutes--;
            seconds = 59;
          }
          else
          {
            state = TIMER_FINISHED;
          }
        }
        else
        {
          seconds--;
        }
        if (state != TIMER_FINISHED)
        {
          display_new_timer();
        }
      }
    }
  }

  void start()
  {
    if (state == TIMER_STOPPED && (minutes > 0 || seconds > 0))
    {
      state = TIMER_RUNNING;
      previousMillis = millis();
      showBMP("/timerIcon/pause.bmp", 400, 125);
    }
  }

  void stop()
  {
    if (state == TIMER_RUNNING)
    {
      state = TIMER_STOPPED;
      showBMP("/timerIcon/play.bmp", 400, 125);
    }
  }

  void reset()
  {
    minutes = 0;
    seconds = 0;
    noTone(buzzer);
    showBMP("/timerIcon/play.bmp", 400, 125);
    state = TIMER_STOPPED;
  }
  void addMinute()
  {
    if (state = TIMER_RUNNING)
    {
      state = TIMER_STOPPED;
      showBMP("/timerIcon/play.bmp", 400, 125);
    }
    minutes = (minutes + 1) % 100;
  }
  void subtractMinute()
  {
    if (state = TIMER_RUNNING)
    {
      state = TIMER_STOPPED;
      showBMP("/timerIcon/play.bmp", 400, 125);
    }
    if (minutes > 0)
    {
      minutes--;
    }
  }
  void addSecond()
  {
    if (state = TIMER_RUNNING)
    {
      state = TIMER_STOPPED;
      showBMP("/timerIcon/play.bmp", 400, 125);
    }
    seconds = (seconds + 1) % 60;
  }
  void subtractSecond()
  {
    if (state = TIMER_RUNNING)
    {
      state = TIMER_STOPPED;
      showBMP("/timerIcon/play.bmp", 400, 125);
    }
    if (seconds > 0)
    {
      seconds--;
    }
  }
  void setTime(int minutesIn, int secondsIn)
  {
    if (state = TIMER_RUNNING)
    {
      state = TIMER_STOPPED;
      showBMP("/timerIcon/play.bmp", 400, 125);
    }
    minutes = minutesIn;
    seconds = secondsIn;
  }
};

Timer timer;

void setup()
{
  Serial.begin(9600);
  // bool good = SD.begin(SD_CS);
  // if (!good) {
  //     Serial.print(F("cannot start SD"));
  //     while (1);
  // }
  if (!SD.begin(SD_CS))
  {
    Serial.println("Card failed, or not present");
    // Don't do anything more:
    return;
  }
  Serial.println("SD card initialized.");
  pinMode(buzzer, OUTPUT);
  Wire.begin(8);                // address of the Mega
  Wire.onReceive(receiveEvent); // set the receive event
  uint16_t ID = tft.readID();
  tft.begin(ID);
  tft.setRotation(Orientation);
  tft.fillScreen(BLACK);

  alarm_button.initButton(&tft, 52, 262, 120, 100, BLACK, BLACK, BLACK, "", 1);
  timer_button.initButton(&tft, 177, 262, 120, 100, BLACK, BLACK, BLACK, "", 1);
  temp_button.initButton(&tft, 302, 262, 120, 100, BLACK, BLACK, BLACK, "", 1);
  forecast_button.initButton(&tft, 427, 262, 120, 100, BLACK, BLACK, BLACK, "", 1);

  back_button.initButton(&tft, 430, 40, 96, 96, BLACK, BLACK, BLACK, "", 1);

  timerMinuteAdd_button.initButton(&tft, 185, 40, 75, 75, BLACK, BLACK, BLACK, "", 1);
  timerSecAdd_button.initButton(&tft, 305, 40, 75, 75, BLACK, BLACK, BLACK, "", 1);
  timerMinuteMinus_button.initButton(&tft, 185, 190, 75, 75, BLACK, BLACK, BLACK, "", 1);
  timerSecMinus_button.initButton(&tft, 305, 190, 75, 75, BLACK, BLACK, BLACK, "", 1);
  timerClear_Button.initButton(&tft, 70, 110, 75, 75, BLACK, BLACK, BLACK, "", 1);
  timerStart_button.initButton(&tft, 430, 155, 75, 75, BLACK, BLACK, BLACK, "", 1);

  timer5_button.initButton(&tft, 150, 280, 75, 75, BLACK, BLACK, BLACK, "", 1);
  timer10_button.initButton(&tft, 250, 280, 75, 75, BLACK, BLACK, BLACK, "", 1);
  timer15_button.initButton(&tft, 350, 280, 75, 75, BLACK, BLACK, BLACK, "", 1);

  hour_up.initButton(&tft, 150, 230, 96, 96, WHITE, WHITE, WHITE, "", 1);
  minutes_up.initButton(&tft, 270, 230, 96, 96, WHITE, WHITE, WHITE, "", 1);
  dismissButton.initButton(&tft, 235, 245, 211, 92, BLACK, BLACK, BLACK, "", 1);

  forecast_button.drawButton(false);
  temp_button.drawButton(false);
  alarm_button.drawButton(false);
  timer_button.drawButton(false);
  defualtMain();
}

void loop()
{
  timer.update();
  if (timer.state == TIMER_FINISHED)
  {
    timerActivesScreen();
  }
  else
  {
    if ((alarmHours != hours || alarmMinutes != minutes) && !wasScreenBack)
    {
      wasScreenBack = true;
      noTone(buzzer);
      tft.fillScreen(BLACK);
      defualtMain();
    }
    if (alarmHours == hours && alarmMinutes == minutes && canBeOnDelay && currentScreen != ALARM)
    {
      alarmActivesScreen();
    }
    else
    {
      switch (currentScreen)
      {
      case MAIN:
        mainScreen();
        break;
      case ALARM:
        alarmScreen();
        break;
      case TEMP:
        tempScreen();
        break;
      case FORECAST:
        forecastScreen();
        break;
      case TIMER:
        timerScreen();
        break;
      }
    }
  }
  if (alarmMinutes != minutes)
  {
    canBeOnDelay = true;
    alarmScreenShown = false;
  }
}
void mainScreen()
{
  bool down = Touch_getXY();
  if (down)
  {
    temp_button.press(temp_button.contains(pixel_x, pixel_y));
    alarm_button.press(alarm_button.contains(pixel_x, pixel_y));
    forecast_button.press(forecast_button.contains(pixel_x, pixel_y));
    timer_button.press(timer_button.contains(pixel_x, pixel_y));
  }
  else
  {
    temp_button.press(false);
    alarm_button.press(false);
    forecast_button.press(false);
    timer_button.press(false);
  }
  if (temp_button.justPressed())
  {
    tft.fillScreen(BLACK);
    Serial.println("pressed temp");
    currentScreen = TEMP;
    back_button.drawButton(false);
    // tft.drawRGBBitmap(390, 5, homeScreen, 80, 80);
    showBMP("/home.bmp", 390, 5);
    reloadTemp = false;
    defaultTemp();
    return;
  }
  if (alarm_button.justPressed())
  {
    Serial.println("pressed alarm");
    tft.fillScreen(BLACK);
    currentScreen = ALARM;
    tft.setFont(&FreeSans15pt7b);
    back_button.drawButton(false);
    hour_up.drawButton(false);
    minutes_up.drawButton(false);
    defaultAlarm();
    return;
  }
  if (forecast_button.justPressed())
  {
    Serial.println("pressed forecast");
    tft.fillScreen(BLACK);
    currentScreen = FORECAST;
    back_button.drawButton(false);
    reloadForecast = false;
    defaultForecast();
    // tft.drawRGBBitmap(390, 5, homeScreen, 80, 80);
    showBMP("/home.bmp", 390, 5);
    showBMP("/arrow_up_icon.bmp", 10, 160);
    showBMP("/arrow_down_icon.bmp", 10, 200);
    showBMP("/rainChanceIcon.bmp", 10, 240);

    showBMP("/arrow_up_icon.bmp", 130, 160);
    showBMP("/arrow_down_icon.bmp", 130, 200);
    showBMP("/rainChanceIcon.bmp", 130, 240);

    showBMP("/arrow_up_icon.bmp", 250, 160);
    showBMP("/arrow_down_icon.bmp", 250, 200);
    showBMP("/rainChanceIcon.bmp", 250, 240);
    return;
  }
  if (timer_button.justPressed())
  {
    Serial.println("pressed timer");
    tft.fillScreen(BLACK);
    currentScreen = TIMER;
    tft.setFont(&FreeSans15pt7b);
    back_button.drawButton(false);
    timerMinuteAdd_button.drawButton(false);
    timerSecAdd_button.drawButton(false);
    timerMinuteMinus_button.drawButton(false);
    timerSecMinus_button.drawButton(false);
    timerStart_button.drawButton(false);
    timerClear_Button.drawButton(false);
    timer5_button.drawButton(false);
    timer10_button.drawButton(false);
    timer15_button.drawButton(false);

    showBMP("/home.bmp", 390, 5);
    showBMP("/timerIcon/minus.bmp", 155, 155);
    showBMP("/timerIcon/add.bmp", 155, 10);
    showBMP("/timerIcon/minus.bmp", 275, 155);
    showBMP("/timerIcon/add.bmp", 275, 10);
    showBMP("/timerIcon/play.bmp", 400, 125);
    showBMP("/timerIcon/restart.bmp", 40, 80);
    showBMP("/timerIcon/number-5.bmp", 120, 250);
    showBMP("/timerIcon/number-10.bmp", 220, 250);
    showBMP("/timerIcon/number-15.bmp", 320, 250);

    display_new_timer();
    return;
  }
  // image+temp
  if (reloadTempImage)
  {
    tft.fillRect(15, 0, 100, 120, BLACK);
    displayImage(timeOfDayForImageToday, imageNumber, 15, 50);
    tft.setFont(&FreeSans15pt7b);
    tft.setTextSize(1);
    tft.setCursor(20, 30);
    tft.setTextColor(WHITE);
    tft.print("T: ");
    tft.print(String(temperature));
    tft.print(" C");
    reloadTempImage = false;
  }

  // date
  if (oldDayMonth != dayMonth)
  {
    tft.fillRect(225, 0, 240, 50, BLACK);
    tft.setCursor(230, 30);
    tft.setTextSize(1);
    tft.setFont(&FreeSans15pt7b);
    tft.setTextColor(WHITE);
    tft.print(getDayOfWeek(dayWeek) + "  " + String(dayMonth / 10) + String(dayMonth % 10) + "." + String(month / 10) + String(month % 10) + "." + String(year));
    oldDayMonth = dayMonth;
  }

  // hours:minutes
  if (oldMin != minutes)
  {
    tft.fillRect(155, 100, 200, 90, BLACK);
    tft.setCursor(160, 160);
    tft.setTextColor(GREEN);
    tft.setTextSize(2.9);
    tft.setFont(&digital20pt7b);
    tft.print(String(hours / 10) + String(hours % 10) + ":" + String(minutes / 10) + String(minutes % 10));
    tft.setFont(&FreeSans15pt7b);
    tft.setTextColor(WHITE);
    oldMin = minutes;
  }
}
void alarmScreen()
{
  bool down = Touch_getXY();
  back_button.press(down && back_button.contains(pixel_x, pixel_y));
  hour_up.press(down && hour_up.contains(pixel_x, pixel_y));
  minutes_up.press(down && minutes_up.contains(pixel_x, pixel_y));
  if (back_button.justPressed())
  {
    Serial.println("pressed back");
    if (alarmHours == hours && alarmMinutes == minutes && canBeOnDelay)
    {
      currentScreen = MAIN;
      alarmActivesScreen();
      return;
    }
    tft.fillScreen(BLACK);
    currentScreen = MAIN;
    defualtMain();
    return;
  }
  if (hour_up.justPressed())
  {
    alarmHours = (alarmHours + 1) % 24;
    tft.fillRect(105, 90, 200, 80, BLACK);
    tft.setFont(&digital20pt7b);
    tft.setTextColor(GREEN);
    tft.setCursor(120, 150);
    tft.setTextSize(2);
    tft.print(String(alarmHours / 10) + String(alarmHours % 10) + ":" + String(alarmMinutes / 10) + String(alarmMinutes % 10));
  }
  if (minutes_up.justPressed())
  {
    canBeOnDelay = true;
    alarmMinutes = (alarmMinutes + 1) % 60;
    tft.fillRect(105, 90, 200, 80, BLACK);
    tft.setFont(&digital20pt7b);
    tft.setTextColor(GREEN);
    tft.setCursor(120, 150);
    tft.setTextSize(2);
    tft.print(String(alarmHours / 10) + String(alarmHours % 10) + ":" + String(alarmMinutes / 10) + String(alarmMinutes % 10));
  }
}
void tempScreen()
{
  bool down = Touch_getXY();
  back_button.press(down && back_button.contains(pixel_x, pixel_y));
  if (back_button.justPressed())
  {
    Serial.println("pressed back");
    tft.fillScreen(BLACK);
    currentScreen = MAIN;
    defualtMain();
    return;
  }

  displayImage(timeOfDayForImageToday, imageNumber, 390, 250);
  if (reloadTemp)
  {
    tft.fillRect(30, 70, 90, 40, BLACK);
    tft.fillRect(160, 70, 90, 40, BLACK);
    tft.fillRect(270, 70, 120, 40, BLACK);
    tft.fillRect(30, 200, 90, 40, BLACK);
    tft.fillRect(160, 200, 35, 40, BLACK);
    tft.fillRect(230, 200, 160, 40, BLACK);
    defaultTemp();
    reloadTemp = false;
  }
}
void forecastScreen()
{
  bool down = Touch_getXY();
  back_button.press(down && back_button.contains(pixel_x, pixel_y));
  if (back_button.justPressed())
  {
    Serial.println("pressed back");
    tft.fillScreen(BLACK);
    defualtMain();
    currentScreen = MAIN;
    return;
  }

  if (reloadForecast)
  {
    tft.fillRect(45, 20, 70, 250, BLACK);
    tft.fillRect(165, 20, 70, 250, BLACK);
    tft.fillRect(285, 20, 70, 250, BLACK);
    defaultForecast();
    reloadForecast = false;
  }
}
void alarmActivesScreen()
{
  wasScreenBack = false;
  static unsigned long lastBuzzTime = 0;
  if (!alarmScreenShown)
  {
    tft.fillScreen(BLACK);
    defaultAlarmActive();
    alarmScreenShown = true;
  }

  unsigned long currentMillisAlarm = millis();
  if (currentMillisAlarm - previousMillisAlarm >= intervalAlarm)
  {
    previousMillisAlarm = currentMillisAlarm;
    if (soundOn)
    {
      soundOn = false;
      noTone(buzzer);
    }
    else
    {
      soundOn = true;
      analogWrite(buzzer, 200);
    }
  }

  bool down = Touch_getXY();
  dismissButton.press(down && dismissButton.contains(pixel_x, pixel_y));
  if (dismissButton.justPressed())
  {
    Serial.println("pressed dismiss");
    tft.fillScreen(BLACK);
    currentScreen = MAIN;
    noTone(buzzer);
    defualtMain();
    canBeOnDelay = false;
    wasScreenBack = true;
    return;
  }
}
void timerScreen()
{
  bool down = Touch_getXY();
  back_button.press(down && back_button.contains(pixel_x, pixel_y));
  timerMinuteAdd_button.press(down && timerMinuteAdd_button.contains(pixel_x, pixel_y));
  timerSecAdd_button.press(down && timerSecAdd_button.contains(pixel_x, pixel_y));
  timerMinuteMinus_button.press(down && timerMinuteMinus_button.contains(pixel_x, pixel_y));
  timerSecMinus_button.press(down && timerSecMinus_button.contains(pixel_x, pixel_y));
  timerStart_button.press(down && timerStart_button.contains(pixel_x, pixel_y));
  timerClear_Button.press(down && timerClear_Button.contains(pixel_x, pixel_y));
  timer5_button.press(down && timer5_button.contains(pixel_x, pixel_y));
  timer10_button.press(down && timer10_button.contains(pixel_x, pixel_y));
  timer15_button.press(down && timer15_button.contains(pixel_x, pixel_y));

  if (back_button.justPressed())
  {
    tft.fillScreen(BLACK);
    currentScreen = MAIN;
    defualtMain();
    return;
  }
  if (timerClear_Button.justPressed())
  {
    timer.reset();
    display_new_timer();
  }
  if (timerMinuteAdd_button.justPressed())
  {
    timer.addMinute();
    display_new_timer();
  }
  if (timerMinuteMinus_button.justPressed())
  {
    timer.subtractMinute();
    display_new_timer();
  }
  if (timerSecAdd_button.justPressed())
  {
    timer.addSecond();
    display_new_timer();
  }
  if (timerSecMinus_button.justPressed())
  {
    timer.subtractSecond();
    display_new_timer();
  }
  if (timerStart_button.justPressed())
  {
    if (timer.state == TIMER_RUNNING)
    {
      timer.stop();
    }
    else
    {
      timer.start();
    }
    delay(500);
  }
  if (timer5_button.justPressed())
  {
    timer.setTime(5, 0);
    display_new_timer();
  }
  if (timer10_button.justPressed())
  {
    timer.setTime(10, 0);
    display_new_timer();
  }
  if (timer15_button.justPressed())
  {
    timer.setTime(15, 0);
    display_new_timer();
  }
}
void timerActivesScreen()
{
  static bool timerScreenShown = false;
  static unsigned long startMillis;
  static const unsigned long duration = 30000; // 30 seconds
  unsigned long currentMillisTimer = millis();
  if (currentMillisTimer - previousMillisTimer >= intervalAlarm)
  {
    previousMillisTimer = currentMillisTimer;
    if (soundOn)
    {
      soundOn = false;
      noTone(buzzer);
    }
    else
    {
      soundOn = true;
      analogWrite(buzzer, 200);
    }
  }

  if (!timerScreenShown)
  {
    tft.fillScreen(BLACK);
    defaultTimerActive();
    timerScreenShown = true;
    startMillis = millis();
  }

  unsigned long currentMillis = millis();
  if (currentMillis - startMillis >= duration)
  {
    timerScreenShown = false;
    tft.fillScreen(BLACK);
    currentScreen = MAIN;
    timer.reset();
    defualtMain();
    return;
  }

  bool down = Touch_getXY();
  dismissButton.press(down && dismissButton.contains(pixel_x, pixel_y));
  if (dismissButton.justPressed())
  {
    timerScreenShown = false;
    tft.fillScreen(BLACK);
    currentScreen = MAIN;
    timer.reset();
    defualtMain();
    return;
  }
}
void display_new_timer()
{
  tft.fillRect(140, 85, 200, 65, BLACK);
  tft.setFont(&digital20pt7b);
  tft.setTextColor(GREEN);
  tft.setCursor(155, 140);
  tft.setTextSize(2);
  tft.print(String(timer.minutes / 10) + String(timer.minutes % 10) + ":" + String(timer.seconds / 10) + String(timer.seconds % 10));
}

String getDayOfWeek(int dayOfWeek)
{
  String dayName; // Initialize empty string for day name
  if (dayOfWeek > 7)
  {
    dayOfWeek -= 7;
  }
  switch (dayOfWeek)
  {
  case 1:
    dayName = "Sun";
    break;
  case 2:
    dayName = "Mon";
    break;
  case 3:
    dayName = "Tue";
    break;
  case 4:
    dayName = "Wed";
    break;
  case 5:
    dayName = "Thu";
    break;
  case 6:
    dayName = "Fri";
    break;
  case 7:
    dayName = "Sat";
    break;
  default:
    dayName = ""; // Invalid day of week integer
    break;
  }

  return dayName;
}

void displayImage(String timeOfDay, int imageNumber, int x, int y)
{
  String filename = "/" + timeOfDay + "/" + String(imageNumber) + ".bmp";
  showBMP(filename.c_str(), x, y);
}

void defaultForecast()
{
  Serial.println(tommorowIcon);
  tft.setTextColor(WHITE);
  tft.setFont(&FreeSans15pt7b);
  tft.setTextSize(1);

  // today
  tft.setCursor(50, 60);
  tft.print(getDayOfWeek(dayWeek));
  displayImage("day", todayIcon, 40, 80);
  tft.setCursor(50, 180);
  tft.print(String(todayMax) + "C");
  tft.setCursor(50, 220);
  tft.print(String(todayMin) + "C");
  tft.setCursor(50, 260);
  tft.print(String(todayRain) + "%");

  // tommorow
  tft.setCursor(170, 60);
  tft.print(getDayOfWeek((dayWeek + 1)));
  displayImage("day", tommorowIcon, 160, 80);
  tft.setCursor(170, 180);
  tft.print(String(tommorowMax) + "C");
  tft.setCursor(170, 220);
  tft.print(String(tommorowMin) + "C");
  tft.setCursor(170, 260);
  tft.print(String(tommorowRain) + "%");

  // the day after tommorow
  tft.setCursor(290, 60);
  tft.print(getDayOfWeek((dayWeek + 2)));
  displayImage("day", tommorow1Icon, 280, 80);
  tft.setCursor(290, 180);
  tft.print(String(tommorow1Max) + "C");
  tft.setCursor(290, 220);
  tft.print(String(tommorow1Min) + "C");
  tft.setCursor(290, 260);
  tft.print(String(tommorow1Rain) + "%");
}
void defualtMain()
{
  tft.fillScreen(BLACK);
  // showBMP("/timer.bmp", 145, 230);
  // tft.drawRGBBitmap(20, 230, alarmIcon, 64, 64);
  showBMP("/clock.bmp", 20, 230);
  showBMP("/timer.bmp", 145, 230);
  showBMP("/temperature.bmp", 270, 230);
  // tft.drawRGBBitmap(270, 230, tempIcon, 64, 64);
  showBMP("/forecast.bmp", 395, 230);
  // tft.drawRGBBitmap(395, 230, forecastIcon, 64, 64);

  displayImage(timeOfDayForImageToday, imageNumber, 15, 50);
  tft.fillRect(15, 0, 150, 50, BLACK);
  tft.setFont(&FreeSans15pt7b);
  tft.setTextSize(1);
  tft.setCursor(20, 30);
  tft.setTextColor(WHITE);
  tft.print("T: ");
  tft.print(String(temperature));
  tft.print(" C");
  tft.fillRect(225, 0, 240, 50, BLACK);
  tft.setCursor(230, 30);
  tft.setTextSize(1);
  tft.setFont(&FreeSans15pt7b);
  tft.setTextColor(WHITE);
  tft.print(getDayOfWeek(dayWeek) + "  " + String(dayMonth / 10) + String(dayMonth % 10) + "." + String(month / 10) + String(month % 10) + "." + String(year));
  tft.fillRect(155, 100, 200, 90, BLACK);
  tft.setCursor(160, 160);
  tft.setTextColor(GREEN);
  tft.setTextSize(2.9);
  tft.setFont(&digital20pt7b);
  tft.print(String(hours / 10) + String(hours % 10) + ":" + String(minutes / 10) + String(minutes % 10));
  tft.setTextColor(WHITE);
  tft.setTextSize(1);
}
void defaultTemp()
{
  tft.setTextSize(1);
  // Temperature
  tft.setCursor(20, 60);
  tft.setTextColor(GRAY);
  tft.setFont(&FreeSans10pt7b);
  tft.print("Temperature");
  tft.setTextColor(WHITE);
  tft.setFont(&FreeSans20pt7b);
  tft.setCursor(40, 105);
  tft.print(String(temperature) + "C");

  // humidity
  tft.setCursor(160, 60);
  tft.setTextColor(GRAY);
  tft.setFont(&FreeSans10pt7b);
  tft.print("Humidity");
  tft.setTextColor(WHITE);
  tft.setFont(&FreeSans20pt7b);
  tft.setCursor(160, 105);
  tft.print(String(humidity) + "%");

  // Pressure
  tft.setCursor(270, 60);
  tft.setTextColor(GRAY);
  tft.setFont(&FreeSans10pt7b);
  tft.print("Pressure");
  tft.setTextColor(WHITE);
  tft.setFont(&FreeSans20pt7b);
  tft.setCursor(270, 105);
  tft.print(pressure);

  // Feels Like
  tft.setCursor(20, 190);
  tft.setTextColor(GRAY);
  tft.setFont(&FreeSans10pt7b);
  tft.print("Feels Like");
  tft.setTextColor(WHITE);
  tft.setFont(&FreeSans20pt7b);
  tft.setCursor(35, 230);
  tft.print(String(feelsLike) + "C");

  // UV
  tft.setCursor(160, 190);
  tft.setTextColor(GRAY);
  tft.setFont(&FreeSans10pt7b);
  tft.print("UV");
  tft.setTextColor(WHITE);
  tft.setFont(&FreeSans20pt7b);
  tft.setCursor(165, 230);
  tft.print(UV);

  // wind Speed
  tft.setCursor(255, 190);
  tft.setTextColor(GRAY);
  tft.setFont(&FreeSans10pt7b);
  tft.print("wind Speed");
  tft.setTextColor(WHITE);
  tft.setFont(&FreeSans20pt7b);
  tft.setCursor(230, 230);
  tft.print(String(windSpeed, 1) + "KPH");
}
void defaultAlarm()
{
  tft.fillRect(102, 182, 96, 96, WHITE);
  tft.fillRect(105, 185, 90, 90, BLACK);
  tft.setCursor(120, 255);
  tft.setFont(&FreeSans20pt7b);
  tft.setTextSize(2);
  tft.print("H");
  tft.fillRect(222, 182, 96, 96, WHITE);
  tft.fillRect(225, 185, 90, 90, BLACK);
  tft.setCursor(240, 255);
  tft.setFont(&FreeSans20pt7b);
  tft.setTextSize(2);
  tft.print("M");
  // tft.drawRGBBitmap(390, 5, homeScreen, 80, 80);
  showBMP("/home.bmp", 390, 5);
  tft.setTextColor(WHITE);
  tft.setFont(&FreeSans15pt7b);
  tft.setTextSize(1);
  tft.setCursor(150, 40);
  tft.print("Set Alarm");
  tft.setFont(&digital20pt7b);
  tft.setTextColor(GREEN);
  tft.setCursor(120, 150);
  tft.setTextSize(2);
  tft.print(String(alarmHours / 10) + String(alarmHours % 10) + ":" + String(alarmMinutes / 10) + String(alarmMinutes % 10));
}
void defaultAlarmActive()
{
  dismissButton.drawButton(false);
  tft.setTextSize(1);
  tft.setTextColor(WHITE);
  tft.setFont(&FreeSans20pt7b);
  // tft.drawRGBBitmap(180, 20, alarmIcon, 64, 64);
  showBMP("/clock.bmp", 180, 20);
  tft.setCursor(150, 140);
  tft.print("ALARM");
  tft.setCursor(150, 200);
  tft.print(String(alarmHours / 10) + String(alarmHours % 10) + ":" + String(alarmMinutes / 10) + String(alarmMinutes % 10));
  tft.fillRect(150, 220, 174, 48, WHITE);
...

This file has been truncated, please download it to see its full contents.

Arduino RP2040 Code

Arduino
/* 
  Arduino IoT Cloud Variables description
  The following variables are automatically generated and updated when changes are made to the Thing
  String time_str;
  int dayMonth;
  int dayWeek;
  int hours;
  int humidity;
  int minutes;+++
  int monthNow;
  int pressure;
  int temp;
  int yearNow;
  bool override_send;
  Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
  which are called when their values are changed from the Dashboard.
  These functions are generated with the Thing and added at the end of this sketch.
*/

#include "thingProperties.h"
#include <Wire.h>
#include <WiFi.h>
#include <ArduinoJson.h>
#include <ArduinoHttpClient.h>
#include "TimeLib.h"
#include <WiFiClient.h>


// Set up WiFi network credentials
const char* ssid = "ssid";
const char* password = "password";

const char* host = "api.weatherapi.com";
const int port = 80;

// Your API key
const char* apiKey = "key";

WiFiClient wifiClient;
HttpClient client = HttpClient(wifiClient, host, port);

unsigned long previousMillis = 0;       
// constants won't change:
const long interval = 300000; //5 min
// const long interval = 5000; //5 sec

unsigned long previousMillis1 = 0;        
// constants won't change:
const long interval1 = 30000; //30 sec

int minuteOld = 0;

void setup() {
  // Initialize serial and wait for port to open:
  Serial.begin(9600);
  Wire.begin();
  delay(1000);
  WiFi.begin(ssid, password);
  
  int failedAttempts = 0;
  while (WiFi.status() != WL_CONNECTED) {
    if (failedAttempts >= 5) {
      Serial.println("Failed to connect to WiFi. Resetting...");
      delay(2000);
      NVIC_SystemReset(); // Reset the Arduino RP2040
    }

    delay(1000);
    Serial.println("Connecting to WiFi...");
    failedAttempts++;
  }
  
  Serial.println("Connected to WiFi");
  //timeZoneOffset = ArduinoCloud.getTimeZoneOffset();
  // This delay gives the chance to wait for a Serial Monitor without blocking if none is found
  delay(1500); 

  // Defined in thingProperties.h
  initProperties();

  // Connect to Arduino IoT Cloud
  ArduinoCloud.begin(ArduinoIoTPreferredConnection);
  
  /*
     The following function allows you to obtain more information
     related to the state of network and IoT Cloud connection and errors
     the higher number the more granular information you’ll get.
     The default is 0 (only errors).
     Maximum is 4
 */
  setDebugMessageLevel(2);
  ArduinoCloud.printDebugInfo();
}

void loop() {
  ArduinoCloud.update();
  //5 min delay
  
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval || override_send) {
    previousMillis = currentMillis;
    if (client.connect(host, port)) {
      Serial.println("Connected to server");
      String url = "/v1/forecast.json?key=";
      url += apiKey;
      url += "&q=modiin,IL&days=3";
      
      client.print(String("GET ") + url + " HTTP/1.1\r\n" +
                "Host: " + host + "\r\n" +
                "Connection: close\r\n\r\n"); // remove the headers section
  
      while (!client.available()) {
        delay(10);
      }
      String response = client.readString();
      Serial.println("----");
      Serial.println(response);
      int firstOpeiningBracket = response.indexOf('{');
      String data = response.substring(firstOpeiningBracket);
      
      StaticJsonDocument<5000> doc;
      
      DeserializationError error = deserializeJson(doc, data);
  
      if (error) {
        Serial.print("deserializeJson() failed: ");
        Serial.println(error.f_str());
        return;
      }
  
      temp = doc["current"]["temp_c"];
      humidity = doc["current"]["humidity"];
      pressure = doc["current"]["pressure_mb"];
  
  
      String todayIconUrl = doc["current"]["condition"]["icon"];
      
      StaticJsonDocument<200> jsonOpenWeatherMapTHP;
  
      jsonOpenWeatherMapTHP["tw"] = temp;
      jsonOpenWeatherMapTHP["hw"] = humidity;
      jsonOpenWeatherMapTHP["pw"] = pressure;
      
      String jsonStringOpenWeatherMapTHP;
      serializeJson(jsonOpenWeatherMapTHP, jsonStringOpenWeatherMapTHP);
      Wire.beginTransmission(8);
      if(jsonStringOpenWeatherMapTHP.length() < 34){
        Wire.write(jsonStringOpenWeatherMapTHP.c_str(), jsonStringOpenWeatherMapTHP.length());
      }else{
        Serial.println("Too long max of 32 bytes");
      }
      Wire.endTransmission();
      
      StaticJsonDocument<200> jsonOpenWeatherMapTodayIcon;
      
      int start = todayIconUrl.lastIndexOf("/") + 1;
      int end = todayIconUrl.lastIndexOf(".");
      String numStr = todayIconUrl.substring(start, end);
      int num = numStr.toInt();
      
      String timeOfDayForImageToday = "day";
      if (todayIconUrl.indexOf("day") != -1) {
        timeOfDayForImageToday = "day";
      } else if (todayIconUrl.indexOf("night") != -1) {
        timeOfDayForImageToday = "night";
      }
      
      jsonOpenWeatherMapTodayIcon["int"] = num;
      jsonOpenWeatherMapTodayIcon["nort"] = timeOfDayForImageToday;
      
      
      String jsonStringOpenWeatherMapTodayIcon;
      serializeJson(jsonOpenWeatherMapTodayIcon, jsonStringOpenWeatherMapTodayIcon);
      Wire.beginTransmission(8);
      if(jsonStringOpenWeatherMapTodayIcon.length() < 34){
        Wire.write(jsonStringOpenWeatherMapTodayIcon.c_str(), jsonStringOpenWeatherMapTodayIcon.length());
      }else{
        Serial.println("Too long max of 32 bytes");
      }
      Wire.endTransmission();
      
      StaticJsonDocument<200> jsonOpenWeatherMapTodayFullInfo;
      
      int feels_like = doc["current"]["feelslike_c"];
      int uv = doc["current"]["uv"];
      float wind_speed = doc["current"]["wind_kph"];
      jsonOpenWeatherMapTodayFullInfo["flt"] = feels_like;
      jsonOpenWeatherMapTodayFullInfo["uv"] = uv;
      jsonOpenWeatherMapTodayFullInfo["wst"] = String(wind_speed,1);
      
      String jsonStringOpenWeatherMapFullInfo;
      serializeJson(jsonOpenWeatherMapTodayFullInfo, jsonStringOpenWeatherMapFullInfo);
      Wire.beginTransmission(8);
      if(jsonStringOpenWeatherMapFullInfo.length() < 34){
        Wire.write(jsonStringOpenWeatherMapFullInfo.c_str(), jsonStringOpenWeatherMapFullInfo.length());
      }else{
        Serial.println("Too long max of 32 bytes");
      }
      Wire.endTransmission();
      
      StaticJsonDocument<200> jsonOpenWeatherMapTodayForecast;
      
      int todayMaxTemp = doc["forecast"]["forecastday"][0]["day"]["maxtemp_c"];
      int todayMinTemp = doc["forecast"]["forecastday"][0]["day"]["mintemp_c"];
      int todayRain = doc["forecast"]["forecastday"][0]["day"]["daily_chance_of_rain"];
      String todayIcon = doc["forecast"]["forecastday"][0]["day"]["condition"]["icon"];
      
      start = todayIcon.lastIndexOf("/") + 1;
      end = todayIcon.lastIndexOf(".");
      numStr = todayIcon.substring(start, end);
      int iconNumberTodayForecast = numStr.toInt();
      
      jsonOpenWeatherMapTodayForecast["t1mx"] = todayMaxTemp;
      jsonOpenWeatherMapTodayForecast["t1mi"] = todayMinTemp;
      jsonOpenWeatherMapTodayForecast["t1r"] = todayRain;

      String jsonStringOpenWeatherMapTodayForecast;
      serializeJson(jsonOpenWeatherMapTodayForecast, jsonStringOpenWeatherMapTodayForecast);
      Wire.beginTransmission(8);
      if(jsonStringOpenWeatherMapTodayForecast.length() < 34){
        Wire.write(jsonStringOpenWeatherMapTodayForecast.c_str(), jsonStringOpenWeatherMapTodayForecast.length());
      }else{
        Serial.println("Too long max of 32 bytes");
      }
      Wire.endTransmission();
      
      StaticJsonDocument<200> jsonOpenWeatherMapTommorowForecast;
      
      int tommorowMaxTemp = doc["forecast"]["forecastday"][1]["day"]["maxtemp_c"];
      int tommorowMinTemp = doc["forecast"]["forecastday"][1]["day"]["mintemp_c"];
      int tommorowRain = doc["forecast"]["forecastday"][1]["day"]["daily_chance_of_rain"];
      String tommorowIcon = doc["forecast"]["forecastday"][1]["day"]["condition"]["icon"];
      
      start = tommorowIcon.lastIndexOf("/") + 1;
      end = tommorowIcon.lastIndexOf(".");
      numStr = tommorowIcon.substring(start, end);
      int iconNumberTommorowForecast = numStr.toInt();
      
      jsonOpenWeatherMapTommorowForecast["t2mx"] = tommorowMaxTemp;
      jsonOpenWeatherMapTommorowForecast["t2mi"] = tommorowMinTemp;
      jsonOpenWeatherMapTommorowForecast["t2r"] = tommorowRain;

      String jsonStringOpenWeatherMapTommorowForecast;
      serializeJson(jsonOpenWeatherMapTommorowForecast, jsonStringOpenWeatherMapTommorowForecast);
      Wire.beginTransmission(8);
      if(jsonStringOpenWeatherMapTommorowForecast.length() < 34){
        Wire.write(jsonStringOpenWeatherMapTommorowForecast.c_str(), jsonStringOpenWeatherMapTommorowForecast.length());
      }else{
        Serial.println("Too long max of 32 bytes");
      }
      Wire.endTransmission();
      
      StaticJsonDocument<200> jsonOpenWeatherMapTommorow1Forecast;
      
      int tommorow1MaxTemp = doc["forecast"]["forecastday"][2]["day"]["maxtemp_c"];
      int tommorow1MinTemp = doc["forecast"]["forecastday"][2]["day"]["mintemp_c"];
      int tommorow1Rain = doc["forecast"]["forecastday"][2]["day"]["daily_chance_of_rain"];
      String tommorow1Icon = doc["forecast"]["forecastday"][2]["day"]["condition"]["icon"];
      
      start = tommorow1Icon.lastIndexOf("/") + 1;
      end = tommorow1Icon.lastIndexOf(".");
      numStr = tommorow1Icon.substring(start, end);
      int iconNumberTommorow1Forecast = numStr.toInt();
      
      jsonOpenWeatherMapTommorow1Forecast["t3mx"] = tommorow1MaxTemp;
      jsonOpenWeatherMapTommorow1Forecast["t3mi"] = tommorow1MinTemp;
      jsonOpenWeatherMapTommorow1Forecast["t3r"] = tommorow1Rain;

      String jsonStringOpenWeatherMapTommorow1Forecast;
      serializeJson(jsonOpenWeatherMapTommorow1Forecast, jsonStringOpenWeatherMapTommorow1Forecast);
      Wire.beginTransmission(8);
      if(jsonStringOpenWeatherMapTommorow1Forecast.length() < 34){
        Wire.write(jsonStringOpenWeatherMapTommorow1Forecast.c_str(), jsonStringOpenWeatherMapTommorow1Forecast.length());
      }else{
        Serial.println("Too long max of 32 bytes");
      }
      Wire.endTransmission();
      
      StaticJsonDocument<200> jsonOpenWeatherMapForecastIcons;
      
      jsonOpenWeatherMapForecastIcons["t1c"] = iconNumberTodayForecast;
      jsonOpenWeatherMapForecastIcons["t2c"] = iconNumberTommorowForecast;
      jsonOpenWeatherMapForecastIcons["t3c"] = iconNumberTommorow1Forecast;

      String jsonStringOpenWeatherMapForecastIcons;
      serializeJson(jsonOpenWeatherMapForecastIcons, jsonStringOpenWeatherMapForecastIcons);
      Wire.beginTransmission(8);
      if(jsonStringOpenWeatherMapForecastIcons.length() < 34){
        Wire.write(jsonStringOpenWeatherMapForecastIcons.c_str(), jsonStringOpenWeatherMapForecastIcons.length());
      }else{
        Serial.println("Too long max of 32 bytes");
      }
      Wire.endTransmission();
      
      
      Serial.print("Temperature: ");
      Serial.println(temp);
      Serial.print("Humidity: ");
      Serial.println(humidity);
      Serial.print("Pressure: ");
      Serial.println(pressure);
      Serial.println(todayMaxTemp);
      Serial.print("Today's Min Temperature: ");
      Serial.println(todayMinTemp);
      Serial.print("Today's Icon URL: ");
      Serial.println(todayIconUrl);
      
      client.stop();
    } else {
      Serial.println("Connection failed");
    }
  }
  time_t utcCalc = ArduinoCloud.getLocalTime();
  minutes = minute(utcCalc);
  if (minutes != minuteOld) {
    previousMillis1 = currentMillis;
    time_t utcCalc = ArduinoCloud.getLocalTime();
    hours = hour(utcCalc);
    minutes = minute(utcCalc);
    monthNow = month(utcCalc);
    yearNow = year(utcCalc);
    dayMonth = day(utcCalc);
    dayWeek = weekday(utcCalc);
    
    StaticJsonDocument<200> jsonTimeHMM;
    jsonTimeHMM["ht"] = hours;
    jsonTimeHMM["mit"] = minutes;
    jsonTimeHMM["mot"] = monthNow;
    
    StaticJsonDocument<200> jsonTimeYDD;
    jsonTimeYDD["yt"] = yearNow;
    jsonTimeYDD["dmt"] = dayMonth;
    jsonTimeYDD["dwt"] = dayWeek;
    
    time_str = String(hours/10)+String(hours%10)+":"+String(minutes/10)+String(minutes%10);
    
    String jsonStringTimeHMM;
    serializeJson(jsonTimeHMM, jsonStringTimeHMM);
    String jsonStringTimeYDD;
    serializeJson(jsonTimeYDD, jsonStringTimeYDD);
    Wire.beginTransmission(8);
    if(jsonStringTimeHMM.length() < 34){
      Wire.write(jsonStringTimeHMM.c_str(), jsonStringTimeHMM.length());
    }else{
      Serial.println("Too long max of 32 bytes");
    }
    Wire.endTransmission();
    Wire.beginTransmission(8);
    if(jsonStringTimeYDD.length() < 34){
      Wire.write(jsonStringTimeYDD.c_str(), jsonStringTimeYDD.length());
    }else{
      Serial.println("Too long max of 32 bytes");
    }
    Wire.endTransmission();
    
    
    Serial.print(hours);
    Serial.print(":");
    Serial.println(minutes);
    Serial.print(dayMonth);
    Serial.print(".");
    Serial.print(monthNow);
    Serial.print(".");
    Serial.print(yearNow);
    Serial.print(".");
    Serial.println(dayWeek);
    
  }
  minuteOld = minutes;
}

/*
  Since DayWeek is READ_WRITE variable, onDayWeekChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onDayWeekChange()  {
  // Add your code here to act upon DayWeek change
}
/*
  Since Minutes is READ_WRITE variable, onMinutesChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onMinutesChange()  {
  // Add your code here to act upon Minutes change
}
/*
  Since OverrideSend is READ_WRITE variable, onOverrideSendChange() is
  executed every time a new value is received from IoT Cloud.
*/
void onOverrideSendChange()  {
  // Add your code here to act upon OverrideSend change
}

Images

Fonts

Icons

Credits

Daniel Harel
1 project • 2 followers
16 years old, Loves building, programming electronics and 3D printing.
Contact

Comments

Please log in or sign up to comment.