Merge branch 'development' of https://github.com/cyberman54/ESP32-Paxcounter into development

This commit is contained in:
Marius Gripp committed 2019-10-01 16:29:02 +02:00
commit 014e6cd4a5
53 files changed
+1239 -547

No files matched your search

+1 -1
View File
@@ -37,7 +37,7 @@ function Decoder(bytes, port) {
if (port === 3) {
// device config data
return decode(bytes, [uint8, uint8, int16, uint8, uint8, uint8, uint8, bitmap1, bitmap2, version], ['lorasf', 'txpower', 'rssilimit', 'sendcycle', 'wifichancycle', 'blescantime', 'rgblum', 'flags', 'payloadmask', 'version']);
return decode(bytes, [uint8, uint8, int16, uint8, uint8, uint8, uint8, bitmap1, bitmap2, version], ['loradr', 'txpower', 'rssilimit', 'sendcycle', 'wifichancycle', 'blescantime', 'rgblum', 'flags', 'payloadmask', 'version']);
}
if (port === 4) {
-69
View File
@@ -1,69 +0,0 @@
#include "globals.h"
// Local logging tag
static const char TAG[] = __FILE__;
#ifdef BAT_MEASURE_ADC
esp_adc_cal_characteristics_t *adc_characs =
(esp_adc_cal_characteristics_t *)calloc(
1, sizeof(esp_adc_cal_characteristics_t));
static const adc1_channel_t adc_channel = BAT_MEASURE_ADC;
static const adc_atten_t atten = ADC_ATTEN_DB_11;
static const adc_unit_t unit = ADC_UNIT_1;
#endif
void calibrate_voltage(void) {
#ifdef BAT_MEASURE_ADC
// configure ADC
ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12));
ESP_ERROR_CHECK(adc1_config_channel_atten(adc_channel, atten));
// calibrate ADC
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(
unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_characs);
// show ADC characterization base
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
ESP_LOGI(TAG,
"ADC characterization based on Two Point values stored in eFuse");
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
ESP_LOGI(TAG,
"ADC characterization based on reference voltage stored in eFuse");
} else {
ESP_LOGI(TAG, "ADC characterization based on default reference voltage");
}
#endif
}
uint16_t read_voltage() {
#ifdef BAT_MEASURE_ADC
// multisample ADC
uint32_t adc_reading = 0;
for (int i = 0; i < NO_OF_SAMPLES; i++) {
adc_reading += adc1_get_raw(adc_channel);
}
adc_reading /= NO_OF_SAMPLES;
// Convert ADC reading to voltage in mV
uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_characs);
#ifdef BAT_VOLTAGE_DIVIDER
voltage *= BAT_VOLTAGE_DIVIDER;
#endif
#ifdef BAT_MEASURE_EN // turn ext. power off
digitalWrite(EXT_POWER_SW, EXT_POWER_OFF);
#endif
return (uint16_t)voltage;
#else
return 0;
#endif
}
bool batt_sufficient() {
#ifdef BAT_MEASURE_ADC
uint16_t volts = read_voltage();
return ((volts < 1000) ||
(volts > OTA_MIN_BATT)); // no battery or battery sufficient
#else
return true;
#endif
}
+3 -1
View File
@@ -143,7 +143,9 @@ int checkIaqSensorStatus(void) {
// store current BME sensor data in struct
void bme_storedata(bmeStatus_t *bme_store) {
if (I2C_MUTEX_LOCK()) { // block i2c bus access
if ((cfg.payloadmask & MEMS_DATA) &&
(I2C_MUTEX_LOCK())) { // block i2c bus access
#ifdef HAS_BME680
if (iaqSensor.run()) { // if new data is available
+21 -16
View File
@@ -8,16 +8,21 @@ static const char TAG[] = "flash";
nvs_handle my_handle;
esp_err_t err;
#define PAYLOADMASK \
((GPS_DATA | ALARM_DATA | MEMS_DATA | COUNT_DATA | SENSOR1_DATA | \
SENSOR2_DATA | SENSOR3_DATA) & \
~BATT_DATA)
// populate cfg vars with factory settings
void defaultConfig() {
cfg.lorasf = LORASFDEFAULT; // 7-12, initial lora sf, see pacounter.conf
cfg.txpower = 15; // 2-15, lora tx power
cfg.adrmode = 1; // 0=disabled, 1=enabled
cfg.screensaver = 0; // 0=disabled, 1=enabled
cfg.screenon = 1; // 0=disabled, 1=enabled
cfg.countermode = 0; // 0=cyclic, 1=cumulative, 2=cyclic confirmed
cfg.rssilimit = 0; // threshold for rssilimiter, negative value!
cfg.sendcycle = SENDCYCLE; // payload send cycle [seconds/2]
cfg.loradr = LORADRDEFAULT; // 0-15, lora datarate, see pacounter.conf
cfg.txpower = LORATXPOWDEFAULT; // 0-15, lora tx power
cfg.adrmode = 1; // 0=disabled, 1=enabled
cfg.screensaver = 0; // 0=disabled, 1=enabled
cfg.screenon = 1; // 0=disabled, 1=enabled
cfg.countermode = 0; // 0=cyclic, 1=cumulative, 2=cyclic confirmed
cfg.rssilimit = 0; // threshold for rssilimiter, negative value!
cfg.sendcycle = SENDCYCLE; // payload send cycle [seconds/2]
cfg.wifichancycle =
WIFI_CHANNEL_SWITCH_INTERVAL; // wifi channel switch cycle [seconds/100]
cfg.blescantime =
@@ -29,7 +34,7 @@ void defaultConfig() {
cfg.rgblum = RGBLUMINOSITY; // RGB Led luminosity (0..100%)
cfg.monitormode = 0; // 0=disabled, 1=enabled
cfg.runmode = 0; // 0=normal, 1=update
cfg.payloadmask = 0xFF; // all payload switched on
cfg.payloadmask = PAYLOADMASK; // all payload switched on
cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE] = {
0}; // init BSEC state for BME680 sensor
@@ -92,9 +97,9 @@ void saveConfig() {
strcmp(storedversion, cfg.version) != 0)
nvs_set_str(my_handle, "version", cfg.version);
if (nvs_get_i8(my_handle, "lorasf", &flash8) != ESP_OK ||
flash8 != cfg.lorasf)
nvs_set_i8(my_handle, "lorasf", cfg.lorasf);
if (nvs_get_i8(my_handle, "loradr", &flash8) != ESP_OK ||
flash8 != cfg.loradr)
nvs_set_i8(my_handle, "loradr", cfg.loradr);
if (nvs_get_i8(my_handle, "txpower", &flash8) != ESP_OK ||
flash8 != cfg.txpower)
@@ -217,11 +222,11 @@ void loadConfig() {
ESP_LOGI(TAG, "bsecstate = %d", cfg.bsecstate[BSEC_MAX_STATE_BLOB_SIZE]);
};
if (nvs_get_i8(my_handle, "lorasf", &flash8) == ESP_OK) {
cfg.lorasf = flash8;
ESP_LOGI(TAG, "lorasf = %d", flash8);
if (nvs_get_i8(my_handle, "loradr", &flash8) == ESP_OK) {
cfg.loradr = flash8;
ESP_LOGI(TAG, "loradr = %d", flash8);
} else {
ESP_LOGI(TAG, "lorasf set to default %d", cfg.lorasf);
ESP_LOGI(TAG, "loradr set to default %d", cfg.loradr);
saveConfig();
}
+11 -2
View File
@@ -30,6 +30,9 @@ void doHousekeeping() {
#if (HAS_LORA)
ESP_LOGD(TAG, "LMiCtask %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(lmicTask), eTaskGetState(lmicTask));
ESP_LOGD(TAG, "Lorasendtask %d bytes left | Taskstate = %d",
uxTaskGetStackHighWaterMark(lorasendTask),
eTaskGetState(lorasendTask));
#endif
#if (HAS_GPS)
ESP_LOGD(TAG, "Gpsloop %d bytes left | Taskstate = %d",
@@ -52,9 +55,15 @@ void doHousekeeping() {
#endif
// read battery voltage into global variable
#ifdef BAT_MEASURE_ADC
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
batt_voltage = read_voltage();
ESP_LOGI(TAG, "Voltage: %dmV", batt_voltage);
if (batt_voltage == 0xffff)
ESP_LOGI(TAG, "Battery: external power");
else
ESP_LOGI(TAG, "Battery: %dmV", batt_voltage);
#ifdef HAS_PMU
AXP192_showstatus();
#endif
#endif
// display BME680/280 sensor data
+17 -32
View File
@@ -10,15 +10,17 @@ Display-Mask (128 x 64 pixel):
0|PAX:aabbccddee
1|PAX:aabbccddee
2|B:a.bcV Sats:ab
3|BLTH:abcde SF:ab
3|BLTH:abcde SFab
4|WIFI:abcde ch:ab
5|RLIM:abcd abcdKB
6|xxxxxxxxxxxxxxxx
6|20:27:00* 27.Feb
7|yyyyyyyyyyyyyyab
line 6: x = Text for LORA status OR time/date
line 7: y = Text for LMIC status; ab = payload queue
line 6: * = char {L|G|R|?} indicates time source,
inverse = clock controller is active,
pulsed = pps input signal is active
line 7: y = LMIC event message; ab = payload queue length
*/
@@ -30,19 +32,6 @@ line 7: y = Text for LMIC status; ab = payload queue
HAS_DISPLAY u8x8(MY_OLED_RST, MY_OLED_SCL, MY_OLED_SDA);
// helper string for converting LoRa spread factor values
#if defined(CFG_eu868)
const char lora_datarate[] = {"1211100908077BFSNA"};
#elif defined(CFG_us915)
const char lora_datarate[] = {"100908078CNA121110090807"};
#elif defined(CFG_as923)
const char lora_datarate[] = {"1211100908077BFSNA"};
#elif defined(CFG_au921)
const char lora_datarate[] = {"1211100908078CNA1211109C8C7C"};
#elif defined(CFG_in866)
const char lora_datarate[] = {"121110090807FSNA"};
#endif
// helper arry for converting month values to text
const char *printmonth[] = {"xxx", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
@@ -188,9 +177,12 @@ void draw_page(time_t t, uint8_t page) {
case 0:
// update Battery status (line 2)
#ifdef BAT_MEASURE_ADC
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
u8x8.setCursor(0, 2);
u8x8.printf("B:%.2fV", batt_voltage / 1000.0);
if (batt_voltage == 0xffff)
u8x8.printf("B:USB ");
else
u8x8.printf("B:%.2fV", batt_voltage / 1000.0);
#endif
// update GPS status (line 2)
@@ -215,13 +207,11 @@ void draw_page(time_t t, uint8_t page) {
#endif
#if (HAS_LORA)
u8x8.setCursor(11, 3);
u8x8.printf("SF:");
if (cfg.adrmode) // if ADR=on then display SF value inverse
u8x8.setCursor(12, 3);
if (!cfg.adrmode) // if ADR=off then display SF value inverse
u8x8.setInverseFont(1);
u8x8.printf("%c%c", lora_datarate[LMIC.datarate * 2],
lora_datarate[LMIC.datarate * 2 + 1]);
if (cfg.adrmode) // switch off inverse if it was turned on
u8x8.printf("%-4s", getSfName(updr2rps(LMIC.datarate)));
if (!cfg.adrmode) // switch off inverse if it was turned on
u8x8.setInverseFont(0);
#endif // HAS_LORA
@@ -254,17 +244,13 @@ void draw_page(time_t t, uint8_t page) {
#endif // HAS_DCF77 || HAS_IF482
if (timeSource != _unsynced)
u8x8.printf(" %2d.%3s", day(t), printmonth[month(t)]);
#else // update LoRa status display
#if (HAS_LORA)
u8x8.printf("%-16s", display_line6);
#endif
#endif // TIME_SYNC_INTERVAL
#if (HAS_LORA)
// line 7: update LMiC event display
u8x8.setCursor(0, 7);
u8x8.printf("%-14s", display_line7);
u8x8.printf("%-14s", lmic_event_msg);
// update LoRa send queue display
msgWaiting = uxQueueMessagesWaiting(LoraSendQueue);
@@ -274,7 +260,6 @@ void draw_page(time_t t, uint8_t page) {
u8x8.printf("%-2s", msgWaiting == SEND_QUEUE_SIZE ? "<>" : buff);
} else
u8x8.printf(" ");
#endif // HAS_LORA
break; // page0
+9 -9
View File
@@ -90,13 +90,12 @@ time_t fetch_gpsTime(uint16_t *msec) {
// poll NMEA $GPZDA sentence
#ifdef GPS_SERIAL
GPS_Serial.print(ZDA_Request);
// wait for gps NMEA answer
vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL));
#elif defined GPS_I2C
Wire.print(ZDA_Request);
#endif
// wait for gps NMEA answer
//vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL));
// did we get a current time?
if (gpstime.isUpdated() && gpstime.isValid()) {
@@ -104,7 +103,8 @@ time_t fetch_gpsTime(uint16_t *msec) {
String rawtime = gpstime.value();
uint32_t time_bcd = rawtime.toFloat() * 100;
uint32_t delay_ms = gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR;
uint32_t delay_ms =
gpstime.age() + nmea_txDelay_ms + NMEA_COMPENSATION_FACTOR;
uint8_t year =
CalendarYrToTm(gps.date.year()); // year offset from 1970 in microTime.h
@@ -138,7 +138,7 @@ void gps_loop(void *pvParameters) {
while (1) {
if (cfg.payloadmask && GPS_DATA) {
if (cfg.payloadmask & GPS_DATA) {
#ifdef GPS_SERIAL
// feed GPS decoder with serial NMEA data from GPS device
while (GPS_Serial.available()) {
@@ -153,10 +153,10 @@ void gps_loop(void *pvParameters) {
#endif
} // if
// show NMEA data in verbose mode, useful for debugging GPS
ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: %u",
gps.passedChecksum(), gps.failedChecksum(),
gps.sentencesWithFix());
// show NMEA data in verbose mode, useful for debugging GPS, bu tvery noisy
//ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: %u",
// gps.passedChecksum(), gps.failedChecksum(),
// gps.sentencesWithFix());
delay(2); // yield to CPU
+1 -1
View File
@@ -14,7 +14,7 @@
#define CFG_sx1276_radio 1
#define HAS_LED (22) // Green LED on board
#define HAS_RGB_LED (2) // WS2812B RGB LED on board
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_2) // WS2812B RGB LED on board
#define HAS_BUTTON (0) // button "FLASH" on board
#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
+1 -1
View File
@@ -13,7 +13,7 @@
#define CFG_sx1272_radio 1
#define HAS_LED NOT_A_PIN // FiPy has no on board LED, so we use RGB LED
#define HAS_RGB_LED GPIO_NUM_0 // WS2812B RGB LED on GPIO0
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_0) // WS2812B RGB LED on GPIO0
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM
// Pins for LORA chip SPI interface, reset line and interrupt lines
+1 -1
View File
@@ -51,7 +51,7 @@
#define HAS_LED (21) // on board LED
#define HAS_BUTTON (39) // on board button
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_0) // WS2812B RGB LED on GPIO0
// GPS settings
#define HAS_GPS 1 // use on board GPS
+6 -1
View File
@@ -20,8 +20,13 @@
#define HAS_LED LED_BUILTIN // white LED on board
#define HAS_BUTTON KEY_BUILTIN // button "PROG" on board
// caveat: activating ADC2 conflicts with Wifi in current arduino-esp32
// see https://github.com/espressif/arduino-esp32/issues/3222
// thus we must waiver of battery monitoring
//#define BAT_MEASURE_ADC ADC2_GPIO13_CHANNEL // battery probe GPIO pin
//#define BAT_VOLTAGE_DIVIDER 4 // voltage divider 220k/100k on board
//#define BAT_MEASURE_ADC_UNIT 2 // ADC 2
//#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 220k/100k on board
#define EXT_POWER_SW Vext // switches battery power, Vext control 0 = on / 1 = off
#define EXT_POWER_ON 0
//#define EXT_POWER_OFF 1
+1 -1
View File
@@ -17,7 +17,7 @@
//#define DISPLAY_FLIP 1 // uncomment this for rotated display
#define HAS_LED 22 // ESP32 GPIO12 (pin22) On Board LED
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_13) // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
+1 -1
View File
@@ -18,7 +18,7 @@
#define HAS_LED NOT_A_PIN // Led os on same pin as Lora SS pin, to avoid problems, we don't use it
#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
// Anyway shield is on over the LoLin32 board, so we won't be able to see this LED
#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_13) // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
+1 -1
View File
@@ -12,7 +12,7 @@
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define CFG_sx1272_radio 1
#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_0) // WS2812B RGB LED on GPIO0 (P2)
// Note: Pins for LORA chip SPI interface come from board file pins_arduino.h
+2 -2
View File
@@ -20,8 +20,8 @@
//#define SPI_CS GPIO_NUM_36
#define CFG_sx1276_radio 1
//#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
#define HAS_RGB_LED (0) // WS2812B RGB LED on GPIO0 (P2)
#define HAS_LED NOT_A_PIN // LoPy4 has no on board mono LED, we use on board RGB LED
#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_0) // WS2812B RGB LED on GPIO0 (P2)
#define BOARD_HAS_PSRAM // use extra 4MB extern RAM
// Note: Pins for LORA chip SPI interface come from board file pins_arduino.h
+1 -1
View File
@@ -23,7 +23,7 @@
#define HAS_LED 13 // ESP32 GPIO12 (pin22) On Board LED
//#define LED_ACTIVE_LOW 1 // Onboard LED is active when pin is LOW
//#define HAS_RGB_LED 13 // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
//#define HAS_RGB_LED SmartLed rgb_led(LED_WS2812, 1, GPIO_NUM_13) // ESP32 GPIO13 (pin13) On Board Shield WS2812B RGB LED
//#define HAS_BUTTON 15 // ESP32 GPIO15 (pin15) Button is on the LoraNode32 shield
//#define BUTTON_PULLUP 1 // Button need pullup instead of default pulldown
+24
View File
@@ -0,0 +1,24 @@
// clang-format off
// upload_speed 921600
// board esp32dev
#ifndef _TINYPICO_H
#define _TINYPICO_H
#include <stdint.h>
// Hardware related definitions for crowdsupply tinypico board
#define HAS_LED NOT_A_PIN // Green LED on board
#define HAS_RGB_LED Apa102 rgb_led(1, GPIO_NUM_12, GPIO_NUM_2) // APA102 RGB LED on board
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
#define BAT_VOLTAGE_DIVIDER 2.7625f // voltage divider 160k/442k on board
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
#define LED_POWER_SW (13) // switches LED power
#define LED_POWER_ON 0 // switch on transistor for LED power
#define LED_POWER_OFF 1
#endif
+46
View File
@@ -0,0 +1,46 @@
// clang-format off
// upload_speed 921600
// board esp32dev
#ifndef _TINYPICO_H
#define _TINYPICO_H
#include <stdint.h>
// Hardware related definitions for crowdsupply tinypico board
// for operating a 96x16 shift register LED matrix display
#define HAS_LED NOT_A_PIN // Green LED on board
#define HAS_RGB_LED Apa102 rgb_led(1, GPIO_NUM_12, GPIO_NUM_2) // APA102 RGB LED on board
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
#define BAT_VOLTAGE_DIVIDER 2.7625f // voltage divider 160k/442k on board
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
#define LED_POWER_SW (13) // switches LED power
#define LED_POWER_ON 0 // switch on transistor for LED power
#define LED_POWER_OFF 1
// LED Matrix display settings
#define HAS_MATRIX_DISPLAY 1 // Uncomment to enable LED matrix display output
#define LED_MATRIX_WIDTH (32*2) // Width (cols) in pixels (LEDs) of your display, must be 32X
#define LED_MATRIX_HEIGHT (16*1) // Height (rows) in pixels (LEDs) of your display, must be 16X
// Explanation of pin signals see https://learn.adafruit.com/32x16-32x32-rgb-led-matrix/new-wiring
#define MATRIX_DISPLAY_SCAN_US 500 // Matrix display scan rate in microseconds (1ms is about 'acceptable')
#define LED_MATRIX_LATCHPIN 32 // LAT (or STB = Strobe)
#define LED_MATRIX_CLOCKPIN 33 // CLK
#define LED_MATRIX_EN_74138 21 // EN (or OE)
#define LED_MATRIX_LA_74138 23 // LA (or A)
#define LED_MATRIX_LB_74138 19 // LB (or B)
#define LED_MATRIX_LC_74138 18 // LC (or C)
#define LED_MATRIX_LD_74138 5 // LD (or D)
#define LED_MATRIX_DATA_R1 22 // R1 (or R0)
// CLK: The clock signal moves the data bits from pin R1 ("red") in the shift registers
// LAT: The latch signal enables LEDs according to the shift register's contents
// Line Selects: LA, LB, LC, LD select which rows of the display are currently lit (0 .. 15)
// OE: Output enable switches the LEDs on/off while transitioning from one row to the next
#endif
+3 -4
View File
@@ -17,7 +17,6 @@
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
#define BOARD_HAS_PSRAM // use extra 4MB external RAM
#define HAS_BUTTON GPIO_NUM_39 // on board button (next to reset)
#define BAT_MEASURE_ADC ADC1_GPIO35_CHANNEL // battery probe GPIO pin -> ADC1_CHANNEL_7
#define BAT_VOLTAGE_DIVIDER 2 // voltage divider 100k/100k on board
@@ -29,9 +28,9 @@
// enable only if device has these sensors, otherwise comment these lines
// 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 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 !!
// display (if connected)
#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
+74
View File
@@ -0,0 +1,74 @@
// clang-format off
// upload_speed 921600
// board ttgo-t-beam
#ifndef _TTGOBEAM_H
#define _TTGOBEAM_H
#include <stdint.h>
/*
Hardware related definitions for TTGO T-Beam board
(only) for newer T-Beam version T22_V10
pinouts taken from https://github.com/lewisxhe/TTGO-T-Beam
/// Button functions: ///
Power, short press -> set device on (toggles display while device is on)
Power, long press -> set device off
User, short press -> flip display page
User, long press -> send LORA message
Reset -> reset device
*/
//#define HAS_DISPLAY U8X8_SSD1306_128X64_NONAME_HW_I2C
#define MY_OLED_SDA SDA
#define MY_OLED_SCL SCL
#define MY_OLED_RST U8X8_PIN_NONE
//#define DISPLAY_FLIP 1 // use if display is rotated
#define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define CFG_sx1276_radio 1 // HPD13A LoRa SoC
#define HAS_BUTTON GPIO_NUM_38 // middle on board button
#define HAS_PMU 1 // AXP192 power management chip
#define PMU_INT GPIO_NUM_35 // AXP192 interrupt
#define HAS_LED NOT_A_PIN
// GPS settings
#define HAS_GPS 1 // use on board GPS
#define GPS_SERIAL 9600, SERIAL_8N1, GPIO_NUM_34, GPIO_NUM_12 // UBlox NEO 6M
#define GPS_INT GPIO_NUM_37 // 30ns accurary timepulse generated by NEO 6M Pin #3
// enable only if device has these sensors, otherwise comment these lines
// 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 !!
// user defined sensors (if connected)
//#define HAS_SENSORS 1 // comment out if device has user defined sensors
//#define DISABLE_BROWNOUT 1 // comment out if you want to keep brownout feature
#endif
/*
// T-Beam V10 has on board power management by AXP192 PMU chip:
//
// DCDC1 0.7-3.5V @ 1200mA -> OLED
// DCDC3 0.7-3.5V @ 700mA -> ESP32 (keep this on!)
// LDO1 30mA -> GPS Backup
// LDO2 200mA -> LORA
// LDO3 200mA -> GPS
// Wiring for I2C OLED display:
//
// Signal Header OLED
// 3V3 7 VCC
// GND 8 GND
// IO22(SCL) 9 SCL
// IO21(SDA) 10 SDA
//
*/
+2 -2
View File
@@ -11,8 +11,8 @@
// LED Matrix display settings
#define HAS_MATRIX_DISPLAY 1 // Uncomment to enable LED matrix display output
#define LED_MATRIX_WIDTH 64 // Width in pixels (LEDs) of your display
#define LED_MATRIX_HEIGHT 16 // Height in pixels (LEDs ) of your display
#define LED_MATRIX_WIDTH (32*2) // Width (cols) in pixels (LEDs) of your display, must be 32X
#define LED_MATRIX_HEIGHT (16*1) // Height (rows) in pixels (LEDs) of your display, must be 16X
// Pin numbers work fine for Wemos Lolin32 board (all used pins are on 1 side of the board)
// Explanation of pin signals see https://learn.adafruit.com/32x16-32x32-rgb-led-matrix/new-wiring
+66
View File
@@ -0,0 +1,66 @@
// Basic config
#include "globals.h"
#include "i2cscan.h"
// Local logging tag
static const char TAG[] = __FILE__;
int i2c_scan(void) {
int i2c_ret, addr;
int devices = 0;
ESP_LOGI(TAG, "Starting I2C bus scan...");
// block i2c bus access
if (I2C_MUTEX_LOCK()) {
for (addr = 8; addr <= 119; addr++) {
// scan i2c bus with no more to 100KHz
Wire.beginTransmission(addr);
Wire.write(addr);
i2c_ret = Wire.endTransmission();
if (i2c_ret == 0) {
devices++;
switch (addr) {
case SSD1306_PRIMARY_ADDRESS:
case SSD1306_SECONDARY_ADDRESS:
ESP_LOGI(TAG, "0x%X: SSD1306 Display controller", addr);
break;
case BME_PRIMARY_ADDRESS:
case BME_SECONDARY_ADDRESS:
ESP_LOGI(TAG, "0x%X: Bosch BME MEMS", addr);
break;
case AXP192_PRIMARY_ADDRESS:
ESP_LOGI(TAG, "0x%X: AXP192 power management", addr);
break;
case QUECTEL_GPS_PRIMARY_ADDRESS:
ESP_LOGI(TAG, "0x%X: Quectel GPS", addr);
break;
case MCP_24AA02E64_PRIMARY_ADDRESS:
ESP_LOGI(TAG, "0x%X: 24AA02E64 serial EEPROM", addr);
break;
default:
ESP_LOGI(TAG, "0x%X: Unknown device", addr);
break;
}
} // switch
} // for loop
ESP_LOGI(TAG, "I2C scan done, %u devices found.", devices);
I2C_MUTEX_UNLOCK(); // release i2c bus access
} else
ESP_LOGE(TAG, "I2c bus busy - scan error");
return devices;
}
+19 -1
View File
@@ -63,9 +63,15 @@ void irqHandler(void *pvParameters) {
}
#endif
// do we have a power event?
#if (HAS_PMU)
if (InterruptStatus & PMU_IRQ)
power_event_IRQ();
#endif
// is time to send the payload?
if (InterruptStatus & SENDCYCLE_IRQ)
sendCounter();
sendData();
}
}
@@ -106,6 +112,18 @@ void IRAM_ATTR ButtonIRQ() {
}
#endif
#ifdef HAS_PMU
void IRAM_ATTR PMUIRQ() {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(irqHandlerTask, PMU_IRQ, eSetBits,
&xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR();
}
#endif
void mask_user_IRQ() { xTaskNotify(irqHandlerTask, MASK_IRQ, eSetBits); }
void unmask_user_IRQ() { xTaskNotify(irqHandlerTask, UNMASK_IRQ, eSetBits); }
+1 -1
View File
@@ -14,7 +14,7 @@ unsigned long LEDBlinkStarted = 0; // When (in millis() led blink started)
#ifdef HAS_RGB_LED
// RGB Led instance
SmartLed rgb_led(LED_WS2812, 1, HAS_RGB_LED);
HAS_RGB_LED;
float rgb_CalcColor(float p, float q, float t) {
if (t < 0.0f)
+66 -12
View File
@@ -2,13 +2,14 @@
#include "globals.h"
#define NUMCHARS 5
#define MATRIX_DISPLAY_PAGES (2) // number of display pages
#define LINE_DIAGRAM_DIVIDER (2) // scales pax numbers to led rows
// local Tag for logging
static const char TAG[] = __FILE__;
uint8_t MatrixDisplayIsOn = 0;
static uint8_t displaybuf[LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT / 8] = {0};
static unsigned long ulLastNumMacs = 0;
static time_t ulLastTime = myTZ.toLocal(now());
@@ -16,9 +17,6 @@ LEDMatrix matrix(LED_MATRIX_LA_74138, LED_MATRIX_LB_74138, LED_MATRIX_LC_74138,
LED_MATRIX_LD_74138, LED_MATRIX_EN_74138, LED_MATRIX_DATA_R1,
LED_MATRIX_LATCHPIN, LED_MATRIX_CLOCKPIN);
// Display Buffer 128 = 64 * 16 / 8
uint8_t displaybuf[LED_MATRIX_WIDTH * LED_MATRIX_HEIGHT / NUMCHARS];
// --- SELECT YOUR FONT HERE ---
const FONT_INFO *ActiveFontInfo = &digital7_18ptFontInfo;
// const FONT_INFO *ActiveFontInfo = &arialNarrow_17ptFontInfo;
@@ -31,14 +29,21 @@ const FONT_CHAR_INFO *ActiveFontCharInfo = ActiveFontInfo->Descriptors;
void init_matrix_display(bool reverse) {
ESP_LOGI(TAG, "Initializing LED Matrix display");
matrix.begin(displaybuf, LED_MATRIX_WIDTH, LED_MATRIX_HEIGHT);
if (MatrixDisplayIsOn)
matrix.on();
else
matrix.off();
if (reverse)
matrix.reverse();
matrix.clear();
DrawNumber(String("0"));
matrix.drawPoint(0, LED_MATRIX_HEIGHT - 1, 1);
} // init_display
void refreshTheMatrixDisplay(bool nextPage) {
static uint8_t DisplayPage = 0;
static uint8_t DisplayPage = 0, col = 0, row = 0;
uint8_t level;
char buff[16];
// if Matrixdisplay is switched off we don't refresh it to relax cpu
@@ -48,25 +53,60 @@ void refreshTheMatrixDisplay(bool nextPage) {
// set display on/off according to current device configuration
if (MatrixDisplayIsOn != cfg.screenon) {
MatrixDisplayIsOn = cfg.screenon;
if (MatrixDisplayIsOn)
matrix.on();
else
matrix.off();
}
if (nextPage) {
DisplayPage =
(DisplayPage >= MATRIX_DISPLAY_PAGES - 1) ? 0 : (DisplayPage + 1);
matrix.clear();
col = 0;
}
switch (DisplayPage % MATRIX_DISPLAY_PAGES) {
// page 0: pax
// page 1: time
// page 0: number of current pax OR footfall line diagram
// page 1: time of day
case 0:
if (ulLastNumMacs != macs.size()) {
ulLastNumMacs = macs.size();
matrix.clear();
DrawNumber(String(ulLastNumMacs));
if (cfg.countermode == 1)
{ // cumulative counter mode -> display total number of pax
if (ulLastNumMacs != macs.size()) {
ulLastNumMacs = macs.size();
matrix.clear();
DrawNumber(String(ulLastNumMacs));
}
}
else { // cyclic counter mode -> plot a line diagram
if (ulLastNumMacs != macs.size()) {
// next count cycle?
if (macs.size() == 0) {
// matrix full? then scroll left 1 dot, else increment column
if (col < (LED_MATRIX_WIDTH - 1))
col++;
else
ScrollLeft(displaybuf, LED_MATRIX_WIDTH, LED_MATRIX_HEIGHT);
} else
matrix.drawPoint(col, row, 0); // clear current dot
// scale and set new dot
ulLastNumMacs = macs.size();
level = ulLastNumMacs / LINE_DIAGRAM_DIVIDER;
row = level <= LED_MATRIX_HEIGHT
? LED_MATRIX_HEIGHT - 1 - level % LED_MATRIX_HEIGHT
: 0;
matrix.drawPoint(col, row, 1);
}
}
break;
@@ -164,4 +204,18 @@ uint8_t GetCharWidth(char cChar) {
return CharDescriptor.width;
}
void ScrollLeft(uint8_t *buf, const uint16_t cols, const uint16_t rows) {
uint32_t i, k, idx;
const uint32_t x = cols / 8;
for (k = 0; k < rows; k++) {
// scroll a line with x bytes one dot to the left
for (i = 0; i < x - 1; ++i) {
idx = i + k * x;
buf[idx] = (buf[idx] << 1) | ((buf[idx + 1] >> 7) & 1);
}
buf[idx + 1] <<= 1;
}
}
#endif // HAS_MATRIX_DISPLAY
+23 -25
View File
@@ -2,40 +2,33 @@
// COUNTRY SETTINGS
// --> please check with you local regulations for ISM band frequency use!
//
// CFG_eu868 EU 863-870 MHz
// CFG_us915 US 902-928 MHz
// CFG_au921 Australia 915-928 MHz
// CFG_as923 Asia 923 MHz
// CFG_in866 India 865-867 MHz
#define CFG_eu868 1
//#define CFG_us915 1
//#define CFG_in866 1
//#define CFG_au921 1
//#define CFG_as923 1
//#define LMIC_COUNTRY_CODE LMIC_COUNTRY_CODE_JP /* for as923-JP */
#define CFG_eu868 1 // Europe (high band)
//#define CFG_eu433 1 // Europe (low band)
//#define CFG_us915 1 // USA, Canada and South America
//#define CFG_in866 1 // India
//#define CFG_au921 1 // Australia
//#define CFG_as923 1 // Asia
//#define CFG_cn783 1 // China (high band)
//#define CFG_cn490 1 // China (low band)
//#define CFG_kr920 1 // Korea
// LMIC LORAWAN STACK SETTINGS
// --> adapt to your device only if necessary
//#define LMIC_USE_INTERRUPTS 1
// use interrupts only if LORA_IRQ and LORA_DIO are connected to interrupt
// capable GPIO pins on your board, if not disable interrupts
#define LMIC_USE_INTERRUPTS 1
//time sync via LoRaWAN network, is not yet supported by TTN (LoRaWAN spec v1.0.3)
//#define LMIC_ENABLE_DeviceTimeReq 1
// 16 μs per tick
// LMIC requires ticks to be 15.5μs - 100 μs long
#define US_PER_OSTICK_EXPONENT 4
#define US_PER_OSTICK (1 << US_PER_OSTICK_EXPONENT)
#define OSTICKS_PER_SEC (1000000 / US_PER_OSTICK)
// time sync via LoRaWAN network, note: not supported by TTNv2
// #define LMIC_ENABLE_DeviceTimeReq 1
// This tells LMIC to make the receive windows bigger, in case your clock is
// faster or slower. This causes the transceiver to be earlier switched on,
// so consuming more power. You may sharpen (reduce) this value if you are
// limited on battery.
// limited on battery.
// ATTN: VALUES > 7 WILL CAUSE RECEPTION AND JOIN PROBLEMS WITH HIGH SF RATES
#define CLOCK_ERROR_PROCENTAGE 5
//#define CLOCK_ERROR_PROCENTAGE 7
// Set this to 1 to enable some basic debug output (using printf) about
// RF settings used during transmission and reception. Set to 2 to
@@ -49,6 +42,11 @@
// current implementation only works on AVR, though.
//#define LMIC_PRINTF_TO Serial
// Change the SPI clock speed if you encounter errors
// communicating with the radio.
// The standard range is 125kHz-8MHz, but some boards can go faster.
//#define LMIC_SPI_FREQ 1E6
// Any runtime assertion failures are printed to this serial port (or
// any other Print object). If this is unset, any failures just silently
// halt execution.
@@ -90,7 +88,7 @@
// implementation is optimized for speed on 32-bit processors using
// fairly big lookup tables, but it takes up big amounts of flash on the
// AVR architecture.
//#define USE_ORIGINAL_AES
#define USE_ORIGINAL_AES
//
// This selects the AES implementation written by Ideetroon for their
// own LoRaWAN library. It also uses lookup tables, but smaller
@@ -98,4 +96,4 @@
// also about twice as slow as the original).
// #define USE_IDEETRON_AES
//
#define USE_MBEDTLS_AES
//#define USE_MBEDTLS_AES
+261 -144
View File
@@ -18,9 +18,31 @@ static const char TAG[] = "lora";
#endif
#endif
osjob_t sendjob;
QueueHandle_t LoraSendQueue;
TaskHandle_t lmicTask = NULL;
TaskHandle_t lmicTask = NULL, lorasendTask = NULL;
// table of LORAWAN MAC messages sent by the network to the device
// format: opcode, cmdname (max 19 chars), #bytes params
// source: LoRaWAN 1.1 Specification (October 11, 2017)
static const mac_t MACdn_table[] = {
{0x01, "ResetConf", 1}, {0x02, "LinkCheckAns", 2},
{0x03, "LinkADRReq", 4}, {0x04, "DutyCycleReq", 1},
{0x05, "RXParamSetupReq", 4}, {0x06, "DevStatusReq", 0},
{0x07, "NewChannelReq", 5}, {0x08, "RxTimingSetupReq", 1},
{0x09, "TxParamSetupReq", 1}, {0x0A, "DlChannelReq", 4},
{0x0B, "RekeyConf", 1}, {0x0C, "ADRParamSetupReq", 1},
{0x0D, "DeviceTimeAns", 5}, {0x0E, "ForceRejoinReq", 2},
{0x0F, "RejoinParamSetupReq", 1}};
// table of LORAWAN MAC messages sent by the device to the network
static const mac_t MACup_table[] = {
{0x01, "ResetInd", 1}, {0x02, "LinkCheckReq", 0},
{0x03, "LinkADRAns", 1}, {0x04, "DutyCycleAns", 0},
{0x05, "RXParamSetupAns", 1}, {0x06, "DevStatusAns", 2},
{0x07, "NewChannelAns", 1}, {0x08, "RxTimingSetupAns", 0},
{0x09, "TxParamSetupAns", 0}, {0x0A, "DlChannelAns", 1},
{0x0B, "RekeyInd", 1}, {0x0C, "ADRParamSetupAns", 0},
{0x0D, "DeviceTimeReq", 0}, {0x0F, "RejoinParamSetupAns", 1}};
class MyHalConfig_t : public Arduino_LMIC::HalConfiguration_t {
@@ -135,7 +157,7 @@ void get_hard_deveui(uint8_t *pdeveui) {
uint8_t i2c_ret;
// Init this just in case, no more to 100KHz
Wire.begin(MY_OLED_SDA, MY_OLED_SCL, 100000);
Wire.begin(SDA, SCL, 100000);
Wire.beginTransmission(MCP_24AA02E64_I2C_ADDRESS);
Wire.write(MCP_24AA02E64_MAC_ADDRESS);
i2c_ret = Wire.endTransmission();
@@ -209,19 +231,17 @@ void onEvent(ev_t ev) {
case EV_JOINED:
strcpy_P(buff, PSTR("JOINED"));
sprintf(display_line6, " "); // clear previous lmic status
// set data rate adaptation according to saved setting
LMIC_setAdrMode(cfg.adrmode);
// set cyclic lmic link check to off if no ADR because is not supported by
// ttn (but enabled by lmic after join)
LMIC_setLinkCheckMode(cfg.adrmode);
// Set data rate and transmit power (note: txpower seems to be ignored by
// the library)
switch_lora(cfg.lorasf, cfg.txpower);
// kickoff first send job
os_setCallback(&sendjob, lora_send);
// show effective LoRa parameters after join
// set data rate and transmit power to defaults only if we have no ADR
if (!cfg.adrmode)
LMIC_setDrTxpow(assertDR(cfg.loradr), cfg.txpower);
// show current devaddr
ESP_LOGI(TAG, "DEVaddr=%08X", LMIC.devaddr);
ESP_LOGI(TAG, "Radio parameters %s / %s / %s",
getSfName(updr2rps(LMIC.datarate)),
getBwName(updr2rps(LMIC.datarate)),
getCrName(updr2rps(LMIC.datarate)));
break;
case EV_RFU1:
@@ -237,46 +257,7 @@ void onEvent(ev_t ev) {
break;
case EV_TXCOMPLETE:
#if (TIME_SYNC_LORASERVER)
// if last packet sent was a timesync request, store TX timestamp
if (LMIC.pendTxPort == TIMEPORT)
store_time_sync_req(osticks2ms(LMIC.txend)); // milliseconds
#endif
strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED ACK")
: PSTR("TX COMPLETE"));
sprintf(display_line6, " "); // clear previous lmic status
if (LMIC.dataLen) { // did we receive payload data -> display info
ESP_LOGI(TAG, "Received %d bytes of payload, RSSI %d SNR %d",
LMIC.dataLen, LMIC.rssi, LMIC.snr / 4);
sprintf(display_line6, "RSSI %d SNR %d", LMIC.rssi, LMIC.snr / 4);
if (LMIC.txrxFlags & TXRX_PORT) { // FPort -> use to switch
switch (LMIC.frame[LMIC.dataBeg - 1]) {
case RCMDPORT: // opcode -> call rcommand interpreter
rcommand(LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
break;
default:
#if (TIME_SYNC_LORASERVER)
// timesync answer -> call timesync processor
if (LMIC.frame[LMIC.dataBeg - 1] == TIMEPORT) {
recv_timesync_ans(LMIC.frame + LMIC.dataBeg, LMIC.dataLen);
break;
}
#endif
// unknown port -> display info
ESP_LOGI(TAG, "Received data on unsupported port #%d",
LMIC.frame[LMIC.dataBeg - 1]);
break;
}
}
}
strcpy_P(buff, PSTR("TX COMPLETE"));
break;
case EV_LOST_TSYNC:
@@ -306,13 +287,7 @@ void onEvent(ev_t ev) {
case EV_TXSTART:
if (!(LMIC.opmode & OP_JOINING)) {
#if (TIME_SYNC_LORASERVER)
// if last packet sent was a timesync request, store TX time
if (LMIC.pendTxPort == TIMEPORT)
strcpy_P(buff, PSTR("TX TIMESYNC"));
else
#endif
strcpy_P(buff, PSTR("TX START"));
strcpy_P(buff, PSTR("TX START"));
}
break;
@@ -336,80 +311,58 @@ void onEvent(ev_t ev) {
// Log & Display if asked
if (*buff) {
ESP_LOGI(TAG, "%s", buff);
sprintf(display_line7, buff);
sprintf(lmic_event_msg, buff);
}
}
// helper function to assign LoRa datarates to numeric spreadfactor values
void switch_lora(uint8_t sf, uint8_t tx) {
if (tx > 20)
return;
cfg.txpower = tx;
switch (sf) {
case 7:
LMIC_setDrTxpow(DR_SF7, tx);
cfg.lorasf = sf;
break;
case 8:
LMIC_setDrTxpow(DR_SF8, tx);
cfg.lorasf = sf;
break;
case 9:
LMIC_setDrTxpow(DR_SF9, tx);
cfg.lorasf = sf;
break;
case 10:
LMIC_setDrTxpow(DR_SF10, tx);
cfg.lorasf = sf;
break;
case 11:
#if defined(CFG_us915)
LMIC_setDrTxpow(DR_SF11CR, tx);
cfg.lorasf = sf;
break;
#else
LMIC_setDrTxpow(DR_SF11, tx);
cfg.lorasf = sf;
break;
#endif
case 12:
#if defined(CFG_us915)
LMIC_setDrTxpow(DR_SF12CR, tx);
cfg.lorasf = sf;
break;
#else
LMIC_setDrTxpow(DR_SF12, tx);
cfg.lorasf = sf;
break;
#endif
default:
break;
}
}
// LMIC send task
void lora_send(void *pvParameters) {
configASSERT(((uint32_t)pvParameters) == 1); // FreeRTOS check
void lora_send(osjob_t *job) {
MessageBuffer_t SendBuffer;
// Check if there is a pending TX/RX job running, if yes don't eat data
// since it cannot be sent right now
if ((LMIC.opmode & (OP_JOINING | OP_REJOIN | OP_TXDATA | OP_POLL)) != 0) {
// waiting for LoRa getting ready
} else {
if (xQueueReceive(LoraSendQueue, &SendBuffer, (TickType_t)0) == pdTRUE) {
// SendBuffer now filled with next payload from queue
if (!LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
SendBuffer.MessageSize, (cfg.countermode & 0x02))) {
ESP_LOGI(TAG, "%d byte(s) sent to LoRa", SendBuffer.MessageSize);
} else {
ESP_LOGE(TAG, "could not send %d byte(s) to LoRa",
SendBuffer.MessageSize);
}
// sprintf(display_line7, "PACKET QUEUED");
while (1) {
// postpone until we are joined if we are not
while (!LMIC.devaddr) {
vTaskDelay(pdMS_TO_TICKS(500));
}
// fetch next or wait for payload to send from queue
if (xQueueReceive(LoraSendQueue, &SendBuffer, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
continue;
}
// attempt to transmit payload
else {
switch (LMIC_sendWithCallback_strict(
SendBuffer.MessagePort, SendBuffer.Message, SendBuffer.MessageSize,
(cfg.countermode & 0x02), myTxCallback, NULL)) {
case LMIC_ERROR_SUCCESS:
ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize);
break;
case LMIC_ERROR_TX_BUSY: // LMIC already has a tx message pending
case LMIC_ERROR_TX_FAILED: // message was not sent
// ESP_LOGD(TAG, "LMIC busy, message re-enqueued"); // very noisy
vTaskDelay(pdMS_TO_TICKS(1000 + random(500))); // wait a while
lora_enqueuedata(&SendBuffer); // re-enqueue the undelivered message
break;
case LMIC_ERROR_TX_TOO_LARGE: // message size exceeds LMIC buffer size
case LMIC_ERROR_TX_NOT_FEASIBLE: // message too large for current datarate
ESP_LOGI(TAG,
"Message too large to send, message not sent and deleted");
// we need some kind of error handling here -> to be done
break;
default: // other LMIC return code
ESP_LOGE(TAG, "LMIC error, message not sent and deleted");
} // switch
}
delay(2); // yield to CPU
}
// reschedule job every 0,5 - 1 sec. including a bit of random to prevent
// systematic collisions
os_setTimedCallback(job, os_getTime() + 500 + ms2osticks(random(500)),
lora_send);
}
esp_err_t lora_stack_init() {
@@ -422,13 +375,13 @@ esp_err_t lora_stack_init() {
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
SEND_QUEUE_SIZE * sizeof(MessageBuffer_t));
// starting lorawan stack
// start lorawan stack
ESP_LOGI(TAG, "Starting LMIC...");
xTaskCreatePinnedToCore(lmictask, // task function
"lmictask", // name of task
4096, // stack size of task
(void *)1, // parameter of the task
2, // priority of the task
5, // priority of the task
&lmicTask, // task handle
1); // CPU core
@@ -436,18 +389,31 @@ esp_err_t lora_stack_init() {
ESP_LOGI(TAG, "Already joined");
}
return ESP_OK; // continue main program
// start lmic send task
xTaskCreatePinnedToCore(lora_send, // task function
"lorasendtask", // name of task
3072, // stack size of task
(void *)1, // parameter of the task
1, // priority of the task
&lorasendTask, // task handle
1); // CPU core
return ESP_OK;
}
void lora_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
void lora_enqueuedata(MessageBuffer_t *message) {
// enqueue message in LORA send queue
BaseType_t ret;
BaseType_t ret = pdFALSE;
MessageBuffer_t DummyBuffer;
sendprio_t prio = message->MessagePrio;
switch (prio) {
case prio_high:
// clear space in queue if full, then fallthrough to normal
if (uxQueueSpacesAvailable(LoraSendQueue) == 0)
// clear some space in queue if full, then fallthrough to prio_normal
if (uxQueueSpacesAvailable(LoraSendQueue) == 0) {
xQueueReceive(LoraSendQueue, &DummyBuffer, (TickType_t)0);
ESP_LOGW(TAG, "LORA sendqueue purged, data is lost");
}
case prio_normal:
ret = xQueueSendToFront(LoraSendQueue, (void *)message, (TickType_t)0);
break;
@@ -519,17 +485,17 @@ void lmictask(void *pvParameters) {
os_init(); // initialize lmic run-time environment
LMIC_reset(); // initialize lmic MAC
LMIC_setLinkCheckMode(0);
// This tells LMIC to make the receive windows bigger, in case your clock is
// faster or slower. This causes the transceiver to be earlier switched on,
// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE
// in src/lmic_config.h if you are limited on battery.
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
// Set the data rate to Spreading Factor 7. This is the fastest supported
// rate for 125 kHz channels, and it minimizes air time and battery power.
// Set the transmission power to 14 dBi (25 mW).
LMIC_setDrTxpow(DR_SF7, 14);
#if defined(CFG_US915) || defined(CFG_au921)
// This tells LMIC to make the receive windows bigger, in case your clock is
// faster or slower. This causes the transceiver to be earlier switched on,
// so consuming more power. You may sharpen (reduce) CLOCK_ERROR_PERCENTAGE
// in src/lmic_config.h if you are limited on battery.
#ifdef CLOCK_ERROR_PROCENTAGE
LMIC_setClockError(MAX_CLOCK_ERROR * CLOCK_ERROR_PROCENTAGE / 100);
#endif
//#if defined(CFG_US915) || defined(CFG_au921)
#if CFG_LMIC_US_like
// in the US, with TTN, it saves join time if we start on subband 1
// (channels 8-15). This will get overridden after the join by parameters
// from the network. If working with other networks or in other regions,
@@ -537,10 +503,161 @@ void lmictask(void *pvParameters) {
LMIC_selectSubBand(1);
#endif
// Set the data rate to Spreading Factor 7. This is the fastest supported
// rate for 125 kHz channels, and it minimizes air time and battery power.
// Set the transmission power to 14 dBi (25 mW).
LMIC_setDrTxpow(DR_SF7, 14);
// register a callback for downlink messages. We aren't trying to write
// reentrant code, so pUserData is NULL.
LMIC_registerRxMessageCb(myRxCallback, NULL);
while (1) {
os_runloop_once(); // execute lmic scheduled jobs and events
delay(2); // yield to CPU
}
} // lmictask
// receive message handler
void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
size_t nMsg) {
// display type of received data
if (nMsg)
ESP_LOGI(TAG, "Received %u byte(s) of payload on port %u", nMsg, port);
else if (port)
ESP_LOGI(TAG, "Received empty message on port %u", port);
// list MAC messages, if any
uint8_t nMac = pMsg - &LMIC.frame[0];
if (port != MACPORT)
--nMac;
if (nMac) {
ESP_LOGI(TAG, "%u byte(s) downlink MAC commands", nMac);
// NOT WORKING YET
// whe need to unwrap the MAC command from LMIC.frame here
// mac_decode(LMIC.frame, nMac, MACdn_table, sizeof(MACdn_table) /
// sizeof(MACdn_table[0]));
}
if (LMIC.pendMacLen) {
ESP_LOGI(TAG, "%u byte(s) uplink MAC commands", LMIC.pendMacLen);
mac_decode(LMIC.pendMacData, LMIC.pendMacLen, MACup_table,
sizeof(MACup_table) / sizeof(MACup_table[0]));
}
switch (port) {
// ignore mac messages
case MACPORT:
break;
// rcommand received -> call interpreter
case RCMDPORT:
rcommand(pMsg, nMsg);
break;
default:
#if (TIME_SYNC_LORASERVER)
// valid timesync answer -> call timesync processor
if ((port >= TIMEANSWERPORT_MIN) && (port <= TIMEANSWERPORT_MAX)) {
recv_timesync_ans(port, pMsg, nMsg);
break;
}
#endif
// unknown port -> display info
ESP_LOGI(TAG, "Received data on unsupported port %u", port);
break;
} // switch
}
// transmit complete message handler
void myTxCallback(void *pUserData, int fSuccess) {
#if (TIME_SYNC_LORASERVER)
// if last packet sent was a timesync request, store TX timestamp
if (LMIC.pendTxPort == TIMEPORT)
store_time_sync_req(osticks2ms(LMIC.txend)); // milliseconds
#endif
}
// decode LORAWAN MAC message
void mac_decode(const uint8_t cmd[], const uint8_t cmdlen, const mac_t table[],
const uint8_t tablesize) {
if (!cmdlen)
return;
uint8_t foundcmd[cmdlen], cursor = 0;
while (cursor < cmdlen) {
int i = tablesize; // number of commands in table
while (i--) {
if (cmd[cursor] == table[i].opcode) { // lookup command in opcode table
cursor++; // strip 1 byte opcode
if ((cursor + table[i].params) <= cmdlen) {
memmove(foundcmd, cmd + cursor,
table[i].params); // strip opcode from cmd array
cursor += table[i].params;
ESP_LOGD(TAG, "MAC command %s", table[i].cmdname);
} else
ESP_LOGD(TAG, "MAC command 0x%02X with missing parameter(s)",
table[i].opcode);
break; // command found -> exit table lookup loop
} // end of command validation
} // end of command table lookup loop
if (i < 0) { // command not found -> skip it
ESP_LOGD(TAG, "Unknown MAC command 0x%02X", cmd[cursor]);
cursor++;
}
} // command parsing loop
} // mac_decode()
uint8_t getBattLevel() {
/*
return values:
MCMD_DEVS_EXT_POWER = 0x00, // external power supply
MCMD_DEVS_BATT_MIN = 0x01, // min battery value
MCMD_DEVS_BATT_MAX = 0xFE, // max battery value
MCMD_DEVS_BATT_NOINFO = 0xFF, // unknown battery level
*/
#if (defined HAS_PMU || defined BAT_MEASURE_ADC)
uint16_t voltage = read_voltage();
switch (voltage) {
case 0:
return MCMD_DEVS_BATT_NOINFO;
case 0xffff:
return MCMD_DEVS_EXT_POWER;
default:
return (voltage > OTA_MIN_BATT ? MCMD_DEVS_BATT_MAX : MCMD_DEVS_BATT_MIN);
}
#else // we don't have any info on battery level
return MCMD_DEVS_BATT_NOINFO;
#endif
} // getBattLevel()
// u1_t os_getBattLevel(void) { return getBattLevel(); };
const char *getSfName(rps_t rps) {
const char *const t[] = {"FSK", "SF7", "SF8", "SF9",
"SF10", "SF11", "SF12", "SF?"};
return t[getSf(rps)];
}
const char *getBwName(rps_t rps) {
const char *const t[] = {"BW125", "BW250", "BW500", "BW?"};
return t[getBw(rps)];
}
const char *getCrName(rps_t rps) {
const char *const t[] = {"CR 4/5", "CR 4/6", "CR 4/7", "CR 4/8"};
return t[getCr(rps)];
}
#endif // HAS_LORA
+47 -33
View File
@@ -31,12 +31,12 @@ ledloop 0 3 blinks LEDs
spiloop 0 2 reads/writes data on spi interface
IDLE 0 0 ESP32 arduino scheduler -> runs wifi sniffer
lmictask 1 5 MCCI LMiC LORAWAN stack
clockloop 1 4 generates realtime telegrams for external clock
timesync_req 1 3 processes realtime time sync requests
lmictask 1 2 MCCI LMiC LORAWAN stack
irqhandler 1 1 display, timesync, gps, etc. triggered by timers
gpsloop 1 1 reads data from GPS via serial or i2c
looptask 1 1 arduino loop (unused)
lorasendtask 1 1 feed data from lora sendqueue to lmcic
IDLE 1 0 ESP32 arduino scheduler -> runs wifi channel rotator
Low priority numbers denote low priority tasks.
@@ -58,6 +58,7 @@ fired by hardware
DisplayIRQ -> esp32 timer 0 -> irqHandlerTask (Core 1)
CLOCKIRQ -> esp32 timer 1 -> ClockTask (Core 1)
ButtonIRQ -> external gpio -> irqHandlerTask (Core 1)
PMUIRQ -> PMU chip gpio -> irqHandlerTask (Core 1)
fired by software (Ticker.h)
TIMESYNC_IRQ -> timeSync() -> irqHandlerTask (Core 1)
@@ -75,9 +76,9 @@ triggers pps 1 sec impulse
// Basic Config
#include "main.h"
configData_t cfg; // struct holds current device configuration
char display_line6[16], display_line7[16]; // display buffers
uint8_t volatile channel = 0; // channel rotation counter
configData_t cfg; // struct holds current device configuration
char lmic_event_msg[16]; // display buffer for LMIC event message
uint8_t volatile channel = 0; // channel rotation counter
uint16_t volatile macs_total = 0, macs_wifi = 0, macs_ble = 0,
batt_voltage = 0; // globals for display
@@ -169,24 +170,45 @@ void setup() {
ESP_LOGI(TAG, "TinyGPS+ version %s", TinyGPSPlus::libraryVersion());
#endif
// open i2c bus
#ifdef HAS_DISPLAY
Wire.begin(MY_OLED_SDA, MY_OLED_SCL, 100000);
#else
Wire.begin(SDA, SCL, 100000);
#endif
// setup power on boards with power management logic
#ifdef EXT_POWER_SW
pinMode(EXT_POWER_SW, OUTPUT);
digitalWrite(EXT_POWER_SW, EXT_POWER_ON);
strcat_P(features, " VEXT");
#endif
#ifdef HAS_PMU
AXP192_init();
strcat_P(features, " PMU");
#endif
// scan i2c bus for devices
i2c_scan();
#endif // verbose
// read (and initialize on first run) runtime settings from NVRAM
loadConfig(); // includes initialize if necessary
// initialize display
#ifdef HAS_DISPLAY
strcat_P(features, " OLED");
DisplayIsOn = cfg.screenon;
init_display(PRODUCTNAME, PROGVERSION); // note: blocking call
#endif
#ifdef BOARD_HAS_PSRAM
assert(psramFound());
ESP_LOGI(TAG, "PSRAM found and initialized");
strcat_P(features, " PSRAM");
#endif
// set external power mode
#ifdef EXT_POWER_SW
pinMode(EXT_POWER_SW, OUTPUT);
digitalWrite(EXT_POWER_SW, EXT_POWER_ON);
strcat_P(features, " VEXT");
#endif
#ifdef BAT_MEASURE_EN
pinMode(BAT_MEASURE_EN, OUTPUT);
#endif
@@ -195,17 +217,25 @@ void setup() {
#if (HAS_LED != NOT_A_PIN)
pinMode(HAS_LED, OUTPUT);
strcat_P(features, " LED");
#ifdef LED_POWER_SW
pinMode(LED_POWER_SW, OUTPUT);
digitalWrite(LED_POWER_SW, LED_POWER_ON);
#endif
#ifdef HAS_TWO_LED
pinMode(HAS_TWO_LED, OUTPUT);
strcat_P(features, " LED1");
#endif
// use LED for power display if we have additional RGB LED, else for status
#ifdef HAS_RGB_LED
switch_LED(LED_ON);
strcat_P(features, " RGB");
rgb_set_color(COLOR_PINK);
#endif
#endif
#endif // HAS_LED
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
// start led loop
@@ -227,7 +257,7 @@ void setup() {
#endif
// initialize battery status
#ifdef BAT_MEASURE_ADC
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
strcat_P(features, " BATT");
calibrate_voltage();
batt_voltage = read_voltage();
@@ -297,13 +327,6 @@ void setup() {
strcat_P(features, " FILTER");
#endif
// initialize display
#ifdef HAS_DISPLAY
strcat_P(features, " OLED");
DisplayIsOn = cfg.screenon;
init_display(PRODUCTNAME, PROGVERSION); // note: blocking call
#endif
// initialize matrix display
#ifdef HAS_MATRIX_DISPLAY
strcat_P(features, " LED_MATRIX");
@@ -419,12 +442,10 @@ void setup() {
#warning you did not specify a time source, time will not be synched
#endif
/*
// initialize gps time
#if (HAS_GPS)
fetch_gpsTime();
#endif
*/
#if (defined HAS_IF482 || defined HAS_DCF77)
ESP_LOGI(TAG, "Starting Clock Controller...");
@@ -445,18 +466,11 @@ void setup() {
ESP_LOGI(TAG, "Features:%s", features);
macsniff_setup();
uart_setup();
} // setup()
void loop() {
} // setup()
while (1) {
#if (HAS_LORA)
os_runloop_once(); // execute lmic scheduled jobs and events
#else
delay(2); // yield to CPU
#endif
}
}
void loop() { vTaskDelete(NULL); }
+7 -3
View File
@@ -1,3 +1,5 @@
// clang-format off
// ----- Paxcounter user config file ------
//
// --> adapt to your needs and use case <--
@@ -46,7 +48,8 @@
#define MEM_LOW 2048 // [Bytes] low memory threshold triggering a send cycle
#define RETRANSMIT_RCMD 5 // [seconds] wait time before retransmitting rcommand results
#define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit
#define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs
#define LORADRDEFAULT 5 // 0 .. 15, LoRaWAN datarate, according to regional LoRaWAN specs [default = 5]
#define LORATXPOWDEFAULT 7 // 0 .. 255, LoRaWAN TX power in dBm [default = 14]
#define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy
#define SEND_QUEUE_SIZE 10 // maximum number of messages in payload send queue [1 = no queue]
@@ -72,7 +75,7 @@
#define TIME_SYNC_INTERVAL_RETRY 10 // retry time sync after lost sync each .. minutes [default = 10], 0 means off
#define TIME_SYNC_COMPILEDATE 0 // set to 1 to use compile date to initialize RTC after power outage [default = 0]
#define TIME_SYNC_LORAWAN 0 // set to 1 to use LORA network as time source, 0 means off [default = 0]
#define TIME_SYNC_LORASERVER 1 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0]
#define TIME_SYNC_LORASERVER 0 // set to 1 to use LORA timeserver as time source, 0 means off [default = 0]
// settings for syncing time with timeserver applications
#define TIME_SYNC_SAMPLES 1 // number of time requests for averaging
@@ -85,6 +88,7 @@
// Ports on which the device sends and listenes on LoRaWAN and SPI
#define COUNTERPORT 1 // counts
#define MACPORT 0 // network commands
#define RCMDPORT 2 // remote commands
#define STATUSPORT 2 // remote command results
#define CONFIGPORT 3 // config query results
@@ -107,4 +111,4 @@
#define CAYENNE_ACTUATOR 10 // actuator commands
#define CAYENNE_DEVICECONFIG 11 // device period configuration
#define CAYENNE_SENSORREAD 13 // sensor period configuration
#define CAYENNE_SENSORENABLE 14 // sensor enable configuration
#define CAYENNE_SENSORENABLE 14 // sensor enable configuration
+3 -3
View File
@@ -43,7 +43,7 @@ void PayloadConvert::addVoltage(uint16_t value) {
}
void PayloadConvert::addConfig(configData_t value) {
buffer[cursor++] = value.lorasf;
buffer[cursor++] = value.loradr;
buffer[cursor++] = value.txpower;
buffer[cursor++] = value.adrmode;
buffer[cursor++] = value.screensaver;
@@ -164,7 +164,7 @@ void PayloadConvert::addAlarm(int8_t rssi, uint8_t msg) {
void PayloadConvert::addVoltage(uint16_t value) { writeUint16(value); }
void PayloadConvert::addConfig(configData_t value) {
writeUint8(value.lorasf);
writeUint8(value.loradr);
writeUint8(value.txpower);
writeUint16(value.rssilimit);
writeUint8(value.sendcycle);
@@ -378,7 +378,7 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, float celsius,
uint32_t mem, uint8_t reset1, uint8_t reset2) {
uint16_t temp = celsius * 10;
uint16_t volt = voltage / 10;
#ifdef BAT_MEASURE_ADC
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
#if (PAYLOAD_ENCODER == 3)
buffer[cursor++] = LPP_BATT_CHANNEL;
#endif
+231
View File
@@ -0,0 +1,231 @@
// Basic config
#include "globals.h"
#include "power.h"
// Local logging tag
static const char TAG[] = __FILE__;
#ifdef HAS_PMU
AXP20X_Class pmu;
void power_event_IRQ(void) {
if (!I2C_MUTEX_LOCK())
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
else {
pmu.readIRQ();
// put your power event handler code here
if (pmu.isVbusOverVoltageIRQ())
ESP_LOGI(TAG, "USB voltage %.2fV too high.", pmu.getVbusVoltage() / 1000);
if (pmu.isVbusPlugInIRQ())
ESP_LOGI(TAG, "USB plugged, %.2fV @ %.0mA", pmu.getVbusVoltage() / 1000,
pmu.getVbusCurrent());
if (pmu.isVbusRemoveIRQ())
ESP_LOGI(TAG, "USB unplugged.");
if (pmu.isBattPlugInIRQ())
ESP_LOGI(TAG, "Battery is connected.");
if (pmu.isBattRemoveIRQ())
ESP_LOGI(TAG, "Battery was removed.");
if (pmu.isChargingIRQ())
ESP_LOGI(TAG, "Battery charging.");
if (pmu.isChargingDoneIRQ())
ESP_LOGI(TAG, "Battery charging done.");
if (pmu.isBattTempLowIRQ())
ESP_LOGI(TAG, "Battery high temperature.");
if (pmu.isBattTempHighIRQ())
ESP_LOGI(TAG, "Battery low temperature.");
// display on/off
if (pmu.isPEKShortPressIRQ()) {
cfg.screenon = !cfg.screenon;
}
// shutdown power
if (pmu.isPEKLongtPressIRQ()) {
AXP192_power(false); // switch off Lora, GPS, display
pmu.shutdown();
}
pmu.clearIRQ();
I2C_MUTEX_UNLOCK();
} // mutex
// refresh stored voltage value
read_voltage();
}
void AXP192_power(bool on) {
if (on) {
pmu.setPowerOutPut(AXP192_LDO2, AXP202_ON); // Lora on T-Beam V1.0
pmu.setPowerOutPut(AXP192_LDO3, AXP202_ON); // Gps on T-Beam V1.0
pmu.setPowerOutPut(AXP192_DCDC1, AXP202_ON); // OLED on T-Beam v1.0
// pmu.setChgLEDMode(AXP20X_LED_LOW_LEVEL);
pmu.setChgLEDMode(AXP20X_LED_BLINK_1HZ);
} else {
pmu.setChgLEDMode(AXP20X_LED_OFF);
pmu.setPowerOutPut(AXP192_DCDC1, AXP202_OFF);
pmu.setPowerOutPut(AXP192_LDO3, AXP202_OFF);
pmu.setPowerOutPut(AXP192_LDO2, AXP202_OFF);
}
}
void AXP192_showstatus(void) {
if (!I2C_MUTEX_LOCK())
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
else {
if (pmu.isBatteryConnect())
if (pmu.isChargeing())
ESP_LOGI(TAG, "Battery charging, %.2fV @ %.0fmAh",
pmu.getBattVoltage() / 1000, pmu.getBattChargeCurrent());
else
ESP_LOGI(TAG, "Battery not charging");
else
ESP_LOGI(TAG, "No Battery");
if (pmu.isVBUSPlug())
ESP_LOGI(TAG, "USB powered, %.0fmW",
pmu.getVbusVoltage() / 1000 * pmu.getVbusCurrent());
else
ESP_LOGI(TAG, "USB not present");
I2C_MUTEX_UNLOCK();
} // mutex
}
void AXP192_init(void) {
// block i2c bus access
if (I2C_MUTEX_LOCK()) {
if (pmu.begin(Wire, AXP192_PRIMARY_ADDRESS))
ESP_LOGI(TAG, "AXP192 PMU initialization failed");
else {
// configure AXP192
pmu.setDCDC1Voltage(3300); // for external OLED display
pmu.setTimeOutShutdown(false); // no automatic shutdown
pmu.setTSmode(AXP_TS_PIN_MODE_DISABLE); // TS pin mode off to save power
// switch ADCs on
pmu.adc1Enable(AXP202_BATT_VOL_ADC1, true);
pmu.adc1Enable(AXP202_BATT_CUR_ADC1, true);
pmu.adc1Enable(AXP202_VBUS_VOL_ADC1, true);
pmu.adc1Enable(AXP202_VBUS_CUR_ADC1, true);
// switch power rails on
AXP192_power(true);
// I2C access of AXP202X library currently is not mutexable
// so we better should disable AXP interrupts... ?
#ifdef PMU_INT
pinMode(PMU_INT, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PMU_INT), PMUIRQ, FALLING);
pmu.enableIRQ(AXP202_VBUS_REMOVED_IRQ | AXP202_VBUS_CONNECT_IRQ |
AXP202_BATT_REMOVED_IRQ | AXP202_BATT_CONNECT_IRQ |
AXP202_CHARGING_FINISHED_IRQ,
1);
pmu.clearIRQ();
#endif // PMU_INT
ESP_LOGI(TAG, "AXP192 PMU initialized");
}
I2C_MUTEX_UNLOCK(); // release i2c bus access
} else
ESP_LOGE(TAG, "I2c bus busy - PMU initialization error");
}
#endif // HAS_PMU
#ifdef BAT_MEASURE_ADC
esp_adc_cal_characteristics_t *adc_characs =
(esp_adc_cal_characteristics_t *)calloc(
1, sizeof(esp_adc_cal_characteristics_t));
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
static const adc1_channel_t adc_channel = BAT_MEASURE_ADC;
#else // ADC2
static const adc2_channel_t adc_channel = BAT_MEASURE_ADC;
#endif
static const adc_atten_t atten = ADC_ATTEN_DB_11;
static const adc_unit_t unit = ADC_UNIT_1;
#endif // BAT_MEASURE_ADC
void calibrate_voltage(void) {
#ifdef BAT_MEASURE_ADC
// configure ADC
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12));
ESP_ERROR_CHECK(adc1_config_channel_atten(adc_channel, atten));
#else // ADC2
// ESP_ERROR_CHECK(adc2_config_width(ADC_WIDTH_BIT_12));
ESP_ERROR_CHECK(adc2_config_channel_atten(adc_channel, atten));
#endif
// calibrate ADC
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(
unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_characs);
// show ADC characterization base
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
ESP_LOGI(TAG,
"ADC characterization based on Two Point values stored in eFuse");
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
ESP_LOGI(TAG,
"ADC characterization based on reference voltage stored in eFuse");
} else {
ESP_LOGI(TAG, "ADC characterization based on default reference voltage");
}
#endif
}
bool batt_sufficient() {
#if (defined HAS_PMU || defined BAT_MEASURE_ADC)
uint16_t volts = read_voltage();
return ((volts < 1000) ||
(volts > OTA_MIN_BATT)); // no battery or battery sufficient
#else
return true;
#endif
}
uint16_t read_voltage() {
uint16_t voltage = 0;
#ifdef HAS_PMU
if (!I2C_MUTEX_LOCK())
ESP_LOGW(TAG, "[%0.3f] i2c mutex lock failed", millis() / 1000.0);
else {
voltage = pmu.isVBUSPlug() ? 0xffff : pmu.getBattVoltage();
I2C_MUTEX_UNLOCK();
}
#else
#ifdef BAT_MEASURE_ADC
// multisample ADC
uint32_t adc_reading = 0;
int adc_buf = 0;
for (int i = 0; i < NO_OF_SAMPLES; i++) {
#ifndef BAT_MEASURE_ADC_UNIT // ADC1
adc_reading += adc1_get_raw(adc_channel);
#else // ADC2
ESP_ERROR_CHECK(adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf));
adc_reading += adc_buf;
#endif
}
adc_reading /= NO_OF_SAMPLES;
// Convert ADC reading to voltage in mV
voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_characs);
#endif // BAT_MEASURE_ADC
#ifdef BAT_VOLTAGE_DIVIDER
voltage *= BAT_VOLTAGE_DIVIDER;
#endif // BAT_VOLTAGE_DIVIDER
#endif // HAS_PMU
return voltage;
}
+72 -26
View File
@@ -19,32 +19,25 @@ void do_reset() {
void set_reset(uint8_t val[]) {
switch (val[0]) {
case 0: // restart device
sprintf(display_line6, "Reset pending");
do_reset();
break;
case 1: // reset MAC counter
ESP_LOGI(TAG, "Remote command: reset MAC counter");
reset_counters(); // clear macs
get_salt(); // get new salt
sprintf(display_line6, "Reset counter");
break;
case 2: // reset device to factory settings
ESP_LOGI(TAG, "Remote command: reset device to factory settings");
sprintf(display_line6, "Factory reset");
eraseConfig();
break;
case 3: // reset send queues
ESP_LOGI(TAG, "Remote command: flush send queue");
sprintf(display_line6, "Queue reset");
flushQueues();
break;
case 9: // reset and ask for software update via Wifi OTA
ESP_LOGI(TAG, "Remote command: software update via Wifi");
#if (USE_OTA)
sprintf(display_line6, "Software update");
cfg.runmode = 1;
#else
sprintf(display_line6, "Software update not implemented");
#endif // USE_OTA
break;
@@ -127,10 +120,34 @@ void set_gps(uint8_t val[]) {
if (val[0]) {
cfg.payloadmask |= (uint8_t)GPS_DATA; // set bit in mask
} else {
cfg.payloadmask &= ~(uint8_t)GPS_DATA; // clear bit in mask
cfg.payloadmask &= (uint8_t)~GPS_DATA; // clear bit in mask
}
}
void set_bme(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set BME mode to %s", val[0] ? "on" : "off");
if (val[0]) {
cfg.payloadmask |= (uint8_t)MEMS_DATA; // set bit in mask
} else {
cfg.payloadmask &= (uint8_t)~MEMS_DATA; // clear bit in mask
}
}
void set_batt(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set battery mode to %s",
val[0] ? "on" : "off");
if (val[0]) {
cfg.payloadmask |= (uint8_t)BATT_DATA; // set bit in mask
} else {
cfg.payloadmask &= (uint8_t)~BATT_DATA; // clear bit in mask
}
}
void set_payloadmask(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: set payload mask to %X", val[0]);
cfg.payloadmask = val[0];
}
void set_sensor(uint8_t val[]) {
#if (HAS_SENSORS)
switch (val[0]) { // check if valid sensor number 1...4
@@ -169,10 +186,22 @@ void set_monitor(uint8_t val[]) {
cfg.monitormode = val[0] ? 1 : 0;
}
void set_lorasf(uint8_t val[]) {
void set_loradr(uint8_t val[]) {
#if (HAS_LORA)
ESP_LOGI(TAG, "Remote command: set LoRa SF to %d", val[0]);
switch_lora(val[0], cfg.txpower);
if (validDR(val[0])) {
cfg.loradr = val[0];
ESP_LOGI(TAG, "Remote command: set LoRa Datarate to %d", cfg.loradr);
LMIC_setDrTxpow(assertDR(cfg.loradr), cfg.txpower);
ESP_LOGI(TAG, "Radio parameters now %s / %s / %s",
getSfName(updr2rps(LMIC.datarate)),
getBwName(updr2rps(LMIC.datarate)),
getCrName(updr2rps(LMIC.datarate)));
} else
ESP_LOGI(
TAG,
"Remote command: set LoRa Datarate called with illegal datarate %d",
val[0]);
#else
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
#endif // HAS_LORA
@@ -223,8 +252,16 @@ void set_rgblum(uint8_t val[]) {
void set_lorapower(uint8_t val[]) {
#if (HAS_LORA)
ESP_LOGI(TAG, "Remote command: set LoRa TXPOWER to %d", val[0]);
switch_lora(cfg.lorasf, val[0]);
// 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);
LMIC_setDrTxpow(assertDR(cfg.loradr), cfg.txpower);
} else
ESP_LOGI(
TAG,
"Remote command: set LoRa TXPOWER, not executed because ADR is on");
#else
ESP_LOGW(TAG, "Remote command: LoRa not implemented");
#endif // HAS_LORA
@@ -239,14 +276,10 @@ void get_config(uint8_t val[]) {
void get_status(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get device status");
#ifdef BAT_MEASURE_ADC
uint16_t voltage = read_voltage();
#else
uint16_t voltage = 0;
#endif
payload.reset();
payload.addStatus(voltage, uptime() / 1000, temperatureRead(), getFreeRAM(),
rtc_get_reset_reason(0), rtc_get_reset_reason(1));
payload.addStatus(read_voltage(), uptime() / 1000, temperatureRead(),
getFreeRAM(), rtc_get_reset_reason(0),
rtc_get_reset_reason(1));
SendPayload(STATUSPORT, prio_high);
};
@@ -274,6 +307,17 @@ void get_bme(uint8_t val[]) {
#endif
};
void get_batt(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get battery voltage");
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
payload.reset();
payload.addVoltage(read_voltage());
SendPayload(BATTPORT, prio_normal);
#else
ESP_LOGW(TAG, "Battery voltage not supported");
#endif
};
void get_time(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get time");
payload.reset();
@@ -297,26 +341,28 @@ void set_flush(uint8_t val[]) {
// format: opcode, function, #bytes params,
// flag (true = do make settings persistent / false = don't)
//
cmd_t table[] = {
static cmd_t table[] = {
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true},
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true},
{0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 1, true},
{0x05, set_loradr, 1, true}, {0x06, set_lorapower, 1, true},
{0x07, set_loraadr, 1, true}, {0x08, set_screensaver, 1, true},
{0x09, set_reset, 1, true}, {0x0a, set_sendcycle, 1, true},
{0x0b, set_wifichancycle, 1, true}, {0x0c, set_blescantime, 1, true},
{0x0d, set_vendorfilter, 1, false}, {0x0e, set_blescan, 1, true},
{0x0f, set_wifiant, 1, true}, {0x10, set_rgblum, 1, true},
{0x11, set_monitor, 1, true}, {0x12, set_beacon, 7, false},
{0x13, set_sensor, 2, true}, {0x80, get_config, 0, false},
{0x81, get_status, 0, false}, {0x84, get_gps, 0, false},
{0x13, set_sensor, 2, true}, {0x14, set_payloadmask, 1, true},
{0x15, set_bme, 1, true}, {0x16, set_batt, 1, true},
{0x80, get_config, 0, false}, {0x81, get_status, 0, false},
{0x83, get_batt, 0, false}, {0x84, get_gps, 0, false},
{0x85, get_bme, 0, false}, {0x86, get_time, 0, false},
{0x87, set_time, 0, false}, {0x99, set_flush, 0, false}};
const uint8_t cmdtablesize =
static const uint8_t cmdtablesize =
sizeof(table) / sizeof(table[0]); // number of commands in command table
// check and execute remote command
void rcommand(uint8_t cmd[], uint8_t cmdlength) {
void rcommand(const uint8_t cmd[], const uint8_t cmdlength) {
if (cmdlength == 0)
return;
+10 -7
View File
@@ -10,9 +10,12 @@ void sendcycle() {
// put data to send in RTos Queues used for transmit over channels Lora and SPI
void SendPayload(uint8_t port, sendprio_t prio) {
MessageBuffer_t SendBuffer; // contains MessageSize, MessagePort, Message[]
MessageBuffer_t
SendBuffer; // contains MessageSize, MessagePort, MessagePrio, Message[]
SendBuffer.MessageSize = payload.getSize();
SendBuffer.MessagePrio = prio;
switch (PAYLOAD_ENCODER) {
case 1: // plain -> no mapping
case 2: // packed -> no mapping
@@ -38,20 +41,20 @@ void SendPayload(uint8_t port, sendprio_t prio) {
default:
SendBuffer.MessagePort = port;
}
memcpy(SendBuffer.Message, payload.getBuffer(), payload.getSize());
memcpy(SendBuffer.Message, payload.getBuffer(), SendBuffer.MessageSize);
// enqueue message in device's send queues
#if (HAS_LORA)
lora_enqueuedata(&SendBuffer, prio);
lora_enqueuedata(&SendBuffer);
#endif
#ifdef HAS_SPI
spi_enqueuedata(&SendBuffer, prio);
spi_enqueuedata(&SendBuffer);
#endif
} // SendPayload
// interrupt triggered function to prepare payload to send
void sendCounter() {
void sendData() {
uint8_t bitmask = cfg.payloadmask;
uint8_t mask = 1;
@@ -115,7 +118,7 @@ void sendCounter() {
break;
#endif
#ifdef BAT_MEASURE_ADC
#if (defined BAT_MEASURE_ADC || defined HAS_PMU)
case BATT_DATA:
payload.reset();
payload.addVoltage(read_voltage());
@@ -128,7 +131,7 @@ void sendCounter() {
mask <<= 1;
} // while (bitmask)
} // sendCounter()
} // sendData()
void flushQueues() {
#if (HAS_LORA)
+4 -2
View File
@@ -56,7 +56,7 @@ void spi_slave_task(void *param) {
memset(txbuf, 0, sizeof(txbuf));
memset(rxbuf, 0, sizeof(rxbuf));
// wait until data to send arrivey
// fetch next or wait for payload to send from queue
if (xQueueReceive(SPISendQueue, &msg, portMAX_DELAY) != pdTRUE) {
ESP_LOGE(TAG, "Premature return from xQueueReceive() with no data!");
continue;
@@ -147,10 +147,12 @@ esp_err_t spi_init() {
return ret;
}
void spi_enqueuedata(MessageBuffer_t *message, sendprio_t prio) {
void spi_enqueuedata(MessageBuffer_t *message) {
// enqueue message in SPI send queue
BaseType_t ret;
MessageBuffer_t DummyBuffer;
sendprio_t prio = message->MessagePrio;
switch (prio) {
case prio_high:
// clear space in queue if full, then fallthrough to normal
+53 -1
View File
@@ -54,12 +54,64 @@ void calibrateTime(void) {
}
#endif
goto finish;
finish:
setMyTime(t, t_msec, timeSource); // set time
setMyTime((uint32_t)t, t_msec, timeSource); // set time
} // calibrateTime()
// adjust system time, calibrate RTC and RTC_INT pps
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
timesource_t mytimesource) {
// called with invalid timesource?
if (mytimesource == _unsynced)
return;
// increment t_sec only if t_msec > 1000
time_t time_to_set = (time_t)(t_sec + t_msec / 1000);
// do we have a valid time?
if (timeIsValid(time_to_set)) {
// if we have msec fraction, then wait until top of second with
// millisecond precision
if (t_msec % 1000) {
time_to_set++;
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000));
}
ESP_LOGD(TAG, "[%0.3f] UTC epoch time: %d.%03d sec", millis() / 1000.0,
time_to_set, t_msec % 1000);
// if we have got an external timesource, set RTC time and shift RTC_INT pulse
// to top of second
#ifdef HAS_RTC
if ((mytimesource == _gps) || (mytimesource == _lora))
set_rtctime(time_to_set);
#endif
// if we have a software pps timer, shift it to top of second
#if (!defined GPS_INT && !defined RTC_INT)
timerWrite(ppsIRQ, 0); // reset pps timer
CLOCKIRQ(); // fire clock pps, this advances time 1 sec
#endif
setTime(time_to_set); // set the time on top of second
timeSource = mytimesource; // set global variable
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync);
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was set | source: %c",
millis() / 1000.0, timeSetSymbols[timeSource]);
} else {
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync);
ESP_LOGI(TAG, "[%0.3f] Timesync failed, invalid time fetched | source: %c",
millis() / 1000.0, timeSetSymbols[timeSource]);
}
}
// helper function to setup a pulse per second for time synchronisation
uint8_t timepulse_init() {
+2 -88
View File
@@ -108,7 +108,7 @@ void process_timesync_req(void *taskparameter) {
payload.addByte(0x99);
SendPayload(RCMDPORT, prio_high);
// ...send a alive open a receive window for last time_sync_answer
// LMIC_sendAlive();
LMIC_sendAlive();
}
} // end of for loop to collect timestamp samples
@@ -181,7 +181,7 @@ int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) {
// the 5th byte contains the fractional seconds in 2^-8 second steps
// (= 1/250th sec), we convert this to ms
uint16_t timestamp_msec = 4 * buf[4];
// pointers to 4 bytes 4 bytes containing UTC seconds since unix epoch, msb
// pointers to 4 bytes containing UTC seconds since unix epoch, msb
uint32_t timestamp_sec, *timestamp_ptr;
// convert buffer to uint32_t, octet order is big endian
@@ -210,92 +210,6 @@ int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) {
}
}
// adjust system time, calibrate RTC and RTC_INT pps
void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec,
timesource_t mytimesource) {
t_sec ++;
time_t time_to_set = (time_t)(t_sec);
// increment t_sec only if t_msec > 1000
time_to_set = (time_t)(t_sec + t_msec / 1000);
// do we have a valid time?
if (timeIsValid(time_to_set)) {
// if we have msec fraction, then wait until top of second with
// millisecond precision
if (t_msec % 1000) {
time_to_set++;
vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000));
}
ESP_LOGD(TAG, "[%0.3f] UTC epoch time: %d.%03d sec", millis() / 1000.0,
time_to_set, t_msec % 1000);
// if we have got an external timesource, set RTC time and shift RTC_INT pulse
// to top of second
#ifdef HAS_RTC
if ((mytimesource == _gps) || (mytimesource == _lora))
set_rtctime(time_to_set);
#endif
// if we have a software pps timer, shift it to top of second
#if (!defined GPS_INT && !defined RTC_INT)
timerWrite(ppsIRQ, 0); // reset pps timer
CLOCKIRQ(); // fire clock pps, this advances time 1 sec
#endif
struct timeval tv;
struct timezone tz;
if(gettimeofday(&tv, &tz) != 0) {
ESP_LOGE(TAG, "ERROR gettimeofday");
}
struct timeval beforeTime = tv;
struct timeval nowTime;
nowTime.tv_sec = t_sec;
nowTime.tv_usec = t_msec;
if(settimeofday(&nowTime, &tz) != 0) {
ESP_LOGE(TAG, "ERROR settimeofday");
}
struct timeval diff;
diff.tv_sec = nowTime.tv_sec-beforeTime.tv_sec;
diff.tv_usec = nowTime.tv_usec-beforeTime.tv_usec;
// sum up diff_s and diff_ms to one ms value
int32_t diff_s = diff.tv_sec;
int32_t diff_ms = diff.tv_usec/1000;
int32_t diff_ms_remain = diff_ms / 1000;
diff_s += diff_ms_remain;
diff_ms += -1000*diff_ms_remain;
if(diff_ms < 0) {
diff_s --;
diff_ms += 1000;
}
// cap diff at 24h (= 86,400s)
diff_s = diff_s % 86400;
int32_t timediff_ms = diff_s * 1000 + diff_ms;
// send diffTime
payload.reset();
payload.addTimeDiff(timediff_ms);
SendPayload(TIMEDIFFPORT, prio_high);
ESP_LOGI(TAG, "timediff_ms: %d", timediff_ms);
timeSource = mytimesource; // set global variable
timesyncer.attach(TIME_SYNC_INTERVAL * 60, timeSync);
time_uart_send_start();
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was set | source: %c",
millis() / 1000.0, timeSetSymbols[timeSource]);
} else {
timesyncer.attach(TIME_SYNC_INTERVAL_RETRY * 60, timeSync);
ESP_LOGI(TAG, "[%0.3f] Timesync failed, invalid time fetched | source: %c",
millis() / 1000.0, timeSetSymbols[timeSource]);
}
}
// create task for timeserver handshake processing, called from main.cpp
void timesync_init() {
xTaskCreatePinnedToCore(process_timesync_req, // task function
+4 -4
View File
@@ -57,7 +57,7 @@ void wifi_sniffer_init(void) {
wificfg.nvs_enable = 0; // we don't need any wifi settings from NVRAM
wificfg.wifi_task_core_id = 0; // we want wifi task running on core 0
//wifi_promiscuous_filter_t filter = {
// wifi_promiscuous_filter_t filter = {
// .filter_mask = WIFI_PROMIS_FILTER_MASK_MGMT}; // only MGMT frames
// .filter_mask = WIFI_PROMIS_FILTER_MASK_ALL}; // we use all frames
@@ -71,9 +71,9 @@ void wifi_sniffer_init(void) {
ESP_ERROR_CHECK(
esp_wifi_set_storage(WIFI_STORAGE_RAM)); // we don't need NVRAM
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
ESP_ERROR_CHECK(esp_wifi_stop());
ESP_ERROR_CHECK(
esp_wifi_set_promiscuous_filter(&filter)); // set frame filter
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); // no modem power saving
ESP_ERROR_CHECK(esp_wifi_start()); // must be started to be able to switch ch
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_filter(&filter)); // set frame filter
ESP_ERROR_CHECK(esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler));
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true)); // now switch on monitor mode