#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_NeoPixel.h>
#include "NeoSegments.h"
// —————— Hardware Constants ——————
#define DATA_PIN 18 // NeoPixel data pin
#define PIXELS_PER_SEGMENT 6 // pixels per 7‑segment digit
#define NUM_DIGITS 4 // HH:MM → 4 digits
#define NUM_SYMBOL_PIXELS (2+1) // +2 for colon, +1 indicator dot
#define NUM_PIXELS (PIXELS_PER_SEGMENT * NUM_DIGITS *7 )+NUM_SYMBOL_PIXELS
// —————— Objects ——————
Adafruit_NeoPixel strip(NUM_PIXELS, DATA_PIN, NEO_GRB + NEO_KHZ800);
NeoSegments segs(strip, PIXELS_PER_SEGMENT);
RTC_DS1307 rtc;//RTC_DS3231 not supported in wokwi, but functionally similar enough
// —————— Alarm Definition ——————
struct Alarm {
bool enabled;
uint8_t hour, minute;
uint32_t color; // use strip.Color(r,g,b)
uint8_t toneID; // 0–2 in this example
bool triggered; // to fire only once per minute
};
// —————— Global Alarms List ——————
std::vector<Alarm> alarms;
// —————— Initialize Some Example Alarms ——————
void initializeAlarms() {
alarms.push_back({ true, 19, 15, strip.Color(0,255,0), 0, false });
alarms.push_back({ true, 6, 40, strip.Color(255,255,0), 1, false });
alarms.push_back({ true, 7, 10, strip.Color(255,255,255), 2, false });
}
// —————— Display Current Time on NeoSegments ——————
void displayTime() {
DateTime now = rtc.now();
char colonChar = (now.second() % 2 == 0) ? ':' : ';';//to make the colon blink!
char buf[6];
sprintf(buf, "%02d%c%02d", now.hour(), colonChar, now.minute());//this will blink the colon
//sprintf(buf, "%02d:%02d", now.hour(), now.minute());//this will make the colon steady
segs.setString(buf);
segs.update();
}
// —————— Check & Trigger Alarms ——————
void checkAlarms() {
DateTime now = rtc.now();
for (auto &alarm : alarms) {
bool match = alarm.enabled
&& now.hour() == alarm.hour
&& now.minute() == alarm.minute;
if (match && !alarm.triggered) {
triggerAlarm(alarm);
alarm.triggered = true;
}
else if (!match) {
alarm.triggered = false;
}
}
}
// —————— What Happens When an Alarm Fires ——————
void triggerAlarm(const Alarm &alarm) {
segs.setDigitColor(alarm.color);/*
// Build the string with the dot indicator at the end
DateTime now = rtc.now();
char buf[8];
sprintf(buf, "%02d:%02d.", now.hour(), now.minute()); // append dot
segs.setString(buf);*/
segs.update();
// 3) Play the selected tone
switch (alarm.toneID) {
//case 0 is silent
case 1: tone(13, 440, 500); break; // A4
case 2: tone(13, 523, 500); break; // C5
case 3: tone(13, 659, 500); break; // E5
}
}
// —————— Arduino Setup ——————
void setup() {
Serial.begin(115200);
Wire.begin();
// 1) RTC
if (!rtc.begin()) {
Serial.println("No RTC found!");
while (true) delay(10);
}
// 2) NeoSegments
segs.begin();
segs.setDigitColor(strip.Color(255,0,0));//default to red
segs.registerSymbol(':', 2, true);//":", two pixels long, visible
segs.registerSymbol(';', 2, false); // same thing but hidden state (off)
segs.registerSymbol('.', 1, true); //".", 1 pixel long, visible
// 3) Load or initialize your alarms
initializeAlarms();//this will be replaced with eeprom
Serial.println("Welcome to NeoSegClock: enter a comand or '?' for more info");
}
void handleSerialInput() {//this whole part is just a filler until the web ui is done
if (Serial.available()) {
String input = Serial.readStringUntil('\n');
input.trim(); // remove leading/trailing whitespace
if (input == "list") {
Serial.println("=== Alarms ===");
for (size_t i = 0; i < alarms.size(); ++i) {
Alarm &a = alarms[i];
Serial.printf("[%d] %02d:%02d | Color: #%06X | Tone: %d | Enabled: %s\n",
i, a.hour, a.minute, a.color, a.toneID, a.enabled ? "Yes" : "No");
}
}
else if (input =="?") {
Serial.println("enter a command");
Serial.println("list, color, enable, disable, edit, add, remove");
}
else if (input =="add") {
Serial.println("enter a new alarm (Hour Minute Red Green Blue Tone");
Serial.println("for example:");
Serial.println("add 12 55 120 0 255 0");
}
else if (input.startsWith("add ")) {
int hh, mm, r, g, b, tone;
sscanf(input.c_str(), "add %d %d %d %d %d %d", &hh, &mm, &r, &g, &b, &tone);
alarms.push_back({true, hh, mm, strip.Color(r, g, b), tone, false});
Serial.println("Alarm added.");
}
else if (input =="remove") {
Serial.println("enter the index of an alarm to remove it");
Serial.println("for example:");
Serial.println("remove 0");
}
else if (input.startsWith("remove ")) {
int index;
sscanf(input.c_str(), "remove %d", &index);
if (index >= 0 && index < alarms.size()) {
alarms.erase(alarms.begin() + index);
Serial.println("Alarm removed.");
} else {
Serial.println("Invalid index.");
}
}
else if (input =="enable") {
Serial.println("enter the index of an alarm to enable it");
Serial.println("for example:");
Serial.println("enable 1");
}
else if (input.startsWith("enable ")) {
int index;
sscanf(input.c_str(), "enable %d", &index);
if (index >= 0 && index < alarms.size()) {
alarms[index].enabled = true;
Serial.println("Alarm enabled.");
} else {
Serial.println("Invalid index.");
}
}
else if (input =="disable") {
Serial.println("enter the index of an alarm to disable it");
Serial.println("for example:");
Serial.println("disable 1");
}
else if (input.startsWith("disable ")) {
int index;
sscanf(input.c_str(), "disable %d", &index);
if (index >= 0 && index < alarms.size()) {
alarms[index].enabled = false;
Serial.println("Alarm disabled.");
} else {
Serial.println("Invalid index.");
}
}
else if (input =="edit") {
Serial.println("enter index hour minute color tone to edit");
Serial.println("for example:");
Serial.println("edit 0 1 25 255 255 0 2");
}
else if (input.startsWith("edit ")) {
int index, hh, mm, r, g, b, tone;
int parsed = sscanf(input.c_str(), "edit %d %d %d %d %d %d %d", &index, &hh, &mm, &r, &g, &b, &tone);
if (parsed == 7 && index >= 0 && index < alarms.size()) {
alarms[index].hour = hh;
alarms[index].minute = mm;
alarms[index].color = strip.Color(r, g, b);
alarms[index].toneID = tone;
Serial.printf("Alarm %d updated to %02d:%02d, Color: #%02X%02X%02X, Tone: %d\n",
index, hh, mm, r, g, b, tone);
}
}
else if (input =="color") {
Serial.println("enter a RGB color to set the clock color");
Serial.println("for example:");
Serial.println("color 0 255 0");
}
else if (input.startsWith("color ")) {
int r, g, b;
int parsed = sscanf(input.c_str(), "color %d %d %d", &r, &g, &b);
if (parsed == 3 && r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
uint32_t color = strip.Color(r, g, b);
segs.setDigitColor(color);
Serial.printf("Digit color set to RGB(%d, %d, %d)\n", r, g, b);
} else {
Serial.println("Invalid color values. Use: color R G B (0–255)");
}
} else {
Serial.println("Invalid command or index.");
}
}
}
// —————— Main Loop ——————
unsigned long lastUpdate = 0;
const unsigned long updateInterval = 1000;
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - lastUpdate >= updateInterval) {
lastUpdate = currentMillis;
displayTime(); // update the clock
checkAlarms(); // check for active alarms
}
handleSerialInput();//this is temporary until we have a bettewr interface
}