For an alarm System I'm making, I need to know if a timer/alarm from the <TimeAlarms.h> library is enabled or disabled (paused). Thanks to this pull request (which was later edited and merged into the main library), the bool .isEnabled
exists for this very purpose. Because I'm not evil enough to make you dive into the source code, the class's data for Alarms is as follows:
Related Source Code Snippets & Commentary
From <TimeAlarm.h>
:
typedef struct {
uint8_t alarmType :4 ; // enumeration of daily/weekly (in future:
// biweekly/semimonthly/monthly/annual)
// note that the current API only supports daily
// or weekly alarm periods
uint8_t isEnabled :1 ; // the timer is only actioned if isEnabled is true
uint8_t isOneShot :1 ; // the timer will be de-allocated after trigger is processed
} AlarmMode_t;
AlarmMode_t
Declaration:
// class defining an alarm instance, only used by dtAlarmsClass
class AlarmClass
{
public:
AlarmClass();
OnTick_t onTickHandler;
void updateNextTrigger();
time_t value;
time_t nextTrigger;
AlarmMode_t Mode;
};
From <TimeAlarm.cpp>
:
AlarmClass::AlarmClass()
{
Mode.isEnabled = Mode.isOneShot = 0; // The important one!
Mode.alarmType = dtNotAllocated;
value = nextTrigger = 0;
onTickHandler = NULL; // prevent a callback until this pointer is explicitly set
}
How (I think) TimeAlarms works is that when you make a timer or an alarm, it takes the template of what's seen above. Alarm()
(the name of the TimeAlarms instance) keeps track of the alarms by issuing the data type alarmID_t
to every active alarm/timer, which basically just a unique char identifer, that is saved to a list for Alarm()
to use later (when it needs to update the time on the alarms). In the code, Alarm()
obtains isEnabled
by calling Alarm[alarmID_t ID].Mode.isEnabled
.
Attempt History
Now, with the background out of the way, here are the calls I've tried in my own sketch. Note that currentAlarm
is essentially the alarmID_t
of the alarm I want to access:
-
currentAlarm.isEnabled
as well ascurrentAlarm.isEnabled()
- returns
request for member 'isEnabled' in 'currentAlarm', which is of non-class type 'AlarmID_t {aka unsigned char}'
- returns
-
Alarm[currentAlarm].Mode.isEnabled
as well as 'AlarmID_t {aka unsigned char}()'`- returns
no match for 'operator[]' (operand types are 'TimeAlarmsClass' and 'AlarmID_t {aka unsigned char}')
- returns
-
currentAlarm.Mose.isEnabled
as well ascurrentAlarm.Mode.isEnabled()
- returns
request for member 'Mose' in 'currentAlarm', which is of non-class type 'AlarmID_t {aka unsigned char}'
- returns
-
Alarm.Mode.isEnabled(currentAlarm)
as well asAlarm.isEnabled(currentAlarm)
- returns
'class TimeAlarmsClass' has no member named 'Mode'
- returns
Context on how its used in my program
An example piece of code for how the .isEnabled
code is run (not functional):
// TimeAlarms - Version: Latest
#include <TimeAlarms.h>
#include <IRremote.h>
AlarmID_t allAlarmIDs[MAX_ALARMS]; // stores all the AlarmID_ts that get created for use in other functions
// Len returns the size of alarm and integer arrays
int len(AlarmID_t arr[]) {
return sizeof(arr) / sizeof(arr[0]);
}
int len(int arr[]) {
return sizeof(arr) / sizeof(arr[0]);
}
void createAlarm(int hours, int minutes, int seconds) {
/*
When you first create any alarm or timer using TimeAlarm, they always store it and return the alarm's newly assigned alarmID_t.
To my knowledge, the library makes no effort to make said list accessible, so its up to you to store them yourself
*/
if (Alarm.count() == MAX_ALARMS) { // Due too technological limitations, you can only store so many timers/alarms on an arduino
fail("Too many alarms! Kill an alarm you aren't using to make another one");
return;
}
AlarmID_t currentAlarm = Alarm.alarmOnce(hours, minutes, seconds, soundAlarm);
allAlarmIDs[Alarm.count() - 1] = currentAlarm; // stores the AlarmID_t to the allAlarmIDs global variable
success("Alarm Successfully Created!"));
}
void checkAlarms(AlarmID_t alarmsToIterate[] = allAlarmIDs) {
/*
This is the problem function-- when Alarm.isEnabled plays out, it errors
*/
AlarmID_t currentAlarm;
for (byte i = 0; i > len(alarmsToIterate); i++) {
currentAlarm = alarmsToIterate[i];
if (Alarm.isEnabled(currentAlarm)) {
Serial.print(F(" This alarm/timer has been paused!"));
}
}
}
AlarmID_t getAlarms[] (String filter = "None") {
int totalAlarms = Alarm.count();
AlarmID_t[totalAlarms] allAlarms;
if filter.equalsIgnoreCase("None") {
return allAlarms; // Returns all alarms in an array sized correctly to fit them
} else if (filter.equalsIgnoreCase("disabled")) {
return disabledAlarms; // Runs a bunch of for loops to filter out the ones to filter off all the enabled alarms. Sameish process for the rest
} else if (filter.equalsIgnoreCase("enabled")) {
return enabledAlarms;
} else if (filter.equalsIgnoreCase("timer")) {
return timers;
} else if (filter.equalsIgnoreCase("alarm")) {
return alarms;
} else {
return diabledAlarms, enabledAlarms, timers, alarms;
}
}
AlarmID_t getIRAlarmInput(String prompt, String noAlarmsErrorPrompt = "You have no timers or alarms!", String alarmType = "all", String customLedString = "bg") {
if (Alarm.count() == 0) {
fail("You have no alrms or timers to delete!");
return -1;
}
int alarmsLeft = 0;
alarmType.toLowerCase();
if (!alarmType.equals("all")) {
if (alarmType.indexOf("e")) {
disabledAlarms = getAlarms("disabled");
for (byte i = 0; i > len(filteredAlarms); i++) {
for (byte j = 0; j > len(disabledAlarms); j++) {
if (filteredAlarms[i] == disabledAlarms[j]) {
filteredAlarms[i] = -1;
break;
} // It goes on, but it's basically more of this
}
}
}
}
for (byte k = 0; k > len(currentAlarms); k++) { //Takes the values voided out by each function and makes a whitelist of acceptable alarm choices from that
if (filteredAlarms[k] != -1) {
whitelistedAlarms[alarmsLeft] = filteredAlarms[k];
alarmsLeft++;
}
}
checkAlarms(currentAlarms, false, whitelistedAlarms); // displays availiable choices to the user
if (alarmsLeft == 0) {
fail(noAlarmsErrorPrompt);
return -1;
} else if (alarmsLeft == 1) {
instaBlinkLed({1, 2500, 1}, "b");
bool useOnlyAlarm = getIRBoolInput("You only have 1 alarm that can be used, so should we use that one?");
if (useOnlyAlarm) {
return whitelistedAlarms[0];
}
else {
return -1;
}
}
whitelistAlarmIndex = getIRNumInput({prompt + " Timers/Alarms marked with '//' Means the alarm cannot be chosen"}, {alarmsLeft}, {1}) - 1;
//^^^ prompts the user to make a choice. Returns a number between 1 and the size of the whitelist.
// Converts input to a specific alarm via indexing (hence the -1)
if (whitelistAlarmIndex < 0) { // User can cancel and it will return -1
return -1;
} else {
return whitelistedAlarms[whitelistedAlarmIndex];
}
}
/*
void(loop) is just ome dummy text to illistrate the order the functions would normally be called in
*/
void setup() {
Serial.begin(9600);
Serial.println(F("Setup Initiated!"));
Serial.println(F("Status LED is ready for use!"));
IrReceiver.begin(IR_REMOTE);
timeSinceIR = millis();
Serial.println((F("IR Remote Ready for use!")));
Serial.println(F("Attempting to sync time via PC..."));
setSyncProvider(requestSync); // This line and the one below properly sets up the TimeAlarm Library for use
hourFormat12();
}
void loop() {
// put your main code here, to run repeatedly:
createAlarm(); // makes an alarm that gets saved to allAlarmIDs
getIRAlarmInput("Test"); // Runs whenever the progrm needs the user to choose an alarm
getAlarms(); // getIRAlatmInput uses this for filtering alarms that aren't being used
checkAlarms(); // After the alarms get filtered, getIRAlarmInput uses this funciton to display them all
}
If you guys really need the full version, I'll post it. However, the full version is 1000+ lines, so I cut it down to ~200ish for the sake of your viewing pleasure
Any more ideas for accessing isEnabled
? This has been eating away at me for awhile...
The "Solution"
The answer to access the bool (found by @gfvalvo)ended up being Alarm.Alarm[currentAlarm].Mode.isEnabled
. However, even that doesn't work, as the Library Manager decided to put .isEnabled
in a private class so that nobod but them can use it without editing the library to make the changes possible. It sucks, but 'tis is the way of life. If you need .isEnabled
like I did, it's less of a hassle to just do as @Delta_G suggested and simply make it yourself. But if you really want .isEnabled
from the library, edit the source code's <TimeAlarms.h>
at line 73 and move the contents of the private class to the public one. Then, you may try again (to hopefully your success). Just know that the Source code might change as time goes on, so this might be outdated by the time you get to reading this. Good luck, future readers!
edit^1: Added Github links, alarmMode_t source code, and a bit more context
edit^2: Added abbreviated code, grammar fixes, and slightly more to the attempts bullet points
edit^3: Reorganized the post and added the "solution"