The concept is pretty simple: I have a work and a config mode, depending on the state of a switch when a block boots. In config mode the block spawns a wireless network, and once connected to 192.168.4.1, provides a form to configure the device. Once booted into boot mode, it grabs the data and pushs it to its destination. This may be a bit too abstract, so let me explain the details.
The first entry defines if the block is supposed to spawn its own access point or connect to an existing network. In the first case the block uses the configured SSID and password as credentials for the created network, while in the second case it’s using this info to connect to a secured network.
The modes are more interesting. There are three: Active HTTP, passive HTTP and UDP. Active HTTP results in the block calling the specified URL every n ms, specified in the refresh interval field. To be specific, it grabs the data of the block, appends it to the URL and takes the answer as input for the block. My temperature sensor for example calls [endpoint].lutan.systems/block/write/[WriteKey]/27, where 27 is the appended value of the current temperature. The answer would be empty and not used by the block.
A block only used to control an RGB LED could call [endpoint].lutan.systems/block/read/[ReadKey], append nothing to that URL, and use the answer as the new color of the LED.
Passive HTTP instead spawns a webserver on the block and waits for incoming requests with a specific parameter strValue. That value can be used to toggle a relay or again set an RGB LED. The answer of the block to the request is the value of the block, if any. To stay with the previous examples, this value could be the current temperature.
UDP kicks away the application layer, works directly on the transport layer and is used for more timing critical blocks, for example a remotely controlled robot. Some source, maybe a laptop connected to a network spawned by the block, sends an UDP packet with some data to the block and in return gets the data (e.g. the current distance of an object in front of the robot). UDP doesn’t care about the URL or the refresh interval, but instead uses the UDP Port parameter to know on which port to listen for UDP packets and to which port on the source to send the answer to.
Update on change is used for active HTTP: If set, it monitors the block value for changes and automatically sends that value to the specified endpoint on a change. This can be used additionally to regular updates, and is especially useful for switches which a) require a heartbeat and b) quick action once the value changes.
But where does the data from the block come from, exactly? And how does it work with the incoming data?
After including my framework, I only have to specify 4 functions: setup_pins, take_value, give_value and run_loop. The functions are already explained by their names: setup_pins is run once directly after booting, take_value is the function which takes the incoming data and where I can define how to react to the data, and give_value is where I set which data should be given out. run_loop is used to run code every single loop. I’m using it mostly for debugging, but it’s also enabling me to work with blocks that need some time to gather useful data.
An IR receiver for example would need to read incoming IR commands all the time. Once a full command is received, it would write the command into a variable as last command, which then can be read by give_value to feed it into my framework. Without the run_loop, the IR receiver wouldn’t be able to read in all the IR info, as 100ms between each read would result in a lot of missing data.
This framework saves me so much time. The time to develop the software for a new block is reduced to almost nothing: I just setup the functions I need, configure the block and run it. Done. For the switch the whole process took me ~3 minutes, of which 2 minutes were waiting for the flashing progress bar and drinking some tea.
Currently I’m only copying around the ESP_Basic.ino into each project I need it in. It’s okay for a small amount of different blocks, but I plan to convert this project into an Arduino library for easier usage. Until then though, I won’t release it into public.