Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: CI

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

env:
PLATFORMIO_VERSION: "6.1.19"
GCOVR_VERSION: "8.6"

jobs:
test:
name: test
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-native-${{ hashFiles('platformio.ini') }}
- name: Install PlatformIO and gcovr
run: pip install "platformio==${PLATFORMIO_VERSION}" "gcovr==${GCOVR_VERSION}"
- name: Unit tests
run: pio test -e native
- name: Coverage
run: >-
gcovr --root . --filter src/ --filter lib/ --exclude-throw-branches
--print-summary --txt -
--fail-under-line 90 --fail-under-branch 80

firmware:
name: firmware
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: "3.12"
- uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.platformio
key: platformio-nodemcuv2-${{ hashFiles('platformio.ini') }}
- name: Install PlatformIO
run: pip install "platformio==${PLATFORMIO_VERSION}"
- name: Example configuration
run: cp include/config.h.example include/config.h
- name: Build
run: pio run -e nodemcuv2
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,23 @@ The firmware is written in C++ using [Visual Studio Code](https://code.visualstu

> ⚠️ Before building the firmware be sure to copy the file `include\config.h.example` to `include/config.h` and change the WiFi and MQTT credentials as well as the other settings in the new file.

### Without WiFi or MQTT

When the device cannot connect to the WiFi, or the MQTT broker does not answer, it counts the failed attempt in RTC memory and waits longer before the next one: 10, 20, 40 and 80 minutes, then every 2 hours (`BACKOFF_MAX_SLEEPTIME`). The first successful transmission returns to the normal 5 minutes and reports the number of failed attempts before it as `connectFailures`. A connection the access point refuses outright (unknown SSID, wrong password) is given up at once instead of after the 20-second timeout.

The wait is made of single 5-minute deep sleeps: a wake that only counts down keeps the radio off and goes back to sleep right away. A single long deep sleep is not used, because the ESP8266 cannot sleep longer than `ESP.deepSleepMax()`, about 3.5 hours.

### Tests

The firmware runs on the build machine against small fakes of the Arduino core and the libraries (`test/support/fakes`), so the tests drive the real `setup()` and `loop()`:

```sh
pio test -e native
gcovr --root . --filter src/ --filter lib/ --exclude-throw-branches --print-summary
```

CI requires 90 % line and 80 % branch coverage.

### Flash via serial connection

Connect RX, TX and GND to your serial interface or [USB-to-TTL converter](https://aliexpress.com/item/32531899568.html). To [enter flash mode on the ESP-12F](https://lukelogbook.tech/2018/02/08/flash-code-to-esp8266-esp-12-module-step-by-step-guide/) you must connect **GPIO0** to **GND** and reset the MCU.
Expand Down
1 change: 1 addition & 0 deletions include/config.h.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ IPAddress ip(192, 168, 0, 2);
// #define OTA_ENABLE_GPIO 15 /**< Override OTA enable GPIO (Default: 14) */

// #define SLEEPTIME 600e6 /**< Time in deepsleep (Default: 300e6 = 300 seconds)*/
// #define BACKOFF_MAX_SLEEPTIME 3600e6 /**< Longest wait between attempts while WiFi or MQTT fails (Default: 7200e6 = 2 hours)*/
// #define DEBUG 1 /**< Set to 1 to debug over serial (Default: 0)*/
// #define OTA 0 /**< Set to 1 to enable OTA waiting loop (Default: 1)*/
19 changes: 19 additions & 0 deletions include/rtc_data.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <stdint.h>

/**
* Kept in RTC memory across deep sleep. The CRC covers everything after it.
*/
struct RtcData
{
uint32_t crc32; // 4 bytes
uint8_t channel; // 1 byte, 5 in total
uint8_t ap_mac[6]; // 6 bytes, 11 in total
uint8_t apValid; // 1 byte, 12 in total: channel and ap_mac are known
uint8_t failures; // 1 byte, 13 in total: failed connection attempts in a row
uint8_t reserved; // 1 byte, 14 in total
uint16_t sleepsLeft; // 2 bytes, 16 in total: sleep periods to skip before the next attempt
};

static_assert(sizeof(RtcData) == 16, "RtcData must stay a multiple of 4 bytes");
34 changes: 34 additions & 0 deletions lib/backoff/backoff.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include <stdint.h>

/**
* Number of whole sleep periods that fit into the longest wait, at least one.
*/
inline uint16_t backoffCap(double maxSleepUs, double sleepUs)
{
if (sleepUs <= 0 || maxSleepUs < sleepUs)
{
return 1;
}
double periods = maxSleepUs / sleepUs;
return periods > 65535 ? 65535 : (uint16_t)periods;
}

/**
* Sleep periods to wait before the next connection attempt after `failures`
* failed attempts in a row: 1, 2, 4, 8, ... up to `cap`.
*/
inline uint16_t backoffSleeps(uint8_t failures, uint16_t cap)
{
if (cap < 1)
{
cap = 1;
}
if (failures >= 16)
{
return cap;
}
uint32_t periods = 1UL << failures;
return periods < cap ? (uint16_t)periods : cap;
}
19 changes: 16 additions & 3 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,26 @@
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[platformio]
default_envs = nodemcuv2

[env:nodemcuv2]
platform = espressif8266
platform = espressif8266@4.2.1
board = nodemcuv2
framework = arduino
lib_deps =
lib_deps =
knolleary/PubSubClient@^2.8
bblanchon/ArduinoJson@^6.21.2
adafruit/Adafruit HTU21DF Library@^1.0.5
board_build.f_cpu = 160000000L
monitor_speed = 115200
monitor_speed = 115200

; src/main.cpp on the build machine, against the fakes in test/support/fakes
[env:native]
platform = native
test_framework = unity
test_build_src = yes
lib_extra_dirs = test/support
lib_deps =
bblanchon/ArduinoJson@^6.21.2
build_flags = -O0 -g --coverage -lgcov
Loading