The attached code is a multi option timer that I've cooked up, that will form part of a LARGE program on a UNO. The application requires multiple things to be happening at the same time, hence why the timer appears to be more complex.
So, my two questions are:
Am I reinventing the wheel here?
and
Can this function be made more compact?
int intAASeq;//Alarm Sequence marker
unsigned long ulngTTarg[2];//Start Millis, Target Millis
void setup() {
}
boolean Timer(byte bytAType) {//
if (bytAType == 0) {//cancels alarm
ulngTTarg[0] = 0;
intAASeq = 0;
return LOW;
}
unsigned long ulngTData[4];//OFF, ON, WAIT, CYCLES
switch (bytAType) {// Alarm Timing Data
case 1: {// Pump 1 Normal
ulngTData[0] = 100;
ulngTData[1] = 100;
ulngTData[2] = 1900;
ulngTData[3] = 3;
break;
}
case 2: {//Pump 2 Normal
ulngTData[0] = 200;
ulngTData[1] = 200;
ulngTData[2] = 1800;
ulngTData[3] = 5;
break;
}
case 3: {//Pump 1 EXCESSIVE
ulngTData[0] = 300;
ulngTData[1] = 500;
ulngTData[2] = 1700;
ulngTData[3] = 7;
break;
}
case 4: {//Pump Fault
ulngTData[0] = 500;
ulngTData[1] = 500;
ulngTData[2] = 0;
ulngTData[3] = 1;
break;
}
}
intAASeq = (intAASeq == 0) ? 1 : intAASeq;// Starts sequence counter if new alarm
// BELOW: 0 = Store millis value at start. 1 = End time of event (target time).
ulngTTarg[0] = (ulngTTarg[0] == 0) ? millis() : ulngTTarg[0];
ulngTTarg[1] = (intAASeq <= (int(ulngTData[3] * 2))) ? ulngTTarg[0] + ulngTData[(intAASeq & 0x01)] : ulngTTarg[0] + ulngTData[2];
boolean booState = ((intAASeq == (int(ulngTData[3] * 2)) + 1 or (intAASeq & 0x01) == 0)) ? LOW : HIGH;
if (millis() >= ulngTTarg[1]) {//Target time met or exceeded
intAASeq++;//increment sequence
ulngTTarg[0] = 0;// when set to 0, will be reset to current millis on next cycle
if (intAASeq > int((ulngTData[3] * 2) + 1)) {//resets sequence to 0 if full sequence has run
intAASeq = 0;
}
}
return booState;
}
void loop() {
//******FOR TESTING ONLY*********
//There are four different timer modes (1 - 4) that can be sent to the Timer() function, by changing the variable.
//Currently set to '1'
digitalWrite(8, Timer(1));
}`
Just feels uncomfortable. Maybe 10 years ago, but today, a big program should target any one of numerous, inexpensive uC boards: ESP32, Teensy, RP2040 and many real Arduino. Need more? Then there is the Raspberry Pi range. Note that Arduino ESP32 automatically incorporates freeRTOS with both cpu used without user configuration.
Hello tentimes
Take some time and describe the desired function of the sketch in simple words and this very simple.
Have a nice day and enjoy coding in C++ and feel free to reply for any help.
without a detailed description what your code does it would take me hours to analyse it.
The names that you are using are not self-descriptive.
Here is a much much simpler example of a code with self-explaining names and additional comments that shall explain what the code does. Again I want to emphasise much much simpler than your code and therefore much lesser functionality. But yet enough to show what I mean with self-descriptive names and comments.
//helper-function for non-blocking timing.
// returns "true" if TimePeriod has expired
// returns false if TimePeriod has NOT yet passed by
boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if ( currentMillis - expireTime >= TimePeriod )
{
expireTime = currentMillis; // set new expireTime
return true; // more time than TimePeriod) has elapsed since last time if-condition was true
}
else return false; // not expired
}
LARGE applications may use timers for multiple purposes. some may be singular events and others for periodic events.
if there are many timers, it's inefficient to check for each event independently. a common approach is to string multiple events together as a linked list, keeping track of the difference in time between events and only checking for the nearest term event to expire. when that event expires and is serviced, it may be rescheduled and the next event on the list is monitored
once such an approach is code, it is easy to maintain and efficient
Thanks for all your responses.
Now, for a little explanation. The project is a bilge pump monitor, alarm and data logger for marine vessels.
In the main program, void loop () would operate something like:
Check two different input pins for triggering event (pump active).
Perform some other calculations, then (if required), call the Timer(with option 1, 2, 3 or 4).
Timer would then return High or LOW (to drive an output pin connected to a buzzer).
The options, 1 to 4, simply cause different on/off/wait periods, so that all 4 alarms are easily distinguished.
Whilst this is all happening, the rest of the void loop() needs to continue uninterrupted, as data logging, display driving and some other time sensitive things are also happening.
Hello tentimes
A sketch running on an Arduino is able to handle differents things to the same time. Either to get started you study the IPO model or look for some similar solution within this forum. At least the sketch has to handle bildge pump objects and a method for in/output and timer. It should be quite simple to design.
Have a nice day and enjoy coding in C++.
Thanks. I've tried a few methods to assign the correct variables. This solution looks UGLY, so I knew there must have been a better way.
Still trying to get my head around the relative scale of resource usage.
Thanks for your reply, but this project is a monitor and alarm only and does not control the pumps in any way. This is to ensure that any failure does not render the pump inoperative.
All this project does is monitors for voltage at an input pin (supplied when the pump is active), then logs the event timing and triggers some alarms.
This piece of code is only a small part of the total project.
Yes, but maybe at the expense of clarity: you decide if it's worth it. In every one of your case statements, you update an array. This behaviour can be taken care of by a function with 4 arguments, as in
void update_data
(const unsigned long a,
const unsigned long b,
const unsigned long c,
const unsigned long d)
{
data_array[0] = a;
// and so on ...
return;
}
and then:
case 1:
update_data (100, 100, 1900, 3);
break;
You don't need to enclose the case statements in braces: that's what break is there for, i.e. telling when no more statements follow.
The next section, starting with
is compact indeed, but also on the verge of obfuscation, at least to me; compactness doesn't always equal clarity. What about
if (alarm_sequence_state == 0)
alarm_sequence_state = 1;
?
I agree with @StefanL38: why the obscure names such as ulngTData and ntAASeq ? Will you remember what they mean in six months?
By the way, have you considered using a structure instead of an array? I don't know what the numbers inside the array are supposed to mean exactly, but if they encode different aspects of the alarm, I do see a few advantages with using a structure:
each member can have it's own data type;
each member can have it's own, meaningful name, rather than easy to forget numerical indices (What's in index 2, again? Off time? Or was that index 1? Compare, for example, ulngTData[2] with alarm_data.off_time);
the functions operating on the structure can do so by pointer, leading to possible code deduplication as the project grows (since you seem concerned by code compactness issues).
Just to level-set the reality of multiple-things-at-once on the UNO-R3: there is only one CPU, so anything happening requiring cpu involvement means that no other cpu instruction can be executed. Hardware peripheral devices may do some self-clocking and triggering, but essentially when the CPU is busy things stall until the CPU is free to execute the next instruction.
From the official data sheet:
131 powerful instructions – most single clock cycle execution
The high-performance AVR ALU operates in direct connection with all the 32 general purpose working registers. Within a
single clock cycle, arithmetic operations between general purpose registers or between a register and an immediate are
executed. The ALU operations are divided into three main categories – arithmetic, logical, and bit-functions. Some
implementations of the architecture also provide a powerful multiplier supporting both signed/unsigned multiplication and
fractional format. See Section “” on page 281 for a detailed description.
Hi 330R and apologies for taking so long to reply.
Maybe I've gone about things the wrong way, but I tried to deal with the array from within a loop and found that the data was then only available within the loop, whether or not I had defined the variable externally.
I'm 99% certain that my response labels me firmly as a NOOB, but that's what I am and shall remain until I learn more.