Skip to content

Development Guide

  • PlatformIO: Build system and toolchain manager
  • VS Code: Recommended IDE with PlatformIO extension
  • Git: Version control
  1. Install VS Code

  2. Install the PlatformIO extension from the VS Code marketplace

  3. Clone the repository:

    Terminal window
    git clone https://github.com/your-org/Multiflexmeter.git
    cd Multiflexmeter
  4. Initialize submodules:

    Terminal window
    git submodule update --init
CommandDescription
pio runBuild firmware
pio run -t uploadBuild and flash via USBasp
pio device monitorOpen serial monitor (115200 baud)
pio run -t cleanClean build artifacts
Multiflexmeter/
├── src/ # Source files
│ ├── main.cpp # Main application logic
│ ├── sensors.cpp # Sensor module interface
│ ├── smbus.cpp # I2C/SMBus driver
│ ├── rom_conf.cpp # EEPROM configuration
│ ├── rpm_calculation.cpp # RPM conversion
│ ├── wdt.cpp # Watchdog handling
│ └── boards/ # Board-specific code
├── include/ # Header files
│ ├── config.h # Global configuration
│ ├── main.h # Main declarations
│ ├── rom_conf.h # EEPROM structure
│ ├── sensors.h # Sensor API
│ ├── smbus.h # SMBus API
│ └── board_config/ # Board pin definitions
├── lib/
│ └── arduino-lmic/ # LoRaWAN stack (submodule)
├── boards/
│ └── mfm_v3_m1284p.json # PlatformIO board definition
├── hardware/ # KiCad design files
└── platformio.ini # Build configuration

Edit include/config.h to modify compile-time settings:

// Enable debug output to serial
#define DEBUG
// Print build date/time on startup
#define PRINT_BUILD_DATE_TIME
// Lowest data rate (highest spreading factor)
#define MIN_LORA_DR 0
// Sensor measurement timeout (ms)
#define SENSOR_JSN_TIMEOUT 200
// Interval limits (seconds)
#define MIN_INTERVAL 20 // Minimum: 20 seconds
#define MAX_INTERVAL 4270 // Maximum: ~71 minutes

The platformio.ini sets these LMIC flags:

FlagPurpose
ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESSUse custom LMIC config
DISABLE_BEACONSReduce code size
DISABLE_PINGDisable Class B ping
LMIC_PRINTF_TO=SerialDebug output target
LMIC_DEBUG_LEVEL=2LMIC verbosity level
  1. Define the command byte in main.cpp:

    #define CMD_MY_COMMAND 0x13
  2. Add handler in processDownlink():

    case CMD_MY_COMMAND:
    if (len >= 2) {
    uint8_t param = payload[1];
    // Handle command
    }
    break;
  3. Test by sending a downlink from TTN console

The firmware uses a passthrough architecture. To add a new sensor:

  1. Connect sensor module to I2C bus at address 0x36

  2. Ensure module responds to:

    • CMD_PERFORM (0x10) - Start measurement
    • CMD_READ (0x11) - Read result (block read)
  3. Implement data decoder in TTN payload formatter

Enable debug output:

// In include/config.h
#define DEBUG

Connect FTDI adapter and monitor:

Terminal window
pio device monitor

Output includes:

  • Build date/time on startup
  • LMIC state transitions
  • Join attempts and results
  • TX/RX events
MessageMeaning
EV_JOININGAttempting to join network
EV_JOINEDSuccessfully joined
EV_JOIN_FAILEDJoin rejected (check credentials)
EV_TXCOMPLETEUplink transmission complete
EV_RXCOMPLETEDownlink received

GitHub Actions automatically builds on:

  • Push to main or v3 branches
  • Version tags (v*.*.*)

Firmware version is encoded in 16 bits:

Bit 15: Protocol (0=dev, 1=release)
Bits 14-10: Major version (0-31)
Bits 9-5: Minor version (0-31)
Bits 4-0: Patch version (0-31)

Tagged builds set version via build flags:

  • -DFW_VERSION_MAJOR=X
  • -DFW_VERSION_MINOR=Y
  • -DFW_VERSION_PATCH=Z
  • Use C++ with Arduino framework patterns
  • Follow existing naming conventions
  • Keep functions focused and small
  • Document non-obvious logic with comments
  • Test changes before committing