Let me start with the TL;DR:
My project simulates lightning strikes outside a window using LED strips and panels. The "main" board blinks attached LED strips via a MOSFET, the other "remote" board is 10' away and blinks a similar set of LEDs via MOSFET. The first board randomly generates an on/off blink sequence to simulate lightning and both boards need to flash their respective LED strips using the same data. A small amount of out of sync is acceptable (no more than 200ms), due to data transmission/decode time.
I have previously attempted just using a wireless turn on / turn off signal from the main board to the remote one, but occasionally the remote side has not received the "off" command (due to RF interference, main board software crash, etc) and left the LEDs on, something most undesirable in a haunted house scenario!
This has been driving me up a wall for weeks now... I'm hoping someone here can point me in the right direction before I claw all my hair out.
Here's the project so far:
- Two ESP32 dev boards (using for convenience of ESP-NOW and a web interface for the user on the control board, but using NRF24L01 or "generic" 315/433mhz radios is also equally acceptable)
- The flash sequence runs randomly every 3-6 minutes or by button push on the main board with the details randomly generated each time.
- The sequence needs to run simultaneously on the local/main board and the second/third/fourth/etc boards.
- The sequence is a minimum of a single flash up to a maximum of 5 flashes.
- The LEDS are turned on for random(100, 1000) ms, then turned off for random(100, 1000) ms, repeated random(1, 5) times as determined by the main board.
A sample in CSV format would look like "3,209,587,228,319,708,702"
There are 3 flashes for this sequence:
LEDs on for 209ms then off for 587ms
LEDs on for 228ms then off for 319ms
LEDs on for 708ms then off for 702ms*
(* The last 702ms off is irrelevant as the next "lightning strike" won't happen for another 3+ minutes, it's just easier to make a for loop with both on and off values)
It works beautifully on the local board with 24v cool white LEDs turned on and off with a MOSFET. I haven't found a way to send the data block to the remote boards in a way that can handle a command sequence that has a variable number of parameters based on the value of the first parameter sent...
I've been messing around with the ESP-NOW auto-pairing examples from RandomNerdTutorials, and there's a section where the variable/struct used is based on a parameter of the incoming data packet. Those packets have a finite number of parameters so I created a struct with the "maximum" amount of data for a lightning simulation and some "extras" for other remote board types eventually. The first 3 elements will be common to all data blocks.
enum MessageType {PAIRING, DATA, LIGHTNING, RELAY, MP3};
typedef struct struct_lightning { // Define LIGHTNING data format
uint8_t msgType; // this is a LIGHTNING packet, relay and sound effects later
uint8_t group; // The board's group ID (1 = lightning LEDs, 2 = relays, 3 = sound effects, etc)
uint8_t address; // The board's individual address (0 = broadcast)
uint8_t flashes; // The number of flashes in the sequence 1-5
uint16_t on1; uint16_t off1;
uint16_t on2; uint16_t off2;
uint16_t on3; uint16_t off3;
uint16_t on4; uint16_t off4;
uint16_t on5; uint16_t off5; // I'm resisting the temptation to add another pair so this doesn't end with an index value of [13] ;)
} struct_lightning;
struct_lightning lightningData;
This works for getting all the data to the remote side, but...
int lastPair = 4 + (lightningData.flashes * 2) - 2; // Set the last element index pair based on the number of flashes specified
// lastPair = 4 + ( 3 * 2 ) - 2 : 3 flashes, [4] is the start, [8]/[9] is the last on/off pair
// lastPair = 4 + ( 5 * 2 ) - 2 : 5 flashes, [4] is the start, [12]/[13] is the last on/off pair
for (int i = 4 ; i <= lastPair ; i + 2) { // First pair starts at element [4] of the struct, then to 6, 8, etc.
digitalWrite(LEDpin, HIGH);
delay(lightningData.flashes[i]); // Wait for [i] ms with the LEDs on
digitalWrite(LEDpin, LOW);
delay(lightningData.flashes[i + 1]); // Read the companion to the previous element and wait
}
...I'm finding you can't address a struct by index (!!??!)...
In function 'void generateLightning()':
remote:139:34: error: expected unqualified-id before '[' token
Serial.println(lightningData.[i]);
^
So how do I tell the loop to only run "lightningData.flashes" times and read lightningData.on1, lightningData.off1, lightningData.on2, lightningData.off2, etc?
Is there something really obvious that I'm missing? Just going wired would be just fine if our house wasn't 3 floors.