merged v2.0.17

This commit is contained in:
Chrisotph Schultz 2020-10-05 00:11:53 +02:00
commit 21d94247bd
9 changed files with 240 additions and 78 deletions

View File

@ -83,12 +83,16 @@ By default bluetooth sniffing not installed (#define *BLECOUNTER* 0 in paxcounte
# Preparing # Preparing
## Install Platformio
Install <A HREF="https://platformio.org/">PlatformIO IDE for embedded development</A> to make this project. Platformio integrates with your favorite IDE, choose eg. Visual Studio, Atom, Eclipse etc.
Compile time configuration is spread across several files. Before compiling the code, edit or create the following files: Compile time configuration is spread across several files. Before compiling the code, edit or create the following files:
## platformio_orig.ini ## platformio.ini
Edit `platformio_orig.ini` and select desired hardware target in section boards. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in sections board. Copy or rename to `platformio.ini`. Edit `platformio_orig.ini` and select desired hardware target in section boards. To add a new board, create an appropriate hardware abstraction layer file in hal subdirectory, and add a pointer to this file in sections board. Copy or rename to `platformio.ini` in the root directory of the project. Now start Platformio. Note: Platformio is looking for `platformio.ini` in the root directory and won't start if it does not find this file.
## src/paxcounter_orig.conf ## src/paxcounter.conf
Edit `src/paxcounter_orig.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. Copy or rename to `src/paxcounter.conf`. Edit `src/paxcounter_orig.conf` and tailor settings in this file according to your needs and use case. Please take care of the duty cycle regulations of the LoRaWAN network you're going to use. Copy or rename to `src/paxcounter.conf`.
If your device has a **real time clock** it can be updated bei either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`. If your device has a **real time clock** it can be updated bei either LoRaWAN network or GPS time, according to settings *TIME_SYNC_INTERVAL* and *TIME_SYNC_LORAWAN* in `paxcounter.conf`.

View File

@ -1,11 +1,12 @@
#ifndef _CONFIGMANAGER_H #ifndef _CONFIGMANAGER_H
#define _CONFIGMANAGER_H #define _CONFIGMANAGER_H
#include <Preferences.h>
#include "globals.h" #include "globals.h"
#include <Preferences.h>
void saveConfig(bool erase = false); void saveConfig(bool erase = false);
bool loadConfig(void); bool loadConfig(void);
void eraseConfig(void); void eraseConfig(void);
int version_compare(const String v1, const String v2);
#endif #endif

View File

@ -60,8 +60,10 @@ enum runmode_t {
}; };
// Struct holding devices's runtime configuration // Struct holding devices's runtime configuration
// using packed to avoid compiler padding, because struct will be memcpy'd to byte array // using packed to avoid compiler padding, because struct will be memcpy'd to
// byte array
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
char version[10]; // Firmware version
uint8_t loradr; // 0-15, lora datarate uint8_t loradr; // 0-15, lora datarate
uint8_t txpower; // 2-15, lora tx power uint8_t txpower; // 2-15, lora tx power
uint8_t adrmode; // 0=disabled, 1=enabled uint8_t adrmode; // 0=disabled, 1=enabled
@ -80,11 +82,12 @@ typedef struct __attribute__((packed)) {
uint8_t monitormode; // 0=disabled, 1=enabled uint8_t monitormode; // 0=disabled, 1=enabled
uint8_t runmode; // 0=normal, 1=update uint8_t runmode; // 0=normal, 1=update
uint8_t payloadmask; // bitswitches for payload data uint8_t payloadmask; // bitswitches for payload data
uint8_t enscount; // 0=disabled 1= enabled uint8_t enscount; // 0=disabled 1= enabled
char version[10]; // Firmware version char version[10]; // Firmware version
#ifdef HAS_BME680 #ifdef HAS_BME680
uint8_t uint8_t bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor
bsecstate[BSEC_MAX_STATE_BLOB_SIZE + 1]; // BSEC state for BME680 sensor
#endif #endif
} configData_t; } configData_t;

View File

@ -6,17 +6,15 @@
#include "globals.h" #include "globals.h"
#include "led.h" #include "led.h"
#include "display.h" #include "display.h"
#include "configmanager.h"
#include <Update.h> #include <Update.h>
#include <WiFi.h> #include <WiFi.h>
#include <WiFiClientSecure.h> #include <WiFiClientSecure.h>
#include <BintrayClient.h> #include <BintrayClient.h>
#include <string>
#include <algorithm>
int do_ota_update(); int do_ota_update();
void start_ota_update(); void start_ota_update();
int version_compare(const String v1, const String v2);
void ota_display(const uint8_t row, const std::string status, void ota_display(const uint8_t row, const std::string status,
const std::string msg); const std::string msg);
void show_progress(unsigned long current, unsigned long size); void show_progress(unsigned long current, unsigned long size);

View File

@ -46,7 +46,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I
[common] [common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c" ; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 2.0.16 release_version = 2.0.17
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running! ; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose ; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
debug_level = 3 debug_level = 3

View File

@ -16,16 +16,22 @@ static const char TAG[] = __FILE__;
Preferences nvram; Preferences nvram;
static const char cfgMagicBytes[] = {0x21, 0x76, 0x87, 0x32, 0xf3}; static const uint8_t cfgMagicBytes[] = {0x21, 0x76, 0x87, 0x32, 0xf4};
static const size_t cfgLen = sizeof(cfg), cfgLen2 = sizeof(cfgMagicBytes); static const size_t cfgLen = sizeof(cfg), cfgLen2 = sizeof(cfgMagicBytes);
static char buffer[cfgLen + cfgLen2]; static uint8_t buffer[cfgLen + cfgLen2];
// populate runtime config with device factory settings
//
// configuration frame structure in NVRAM;
// 1. version header [10 bytes, containing version string]
// 2. user settings [cfgLen bytes, containing default runtime settings
// (configData_t cfg)]
// 3. magicByte [cfgLen2 bytes, containing a fixed identifier]
// populate runtime config with factory settings
static void defaultConfig(configData_t *myconfig) { static void defaultConfig(configData_t *myconfig) {
char version[10]; memcpy(myconfig->version, &PROGVERSION, 10); // Firmware version
snprintf(version, 10, "%-10s", PROGVERSION);
// factory settings // device factory settings
myconfig->loradr = LORADRDEFAULT; // 0-15, lora datarate, see paxcounter.conf myconfig->loradr = LORADRDEFAULT; // 0-15, lora datarate, see paxcounter.conf
myconfig->txpower = LORATXPOWDEFAULT; // 0-15, lora tx power myconfig->txpower = LORATXPOWDEFAULT; // 0-15, lora tx power
myconfig->adrmode = 1; // 0=disabled, 1=enabled myconfig->adrmode = 1; // 0=disabled, 1=enabled
@ -56,6 +62,11 @@ static void defaultConfig(configData_t *myconfig) {
#endif #endif
} }
// migrate runtime configuration from earlier to current version
static void migrateConfig(void) {
// currently no configuration migration rules are implemented
}
// save current configuration from RAM to NVRAM // save current configuration from RAM to NVRAM
void saveConfig(bool erase) { void saveConfig(bool erase) {
ESP_LOGI(TAG, "Storing settings to NVRAM..."); ESP_LOGI(TAG, "Storing settings to NVRAM...");
@ -84,36 +95,65 @@ void saveConfig(bool erase) {
// load configuration from NVRAM into RAM and make it current // load configuration from NVRAM into RAM and make it current
bool loadConfig() { bool loadConfig() {
ESP_LOGI(TAG, "Loading device runtime configuration from NVRAM..."); ESP_LOGI(TAG, "Loading device configuration from NVRAM...");
if (!nvram.begin(DEVCONFIG, true)) { if (!nvram.begin(DEVCONFIG, true)) {
ESP_LOGW(TAG, "NVRAM initialized, device starts with factory settings"); ESP_LOGI(TAG, "NVRAM initialized, device starts with factory settings");
eraseConfig(); eraseConfig();
return true; return true;
}
} else { // simple check that runtime config data matches
// simple check that runtime config data matches if (nvram.getBytesLength(DEVCONFIG) != (cfgLen + cfgLen2)) {
if (nvram.getBytesLength(DEVCONFIG) != (cfgLen + cfgLen2)) { ESP_LOGE(TAG, "Configuration invalid");
ESP_LOGE(TAG, "configuration invalid"); return false;
return false; }
} else { // load device runtime config from nvram and copy it to byte array
// load device runtime config from nvram and copy it to byte array nvram.getBytes(DEVCONFIG, buffer, cfgLen + cfgLen2);
nvram.getBytes(DEVCONFIG, buffer, cfgLen + cfgLen2); nvram.end();
nvram.end();
// validate configuration by checking magic bytes at end of array
if (memcmp(buffer + cfgLen, &cfgMagicBytes, cfgLen2) != 0) {
ESP_LOGW(TAG, "No configuration found");
return false;
} else { // validate loaded configuration by checking magic bytes at end of array
// copy byte array into runtime cfg struct if (memcmp(buffer + cfgLen, &cfgMagicBytes, cfgLen2) != 0) {
memcpy(&cfg, buffer, cfgLen); ESP_LOGW(TAG, "No configuration found");
ESP_LOGI(TAG, "Runtime configuration loaded"); return false;
return true; }
}
} // copy loaded configuration into runtime cfg struct
memcpy(&cfg, buffer, cfgLen);
ESP_LOGI(TAG, "Runtime configuration v%s loaded", cfg.version);
// check if config version matches current firmware version
switch (version_compare(PROGVERSION, cfg.version)) {
case -1: // device configuration belongs to newer than current firmware
ESP_LOGE(TAG, "Incompatible device configuration");
return false;
case 1: // device configuration belongs to older than current firmware
ESP_LOGW(TAG, "Device was updated, attempt to migrate configuration");
migrateConfig();
return true;
default: // device configuration version matches current firmware version
return true;
} }
} }
void eraseConfig(void) { saveConfig(true); } // helper function to convert strings into lower case
bool comp(char s1, char s2) { return (tolower(s1) < tolower(s2)); }
// helper function to lexicographically compare two versions. Returns 1 if v2
// is smaller, -1 if v1 is smaller, 0 if equal
int version_compare(const String v1, const String v2) {
if (v1 == v2)
return 0;
const char *a1 = v1.c_str(), *a2 = v2.c_str();
if (std::lexicographical_compare(a1, a1 + strlen(a1), a2, a2 + strlen(a2),
comp))
return -1;
else
return 1;
}
void eraseConfig(void) { saveConfig(true); }

View File

@ -12,7 +12,10 @@ static const char TAG[] = __FILE__;
// thus precision is only +/- 1 second // thus precision is only +/- 1 second
TinyGPSPlus gps; TinyGPSPlus gps;
TinyGPSCustom gpstime(gps, "GPZDA", 1); // field 1 = UTC time TinyGPSCustom gpstime(gps, "GPZDA", 1); // field 1 = UTC time (hhmmss.ss)
TinyGPSCustom gpsday(gps, "GPZDA", 2); // field 2 = day (01..31)
TinyGPSCustom gpsmonth(gps, "GPZDA", 3); // field 3 = month (01..12)
TinyGPSCustom gpsyear(gps, "GPZDA", 4); // field 4 = year (4-digit)
static const String ZDA_Request = "$EIGPQ,ZDA*39\r\n"; static const String ZDA_Request = "$EIGPQ,ZDA*39\r\n";
TaskHandle_t GpsTask; TaskHandle_t GpsTask;
@ -93,7 +96,7 @@ bool gps_hasfix() {
gps.altitude.age() < 4000); gps.altitude.age() < 4000);
} }
// function to fetch current time from struct; note: this is costly // function to poll current time from GPS data; note: this is costly
time_t get_gpstime(uint16_t *msec) { time_t get_gpstime(uint16_t *msec) {
time_t time_sec = 0; time_t time_sec = 0;
@ -102,34 +105,30 @@ time_t get_gpstime(uint16_t *msec) {
#ifdef GPS_SERIAL #ifdef GPS_SERIAL
GPS_Serial.print(ZDA_Request); GPS_Serial.print(ZDA_Request);
// wait for gps NMEA answer // wait for gps NMEA answer
vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL)); // vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL));
#elif defined GPS_I2C #elif defined GPS_I2C
Wire.print(ZDA_Request); Wire.print(ZDA_Request);
#endif #endif
// did we get a current time? // did we get a current date & time?
if (gpstime.isUpdated() && gpstime.isValid()) { if (gpstime.isUpdated() && gpstime.isValid() && gpsday.isValid()) {
tmElements_t tm; tmElements_t tm;
String rawtime = gpstime.value(); uint32_t time_bcd = atof(gpstime.value()) * 100;
uint32_t time_bcd = rawtime.toFloat() * 100;
uint32_t delay_ms = uint32_t delay_ms =
gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR; gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR;
uint8_t year =
CalendarYrToTm(gps.date.year()); // year offset from 1970 in microTime.h
ESP_LOGD(TAG, "time [bcd]: %u", time_bcd);
tm.Second = (time_bcd / 100) % 100; // second tm.Second = (time_bcd / 100) % 100; // second
tm.Minute = (time_bcd / 10000) % 100; // minute tm.Minute = (time_bcd / 10000) % 100; // minute
tm.Hour = time_bcd / 1000000; // hour tm.Hour = time_bcd / 1000000; // hour
tm.Day = gps.date.day(); // day tm.Day = atoi(gpsday.value()); // day
tm.Month = gps.date.month(); // month tm.Month = atoi(gpsmonth.value()); // month
tm.Year = year; // year tm.Year = CalendarYrToTm(
atoi(gpsyear.value())); // year offset from 1970 in microTime.h
// add protocol delay to time with millisecond precision // add protocol delay to time with millisecond precision
time_sec = makeTime(tm) + delay_ms / 1000; time_sec = makeTime(tm) + delay_ms / 1000 - 1;
*msec = (delay_ms % 1000) ? delay_ms % 1000 : 0; *msec = (delay_ms % 1000) ? delay_ms % 1000 : 0;
} }
@ -163,15 +162,15 @@ void gps_loop(void *pvParameters) {
} }
#endif #endif
// if time hasn't been synchronised yet, and we have a valid GPS time, // (only) while device time is not set or unsynched, and we have a valid GPS
// update time from GPS. // time, we trigger a device time update to poll time from GPS
if (timeSource == _unsynced && gpstime.isUpdated() && gpstime.isValid()) { if (timeSource == _unsynced && gpstime.isUpdated() && gpstime.isValid() &&
gpsday.isValid())
calibrateTime(); calibrateTime();
}
} // if } // if
// show NMEA data in verbose mode, useful for debugging GPS, bu tvery noisy // show NMEA data in verbose mode, useful only for debugging GPS, very noisy
// ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: %u", // ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: %u",
// gps.passedChecksum(), gps.failedChecksum(), // gps.passedChecksum(), gps.failedChecksum(),
// gps.sentencesWithFix()); // gps.sentencesWithFix());

View File

@ -328,22 +328,4 @@ void show_progress(unsigned long current, unsigned long size) {
#endif #endif
} }
// helper function to convert strings into lower case
bool comp(char s1, char s2) { return tolower(s1) < tolower(s2); }
// helper function to lexicographically compare two versions. Returns 1 if v2 is
// smaller, -1 if v1 is smaller, 0 if equal
int version_compare(const String v1, const String v2) {
if (v1 == v2)
return 0;
const char *a1 = v1.c_str(), *a2 = v2.c_str();
if (lexicographical_compare(a1, a1 + strlen(a1), a2, a2 + strlen(a2), comp))
return -1;
else
return 1;
}
#endif // USE_OTA #endif // USE_OTA

135
src/platformio_orig.ini Normal file
View File

@ -0,0 +1,135 @@
; PlatformIO Project Configuration File
; NOTE: PlatformIO v4 is needed!
;
; Please visit documentation for the other options and examples
; http://docs.platformio.org/page/projectconf.html
; ---> SELECT THE TARGET PLATFORM HERE! <---
[board]
halfile = generic.h
;halfile = ebox.h
;halfile = eboxtube.h
;halfile = ecopower.h
;halfile = heltec.h
;halfile = heltecv2.h
;halfile = ttgov1.h
;halfile = ttgov2.h
;halfile = ttgov21old.h
;halfile = ttgov21new.h
;halfile = ttgofox.h
;halfile = ttgobeam.h
;halfile = ttgobeam10.h
;halfile = fipy.h
;halfile = lopy.h
;halfile = lopy4.h
;halfile = lolin32litelora.h
;halfile = lolin32lora.h
;halfile = lolin32lite.h
;halfile = wemos32oled.h
;halfile = wemos32matrix.h
;halfile = octopus32.h
;halfile = tinypico.h
;halfile = tinypicomatrix.h
;halfile = m5core.h
;halfile = m5fire.h
;halfile = olimexpoeiso.h
[platformio]
; upload firmware to board with usb cable
default_envs = usb
; upload firmware to a jfrog bintray repository
;default_envs = ota
; use latest versions of libraries
;default_envs = dev
description = Paxcounter is a device for metering passenger flows in realtime. It counts how many mobile devices are around.
[common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 2.0.16
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
debug_level = 3
extra_scripts = pre:build.py
otakeyfile = ota.conf
lorakeyfile = loraconf.h
lmicconfigfile = lmic_config.h
platform_espressif32 = espressif32@2.0.0
monitor_speed = 115200
upload_speed = 115200 ; set by build.py and taken from hal file
display_library = ; set by build.py and taken from hal file
lib_deps_lora =
mcci-catena/MCCI LoRaWAN LMIC library @ ^3.2.0
lib_deps_display =
bitbank2/OneBitDisplay @ 1.7.2
ricmoo/QRCode @ ^0.0.1
bodmer/TFT_eSPI @ ^2.2.20
lib_deps_ledmatrix =
seeed-studio/Ultrathin_LED_Matrix @ ^1.0.0
lib_deps_rgbled =
roboticsbrno/SmartLeds @ ^1.2.1
lib_deps_gps =
mikalhart/TinyGPSPlus @ ^1.0.2
lib_deps_sensors =
adafruit/Adafruit Unified Sensor @ ^1.1.4
adafruit/Adafruit BME280 Library @ ^2.1.1
adafruit/Adafruit BMP085 Library @ ^1.1.0
boschsensortec/BSEC Software Library @ 1.5.1474
https://github.com/ricki-z/SDS011.git
lib_deps_basic =
bblanchon/ArduinoJson @ <6
jchristensen/Timezone @ ^1.2.4
makuna/RTC @ ^2.3.5
spacehuhn/SimpleButton
lewisxhe/AXP202X_Library @ ^1.1.2
geeksville/esp32-micro-sdcard @ ^0.1.1
256dpi/MQTT @ ^2.4.7
lib_deps_all =
${common.lib_deps_basic}
${common.lib_deps_lora}
${common.lib_deps_display}
${common.lib_deps_rgbled}
${common.lib_deps_gps}
${common.lib_deps_sensors}
${common.lib_deps_ledmatrix}
build_flags_basic =
-include "src/hal/${board.halfile}"
-include "src/paxcounter.conf"
-w
'-DCORE_DEBUG_LEVEL=${common.debug_level}'
'-DLOG_LOCAL_LEVEL=${common.debug_level}'
'-DPROGVERSION="${common.release_version}"'
build_flags_sensors =
-Llib/Bosch-BSEC/src/esp32/
-lalgobsec
build_flags_all =
${common.build_flags_basic}
${common.build_flags_sensors}
-mfix-esp32-psram-cache-issue
[env]
lib_ldf_mode = deep ; #632 Fixes compiler error with OneBitDisplay library
framework = arduino
board = esp32dev
board_build.partitions = min_spiffs.csv
upload_speed = ${common.upload_speed}
;upload_port = COM8
platform = ${common.platform_espressif32}
lib_deps = ${common.lib_deps_all}
build_flags = ${common.build_flags_all}
upload_protocol = ${common.upload_protocol}
extra_scripts = ${common.extra_scripts}
monitor_speed = ${common.monitor_speed}
monitor_filters = time, esp32_exception_decoder, default
[env:ota]
upload_protocol = custom
[env:usb]
upload_protocol = esptool
[env:dev]
upload_protocol = esptool
build_type = debug
platform = https://github.com/platformio/platform-espressif32.git#develop
platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git