Introduction
I'm using an Arduino Nano Every to output to a 7-Segment Display (5641AS, a cheap, 12 pin 4 digit display), receive input from 3 simple buttons, and play an alarm tone on a piezo buzzer. The project is expected to have a weight sensor that prevents the alarm from being disabled as long as the user remains in bed, but the weight sensor hasn't been built yet, which is why you'll see a simple boolean placeholder in the code. (bool weightCap)
The Issue
Using the SevSeg library by Dean Reading I wrote the following code, which has one major issue: After about 10-16 seconds of no user input, the Nano becomes unresponsive and doesn't execute any additional functions beyond running the display. It doesn't update the time on the display (after a minute has passed, the display has remained unchanged), nor does it trigger the alarm function when it should, but it must be running because the display still shows the last set time.
The Code
#include "pitches.h"
#include <TimeLib.h>
#include "SevSeg.h"
SevSeg disp;
int melody[] = { NOTE_DS7, NOTE_GS7, NOTE_DS5, NOTE_GS5, NOTE_DS6, NOTE_GS6, NOTE_DS7, NOTE_GS7 };
int duration[] = { 8, 8, 8, 8, 8, 8, 8, 8 }; //note size: 8 = 1/8 note, 4= 1/4 note, etc.
int pause = 250; //The delay between notes/the tempo;
byte segmentPins[] = { 9, 8, 7, 6, 5, 4, 3, 2 };
byte digitPins[] = { 10, 11, 12, 13 };
int yr = 2023; //variables used with the setCurrent function and the setTime method
byte mth = 1;
byte dy = 1;
byte hr = 12;
byte mn = 0;
int iYr = 2022; //variables used to create time_t alarmTime with the makeTime method
byte iMon = 10;
byte iDay = 1;
byte iHr = 16;
byte iMin = 0;
byte wkDy = 7;
const int buzzer = 14;
const int buttonMinus = 15;
const int buttonSet = 16;
const int buttonPlus = 17;
bool menuState; //is menu active
tmElements_t alarmElements = { 0, iMin, iHr, wkDy, iDay, iMon, (iYr - 1970) }; //list of alarm elements to use makeTime function and set time_t variables.
time_t alarmTime; //compares with now() function to determine if alarm should be played
time_t alarmCeiling; //The alarm will play for up to an hour so long as adequate weight remains on the weight sensor
time_t snoozeEnd; //keeps track of when the alarm should play again after being snoozed
bool snoozer = false; //has alarm been snoozed? Y/N may only be snoozed once
bool weightCap = true; //placeholder for weight sensor
int timer;
void setup() {
//initialize display
disp.begin(COMMON_CATHODE, 4, digitPins, segmentPins, false, false, false, false);
disp.setBrightness(0);
//sets UI buttons to inputs and buzzer as output
pinMode(buttonSet, INPUT_PULLUP);
pinMode(buttonPlus, INPUT_PULLUP);
pinMode(buttonMinus, INPUT_PULLUP);
pinMode(14, OUTPUT);
setCurrent();
alarmTime = makeTime(alarmElements);
alarmCeiling = alarmTime + 3600;
snoozeEnd = alarmTime + 300;
}
void loop() {
updateDisplay();
asyncDelay(200);
if (now() == alarmTime || (snoozer && now() == snoozeEnd)) { //if current time is either the current alarm time or the end of the snoozing period, play the alarm
alarm();
}
if (now() <= alarmCeiling && now() >= snoozeEnd && weightCap) { //if the alarm Ceiling hasn't been reached, snooze is not active, and weight is over capacity, activate alarm)
alarm();
}
if (digitalRead(buttonSet) == LOW) {
menu();
}
}
void menu() {
asyncDelay(250);
int index = 0;
menuState = true;
while (menuState) {
printMenu(index);
disp.refreshDisplay();
if (digitalRead(buttonMinus) == LOW) {
index--;
if (index < 0)
index = 3;
asyncDelay(250);
}
if (digitalRead(buttonPlus) == LOW) {
index++;
if (index > 3)
index = 0;
asyncDelay(250);
}
if (digitalRead(buttonSet) == LOW) {
switch (index) {
case 0:
setCurrent();
break;
case 1:
setAlarm();
break;
case 2:
pickAlarm();
break;
case 3:
setDisp();
break;
}
}
}
}
void alarm() {
bool alarmState = true; //allow the alarm to be active every time the method is called
byte buttonState = 0;
while (alarmState) { //while(true) loop allows the alarm to play until one of the conditions to cancel it is met
if (now() >= alarmCeiling) //if it is past the alarm ceiling (1 hour past alarm time), cancel the alarm.
{
break;
}
for (int thisNote = 0; thisNote < 8; thisNote++) { //play the melody
tone(A0, melody[thisNote], (1000 / duration[thisNote]));
pause = 1000 / duration[thisNote] * 1.3;
asyncDelay(pause);
buttonState += digitalRead(buttonSet);
if (!weightCap) { //ifs user is not in bed, turn off alarm
alarmState = false;
break;
} else if (buttonState != 0 && !snoozer) { //if the snooze button has been pressed and snooze has not yet been used, set snooze timer and disable alarm
alarmState = false;
snoozeEnd = now() + 300;
snoozer = true;
break;
}
}
}
}
void updateDisplay() { //converts hours & minutes to a 4 digit number that can be read by the setNumber function
int disptime = (hour() * 100) + minute();
if (disptime >= 1300) {
disptime -= 1200;
disp.setNumber(disptime, 0);
} else
disp.setNumber(disptime);
disp.refreshDisplay();
}
void printMenu(int index) {
switch (index) {
case 0:
disp.setChars("Chng"); //change the current time
break;
case 1:
disp.setChars("Alar"); //set a new alarm--WIP
break;
case 2:
disp.setChars("Set"); //select from one of four set alarms--WIP
break;
case 3:
disp.setChars("Disp"); //increase/decrease display brightness --WIP
break;
}
}
void setCurrent() {
asyncDelay(250);
while (digitalRead(buttonSet) == HIGH) {
disp.setNumber(yr);
disp.refreshDisplay();
if (digitalRead(buttonMinus) == LOW) {
yr--;
asyncDelay(200);
}
if (digitalRead(buttonPlus) == LOW) {
yr++;
asyncDelay(200);
}
}
asyncDelay(250);
while (digitalRead(buttonSet) == HIGH) {
disp.setNumber(mth);
disp.refreshDisplay();
if (digitalRead(buttonMinus) == LOW) {
mth--;
asyncDelay(150);
if (mth <= 0)
mth = 12;
}
if (digitalRead(buttonPlus) == LOW) {
mth++;
asyncDelay(150);
if (mth >= 12)
mth = 1;
}
}
asyncDelay(250);
while (digitalRead(buttonSet) == HIGH) {
disp.setNumber(dy);
disp.refreshDisplay();
if (digitalRead(buttonMinus) == LOW) {
dy--;
asyncDelay(100);
if (dy <= 0)
dy = 31;
}
if (digitalRead(buttonPlus) == LOW) {
dy++;
asyncDelay(100);
if (dy >= 31)
dy = 1;
}
}
asyncDelay(250);
while (digitalRead(buttonSet) == HIGH) {
disp.setNumber(hr);
disp.refreshDisplay();
if (digitalRead(buttonMinus) == LOW) {
hr--;
asyncDelay(100);
if (hr >= 200)
hr = 23;
}
if (digitalRead(buttonPlus) == LOW) {
hr++;
asyncDelay(100);
if (hr >= 24)
hr = 0;
}
}
asyncDelay(250);
while (digitalRead(buttonSet) == HIGH) {
disp.setNumber(mn);
disp.refreshDisplay();
if (digitalRead(buttonMinus) == LOW) {
mn--;
asyncDelay(50);
if (mn >= 200)
mn = 59;
}
if (digitalRead(buttonPlus) == LOW) {
mn++;
asyncDelay(50);
if (mn >= 60)
mn = 0;
}
}
setTime(hr, mn, 0, dy, mth, yr); //sets the current time from user input
menuState = false;
}
void setAlarm() {
menuState = false;
}
void pickAlarm() {
menuState = false;
}
void setDisp() {
menuState = false;
}
void asyncDelay(int delay) {
timer = millis();
while (millis() <= (timer + delay)) {
disp.refreshDisplay();
}
}
Debugging efforts thus far
After putting a Serial message in the loop that prints the current time, I determined that the Nano became unresponsive, while the display was still running, so I determined that the code must be stuck in a loop somewhere, but putting Serial messages in other parts of the program made more errors, because it slowed the program down too much. According to this issue, refreshDisplay() must be called every two milliseconds. I tried calling refreshDisplay() in the middle of large functions (such as the setCurrent() function) , but this did not solve the issue. I'm currently at a loss as to what I can do to fix this issue, short of using a different display for our project.
The SevSeg_Counter.ino sample code works perfectly with this setup, so it must be something in my code.
I am new to using Arduino and coding in general, so I apologize for any beginner mistakes, and I would appreciate any and all feedback on the code. If anything needs clarified I will make corrections or clarifications as soon as possible.
Thank you!
Schematic for reference:



