Collector Development
The main goals of the collector code are as follows:
- Be flexible in supporting the addition of new types of sensors to the software.
- Provide enough configuration options to support all use cases without modifying the source code.
- Support Micropython.
Architecture
The architecture of the collector code is visualized below and contains the following modules:
- Main Control Loop - This is the entrypoint of the program and is responsible for orchestrating the modules and the endless loop.
ConfigManager- This class is responsible for parsing and validating the configuration contained withinconfig.json.UploadManager- This class is responsible for formatting the collected data and uploading it via HTTP to the configured QuestDB endpoint.- Sensor Polling:
SensorDriver- EachSensorDriverclass instance is responsible for collecting data from a single sensor.SensorManager- This class is responsible for initializing as manySensorDriverinstances as are called for in the configuration, polling them, and sending the data to the main control loop.

Main Control Loop
The main control loop has the following behavior:
- Initializes all required manager classes.
- Updates the metadata tables in the database with the currently collector and sensor configuration.
- On an endless loop:
- Collects sensor data.
- Uploads sensor data.
- If the data could not be uploaded, saves the data in-memory to re-try next loop.
- Waits a length of time determined by the
polling_intervalconfiguration parameter.
stateDiagram-v2
[*] --> Initialization
Initialization --> Metadata : Success
Metadata --> Poll: Success
Initialization --> Error : Failure
Metadata --> Error : Failure
Poll --> Upload
Upload --> Wait
Wait --> Poll : After Polling Interval
Metadata: Metadata Upload
Upload: Data Upload
Config
For documentation on what the configuration values do, see the User Guide.
See the Config Reference for documentation on the configuration-related source code.
Upload
For documentation on the structure of the database, see the Storage Database page.
See the Upload Reference for documentation on the upload-related source code.
Sensors
For documentation on which sensors are currently supported and how to configure them, see the User Guide sensors page.
For instructions on how to add support for new sensors, see the Sensor page.
See the Sensor Reference for documentation on the sensor-related source code.
Architecture & Approach
As the main goal of the sensor code design was to support adding more sensors in the future, the sensor code follows a manager-driver pattern, with the driver code fulfilling an abstract interface.
In software engineering, an interface is a piece of code that contains only specification. For example, a function may define the arguments it accepts, the type of value it returns, and have a documentation string explaining its behaviour, but the actual workings of the function are left blank. The advantage of using interfaces is that it allows seperate pieces of software to be agnostic to specific implementations of the code they depend on, by making them depend on a contract instead. This allows changing those pieces independently. If one piece of the software changes, the changes required to make the rest of the system interact with the new logic should be as limited as possible to the location of the relevant change.

In practice, this means that that the SensorManger class is not directly dependent on any specific implementation of a sensor but is instead dependent on an interface called AbstractSensorDriver. As long as the interface is properly designed, the changes to the codebase required to add new sensors to the software should be isolated to the creation of new implementations of the interface.
Abstract classes are be implemented with Python's Protocol type.
Development
The collector is designed to run under both CPython and MicroPython, but MicroPython deployment is currently experimental and the repository does not include an automated flashing script or release image.
For development on a Raspberry Pi Pico W:
- Flash a current Pico W MicroPython firmware image using the Raspberry Pi documentation.
- Copy
collector/src/config.example.jsontocollector/src/config.jsonand replace the example IDs, network credentials, database address, GPIO assignments, and sensor settings. - Copy the collector modules, required modules from
common/src/common, andconfig.jsonto the board using Thonny,mpremote, or another MicroPython filesystem tool. - Add a small board-level
main.pythat imports the collector entry point and callsmain(), adjusting the import path to match the layout copied to the board. - Reset the board while a serial console is attached. Confirm initialization, metadata upload, repeated polling, and error reporting before disconnecting it.
Keep secrets and deployment-specific configuration out of version control. When changing collector code, test configuration parsing and sensor drivers under CPython first where practical, then repeat the test on real MicroPython hardware: module availability, filesystem paths, timing, and networking behavior differ between the runtimes.
There is not yet a canonical on-device file layout or a continuous-integration hardware test. If you establish a repeatable flashing layout for a board, document the firmware version and exact copy command alongside the change.