Merge pull request #951 from cyberman54/development

Development
This commit is contained in:
Verkehrsrot 2023-03-12 18:41:51 +01:00 committed by GitHub
commit 084b47c78a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 98 additions and 77 deletions

View File

@ -90,7 +90,7 @@ Supported external time sources are GPS, LORAWAN network time and LORAWAN applic
## Syncing multiple paxcounters
A fleet of paxcounters can be synchronized to keep all devices wake up and start scanning at the same time. Synchronization is based on top-of-hour as common time point of reference. This feature requires time-of-day to be present on each device. Thus, `TIME_SYNC_INTERVAL` option, as explained above, must be enabled. Wake up syncing is enabled by setting `SYNCWAKEUP` in `paxcounter.conf` to a value X, greater than zero, and smaller than `SLEEPCYCLE`. This defines a time window, centered at top-of-hour, sized +/- X seconds. If a device, returning from sleep, would wakeup within this time window, it's wakeup will be adjusted to top-of-hour.
A fleet of paxcounters can be synchronized to keep all devices wake up and start scanning at the same time. Synchronization is based on top-of-hour as common time point of reference. This feature requires time-of-day to be present on each device. Thus, `TIME_SYNC_INTERVAL` option, as explained above, must be enabled. Wake up syncing is enabled by setting `SYNCWAKEUP` in `paxcounter.conf` to a value X, in seconds, greater than zero, and smaller than `SLEEPCYCLE` (in seconds/10). This defines a time window, centered at top-of-hour, sized +/- X seconds. If a device, returning from sleep, would wakeup within this time window, it's wakeup will be adjusted to top-of-hour.
## Wall clock controller

View File

@ -91,6 +91,11 @@ Send for example `83` `86` as Downlink on Port 2 to get battery status and time/
0 ... 255 duration for scanning a bluetooth advertising channel in seconds/100
e.g. 8 -> each channel is scanned for 80 milliseconds [default]
#### 0x0D set wakeup sync window
bytes 1..2 = wakeup sync window size in seconds (MSB), 0..255 (0 = no wakuep sync)
e.g. {0x02, 0x58} -> device adjusts it's wakeup time when it is +/- 5 minutes from top-of-hour [default = 0]
#### 0x0E set Bluetooth scanner
0 = disabled

View File

@ -64,6 +64,7 @@ typedef struct __attribute__((packed)) {
int16_t rssilimit; // threshold for rssilimiter, negative value!
uint8_t sendcycle; // payload send cycle [seconds/2]
uint16_t sleepcycle; // sleep cycle [seconds/10]
uint16_t wakesync; // time window [seconds] to sync wakeup on top-of-hour
uint8_t wifichancycle; // wifi channel switch cycle [seconds/100]
uint8_t blescantime; // BLE scan cycle duration [seconds]
uint8_t blescan; // 0=disabled, 1=enabled
@ -109,6 +110,6 @@ typedef struct {
float pm25;
} sdsStatus_t;
extern char clientId[20]; // unique clientID
extern char clientId[20]; // unique clientID
#endif

View File

@ -46,7 +46,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I
[common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 3.4.7
release_version = 3.5.0
; 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
@ -73,7 +73,8 @@ lib_deps_sensors =
adafruit/Adafruit Unified Sensor @ ^1.1.7
adafruit/Adafruit BME280 Library @ ^2.2.2
adafruit/Adafruit BMP085 Library @ ^1.2.2
boschsensortec/BSEC Software Library @ 1.6.1480
;boschsensortec/BSEC Software Library @ 1.8.1492
https://github.com/boschsensortec/BSEC-Arduino-library
lewapek/Nova Fitness Sds dust sensors library @ ^1.5.1
lib_deps_basic =
greyrook/libpax @ ^1.1.0

View File

@ -10,7 +10,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I
[common]
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
release_version = 3.4.7
release_version = 3.5.0
; 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

View File

@ -82,20 +82,20 @@ int bme_init(void) {
int checkIaqSensorStatus(void) {
int rslt = 1; // true = 1 = no error, false = 0 = error
if (iaqSensor.status != BSEC_OK) {
if (iaqSensor.bsecStatus != BSEC_OK) {
rslt = 0;
if (iaqSensor.status < BSEC_OK)
ESP_LOGE(TAG, "BSEC error %d", iaqSensor.status);
if (iaqSensor.bsecStatus < BSEC_OK)
ESP_LOGE(TAG, "BSEC error %d", iaqSensor.bsecStatus);
else
ESP_LOGW(TAG, "BSEC warning %d", iaqSensor.status);
ESP_LOGW(TAG, "BSEC warning %d", iaqSensor.bsecStatus);
}
if (iaqSensor.bme680Status != BME680_OK) {
if (iaqSensor.bme68xStatus != BME68X_OK) {
rslt = 0;
if (iaqSensor.bme680Status < BME680_OK)
ESP_LOGE(TAG, "BME680 error %d", iaqSensor.bme680Status);
if (iaqSensor.bme68xStatus < BME68X_OK)
ESP_LOGE(TAG, "BME680 error %d", iaqSensor.bme68xStatus);
else
ESP_LOGW(TAG, "BME680 warning %d", iaqSensor.bme680Status);
ESP_LOGW(TAG, "BME680 warning %d", iaqSensor.bme68xStatus);
}
return rslt;

View File

@ -3,7 +3,6 @@
#include "globals.h"
#include "configmanager.h"
// namespace for device runtime preferences
#define DEVCONFIG "paxcntcfg"
@ -38,6 +37,7 @@ static void defaultConfig(configData_t *myconfig) {
myconfig->rssilimit = RSSILIMIT; // threshold for rssilimiter, negative value!
myconfig->sendcycle = SENDCYCLE; // payload send cycle [seconds/2]
myconfig->sleepcycle = SLEEPCYCLE; // sleep cycle [seconds/10]
myconfig->wakesync = SYNCWAKEUP; // wakeup sync window [seconds]
myconfig->wifichancycle =
WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100]
myconfig->blescantime =

View File

@ -37,7 +37,7 @@
// BME680 sensor on I2C bus
#define HAS_BME 1 // Enable BME sensors in general
#define HAS_BME680 GPIO_NUM_21, GPIO_NUM_22 // SDA, SCL
#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
#define BME680_ADDR BME68X_I2C_ADDR_LOW // connect SDIO of BME680 to GND
// BME280 sensor on I2C bus
//#define HAS_BME 1 // Enable BME sensors in general

View File

@ -18,7 +18,7 @@
// Octopus32 has a pre-populated BME680 on i2c addr 0x76
#define HAS_BME 1 // Enable BME sensors in general
#define HAS_BME680 GPIO_NUM_23, GPIO_NUM_22 // SDA, SCL
#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // connect SDIO of BME680 to GND
#define BME680_ADDR BME68X_I2C_ADDR_LOW // connect SDIO of BME680 to GND
#define HAS_LED 13 // ESP32 GPIO12 (pin22) On Board LED
//#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW

View File

@ -30,7 +30,7 @@
// BME680 sensor on I2C bus
//#define HAS_BME 1 // Enable BME sensors in general
//#define HAS_BME680 SDA, SCL
//#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !!
//#define BME680_ADDR BME68X_I2C_ADDR_LOW // !! connect SDIO of BME680 to GND !!
// display (if connected)
//#define HAS_DISPLAY 1

View File

@ -52,7 +52,7 @@ Reset -> reset device
// BME680 sensor on I2C bus
//#define HAS_BME 1 // Enable BME sensors in general
//#define HAS_BME680 SDA, SCL
//#define BME680_ADDR BME680_I2C_ADDR_PRIMARY // !! connect SDIO of BME680 to GND !!
//#define BME680_ADDR BME68X_I2C_ADDR_LOW // !! connect SDIO of BME680 to GND !!
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature

View File

@ -2,7 +2,6 @@
#include "globals.h"
#include "rcommand.h"
static QueueHandle_t RcmdQueue;
TaskHandle_t rcmdTask;
@ -59,7 +58,7 @@ void set_rssi(uint8_t val[]) {
current_config.ble_rssi_threshold = cfg.rssilimit;
libpax_update_config(&current_config);
init_libpax();
ESP_LOGI(TAG, "Remote command: set RSSI limit to %d", cfg.rssilimit);
ESP_LOGI(TAG, "Remote command: set RSSI limit to %hd", cfg.rssilimit);
}
void set_sendcycle(uint8_t val[]) {
@ -67,7 +66,7 @@ void set_sendcycle(uint8_t val[]) {
return;
// update send cycle interrupt [seconds / 2]
cfg.sendcycle = val[0];
ESP_LOGI(TAG, "Remote command: set send cycle to %d seconds",
ESP_LOGI(TAG, "Remote command: set send cycle to %u seconds",
cfg.sendcycle * 2);
libpax_counter_stop();
init_libpax();
@ -76,10 +75,16 @@ void set_sendcycle(uint8_t val[]) {
void set_sleepcycle(uint8_t val[]) {
// swap byte order from msb to lsb, note: this is a platform dependent hack
cfg.sleepcycle = __builtin_bswap16(*(uint16_t *)(val));
ESP_LOGI(TAG, "Remote command: set sleep cycle to %d seconds",
ESP_LOGI(TAG, "Remote command: set sleep cycle to %hu seconds",
cfg.sleepcycle * 10);
}
void set_wakesync(uint8_t val[]) {
// swap byte order from msb to lsb, note: this is a platform dependent hack
cfg.wakesync = __builtin_bswap16(*(uint16_t *)(val));
ESP_LOGI(TAG, "Remote command: set wakesync to %hu seconds", cfg.wakesync);
}
void set_wifichancycle(uint8_t val[]) {
cfg.wifichancycle = val[0];
libpax_counter_stop();
@ -193,7 +198,7 @@ void set_sensor(uint8_t val[]) {
return; // invalid sensor number -> exit
}
ESP_LOGI(TAG, "Remote command: set sensor #%d mode to %s", val[0],
ESP_LOGI(TAG, "Remote command: set sensor #%u mode to %s", val[0],
val[1] ? "on" : "off");
if (val[1])
@ -213,7 +218,7 @@ void set_loradr(uint8_t val[]) {
#if (HAS_LORA)
if (validDR(val[0])) {
cfg.loradr = val[0];
ESP_LOGI(TAG, "Remote command: set LoRa Datarate to %d", cfg.loradr);
ESP_LOGI(TAG, "Remote command: set LoRa Datarate to %u", cfg.loradr);
LMIC_setDrTxpow(assertDR(cfg.loradr), KEEP_TXPOW);
ESP_LOGI(TAG, "Radio parameters now %s / %s / %s",
getSfName(updr2rps(LMIC.datarate)),
@ -222,7 +227,7 @@ void set_loradr(uint8_t val[]) {
} else
ESP_LOGI(
TAG,
"Remote command: set LoRa Datarate called with illegal datarate %d",
"Remote command: set LoRa Datarate called with illegal datarate %u",
val[0]);
#else
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
@ -275,15 +280,15 @@ void set_wifiant(uint8_t val[]) {
void set_rgblum(uint8_t val[]) {
// Avoid wrong parameters
cfg.rgblum = (val[0] <= 100) ? (uint8_t)val[0] : RGBLUMINOSITY;
ESP_LOGI(TAG, "Remote command: set RGB Led luminosity %d", cfg.rgblum);
};
ESP_LOGI(TAG, "Remote command: set RGB Led luminosity %u", cfg.rgblum);
}
void set_lorapower(uint8_t val[]) {
#if (HAS_LORA)
// set data rate and transmit power only if we have no ADR
if (!cfg.adrmode) {
cfg.txpower = val[0];
ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %d", cfg.txpower);
ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %u", cfg.txpower);
LMIC_setDrTxpow(assertDR(cfg.loradr), cfg.txpower);
} else
ESP_LOGI(
@ -293,14 +298,14 @@ void set_lorapower(uint8_t val[]) {
#else
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
#endif // HAS_LORA
};
}
void get_config(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get device configuration");
payload.reset();
payload.addConfig(cfg);
SendPayload(CONFIGPORT);
};
}
void get_status(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get device status");
@ -315,7 +320,7 @@ void get_status(uint8_t val[]) {
RTC_restarts);
#endif
SendPayload(STATUSPORT);
};
}
void get_gps(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get gps status");
@ -328,7 +333,7 @@ void get_gps(uint8_t val[]) {
#else
ESP_LOGW(TAG, "GPS function not supported");
#endif
};
}
void get_bme(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get bme680 sensor data");
@ -339,7 +344,7 @@ void get_bme(uint8_t val[]) {
#else
ESP_LOGW(TAG, "BME sensor not supported");
#endif
};
}
void get_batt(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get battery voltage");
@ -350,7 +355,7 @@ void get_batt(uint8_t val[]) {
#else
ESP_LOGW(TAG, "Battery voltage not supported");
#endif
};
}
void get_time(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get time");
@ -359,35 +364,35 @@ void get_time(uint8_t val[]) {
payload.addTime(t);
payload.addByte(sntp_get_sync_status() << 4 | timeSource);
SendPayload(TIMEPORT);
};
}
void set_timesync(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: timesync requested");
setTimeSyncIRQ();
};
}
void set_time(uint8_t val[]) {
// swap byte order from msb to lsb, note: this is a platform dependent hack
uint32_t t = __builtin_bswap32(*(uint32_t *)(val));
ESP_LOGI(TAG, "Remote command: set time to %d", t);
ESP_LOGI(TAG, "Remote command: set time to %lu", t);
setMyTime(t, 0, _set);
};
}
void set_flush(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: flush");
// does nothing
// used to open receive window on LoRaWAN class a nodes
};
}
void set_loadconfig(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: load config from NVRAM");
loadConfig();
};
}
void set_saveconfig(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: save config to NVRAM");
saveConfig(false);
};
}
// assign previously defined functions to set of numeric remote commands
// format: {opcode, function, number of function arguments}
@ -399,17 +404,17 @@ static const cmd_t table[] = {
{0x07, set_loraadr, 1}, {0x08, set_screensaver, 1},
{0x09, set_reset, 1}, {0x0a, set_sendcycle, 1},
{0x0b, set_wifichancycle, 1}, {0x0c, set_blescantime, 1},
{0x0e, set_blescan, 1}, {0x0f, set_wifiant, 1},
{0x10, set_rgblum, 1}, {0x13, set_sensor, 2},
{0x14, set_payloadmask, 1}, {0x15, set_bme, 1},
{0x16, set_batt, 1}, {0x17, set_wifiscan, 1},
{0x18, set_flush, 0}, {0x19, set_sleepcycle, 2},
{0x20, set_loadconfig, 0}, {0x21, set_saveconfig, 0},
{0x80, get_config, 0}, {0x81, get_status, 0},
{0x83, get_batt, 0}, {0x84, get_gps, 0},
{0x85, get_bme, 0}, {0x86, get_time, 0},
{0x87, set_timesync, 0}, {0x88, set_time, 4},
{0x99, set_flush, 0}};
{0x0d, set_wakesync, 2}, {0x0e, set_blescan, 1},
{0x0f, set_wifiant, 1}, {0x10, set_rgblum, 1},
{0x13, set_sensor, 2}, {0x14, set_payloadmask, 1},
{0x15, set_bme, 1}, {0x16, set_batt, 1},
{0x17, set_wifiscan, 1}, {0x18, set_flush, 0},
{0x19, set_sleepcycle, 2}, {0x20, set_loadconfig, 0},
{0x21, set_saveconfig, 0}, {0x80, get_config, 0},
{0x81, get_status, 0}, {0x83, get_batt, 0},
{0x84, get_gps, 0}, {0x85, get_bme, 0},
{0x86, get_time, 0}, {0x87, set_timesync, 0},
{0x88, set_time, 4}, {0x99, set_flush, 0}};
static const uint8_t cmdtablesize =
sizeof(table) / sizeof(table[0]); // number of commands in command table
@ -492,7 +497,7 @@ esp_err_t rcmd_init(void) {
ESP_LOGE(TAG, "Could not create rcommand send queue. Aborting.");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Rcommand send queue created, size %d Bytes",
ESP_LOGI(TAG, "Rcommand send queue created, size %u Bytes",
RCMD_QUEUE_SIZE * sizeof(RcmdBuffer_t));
xTaskCreatePinnedToCore(rcmd_process, // task function

View File

@ -19,6 +19,32 @@ void reset_rtc_vars(void) {
RTC_restarts = 0;
}
#if (HAS_TIME)
void adjust_wakeup(uint32_t *wakeuptime) {
// only adjust wakeup if we have a valid time
if ((timeSource == _unsynced) ||
(sntp_get_sync_status() == SNTP_SYNC_STATUS_IN_PROGRESS)) {
ESP_LOGI(TAG, "Syncwakeup: No valid time for sync");
return;
}
time_t now;
time(&now);
// 1..3600 seconds between next wakeup time and following top-of-hour
uint16_t shift_sec = 3600 - (now + *wakeuptime) % 3600;
if (shift_sec <= SYNCWAKEUP) {
*wakeuptime += shift_sec; // delay wakeup to catch top-of-hour
ESP_LOGI(TAG, "Syncwakeup: Wakeup %hu sec postponed", shift_sec);
} else if (shift_sec >= (3600 - SYNCWAKEUP)) {
*wakeuptime = 3600 - shift_sec; // shorten wake up to next top-of-hour
ESP_LOGI(TAG, "Syncwakeup: Wakeup %hu sec preponed", shift_sec);
} else
ESP_LOGI(TAG, "Syncwakeup: Wakeup keeping unshifted");
}
#endif
void do_reset(bool warmstart) {
if (warmstart) {
ESP_LOGI(TAG, "restarting device (warmstart)");
@ -145,31 +171,14 @@ void enter_deepsleep(uint32_t wakeup_sec, gpio_num_t wakeup_gpio) {
// shutdown i2c bus
i2c_deinit();
#if (HAS_TIME)
if (cfg.wakesync && cfg.sleepcycle)
adjust_wakeup(&wakeup_sec);
#endif
// configure wakeup sources
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/sleep_modes.html
#if (HAS_TIME)
#if (SYNCWAKEUP) && (SLEEPCYCLE)
if ((timeSource != _unsynced) &&
(sntp_get_sync_status() !=
SNTP_SYNC_STATUS_IN_PROGRESS)) { // only sync if we have a valid time
time_t now;
time(&now);
// 1..3600 seconds between next wakeup time and following top-of-hour
uint16_t shift_sec = 3600 - (now + wakeup_sec) % 3600;
if (shift_sec <= SYNCWAKEUP) {
wakeup_sec += shift_sec; // delay wakeup to catch top-of-hour
ESP_LOGI(TAG, "Syncwakeup: Wakeup %hu sec postponed", shift_sec);
} else if (shift_sec >= (3600 - SYNCWAKEUP)) {
wakeup_sec = 3600 - shift_sec; // shorten wake up to next top-of-hour
ESP_LOGI(TAG, "Syncwakeup: Wakeup %hu sec preponed", shift_sec);
}
}
#endif
#endif
// set up RTC wakeup timer, if we have
if (wakeup_sec > 0) {
esp_sleep_enable_timer_wakeup(wakeup_sec * uS_TO_S_FACTOR);

View File

@ -6,8 +6,8 @@
// symbol to display current time source
// G = GPS / R = RTC / L = LORA / * = no sync / ? = never synced
const char timeSetSymbols[] = {'G', 'R', 'L', '*', '?'};
// G = GPS / R = RTC / L = LORA / ? = unsynced / * = set
const char timeSetSymbols[] = {'G', 'R', 'L', '?', '*'};
DRAM_ATTR bool TimePulseTick = false;
#ifdef GPS_INT