C/C++ Embedded Systems & Algorithms
I built this project back in high school, before I really knew anything about version control or had the coding habits I’ve picked up since. So there’s no GitHub repo for it. It was also written at a Dutch-language school, so the comments and variable names are in Dutch rather than English. I’m sharing the code as-is here, unpolished parts included, as an honest look at where I started.
The alarm runs on a custom control loop. No RTOS, no delay()-based timing, just a millis()-driven state machine handling a bunch of things at once. Time and two independent alarms are tracked in software and saved to EEPROM, so your alarm settings survive a power cycle.
Input comes from two places: eight buttons wired through a TM1638 driver I wrote myself, bit-banging the strobe/clock/data lines directly instead of using a library, and an IR remote for navigation, with my own decoder translating raw IR signals into usable commands. Between the two, you can set the time, configure two separate alarms, adjust display brightness, and toggle whether seconds are shown.
Instead of a buzzer, alarms are signaled through blinking LEDs and a flashing display. I ditched the buzzer after deciding it gave me more of a headache than it was worth. You can dismiss an alarm with a button, or snooze it with a wave: an ultrasonic sensor picks up three hand-waves within a few seconds and treats that as “snooze,” temporarily quieting the alarm before it kicks back in.
A lot of the design here comes down to what the Arduino Uno can and can’t do:
millis(). That means the clock resets every time it loses power and has to be manually re-synced. Right now that’s done by sending the time over serial from a computer.millis() instead of blocking delay() calls.This was the first time I wrote a hardware driver completely from scratch. Bit-banging the TM1638’s protocol by hand with shiftOut/shiftIn taught me a lot about what’s actually happening at the bit level when you talk to a chip, instead of just calling a library function and trusting it works. Decoding raw IR signals was a similar kind of lesson, turning a messy physical signal into something my code could actually use.
Looking back at the code, there’s a lot of things I’d do differently now. At the time, I didn’t know how to structure a project beyond one long file with a pile of global variables and boolean flags. That’s simply how I was taught to write it back then, in plain C rather than C++. If I rebuilt this today, I’d bring the C++ knowledge and guidelines I’ve picked up since. Splitting the code into proper classes across separate files. One for the alarm logic, one for the display, one for input, instead of cramming everything into a single loop and a handful of global state. I’d also actually reach for real design patterns instead of stumbling into a rough imitation of one. The State pattern for cleanly handling “current time” vs. “alarm 1” vs. “alarm 2” vs. “set time mode,” and probably the Command pattern for the button and IR input handling, so adding new actions wouldn’t mean digging through a wall of if-else chains.