The software is a single image for all implementations so that there isn't a different version for each sensor or addon card.
Software to implement these ideas can be viewed on github here.
A version of the source code is attached to this web page but there is also more recent versions on the tekyinblack pages of github.
The code fits the standard Arduino pattern of Setup and Loop routines instead of the Input-Decide-Output-Delay architecture of some other microcontroller models.
As far as possible, there are very few global variables defined and used in the core software code. The main function of the core module is to organise timing and farm out monitoring processing to the defined modules. One central array holds all parameters for operation of the software and this is currently set at compilation time and has to be updated OTA.
Once the first deployments of the modules is complete I will add a webserver to allow dynamic updating of this array.
The setup routine carries out the basics of getting the system initialised with WiFi and connected. Once the housekeeping is complete, the core array is scanned for eligible features to be setup and an individual setup routine called for each eligible feature. For each feature, a separate .ino file is attached which includes all variable definitions and functions to support the feature, which currently includes a setup routine, a publishing routine, a subscription routine and a display routine as core requirements. There may be other functions in the file but these will be referenced in the array.
To save complications, dummy routines for all these four functions are in the core software and may be used instead.
Once all setup functions for eligible modules is complete, the setup routine ends and hands control to the main loop.
The main loop runs forever and so has several functions that are run repeatedly. The first function of teh loop is to check for and execute an OTA update. When first run, the OTA update is repeated for 30 seconds to allow the code to be replaced in the event of a fatal code error.
The MQTT connection is then checked and if down, is re-established.
This is currently a blocking function but the facility to store and forward collected data will be included as a future project enhancement.
For every iteration of the loop after startup, the array is scanned for eligible modules and these are processed in the sequence they are held in the array. When a module is found to be eligible, its timer is checked to see if it is now time to run the module, either based on absolute or relative time. If it is time, the publish function for the module is called and allowed to do whatever processing is required, otherwise, the publish function is bypassed. Once publish processing is complete, a second timer for the module is checked to see if it has expired and this then calls the display function for the module if one is active.
The timers for a module may be based on the RTC, if available, or on a simple relative time, such as every 10, 20, 100 seconds.
Once the process for the publish and display functions is complete, the MQTT functions scan the subscribe queues and where there is a message, the associate modules subscribe function is called, passing the received message.
The responsibility of the setup functions is to prepare the monitor feature for use, so should run any initialisation functions required and create and populate any additional data structures that will be required by this module to operate. This also includes the display if it will be used.
The publish function is primarily intended to access the supported hardware for data, format the data and call the MQTT transmit function. It can of course do anything it is capable of, so if gathering data without transmit is all that is required then that will work, or indeed, a software only example is included where the function only passes data to and from MQTT but could be used to update the core array or just general features of the software.
Most of the code in the original is based on off the shelf software code for the modules involved and where credit is due is given.
The publish function is not essential and may be replaced with a dummy function, for instance, if the implementation is to operate a function based on a timer supplied by a subscribed queue.
These are called after the publish functions but may be called on a much more frequent or irregular basis. Their purpose is to update an attached I2C based screen with data gathered by the associated publish function. It is up to this module to format the display screen appropriately but where a valid RTC is available, the screen will optionally include the current time and date as well as other system information. The intention is that the end-user will either use this to provide a constantly updating reflection of the gathered data or a rotating information display on the various monitoring functions.
The display function is not essential to the operation of the system but provides addition functionality that many users may find useful.
These are called when a subscribed-to MQTT queue receives a message. The message is passed to the subscribe function and it is up to the function to carry out any actions required. A subscribe function is not required and it is not necessary to subscribe to an MQTT queue.
As a default operation the core software will loop until its code is updated. With a display and RTC available, the core operation will be to display a running clock, which may be synchronised with the MQTT server.