Getting Started for Developers
This guide will help you understand how the Rhomb.IoT source code was built, its file structure and how the application works in general.
It is important that you first read the Getting Started For Users and the other user documentation to create a development environment.
Contents
Before Start
This guide for developers assumes that you have read the user guide and that you have used Rhomb.IoT as a user at some point. It also assumes that you have worked with and are familiar with VS Code and PlatformIO.
File Structure
All the source code is inside the directory rhomb.iot
. This is a list of the most important files:
Folder / File | Module | Description |
---|---|---|
./batt/ | Battery | Reads voltages of VBAT, VIN and VSYS. The info is added to the messages for the server and also used by sleep module to enable low and sleep modes. |
./bl/ | Bluetoorh | Allows to work with different Bluetooth modules. |
./conf/ | Conf | It contains the main configuration file and libraries to manage the configuration. |
./core/ | Core | Main header files of the library. It contains other modules such as Debug, or Tempo among others |
./gpio/ | GPIOs | Control for GPIOs |
./gps/ | GPS | Allows to work with differente GPS modules. |
./log/ | Data Logger | Allows to work with different data loggers |
./modems/ | Modems | It contains generic functions of a particular modem. For example the SIM868 has GPS, GPRS and Bluetooth. The modem-sim800.h library contains functions that can be used by all 3 modules. |
./msg/ | Message | Library for the creation of messages |
./net/ | Net | Allows to work with different modems to connect to internet |
./sensors/ | Sensors | Contains other libraries to manage sensors for temperature, humidity and so on |
./serial/ | Serial | Library to work with serial ports |
./storage/ | Storage | Different modules to persist data configuration |
./rhoimb.iot.h | Main header file | It is included in the src/main.cpp file of the project |
The main header file is rhomb.iot.h
. This file is included in src/main.cpp
. The rest of the directories are modules of Rhomb.IoT. Each module can be composed by one or several headers.
For example, the net
module contains libraries to work with the GPRS SIM868 modem (net/net-800.h
) and the ESP32 Wifi modem (net/net-esp32.h
). In this case, as there are several libraries, there is also a main module file, which we call the director (net/net.h
), and it is the one in charge of selecting which library should be included in the code according to the configuration we have chosen in our configuration file.
The main header file: rhomb.iot.h
/// @file rhomb.iot.h
#pragma once
#define RHOMBIOT_VERSION "alpha-3"
// The order of factors sometimes matters
#include "core/utils.h"
#include "core/typedefs.h"
#include "conf/conf-user.h"
#include "core/prototypes.h"
#include "core/global.h"
#include "core/names.h"
#include "core/rhio-pinmap.h"
#include "core/puf.h"
#include "serial/serial.h"
#include "core/tempo.h"
#include "core/debug.h"
#include "core/errors.h"
#include "core/sync.h"
#include "conf/cfg.h"
#include "storage/storage.h"
#include "conf/conf.h"
#include "gpio/gpio.h"
#include "core/delay.h"
#include "core/interruption.h"
#include "core/at.h"
#include "core/device.h"
#include "msg/msg.h"
#include "log/log.h"
#include "batt/batt.h"
#include "gps/gps.h"
#include "net/net.h"
#include "msg/msg-login.h"
#include "sensors/sensors.h"
#include "core/opmode.h"
#include "core/rhio-pinmap.h"
namespace core {
void setup() {
opmode::set(OPMODE_BOOT);
opmode::run();
}
void loop() {
puf::emit(EV_BEFORE_LOOP, 0);
opmode::run();
puf::emit(EV_AFTER_LOOP, 0);
}
} // namespace core
The source code is commented in the original file. After including all the required headers we are creating two wrapping functions for the arduino main methods setup()
and loop()
. This doesn't mean that we can't use these methods in the main.cpp
, in fact they are used, simply when the Arduino library calls those methods we will take control with ours.
Tip
You can see the puf
library inside the loop()
function. It is an important part of the code which allow us to implement and event driven architecture. We'll explain it later, keep your eyes open :)
The main.cpp
file of our application:
/// @file src/main.cpp
#include <Arduino.h>
#include <Wire.h>
#include "rhomb.iot.h"
void setup() {
core::setup();
}
void loop() {
core::loop();
}
Simple enough? Rhomb.IoT includes all the libraries it needs to work. In the setup()
Rhomb.IoT configures what is needed (using the configuration file as reference). Then it uses the Arduino loop to check the status of timers and interruptions at all times.
Configuration File config-user.h
If you have used Rhomb.IoT you should already know this file. It is the main file where all the configuration of the application is stored. It is on conf/conf-user.h
. Depending on the data in this file the core::setup()
function will perform some actions or others.
You have to be careful when modifying this file manually. A wrong parameter can make the application stop working and debugging the error can be complicated (the voice of experience is speaking).
For more info read the User Configuration File guide.
Life cycle of the application
Rhomb.IoT is an application designed to read data from sensors, creating messages with this data and transporting the information to a web server (using our communication protocol) or data logger.
An overview of the flow or application life cycle
In front of the Arduino loop is the opmode library, which will perform different tasks depending on the state of the application. Read more about the module opmode.
Most of the time we will be in normal opmode, reading data from sensors, creating messages and sending them to the server.
A simplified overview of message creation
- The creation of messages is done through the
TICK1
time interval, configured in seconds in theconfig-user.h
file. - The main mission of the normal opmode is to check this interval and launch the message creation event
EV_CREATE_MSG
. - The modules listen to this event and read their sensors to add the information to the message.
- When the message has been created, the
msg
module triggers the eventEV_MSG_CREATED
and thenet
module reacts by transmitting the message to the server or keeping it in the log.
Launching and listening to events is how the different modules communicate with each other.
Look at this example of the init()
function in the gps-l86.h
module
uint8_t init() {
puf::on(EV_CREATE_MSG, onCreateMessage);
const uint8_t callbacksSize = 6;
uint8_t (*callbacks[callbacksSize])() = {addLocation, addAltitud, addSOG,
addCOG, addSats, addHDOP};
each.add(callbacks, callbacksSize);
return 0;
}
The gps-l86.h
module makes a subscription to the EV_CREATE_MSG event using the puf::on()
method:
puf::on(EV_CREATE_MSG, onCreateMessage);
Meanwhile the msg
module (msg/msg.h
) will trigger the EV_CREATE_MSG event on the method msg::createMessage()
:
uint8_t createMessage(uint8_t param) {
[...]
uint8_t err = puf::emit(EV_CREATE_MSG, EV_CUSTOM_MSG_SENSING);
if (err) return err;
puf::emit(EV_MSG_CREATED, 0);
[...]
}