How about something like the following simulator code:
Code:
#include <stdio.h>
/* Let's assume we use 0.1 second precision. */
#define PER_SEC 10
/* LCD updates. Happen at the start. */
#define LCD_PERIOD (PER_SEC)
#define LCD_UPDATE (0)
/* Relay states. */
#define RELAY_WAIT 0 /* Before activation */
#define RELAY_ON 1 /* Active */
#define RELAY_OFF 2 /* After activation */
/* Relay 1 state durations. */
#define RELAY_1_WAIT (1*PER_SEC)
#define RELAY_1_ON (1*PER_SEC)
#define RELAY_1_OFF (3*PER_SEC - RELAY_1_ON - RELAY_1_WAIT)
/* Relay 2 state durations. */
#define RELAY_2_WAIT (15*PER_SEC/10)
#define RELAY_2_ON (1*PER_SEC/10)
#define RELAY_2_OFF (3*PER_SEC - RELAY_2_ON - RELAY_2_WAIT)
/* Relay 3 state durations. */
#define RELAY_3_WAIT (7*24*60*60*PER_SEC - RELAY_3_ON - RELAY_3_OFF)
#define RELAY_3_ON (120*PER_SEC)
#define RELAY_3_OFF (0)
int main(void)
{
long lcd_ticks = LCD_UPDATE;
long relay_1_ticks = RELAY_1_WAIT;
long relay_2_ticks = RELAY_2_WAIT;
long relay_3_ticks = RELAY_3_WAIT;
long ticks, sleepticks;
char relay_1_state = RELAY_WAIT;
char relay_2_state = RELAY_WAIT;
char relay_3_state = RELAY_WAIT;
sleepticks = 0L;
while (1) {
/* Obtain actual elapsed time using
* prev_timer = curr_timer;
* curr_timer = timer();
* ticks = curr_timer - prev_timer;
* where timer() is an RTC at PER_SEC resolution.
* Note that you should check for wraparound (in ticks),
* the following code assumes ticks is >= 0 and small.
*
* Here, we just assume our sleep was perfect.
*/
ticks = sleepticks;
lcd_ticks -= ticks;
if (lcd_ticks <= 0L) {
printf("Update LCD.\n");
lcd_ticks += LCD_PERIOD;
}
relay_1_ticks -= ticks;
while (relay_1_ticks <= 0L)
switch (relay_1_state) {
case RELAY_WAIT:
relay_1_state = RELAY_ON;
relay_1_ticks += RELAY_1_ON;
printf("Activate relay 1.\n");
break;
case RELAY_ON:
relay_1_state = RELAY_OFF;
relay_1_ticks += RELAY_1_OFF;
printf("Deactivate relay 1.\n");
break;
default:
relay_1_state = RELAY_WAIT;
relay_1_ticks += RELAY_1_WAIT;
}
relay_2_ticks -= ticks;
while (relay_2_ticks <= 0L)
switch (relay_2_state) {
case RELAY_WAIT:
relay_2_state = RELAY_ON;
relay_2_ticks += RELAY_2_ON;
printf("Activate relay 2.\n");
break;
case RELAY_ON:
relay_2_state = RELAY_OFF;
relay_2_ticks += RELAY_2_OFF;
printf("Deactivate relay 2.\n");
break;
default:
relay_2_state = RELAY_WAIT;
relay_2_ticks += RELAY_2_WAIT;
}
relay_3_ticks -= ticks;
while (relay_3_ticks <= 0L)
switch (relay_3_state) {
case RELAY_WAIT:
relay_3_state = RELAY_ON;
relay_3_ticks += RELAY_3_ON;
printf("Activate relay 3.\n");
break;
case RELAY_ON:
relay_3_state = RELAY_OFF;
relay_3_ticks += RELAY_3_OFF;
printf("Deactivate relay 3.\n");
break;
default:
relay_3_state = RELAY_WAIT;
relay_3_ticks += RELAY_3_WAIT;
}
/* Now, determine how long to sleep. */
sleepticks = lcd_ticks;
if (relay_1_ticks < sleepticks)
sleepticks = relay_1_ticks;
if (relay_2_ticks < sleepticks)
sleepticks = relay_2_ticks;
if (relay_3_ticks < sleepticks)
sleepticks = relay_3_ticks;
printf("Sleep for %.1f seconds.\n", (double)sleepticks / 10.0);
}
/* Never reached; press Ctrl+C to break. */
return 0;
}
Note that I deliberately didn't use arrays, as this is microcontroller code, and I wanted to make it as easy as possible to add per-relay quirks later on.
Using while instead of if allows you to use zero duration for some events. For example, the third latch has zero "off" duration, i.e. it turns off exactly at the start of a new period.
At 0.1 second precision, 32-bit signed ints can hold a period of over six years. Note that the ticks variables do need to be signed, in case you occasionally sleep maybe one or few ticks longer than intended. (If you just zero the ticks variables, you'll induce clock drift between the latches/LCD.)
Instead of compile-time constants, you can use variables to hold the durations. If you have an UI for the device (say, a small LCD display with three to five buttons), you could let the user reprogram the intervals (saving to spare variables), then copy all the states at once to the ticks and states variables ("update now?"), to resync or start a new period. State durations can be modified at any time, but they won't take effect until the next time that state is entered into.
Questions?