start time should be in unix utc, but RTC uses seconds rather than milliseconds
subtract current time from start time in milliseconds
use timer.at(time, function_to_call); (with time in milliseconds?) RTC built-in alarm
only start the program if the controller is on when the start time happens, not if the controller is powered on after the start time. This is the simplest (?) way to keep all the timing together across multiple controllers
function should then call timer.every(interval, function_to_call); and expire
the timer.every() function is probably a wrapper to update the variables that go into morseToSend (and `morseToSend_blink).
use some math to determine, according to the sending speed, step length, and number of transmitters,
how long the timer.every() interval should be
how many times to JLed Repeat() send to fill the step length
account for any changes in webform along the way
If using ardino-timer (https://github.com/contrem/arduino-timer), how to set this up?
- [x] get start time of program from webform
- [x] start time should be in unix utc, but RTC uses seconds rather than milliseconds
- ~~subtract current time from start time in milliseconds~~
- [x] use ~~`timer.at(time, function_to_call);` (with time in milliseconds?)~~ RTC built-in alarm
- [x] only start the program if the controller is on when the start time happens, not if the controller is powered on after the start time. This is the simplest (?) way to keep all the timing together across multiple controllers
- keep track of whether a program is running or is scheduled to run in the future--some sort of status header that goes along with the value of `yourInputSend`. - #37
- ~~function should then call `timer.every(interval, function_to_call);` and expire~~
- ~~the `timer.every()` function is probably a wrapper to update the variables that go into `morseToSend` (and `morseToSend_blink).~~
- [x] use some math to determine, according to the sending speed, step length, and number of transmitters,
- ~~how long the `timer.every()` interval should be~~
- ~~how many times to JLed `Repeat()` send to fill the step length~~
- [x] account for any changes in webform along the way
HTML field is datetime-local, which needs to be labeled as UTC.
https://www.w3schools.com/tags/att_input_type_datetime-local.asp
Comes out as `2023-09-02T05:34` format.
Can I use JS to convert this to UTC?
Maybe I'll go back and make it use local time everywhere.
Huh, `Date.now()` gives UTC. Maybe I can use TZ offset on one of the unix timestamps to get where I want to be. https://stackoverflow.com/a/61082536/2152245
All back on local time now. The current time is saved with an epoch shift according to the timezone offset in JS. The start program time is local by default from the HTML form. Both are stored as unix timestamps on the C++ side.
All back on local time now. The current time is saved with an epoch shift according to the timezone offset in JS. The start program time is local by default from the HTML form. Both are stored as unix timestamps on the C++ side.
It's not horrible. I'd like the two time formats to match.
When I try to format the date differently than return rtc.now().timestamp();, like toString(), the device crashes.
In any case, this is probably the way to go:
handle timestamp display with JS rather than C++
This message when you submit an empty date also isn't horrible, but I'd rather store the existing date until a new one is set.
It's not horrible. I'd like the two time formats to match.

When I try to format the date differently than `return rtc.now().timestamp();`, like `toString()`, the device crashes.
In any case, this is probably the way to go:
- [x] handle timestamp display with JS rather than C++
This message when you submit an empty date also isn't horrible, but I'd rather store the existing date until a new one is set.

Way overthinking this. Time is coming from browser in the first place. If I want to print it, print it from the browser, don't pull it from the controller.
Way overthinking this. Time is coming from browser in the first place. If I want to print it, print it from the browser, don't pull it from the controller.
If the start date isn't set, a value isn't added to the JS, so it just sees s = ; and breaks everything else in the script. Which means you can't put in a new value from the webform.
Right now there is a hardcoded date in JS, but that needs to be fixed.
Wow did I ever get messed up there.
If the start date isn't set, a value isn't added to the JS, so it just sees `s = ;` and breaks everything else in the script. Which means you can't put in a new value from the webform.
Right now there is a hardcoded date in JS, but that needs to be fixed.
Did a Full Clean/Build Filesystem Image/Upload Filesystem Image/Upload and Monitor.
The empty start time/broken JS seems to be fixed by b97f48858d.
Back to the double submit problem.
Did a Full Clean/Build Filesystem Image/Upload Filesystem Image/Upload and Monitor.
The empty start time/broken JS seems to be fixed by b97f48858d7e1ac706e74d34a70b301af7a839cb.
Back to the double submit problem.
introduce a bug because the start time that's filled in gets converted to local time from local time, so it is wrong
No luck on double submit, but I managed to
- fill in the start time so it doesn't disappear
- [x] introduce a bug because the start time that's filled in gets converted to local time from local time, so it is wrong

OK, so it looks like arduino-timer `.at()` uses the `millis()` clock. Are there timers that use real time (like stored in RTC)?
Or should I be using RTC alarms? https://garrysblog.com/2020/07/05/using-the-ds3231-real-time-clock-alarm-with-the-adafruit-rtclib-library/
https://www.arduino.cc/reference/en/libraries/category/timing/
RTC alarms seem to fire at the next opportunity, even if they happened when ESP32 was off. This is a problem I wanted to avoid.
Reset the alarm in setup() if it's in the past?
RTC alarms seem to fire at the next opportunity, even if they happened when ESP32 was off. This is a problem I wanted to avoid.
Reset the alarm in `setup()` if it's in the past?
I'm getting weird dates again. I will look at the hardware connections tomorrow. May be a short between the two boards because I sort of overlapped them!
[E][esp32-hal-i2c.c:776] emptyRxFifo(): RxEmpty(1) call on TxBuffer? dq=0
[E][esp32-hal-i2c.c:1434] i2cCheckLineState(): Bus Invalid State, TwoWire() Can't init sda=0, scl=0
RTC can't set time. Trying again.
I'm getting weird dates again. I will look at the hardware connections tomorrow. May be a short between the two boards because I sort of overlapped them!
```
[E][esp32-hal-i2c.c:776] emptyRxFifo(): RxEmpty(1) call on TxBuffer? dq=0
[E][esp32-hal-i2c.c:1434] i2cCheckLineState(): Bus Invalid State, TwoWire() Can't init sda=0, scl=0
RTC can't set time. Trying again.
```
It's also worth considering using the SQW pin: https://garrysblog.com/2020/07/05/using-the-ds3231-real-time-clock-alarm-with-the-adafruit-rtclib-library/.
It's this bit that's causing the i2cCheckLineState() error and reboot:
if ((rtc.alarmFired(2) == true) ){
// Print current time and date
DateTime now = rtc.now(); // Get the current time
char buff[] = "Alarm triggered at hh:mm:ss DDD, DD MMM YYYY";
Serial.println(now.toString(buff));
// Disable and clear alarm
rtc.clearAlarm(2);
}
It's this bit that's causing the `i2cCheckLineState()` error and reboot:
```
if ((rtc.alarmFired(2) == true) ){
// Print current time and date
DateTime now = rtc.now(); // Get the current time
char buff[] = "Alarm triggered at hh:mm:ss DDD, DD MMM YYYY";
Serial.println(now.toString(buff));
// Disable and clear alarm
rtc.clearAlarm(2);
}
```
Changed true to 1 and it is not crashing. Nope, still crashes. Why intermittent? With the whole if statement commented out, submitting the form doesn't seem to cause crashes. I wonder if it's some race condition between form submission (setting the alarm) and testing the alarm in loop().
Also, even though I get stuff like this, the RTC time seems to be right:
Writing file: /inputMsg.txt
- file written
yourInputTime: 1694179866
UTC time from browser: 2000/0/0 (0) 0:0:0
rtc.now().unixtime(): 3002226120
~~Changed `true` to `1` and it is not crashing.~~ Nope, still crashes. Why intermittent? With the whole `if` statement commented out, submitting the form doesn't seem to cause crashes. I wonder if it's some race condition between form submission (setting the alarm) and testing the alarm in `loop()`.
Also, even though I get stuff like this, the RTC time seems to be right:
```
Writing file: /inputMsg.txt
- file written
yourInputTime: 1694179866
UTC time from browser: 2000/0/0 (0) 0:0:0
rtc.now().unixtime(): 3002226120
```
I tried the same `if ((rtc.alarmFired(2) == true) )` code on the breadboard version and get the same results (crash on form submission).
I will probably breadboard in the SQW pin and see if that works better https://github.com/garrysblog/DS3231-Alarm-With-Adafruit-RTClib-Library/blob/master/DS3231-RTClib-Adafruit-Alarm-Poll-SQW/DS3231-RTClib-Adafruit-Alarm-Poll-SQW.ino.
I can set Alarm2 and have SQW notify of the alarm. I tried
setting, turning off the ESP32, waiting past alarm time, and it does not trigger on boot (this is good)
setting, rebooting ESP32, and waiting for alarm, and it triggers at right time
But I need to make sure that the time is being held between reboots, because the alarm just takes "day of month" HH:MM:SS, not absolute date. So if it reboots and thinks it's May 2000 but the same day, the alarm will trigger anyway.
On boot: Alarm 2 set for at 15:27:00 Mon, 08 May 2000
Added SQW pin on breadboard to GPIO4.
I can set Alarm2 and have SQW notify of the alarm. I tried
- setting, turning off the ESP32, waiting past alarm time, and it does not trigger on boot (this is good)
- setting, rebooting ESP32, and waiting for alarm, and it triggers at right time
But I need to make sure that the time is being held between reboots, because the alarm just takes "day of month" HH:MM:SS, *not* absolute date. So if it reboots and thinks it's May 2000 but the same day, the alarm will trigger anyway.
On boot: `Alarm 2 set for at 15:27:00 Mon, 08 May 2000`
yourInputTime: 1694187947
RTC can't set time. Trying again.
UTC time from browser: 2023/9/8 (5) 15:45:47
rtc.now().unixtime(): 1694187947
Writing file: /inputFloat.txt
- file written
1694187960
Writing file: /inputStartTimeUnix.txt
- file written
1694187960
Alarm 2 set for at 15:46:00 Mon, 08 May 2000
Alarm triggered at 15:46:00 Fri, 08 Sep 2023
Feel slightly better:
```
yourInputTime: 1694187947
RTC can't set time. Trying again.
UTC time from browser: 2023/9/8 (5) 15:45:47
rtc.now().unixtime(): 1694187947
Writing file: /inputFloat.txt
- file written
1694187960
Writing file: /inputStartTimeUnix.txt
- file written
1694187960
Alarm 2 set for at 15:46:00 Mon, 08 May 2000
Alarm triggered at 15:46:00 Fri, 08 Sep 2023
```
Confirming that I can't set an alarm more than a month in advance (or maybe ~2 months if the current month has 30 days and the next month has 31 days, and you set it for the 31st). Wild.
The alarm registers on the DS3231 only allow you to store seconds, minutes, hours, and either the day of the month(1-31) or the day of the week (1-7), so there is no way for it to internally adjust for the last day of the month. If you want an alarm on the last day of the month, presumably your code would need to set the day of the month properly.
This is probably OK. I will leave the edge case alone for now. #33.
Confirming that I can't set an alarm more than a month in advance (or maybe ~2 months if the current month has 30 days and the next month has 31 days, and you set it for the 31st). Wild.
https://forum.arduino.cc/t/ds3231-alarm-when-date-match/929036/5
> The alarm registers on the DS3231 only allow you to store seconds, minutes, hours, and either the day of the month(1-31) or the day of the week (1-7), so there is no way for it to internally adjust for the last day of the month. If you want an alarm on the last day of the month, presumably your code would need to set the day of the month properly.
This is probably OK. I will leave the edge case alone for now. #33.
- [x] add to webform that start time is only accurate to day of month
- [x] cancel the alarm entirely somehow, so it doesn't start up unexpectedly every month on the same day
- [x] cancel the alarm if it's too late; if I set it under "continuous", alarm time passes, then I switch to "cycle," the alarm triggers. This is bad.
- might be because I was using Alarm 2, which only matches minutes, not seconds
- see https://amiok.net/gitea/W1CDN/vulpes/pulls/24#issuecomment-1208
Before I go too far, consider just comparing rtc.now() and the start time in loop() to trigger the scheduled program.
ETA: This is harder than it looks, so I'll avoid it for now.
Before I go too far, consider just comparing `rtc.now()` and the start time in `loop()` to trigger the scheduled program.
ETA: This is harder than it looks, so I'll avoid it for now.
So when I set up Alarm2 but turn off cycle, if I turned cycle back on within the same minute, the alarm was triggered.
I switched to Alarm1.
I thought I had typed this all out, but
- Alarm2 has only minute resolution
- Alarm1 has second resolution
So when I set up Alarm2 but turn off cycle, if I turned cycle back on within the same minute, the alarm was triggered.
I switched to Alarm1.
calls morseToSend and morseToSend_blink reset/update for step length
waits (([step length seconds] * [number of transmitters])) - [number of transmitters]) seconds before starting over
Can this be done with arduino-timer or another timer without rewriting the Jled functions in loop()?
To send a cycle, we need to set up the duration of each repeating message.
- [x] Need webform entries (#8) for
- step length
- cycle ID
- number of transmitters
- [x] Need a function to call that
- is called when timer expires
- calls `morseToSend` and `morseToSend_blink` reset/update for step length
- waits (([step length seconds] * [number of transmitters])) - [number of transmitters]) seconds before starting over
Can this be done with arduino-timer or another timer without rewriting the Jled functions in `loop()`?
So close to being able to use Jled sequences natively, if I could figure out how to calculate the number of times to repeat for a given step length.
This looks helpful (https://arduino.stackexchange.com/a/22275):
```
#include <elapsedMillis.h>
elapsedMillis timeElapsed;
unsigned int interval = 60000; //one minute in ms
while(timeElapsed < interval){
//do stuff
}
```
Or use `millis()` directly like
```
int end_time = millis() + 1000; // 1 second in future
while(millis() < end_time){
// do stuff
}
```
Jled sequences example 1 (you can repeat a sequence n times or forever) (https://github.com/jandelgado/jled/issues/103#issuecomment-1312098221):
```
#include <jled.h>
auto blinkTwice = JLed(1).Blink(250,250).Repeat(2);
auto wait = JLed(1).Off().DelayAfter(1000);
JLed leds[] = {blinkTwice, wait};
auto seq = JLedSequence(JLedSequence::eMode::SEQUENCE, leds).Forever();
void setup() {
}
void loop() {
seq.Update();
}
```
Jled sequences example 2 (https://github.com/jandelgado/jled/blob/master/examples/sequence/sequence.ino):
```
#include <jled.h>
constexpr auto LED_PIN = 3;
JLed leds[] = {
JLed(LED_PIN).Breathe(2000).Repeat(3),
JLed(LED_PIN).Blink(750, 250).Repeat(3),
JLed(LED_PIN).FadeOff(1000).Repeat(3),
JLed(LED_PIN).Blink(500, 500).Repeat(3),
JLed(LED_PIN).FadeOn(1000).Repeat(3),
JLed(LED_PIN).Off()
};
JLedSequence sequence(JLedSequence::eMode::SEQUENCE, leds);
void setup() { }
void loop() {
sequence.Update();
delay(1);
}
```
#include <Arduino.h>
#include<jled.h>
// blink internal LED every second; 1 second on, 0.5 second off.
auto led = JLed(LED_BUILTIN).Blink(100, 500).Forever();
auto changed = false;
void setup() {}
void loop() {
static auto time_start = millis();
if (millis() - time_start > 5000 && !changed) {
changed = true;
led = JLed(LED_BUILTIN).Blink(1000, 500).Forever();
}
led.Update();
}
Can I rewrite this for what I need?
#include <Arduino.h>
#include<jled.h>
// put the forever morseToSend here
auto led = JLed(LED_BUILTIN).Blink(100, 500).Forever();
auto changed = false; // is the step over?
int step_length = 60000; // duration for this transmitter
int cycle_id = 1; // number of this transmitter in cycle
int n_transmitters = 5; number of transmitters total
...?
void setup() {}
void loop() {
static auto time_start = millis();
if (millis() - time_start > 5000 && !changed) {
changed = true;
led = JLed(LED_BUILTIN).Blink(1000, 500).Forever();
}
led.Update();
}
Another example (https://github.com/jandelgado/jled/issues/34):
```
#include <Arduino.h>
#include<jled.h>
// blink internal LED every second; 1 second on, 0.5 second off.
auto led = JLed(LED_BUILTIN).Blink(100, 500).Forever();
auto changed = false;
void setup() {}
void loop() {
static auto time_start = millis();
if (millis() - time_start > 5000 && !changed) {
changed = true;
led = JLed(LED_BUILTIN).Blink(1000, 500).Forever();
}
led.Update();
}
```
Can I rewrite this for what I need?
```
#include <Arduino.h>
#include<jled.h>
// put the forever morseToSend here
auto led = JLed(LED_BUILTIN).Blink(100, 500).Forever();
auto changed = false; // is the step over?
int step_length = 60000; // duration for this transmitter
int cycle_id = 1; // number of this transmitter in cycle
int n_transmitters = 5; number of transmitters total
...?
void setup() {}
void loop() {
static auto time_start = millis();
if (millis() - time_start > 5000 && !changed) {
changed = true;
led = JLed(LED_BUILTIN).Blink(1000, 500).Forever();
}
led.Update();
}
```
GTK (https://github.com/jandelgado/jled/issues/11#issuecomment-429887353):
> There is no need to redefine the object each time.
Just call the variable with the new parameters.
> ex: `leds[0].Blink(50, 1800).DelayBefore(450).Forever();`
Oh wow, you can get the duration of the Morse effect with Serial.println(morseEffectMO5.Period()); or similar.
This will let me calculate how many repeats fit into each step length.
Then hopefully I can make a sequence (from above):
auto blinkTwice = JLed(1).Blink(250,250).Repeat(2);
auto wait = JLed(1).Off().DelayAfter(1000);
JLed leds[] = {blinkTwice, wait};
auto seq = JLedSequence(JLedSequence::eMode::SEQUENCE, leds).Forever();
Oh wow, you can get the duration of the Morse effect with `Serial.println(morseEffectMO5.Period());` or similar.
This will let me calculate how many repeats fit into each step length.
Then hopefully I can make a sequence (from above):
```
auto blinkTwice = JLed(1).Blink(250,250).Repeat(2);
auto wait = JLed(1).Off().DelayAfter(1000);
JLed leds[] = {blinkTwice, wait};
auto seq = JLedSequence(JLedSequence::eMode::SEQUENCE, leds).Forever();
```
The message should send once, but really it is limited to int repeats step_length / morseEffectTEST.Period(); and when step_length = 30000 (30 seconds), a ~19-second message can only be sent once.
Next steps are to
finish this sequence by setting up an LED-off wait (see above) and
set up conditionals under // See which message we are sending to differentiate between continuous and cycle sending.
Very simple example working. To get it going:
- set a different message than 0, save
- set message to 0 and start continuous, save
The message should send once, but really it is limited to `int repeats step_length / morseEffectTEST.Period();` and when `step_length = 30000` (30 seconds), a ~19-second message can only be sent once.
Next steps are to
- finish this sequence by setting up an LED-off wait (see above) and
- set up conditionals under `// See which message we are sending` to differentiate between continuous and cycle sending.
What's funny is that my little test file almost does everything needed but without the overhead of having it configurable away from PlatformIO.
I am debugging this in `Sites/jled/` and there may be a bug with using `DelayAfter()` in a sequence: https://github.com/jandelgado/jled/issues/122.
What's funny is that my little test file almost does everything needed but without the overhead of having it configurable away from PlatformIO.
This is basically working. Observations:
- message and timing are hardcoded for now
- need to update in loop, based on webform entry
- ~~no gap between end of message repeat and beginning of next message repeat (see https://github.com/jandelgado/jled/issues/122)~~
- "working" fix by adding a space (" ") to the end of each message
- ~~if switching continuous to cycle, the continuous message keeps going~~
- it should stop sending
- fixed in 70decbbcca420194fc144ea19fc4d1319038b8a9
- ~~if switching off to cycle, the cycle message keeps going~~
- it should probably not, because who knows if the timing stays the same
- it should switch off sending until the start time is reached
- fixed in 70decbbcca420194fc144ea19fc4d1319038b8a9
So close. Trying to let the user change the cycle message and running to problems.
Things like morse_cycle = morseEffectMOE; just don't work in loop(). And I can't even overwrite an initial auto morse_cycle = morseEffectMOE; with a later MorseEffect morse_cycle("MOE ", ms_per_dit);
I'm going to set this aside and see if anything else can be updated. If not, will need to reassess.
So close. Trying to let the user change the cycle message and running to problems.
Things like `morse_cycle = morseEffectMOE;` just don't work in `loop()`. And I can't even overwrite an initial `auto morse_cycle = morseEffectMOE;` with a later `MorseEffect morse_cycle("MOE ", ms_per_dit);`
I'm going to set this aside and see if anything else can be updated. If not, will need to reassess.
test power cycle between when start time is set and start time is reached. Does it work the way it should? It should start the program at the scheduled time with no input from the webform.
If it doesn't work, need to store programRunning in file and check on boot., No, that should only be needed if we want to resume from a power loss in the middle of a program, and that will require more math to figure out when to restart the program.
- [x] test power cycle between when start time is set and start time is reached. Does it work the way it should? It should start the program at the scheduled time with no input from the webform.
~~If it doesn't work, need to store `programRunning` in file and check on boot.~~, No, that should only be needed if we want to resume from a power loss in the middle of a program, and that will require more math to figure out when to restart the program.
if(startProgram == true){
//auto morse_cycle = morseEffectMOS;
int period = morse_cycle.Period();
int repeats = step_length / period;
int remainder_wait = step_length - (period * repeats);
int total_wait = ((step_length * (n_transmitters - 1) + remainder_wait));
JLed morses_blink[] = {
// WOW it looks like you can't do Repeat() and DelayAfter() at the same time?
// Opened https://github.com/jandelgado/jled/issues/122
JLed(blinker).UserFunc(&morse_cycle).Repeat(repeats),
//JLed(blinker).FadeOn(1000).Repeat(3)
JLed(blinker).Off(total_wait)
};
auto morses_sequence_blink = JLedSequence(JLedSequence::eMode::SEQUENCE, morses_blink);
morses_sequence_blink.Forever().Update();
programRunning = true;
startProgram = false;
}
Maybe this could be a function?
```
if(startProgram == true){
//auto morse_cycle = morseEffectMOS;
int period = morse_cycle.Period();
int repeats = step_length / period;
int remainder_wait = step_length - (period * repeats);
int total_wait = ((step_length * (n_transmitters - 1) + remainder_wait));
JLed morses_blink[] = {
// WOW it looks like you can't do Repeat() and DelayAfter() at the same time?
// Opened https://github.com/jandelgado/jled/issues/122
JLed(blinker).UserFunc(&morse_cycle).Repeat(repeats),
//JLed(blinker).FadeOn(1000).Repeat(3)
JLed(blinker).Off(total_wait)
};
auto morses_sequence_blink = JLedSequence(JLedSequence::eMode::SEQUENCE, morses_blink);
morses_sequence_blink.Forever().Update();
programRunning = true;
startProgram = false;
}
```
Sigh. RTC is off after reset. That's no good. This was at 19:05 local time.
morseEffectTEST.Period(): 20520
RTC time on startup: 2023-09-09T18:30:38
Alarm 1 set for at 23:58:00 Tue, 09 May 2000 (only HH:MM:SS day-of-month are accurate)
Sigh. RTC is off after reset. That's no good. This was at 19:05 local time.
```
morseEffectTEST.Period(): 20520
RTC time on startup: 2023-09-09T18:30:38
Alarm 1 set for at 23:58:00 Tue, 09 May 2000 (only HH:MM:SS day-of-month are accurate)
```
So you disconnect the 5V to the DS3231, but keep the SDA and SCL pins connected? By doing that, you are working outside of the recommended operating conditions.
If you leave the SDA and SCL pins connected, those pins will be at around 5V. The datasheet specifies that those pins shouldn't be higher that Vcc+0.3V. Where Vcc is 0V, in your case.
Removing power from the Arduino board will also remove the power from the I2C pins, keeping things within specifications.
Battery measures 3.8V. Is it possible the battery wiring is bad? Try the other DS3231 next.
Same value between DS3231 pin 14 (Vbat) and 13 (ground).
Don't know if this is good/relevant info or not (https://arduino.stackexchange.com/a/84893):
> So you disconnect the 5V to the DS3231, but keep the SDA and SCL pins connected? By doing that, you are working outside of the recommended operating conditions.
>If you leave the SDA and SCL pins connected, those pins will be at around 5V. The datasheet specifies that those pins shouldn't be higher that Vcc+0.3V. Where Vcc is 0V, in your case.
>Removing power from the Arduino board will also remove the power from the I2C pins, keeping things within specifications.
This is the behavior I see: https://forum.arduino.cc/t/ds3231-not-keeping-time-on-battery/363363/5
Battery measures 3.8V. Is it possible the battery wiring is bad? Try the other DS3231 next.
Same value between DS3231 pin 14 (Vbat) and 13 (ground).

Reviews on what I bought:
Sounds like these might be fake/counterfeit DS3231 or just bad design with the charging circuit. I guess I can sacrifice one to see if it works better without the charging circuit: https://www.amazon.com/gp/customer-reviews/R3TPKHQJHR0RWU/ref=cm_cr_arp_d_rvw_ttl?ie=UTF8&ASIN=B09LLMYBM1
Same behavior I see (lose on time on power loss), all the 2-star reviews: https://www.amazon.com/gp/customer-reviews/R3T8ND2O2X0U0V/ref=cm_cr_getr_d_rvw_ttl?ie=UTF8&ASIN=B09LLMYBM1
I tore the charging circuit resistor out, but still seeing weird results. The chip remembers something because it's showing a time. But why that time? I unplugged it and took the battery out, then replaced it and restarted. The alarm time was reset, but where does 2023-09-09T19:22:31 come from?
RTC time on startup: 1694287351
2023-09-09T19:22:31
Alarm 1 set for at 00:00:00 Sun, 00 May 2000 (only HH:MM:SS day-of-month are accurate)
A bit later: now it seems to be working? I will leave this one unplugged overnight and see what time it shows in the morning.
A bit later: I set a start time and unplugged it for a few minutes, plugged back in before start time. The program started, although it seemed a second or so late.
I tore the charging circuit resistor out, but still seeing weird results. The chip remembers *something* because it's showing a time. But why that time? I unplugged it and took the battery out, then replaced it and restarted. The alarm time was reset, but where does `2023-09-09T19:22:31` come from?
```
RTC time on startup: 1694287351
2023-09-09T19:22:31
Alarm 1 set for at 00:00:00 Sun, 00 May 2000 (only HH:MM:SS day-of-month are accurate)
```
A bit later: now it seems to be working? I will leave this one unplugged overnight and see what time it shows in the morning.
A bit later: I set a start time and unplugged it for a few minutes, plugged back in before start time. The program started, although it seemed a second or so late.
It seems to have worked (turned on at 8:36 AM). Is it 5-6 seconds off?
RTC time on startup: 1694353011
2023-09-10T13:36:51
Alarm 1 set for at 02:16:00 Wed, 10 May 2000 (only HH:MM:SS day-of-month are accurate)
It seems to have worked (turned on at 8:36 AM). Is it 5-6 seconds off?
```
RTC time on startup: 1694353011
2023-09-10T13:36:51
Alarm 1 set for at 02:16:00 Wed, 10 May 2000 (only HH:MM:SS day-of-month are accurate)
```
After some refreshes in the browser, looks like that one is the correct time. I did the same modification to the other one and it seems to be working similarly.
These are DS3231M chips, so maybe that is removing precision.
I'm going to roll with these for a while to see if they revert to not working. If it becomes a problem, will buy something from Adafruit (https://www.adafruit.com/product/5188) or somewhere else that has quality stuff.
After some refreshes in the browser, looks like that one is the correct time. I did the same modification to the other one and it seems to be working similarly.
These are DS3231**M** chips, so maybe that is removing precision.
I'm going to roll with these for a while to see if they revert to not working. If it becomes a problem, will buy something from Adafruit (https://www.adafruit.com/product/5188) or somewhere else that has quality stuff.
Only took 14 hours to "fix" this, I guess: #24 (comment).
Now it looks like the alarm will work after removing/adding power (because the time seems to be kept now).
Back to #24 (comment) now, until something else breaks.
Only took 14 hours to "fix" this, I guess: https://amiok.net/gitea/W1CDN/vulpes/pulls/24#issuecomment-1235.
Now it looks like the alarm will work after removing/adding power (because the time seems to be kept now).
Back to https://amiok.net/gitea/W1CDN/vulpes/pulls/24#issuecomment-1231 now, until something else breaks.
When using DelayAfter(), need to take that into account for remainer_wait and total_wait math, because it's outside of the duration returned by morse_cycle.Period().
When using `DelayAfter()`, need to take that into account for `remainer_wait` and `total_wait` math, because it's outside of the duration returned by `morse_cycle.Period()`.
I'm beginning to think there really is no way to update/replace a Jled sequence on the fly in loop(). So I could set up some basic options and let people choose from a list, until I can figure this out, with Jled or another way.
I'm beginning to think there really is no way to update/replace a Jled sequence on the fly in `loop()`. So I could set up some basic options and let people choose from a list, until I can figure this out, with Jled or another way.
E (37611) ledc: ledc_set_duty(402): channel argument is invalid
E (37616) ledc: ledc_update_duty(334): channel argument is invalid
I am trying to write a function to make new cycle Jled sequences.
Back to this:
```
E (37611) ledc: ledc_set_duty(402): channel argument is invalid
E (37616) ledc: ledc_update_duty(334): channel argument is invalid
```
I am trying to write a function to make new cycle Jled sequences.
The igniter code works pretty well when stripped down, even if I don't understand it. See Sites/igniter.
The problem is that for Morse, I need to know the duration of the message, which means that the MorseEffect needs to be set up first, then the sequence put together. I run into the same issue where it crashes over and over when I try to use the function.
JLedSequence* sequence = NULL;
JLedSequence* make_sequence(JLedSequence* seq, const char* message, int wpm, int step_length, int n_transmitters){
int ms_per_dit = 1000 * (60 / (50 * wpm));
int word_space_ms = ms_per_dit * 7;
MorseEffect morse_effect(message, ms_per_dit);
int period = morse_effect.Period();
int repeats = step_length / period;
int remainder_wait = step_length - (period * repeats);
int total_wait = ((step_length * (n_transmitters - 1) + remainder_wait));
JLed morses_blink[] = {
JLed(blinker).UserFunc(&morse_effect).Repeat(repeats).DelayAfter(word_space_ms),
JLed(blinker).Off(total_wait)
};
if (seq){
delete seq;
//seq = new JLedSequence(JLedSequence::eMode::SEQUENCE, leds);
seq = new JLedSequence(JLedSequence::eMode::SEQUENCE, morses_blink);
}
return seq;
}
// Initial definition of the sequence
JLedSequence* morses_sequence_blink_test = make_sequence(sequence, "MOE", 10, 10000, 2);
The igniter code works pretty well when stripped down, even if I don't understand it. See `Sites/igniter`.
The problem is that for Morse, I need to know the duration of the message, which means that the `MorseEffect` needs to be set up first, then the sequence put together. I run into the same issue where it crashes over and over when I try to use the function.
```
JLedSequence* sequence = NULL;
JLedSequence* make_sequence(JLedSequence* seq, const char* message, int wpm, int step_length, int n_transmitters){
int ms_per_dit = 1000 * (60 / (50 * wpm));
int word_space_ms = ms_per_dit * 7;
MorseEffect morse_effect(message, ms_per_dit);
int period = morse_effect.Period();
int repeats = step_length / period;
int remainder_wait = step_length - (period * repeats);
int total_wait = ((step_length * (n_transmitters - 1) + remainder_wait));
JLed morses_blink[] = {
JLed(blinker).UserFunc(&morse_effect).Repeat(repeats).DelayAfter(word_space_ms),
JLed(blinker).Off(total_wait)
};
if (seq){
delete seq;
//seq = new JLedSequence(JLedSequence::eMode::SEQUENCE, leds);
seq = new JLedSequence(JLedSequence::eMode::SEQUENCE, morses_blink);
}
return seq;
}
// Initial definition of the sequence
JLedSequence* morses_sequence_blink_test = make_sequence(sequence, "MOE", 10, 10000, 2);
```
Lol, if I comment out the division, I can at least run the function.
int ms_per_dit = 60;//1000 * (60 / (50 * wpm));
int repeats = 2;//step_length / period;
So tomorrow I can try to use the object created.
Lol, if I comment out the division, I can at least run the function.
```
int ms_per_dit = 60;//1000 * (60 / (50 * wpm));
int repeats = 2;//step_length / period;
```
So tomorrow I can try to use the object created.
Backtrace: 0x400d1c32:0x3ffb1f40 0x400e2799:0x3ffb1fb0 0x4008a39a:0x3ffb1fd0
#0 0x400d1c32:0x3ffb1f40 in jled::TJLedSequence<jled::JLed, jled::JLedSequence>::Repeat(unsigned short) at .pio/libdeps/esp32doit-devkit-v1/JLed/src/jled_base.h:576
(inlined by) jled::TJLedSequence<jled::JLed, jled::JLedSequence>::Forever() at .pio/libdeps/esp32doit-devkit-v1/JLed/src/jled_base.h:581
(inlined by) loop() at src/main.cpp:613
#1 0x400e2799:0x3ffb1fb0 in loopTask(void*) at /Users/Matt/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:23
#2 0x4008a39a:0x3ffb1fd0 in vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c:355 (discriminator 1)
Hmm.
`morses_sequence_blink_test->Forever().Update();`
```
Backtrace: 0x400d1c32:0x3ffb1f40 0x400e2799:0x3ffb1fb0 0x4008a39a:0x3ffb1fd0
#0 0x400d1c32:0x3ffb1f40 in jled::TJLedSequence<jled::JLed, jled::JLedSequence>::Repeat(unsigned short) at .pio/libdeps/esp32doit-devkit-v1/JLed/src/jled_base.h:576
(inlined by) jled::TJLedSequence<jled::JLed, jled::JLedSequence>::Forever() at .pio/libdeps/esp32doit-devkit-v1/JLed/src/jled_base.h:581
(inlined by) loop() at src/main.cpp:613
#1 0x400e2799:0x3ffb1fb0 in loopTask(void*) at /Users/Matt/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:23
#2 0x4008a39a:0x3ffb1fd0 in vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c:355 (discriminator 1)
```
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
All the keywords in the title, I guess.
Closes #8.
If using ardino-timer (https://github.com/contrem/arduino-timer), how to set this up?
subtract current time from start time in millisecondsRTC built-in alarmtimer.at(time, function_to_call);(with time in milliseconds?)yourInputSend. - Show on webform when cycle/program is active (programRunning == true) (#37)function should then calltimer.every(interval, function_to_call);and expirethetimer.every()function is probably a wrapper to update the variables that go intomorseToSend(and `morseToSend_blink).how long thetimer.every()interval should behow many times to JLedRepeat()send to fill the step lengthHTML field is datetime-local, which needs to be labeled as UTC.
https://www.w3schools.com/tags/att_input_type_datetime-local.asp
Comes out as
2023-09-02T05:34format.Can I use JS to convert this to UTC?
I can get the submitted unix time copied into a file/variable, but it might be local time rather than UTC?
Maybe I'll go back and make it use local time everywhere.
Huh,
Date.now()gives UTC. Maybe I can use TZ offset on one of the unix timestamps to get where I want to be. https://stackoverflow.com/a/61082536/2152245Holy buckets do js missing brackets break everything.
Oh no, DST. The time zone adjustment doesn't know about it. See #26.
All back on local time now. The current time is saved with an epoch shift according to the timezone offset in JS. The start program time is local by default from the HTML form. Both are stored as unix timestamps on the C++ side.
It's not horrible. I'd like the two time formats to match.

When I try to format the date differently than
return rtc.now().timestamp();, liketoString(), the device crashes.In any case, this is probably the way to go:
This message when you submit an empty date also isn't horrible, but I'd rather store the existing date until a new one is set.

Way overthinking this. Time is coming from browser in the first place. If I want to print it, print it from the browser, don't pull it from the controller.
Wow did I ever get messed up there.
If the start date isn't set, a value isn't added to the JS, so it just sees
s = ;and breaks everything else in the script. Which means you can't put in a new value from the webform.Right now there is a hardcoded date in JS, but that needs to be fixed.
Did a Full Clean/Build Filesystem Image/Upload Filesystem Image/Upload and Monitor.
The empty start time/broken JS seems to be fixed by
b97f48858d.Back to the double submit problem.
No luck on double submit, but I managed to
Ooooh, so close. Going to attack #15 now.
Super rad. https://stackoverflow.com/a/48307807
Now. Now maybe I can get to scheduling.
OK, so it looks like arduino-timer
.at()uses themillis()clock. Are there timers that use real time (like stored in RTC)?Or should I be using RTC alarms? https://garrysblog.com/2020/07/05/using-the-ds3231-real-time-clock-alarm-with-the-adafruit-rtclib-library/
https://www.arduino.cc/reference/en/libraries/category/timing/
RTC alarms seem to fire at the next opportunity, even if they happened when ESP32 was off. This is a problem I wanted to avoid.
Reset the alarm in
setup()if it's in the past?I'm getting weird dates again. I will look at the hardware connections tomorrow. May be a short between the two boards because I sort of overlapped them!
It's also worth considering using the SQW pin: https://garrysblog.com/2020/07/05/using-the-ds3231-real-time-clock-alarm-with-the-adafruit-rtclib-library/.
It's this bit that's causing the
i2cCheckLineState()error and reboot:ChangedNope, still crashes. Why intermittent? With the wholetrueto1and it is not crashing.ifstatement commented out, submitting the form doesn't seem to cause crashes. I wonder if it's some race condition between form submission (setting the alarm) and testing the alarm inloop().Also, even though I get stuff like this, the RTC time seems to be right:
I tried the same
if ((rtc.alarmFired(2) == true) )code on the breadboard version and get the same results (crash on form submission).I will probably breadboard in the SQW pin and see if that works better https://github.com/garrysblog/DS3231-Alarm-With-Adafruit-RTClib-Library/blob/master/DS3231-RTClib-Adafruit-Alarm-Poll-SQW/DS3231-RTClib-Adafruit-Alarm-Poll-SQW.ino.
Added SQW pin on breadboard to GPIO4.
I can set Alarm2 and have SQW notify of the alarm. I tried
But I need to make sure that the time is being held between reboots, because the alarm just takes "day of month" HH:MM:SS, not absolute date. So if it reboots and thinks it's May 2000 but the same day, the alarm will trigger anyway.
On boot:
Alarm 2 set for at 15:27:00 Mon, 08 May 2000Feel slightly better:
Confirming that I can't set an alarm more than a month in advance (or maybe ~2 months if the current month has 30 days and the next month has 31 days, and you set it for the 31st). Wild.
https://forum.arduino.cc/t/ds3231-alarm-when-date-match/929036/5
This is probably OK. I will leave the edge case alone for now. #33.
Before I go too far, consider just comparing
rtc.now()and the start time inloop()to trigger the scheduled program.ETA: This is harder than it looks, so I'll avoid it for now.
Random times every once in a while again.
https://www.reddit.com/r/arduino/comments/hdg70t/ds3231_rtc_random_numbers/
I thought I had typed this all out, but
So when I set up Alarm2 but turn off cycle, if I turned cycle back on within the same minute, the alarm was triggered.
I switched to Alarm1.
To send a cycle, we need to set up the duration of each repeating message.
morseToSendandmorseToSend_blinkreset/update for step lengthCan this be done with arduino-timer or another timer without rewriting the Jled functions in
loop()?So close to being able to use Jled sequences natively, if I could figure out how to calculate the number of times to repeat for a given step length.
This looks helpful (https://arduino.stackexchange.com/a/22275):
Or use
millis()directly likeJled sequences example 1 (you can repeat a sequence n times or forever) (https://github.com/jandelgado/jled/issues/103#issuecomment-1312098221):
Jled sequences example 2 (https://github.com/jandelgado/jled/blob/master/examples/sequence/sequence.ino):
Another example (https://github.com/jandelgado/jled/issues/34):
Can I rewrite this for what I need?
Neat but not what I'm looking for: https://github.com/tfeldmann/Arduino-Blinkenlight.
GTK (https://github.com/jandelgado/jled/issues/11#issuecomment-429887353):
Oh wow, you can get the duration of the Morse effect with
Serial.println(morseEffectMO5.Period());or similar.This will let me calculate how many repeats fit into each step length.
Then hopefully I can make a sequence (from above):
Very simple example working. To get it going:
The message should send once, but really it is limited to
int repeats step_length / morseEffectTEST.Period();and whenstep_length = 30000(30 seconds), a ~19-second message can only be sent once.Next steps are to
// See which message we are sendingto differentiate between continuous and cycle sending.My brain needs a rest.
I am debugging this in
Sites/jled/and there may be a bug with usingDelayAfter()in a sequence: https://github.com/jandelgado/jled/issues/122.What's funny is that my little test file almost does everything needed but without the overhead of having it configurable away from PlatformIO.
This is basically working. Observations:
no gap between end of message repeat and beginning of next message repeat (see https://github.com/jandelgado/jled/issues/122)if switching continuous to cycle, the continuous message keeps going70decbbccaif switching off to cycle, the cycle message keeps going70decbbccaMy use of "cycle" and "program" are very confused right now, but making progress.
So close. Trying to let the user change the cycle message and running to problems.
Things like
morse_cycle = morseEffectMOE;just don't work inloop(). And I can't even overwrite an initialauto morse_cycle = morseEffectMOE;with a laterMorseEffect morse_cycle("MOE ", ms_per_dit);I'm going to set this aside and see if anything else can be updated. If not, will need to reassess.
If it doesn't work, need to store, No, that should only be needed if we want to resume from a power loss in the middle of a program, and that will require more math to figure out when to restart the program.programRunningin file and check on boot.Um, no, the program doesn't seem to survive a reboot. Fun.
Well, it works if I push the reset button, but didn't when I unplugged the USB cable.
Maybe this could be a function?
Sigh. RTC is off after reset. That's no good. This was at 19:05 local time.
Don't know if this is good/relevant info or not (https://arduino.stackexchange.com/a/84893):
This is the behavior I see: https://forum.arduino.cc/t/ds3231-not-keeping-time-on-battery/363363/5
Battery measures 3.8V. Is it possible the battery wiring is bad? Try the other DS3231 next.
Same value between DS3231 pin 14 (Vbat) and 13 (ground).

Reviews on what I bought:
Sounds like these might be fake/counterfeit DS3231 or just bad design with the charging circuit. I guess I can sacrifice one to see if it works better without the charging circuit: https://www.amazon.com/gp/customer-reviews/R3TPKHQJHR0RWU/ref=cm_cr_arp_d_rvw_ttl?ie=UTF8&ASIN=B09LLMYBM1
Same behavior I see (lose on time on power loss), all the 2-star reviews: https://www.amazon.com/gp/customer-reviews/R3T8ND2O2X0U0V/ref=cm_cr_getr_d_rvw_ttl?ie=UTF8&ASIN=B09LLMYBM1
I tore the charging circuit resistor out, but still seeing weird results. The chip remembers something because it's showing a time. But why that time? I unplugged it and took the battery out, then replaced it and restarted. The alarm time was reset, but where does
2023-09-09T19:22:31come from?A bit later: now it seems to be working? I will leave this one unplugged overnight and see what time it shows in the morning.
A bit later: I set a start time and unplugged it for a few minutes, plugged back in before start time. The program started, although it seemed a second or so late.
It seems to have worked (turned on at 8:36 AM). Is it 5-6 seconds off?
After some refreshes in the browser, looks like that one is the correct time. I did the same modification to the other one and it seems to be working similarly.
These are DS3231M chips, so maybe that is removing precision.
I'm going to roll with these for a while to see if they revert to not working. If it becomes a problem, will buy something from Adafruit (https://www.adafruit.com/product/5188) or somewhere else that has quality stuff.
Only took 14 hours to "fix" this, I guess: #24 (comment).
Now it looks like the alarm will work after removing/adding power (because the time seems to be kept now).
Back to #24 (comment) now, until something else breaks.
When using
DelayAfter(), need to take that into account forremainer_waitandtotal_waitmath, because it's outside of the duration returned bymorse_cycle.Period().Switching from continuous to off while I was trying to update variables in

loop():I'm beginning to think there really is no way to update/replace a Jled sequence on the fly in
loop(). So I could set up some basic options and let people choose from a list, until I can figure this out, with Jled or another way.Back to this:
I am trying to write a function to make new cycle Jled sequences.
I'm going to look for JLed alternatives, or maybe ask for help developing this function.
This causes it to reboot over and over:
https://github.com/ArduinoGetStarted/led (ezLED) is interesting, but I get errors on build. See
Sites/ezLED.Here's an example function, I think: https://github.com/dariomas/igniter/blob/97ad8480f750b6efcd990ec890027ba8026a4a44/igniter.ino#L118
The igniter code works pretty well when stripped down, even if I don't understand it. See
Sites/igniter.The problem is that for Morse, I need to know the duration of the message, which means that the
MorseEffectneeds to be set up first, then the sequence put together. I run into the same issue where it crashes over and over when I try to use the function.Get a little more information with
monitor_filters = esp32_exception_decoderin platformio.ini.Lol, if I comment out the division, I can at least run the function.
So tomorrow I can try to use the object created.
Hmm.
morses_sequence_blink_test->Forever().Update();