#include <Arduino.h>
#include <Keypad.h>
#include "clock.h"
#include "weather.h"
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include <LiquidCrystal_I2C.h>
#include "Buzzer.h"
#include "clock-arch.h"
#define I2C_ADDR 0x27
#define LCD_COLUMNS 16
#define LCD_LINES 2
uint8_t state;
unsigned long clockTimer;
unsigned long weatherAPItimer;
unsigned long weatherDisplayTimer;
const char* password = "";
const char* ssid = "Wokwi-GUEST";
uint8_t valIndex;
uint8_t cursorPos;
char entered_value [6];
const uint8_t ROWS = 4;
const uint8_t COLS = 4;
char keys[ROWS][COLS] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
uint8_t colPins[COLS] = { 1, 0, 3, 2 };
uint8_t rowPins[ROWS] = { 4, 5, 6, 7 };
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
void enterTime()
{
state = 1;
memset(&entered_value[0], 0, sizeof(entered_value));
cursorPos = 0;
valIndex = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Set clock then ");
lcd.setCursor(0, 1);
lcd.print("press # to save.");
delay(3000);
lcd.clear();
}
void enterAlarm()
{
state = 2;
memset(&entered_value[0], 0, sizeof(entered_value));
cursorPos = 0;
valIndex = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Enter alarm time");
lcd.setCursor(0, 1);
lcd.print("press # to save.");
delay(3000);
lcd.clear();
}
void nextChar(char key)
{
if (valIndex < 6)
{
entered_value[valIndex] = key;
lcd.setCursor(0, 0);
lcd.print(entered_value);
cursorPos++;
valIndex++;
}
}
void eraseChar()
{
if (valIndex > 0 )
{
valIndex--;
cursorPos--;
entered_value[valIndex] = '\0';
lcd.setCursor(cursorPos, 0);
lcd.print(' ');
lcd.setCursor(cursorPos, 0);
}
}
void keyPadState0()
{
char key = keypad.getKey();
switch(key)
{
case 'A':
enterAlarm();
break;
case 'C':
enterTime();
break;
}
}
void keyPadState1()
{
char key = keypad.getKey();
switch(key)
{
case '*':
eraseChar();
break;
default:
if (isDigit(key))
{
nextChar(key);
}
break;
}
}
void keyPadState2()
{
char key = keypad.getKey();
switch(key)
{
case '*':
eraseChar();
break;
default:
if (isDigit(key))
{
nextChar(key);
}
break;
}
}
void getInput()
{
switch (state)
{
case 0:
keyPadState0();
break;
case 1:
keyPadState1();
break;
case 2:
keyPadState2();
break;
}
}
void setup()
{
pinMode(8, OUTPUT);
pinMode(10, OUTPUT);
Wire.begin(18, 19);
lcd.init();
lcd.backlight();
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
lcd.print(".");
delay(1000);
}
lcd.clear();
{
char t [] = __TIME__;
char compileTime [] = { t[0], t[1], t[3], t[4], t[6], t[7] };
}
}
void loop()
{
if (state == 0)
{
unsigned long millisNow = millis();
if (millisNow - clockTimer >= 1000)
if (millisNow - weatherDisplayTimer >= 10000)
{
weatherDisplayTimer = millisNow;
printweather(lcd);
}
if (millisNow - weatherAPItimer >= 3600000)
{
weatherAPItimer = millisNow;
getweather(lcd);
}
}
getInput();
}
#include "Buzzer.h"
void buzzerTone(uint8_t pin, uint16_t duration, uint16_t interval)
{
for (int i = 0; i < duration; ++i)
{
REG_WRITE(GPIO_OUT_W1TS_REG, 1<<pin);
delayMicroseconds(interval);
REG_WRITE(GPIO_OUT_W1TC_REG, 1<<pin);
delayMicroseconds(interval);
}
}
#ifndef BUZZER_H
#define BUZZER_H
#include <Arduino.h>
void buzzerTone(uint8_t pin, uint16_t duration, uint16_t interval);
#endif
#ifndef WEATHER_H
#define WEATHER_H
void getWeather(LiquidCrystal_I2C &lcd);
void printWeather(LiquidCrystal_I2C &lcd);
#endif
#include "weather.h"
uint8_t displayState = 0;
DynamicJsonDocument weatherDoc(2048);
const uint8_t weatherCodes [28] = {
0, 1, 2, 3, 45, 48, 51, 53, 55, 56, 57, 61, 63, 65, 66,
67, 71, 73, 75, 77, 80, 81, 82, 85, 86, 95, 96, 99
};
const char* weatherDescriptions [28] = {
"Clear Sky", "Mainly Clear", "Partly Cloudy", "Overcast", "Fog"
"Rime", "Light Drizzle", "Moderate Drizzle", "Heavy Drizzle", "Freezing Drizzle",
"Freezing Drizzle", "Slight Rain", "Moderate Rain", "Heavy Rain", "Freezing Rain",
"Freezing Rain", "Light Snow", "Moderate Snow", "Heavy Snow", "Snow Grains",
"Light Showers", "Moderate Showers", "Heavy Showers", "Snow Showers", "Snow Storm",
"Thunderstorms", "Slight Hail", "Heavy Hail"
};
const char* codeToDescription(uint8_t code)
{
for (int i = 0; i < 28; ++i)
{
if (weatherCodes[i] == code)
{
return weatherDescriptions[i];
}
}
return "No weather info.";
}
void printTemperature(LiquidCrystal_I2C &lcd)
{
float tempC = weatherDoc["current_weather"]["temperature"];
float tempF = (tempC * 1.8) + 32;
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.print(tempF);
lcd.print((char)223);
lcd.print(" F ");
}
void printConditions(LiquidCrystal_I2C &lcd)
{
uint8_t weatherCode = weatherDoc["current_weather"]["weathercode"];
const char* weatherDescription = codeToDescription(weatherCode);
uint8_t buffer = (16 - strlen(weatherDescription)) / 2;
lcd.setCursor(0, 1);
for (int i = 0; i < buffer; ++i)
{
lcd.print(' ');
}
lcd.print(weatherDescription);
lcd.print(" ");
}
void printDate(LiquidCrystal_I2C &lcd)
{
String dateStr = weatherDoc["current_weather"]["time"].as<String>();
dateStr = dateStr.substring(0, 10);
uint8_t buffer = (16 - dateStr.length()) / 2;
lcd.setCursor(0, 1);
for (int i = 0; i < buffer; ++i)
{
lcd.print(' ');
}
lcd.print(dateStr);
lcd.print(" ");
}
void printweather(LiquidCrystal_I2C &lcd)
{
switch(displayState)
{
case 0:
printTemperature(lcd);
break;
case 1:
printConditions(lcd);
break;
case 2:
printDate(lcd);
break;
}
displayState = displayState < 2 ? displayState + 1 : 0;
}
void getweather(LiquidCrystal_I2C &lcd)
{
if ((WiFi.status() == WL_CONNECTED))
{
HTTPClient http;
int httpCode = http.GET();
if (httpCode > 0)
{
String result = http.getString();
DeserializationError error = deserializeJson(weatherDoc, result);
if (error)
{
lcd.setCursor(4, 1);
lcd.print("--json--");
}
}
else
{
lcd.setCursor(4, 1);
lcd.print("--http--");
}
http.end();
}
else
{
lcd.setCursor(4, 1);
lcd.print("--wifi--");
}
}
#include "clock.h"
Clock::Clock(LiquidCrystal_I2C* display) : lcd(display), alarmSet(false), snooze(0) {}
Clock::Clock(LiquidCrystal_I2C *display)
{
lcd = display;
}
void Clock::alarm()
{
digitalWrite(8, HIGH);
buzzerTone(10, 50, 2000);
digitalWrite(8, LOW);
}
void Clock::silence()
{
if (alarmSet)
{
alarmSet = false;
lcd->clear();
lcd->setCursor(0, 0);
lcd->print("Alarm disabled. ");
delay(3000);
lcd->clear();
}
}
void Clock::addToSnooze()
{
snooze = snooze < 3300 ? snooze + 300 : snooze;
int snoozeMinutes = snooze / 60;
lcd->clear();
lcd->setCursor(0, 0);
lcd->print("Snooze");
lcd->setCursor(0, 1);
lcd->print(snoozeMinutes);
lcd->print(" minutes.");
delay(3000);
lcd->clear();
}
#include "clock.h"
LiquidCrystal_I2C lcd(0x27, 16, 2);
Clock myClock(&lcd);
void Clock::printTime(uint8_t hours, uint8_t minutes, uint8_t seconds)
{
lcd->setCursor(4, 0);
if (hours < 10)
{
lcd->print('0');
}
lcd->print(hours);
lcd->print(':');
if (minutes < 10)
{
lcd->print('0');
}
lcd->print(minutes);
lcd->print(':');
if (seconds < 10)
{
lcd->print('0');
}
lcd->print(seconds);
lcd->print(" ");
}
void Clock::updateClock()
{
gettimeofday(¤tTime, NULL);
int totalMinutes = currentTime.tv_sec / 60;
int hours = totalMinutes / 60;
int minutes = totalMinutes % 60;
int totalSeconds = minutes / 60;
int seconds = currentTime.tv_sec % 60;
printTime(hours, minutes, seconds);
uint32_t c = currentTime.tv_sec;
uint32_t a = alarmTime.tv_sec;
if (alarmSet && c >= a + snooze && c < a + 3600 )
{
alarm();
}
}
bool Clock::setTimeFromAPI()
{
if ((WiFi.status() == WL_CONNECTED))
{
HTTPClient http;
http.begin(endpoint);
int httpCode = http.GET();
if (httpCode > 0)
{
String result = http.getString();
DynamicJsonDocument doc(2048);
DeserializationError error = deserializeJson(doc, result);
if (!error)
{
String timeStr = doc["datetime"].as<String>();
uint8_t i = timeStr.indexOf('T');
timeStr = timeStr.substring(i + 1, i + 9);
timeStr.replace(":", "");
char buf [6];
timeStr.toCharArray(buf, 6);
setTime(buf);
return true;
}
}
http.end();
}
return false;
}
void Clock::setTime(char time [6])
{
char hoursEntered [2] = { time[0], time[1] };
char minutesEntered [2] = { time[2], time[3] };
char secondsEntered [2] = { time[4], time[5] };
uint8_t hours = atoi(hoursEntered);
uint8_t minutes = atoi(minutesEntered);
uint32_t seconds = atoi(secondsEntered);
uint32_t hoursToMinutes = hours * 60;
uint32_t totalMinutes = hoursToMinutes + minutes;
seconds += totalMinutes * 60;
currentTime.tv_sec = seconds;
currentTime.tv_usec = 0;
settimeofday(¤tTime, NULL);
lcd->clear();
}
void Clock::setAlarm(char time [6])
{
char hoursEntered [2] = { time[0], time[1] };
char minutesEntered [2] = { time[2], time[3] };
char secondsEntered [2] = { time[4], time[5] };
uint8_t hours = atoi(hoursEntered);
uint8_t minutes = atoi(minutesEntered);
uint32_t seconds = atoi(secondsEntered);
uint32_t hoursToMinutes = hours * 60;
uint32_t totalMinutes = hoursToMinutes + minutes;
seconds += totalMinutes * 60;
alarmTime.tv_sec = seconds;
alarmTime.tv_usec = 0;
alarmSet = true;
lcd->clear();
}
#ifndef CLOCK_H
#define CLOCK_H
class Clock
{
public:
int snooze;
void silence();
void updateClock();
void addToSnooze();
bool setTimeFromAPI();
void setTime(char time [6]);
void setAlarm(char time [6]);
Clock(LiquidCrystal_I2C *display);
private:
void alarm();
bool alarmSet;
LiquidCrystal_I2C *lcd;
struct timeval alarmTime;
struct timeval currentTime;
void printTime(uint8_t hours, uint8_t minutes, uint8_t seconds);
};
const char* endpoint = "https://api.weather.com/...";
#endif
Loading
esp32-c3-devkitm-1
esp32-c3-devkitm-1
<------- Set Alarm
<------- Set Clock
|
^
------------------------------ Submit / Silence Alarm
Erase / Snooze-----------------
^
|