Merge pull request #131 from cyberman54/development

v1.4.2
This commit is contained in:
Verkehrsrot 2018-08-05 17:50:32 +02:00 committed by GitHub
commit 97b092d12a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 269 additions and 135 deletions

View File

@ -6,6 +6,7 @@
<img src="img/Paxcounter-title.jpg"> <img src="img/Paxcounter-title.jpg">
<img src="img/Paxcounter-ttgo.jpg"> <img src="img/Paxcounter-ttgo.jpg">
<img src="img/Paxcounter-lolin.gif"> <img src="img/Paxcounter-lolin.gif">
<img src="img/Paxcounter-Screen.png">
# Use case # Use case
@ -137,9 +138,10 @@ Hereafter described is the default *plain* format, which uses MSB bit numbering.
**Port #2:** Device status query result **Port #2:** Device status query result
byte 1-2: Battery or USB Voltage [mV], 0 if unreadable byte 1-2: Battery or USB Voltage [mV], 0 if no battery probe
byte 3-10: Uptime [seconds] byte 3-10: Uptime [seconds]
bytes 11-14: CPU temperature [°C] bytes 11-14: CPU temperature [°C]
bytes 15-18: Free RAM [bytes]
**Port #3:** Device configuration query result **Port #3:** Device configuration query result
@ -225,9 +227,9 @@ function Converter(decoded, port) {
# Remote control # Remote control
The device listenes for remote control commands on LoRaWAN Port 2. The device listenes for remote control commands on LoRaWAN Port 2. Multiple commands per downlink are possible by concatenating them.
Note: all settings are stored in NVRAM and will be reloaded when device starts. To reset device to factory settings press button (if device has one), or send remote command 09 02 09 00 unconfirmed(!) once. Note: all settings are stored in NVRAM and will be reloaded when device starts. To reset device to factory settings send remote command 09 02 09 00 unconfirmed(!) once.
0x01 set scan RSSI limit 0x01 set scan RSSI limit
@ -275,6 +277,7 @@ Note: all settings are stored in NVRAM and will be reloaded when device starts.
0 = restart device 0 = restart device
1 = reset MAC counter to zero 1 = reset MAC counter to zero
2 = reset device to factory settings 2 = reset device to factory settings
3 = flush send queues
0x0A set LoRaWAN payload send cycle 0x0A set LoRaWAN payload send cycle

BIN
img/Paxcounter-Screen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -11,8 +11,8 @@
; ---> SELECT TARGET PLATFORM HERE! <--- ; ---> SELECT TARGET PLATFORM HERE! <---
[platformio] [platformio]
;env_default = test
env_default = generic env_default = generic
;env_default = ebox
;env_default = heltec ;env_default = heltec
;env_default = ttgov1 ;env_default = ttgov1
;env_default = ttgov2 ;env_default = ttgov2
@ -40,9 +40,8 @@ lib_deps_gps =
TinyGPSPlus@>=1.0.2 TinyGPSPlus@>=1.0.2
Time@>=1.5 Time@>=1.5
build_flags = build_flags =
; we need build_flag for logging, otherwise we can't use ESP_LOGx in arduino framework
; ---> NOTE: For production run set DEBUG_LEVEL level to NONE! <--- ; ---> NOTE: For production run set DEBUG_LEVEL level to NONE! <---
; otherwise device may crash in dense environments due to serial buffer overflow ; otherwise device may leak RAM
; ;
; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE ; -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE
-DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_INFO
@ -55,10 +54,10 @@ build_flags =
-include "src/hal/${PIOENV}.h" -include "src/hal/${PIOENV}.h"
-w -w
[env:test] [env:ebox]
platform = ${common_env_data.platform_espressif32} platform = ${common_env_data.platform_espressif32}
framework = arduino framework = arduino
board = heltec_wifi_lora_32 board = esp32dev
board_build.partitions = ${common_env_data.board_build.partitions} board_build.partitions = ${common_env_data.board_build.partitions}
upload_speed = 115200 upload_speed = 115200
monitor_speed = 115200 monitor_speed = 115200

View File

@ -18,7 +18,7 @@ function Decoder(bytes, port) {
if (port === 2) { if (port === 2) {
// device status data // device status data
return decode(bytes, [uint16, uptime, temperature], ['voltage', 'uptime', 'cputemp']); return decode(bytes, [uint16, uptime, temperature, uint32], ['voltage', 'uptime', 'cputemp', 'memory']);
} }
@ -56,17 +56,9 @@ var bytesToInt = function (bytes) {
return i; return i;
}; };
var uptime = function (bytes) {
if (bytes.length !== uptime.BYTES) {
throw new Error('uptime must have exactly 8 bytes');
}
return bytesToInt(bytes);
};
uptime.BYTES = 4;
var uint8 = function (bytes) { var uint8 = function (bytes) {
if (bytes.length !== uint8.BYTES) { if (bytes.length !== uint8.BYTES) {
throw new Error('int must have exactly 1 byte'); throw new Error('uint8 must have exactly 1 byte');
} }
return bytesToInt(bytes); return bytesToInt(bytes);
}; };
@ -74,12 +66,20 @@ uint8.BYTES = 1;
var uint16 = function (bytes) { var uint16 = function (bytes) {
if (bytes.length !== uint16.BYTES) { if (bytes.length !== uint16.BYTES) {
throw new Error('int must have exactly 2 bytes'); throw new Error('uint16 must have exactly 2 bytes');
} }
return bytesToInt(bytes); return bytesToInt(bytes);
}; };
uint16.BYTES = 2; uint16.BYTES = 2;
var uint32 = function (bytes) {
if (bytes.length !== uint32.BYTES) {
throw new Error('uint32 must have exactly 4 bytes');
}
return bytesToInt(bytes);
};
uint32.BYTES = 4;
var latLng = function (bytes) { var latLng = function (bytes) {
if (bytes.length !== latLng.BYTES) { if (bytes.length !== latLng.BYTES) {
throw new Error('Lat/Long must have exactly 4 bytes'); throw new Error('Lat/Long must have exactly 4 bytes');
@ -88,6 +88,14 @@ var latLng = function (bytes) {
}; };
latLng.BYTES = 4; latLng.BYTES = 4;
var uptime = function (bytes) {
if (bytes.length !== uptime.BYTES) {
throw new Error('Uptime must have exactly 8 bytes');
}
return bytesToInt(bytes);
};
uptime.BYTES = 8;
var hdop = function (bytes) { var hdop = function (bytes) {
if (bytes.length !== hdop.BYTES) { if (bytes.length !== hdop.BYTES) {
throw new Error('hdop must have exactly 2 bytes'); throw new Error('hdop must have exactly 2 bytes');
@ -169,9 +177,10 @@ var decode = function (bytes, mask, names) {
if (typeof module === 'object' && typeof module.exports !== 'undefined') { if (typeof module === 'object' && typeof module.exports !== 'undefined') {
module.exports = { module.exports = {
uptime: uptime,
uint8: uint8, uint8: uint8,
uint16: uint16, uint16: uint16,
uint32: uint32,
uptime: uptime,
temperature: temperature, temperature: temperature,
humidity: humidity, humidity: humidity,
latLng: latLng, latLng: latLng,

View File

@ -16,7 +16,7 @@ void readButton() {
ESP_LOGI(TAG, "Button pressed"); ESP_LOGI(TAG, "Button pressed");
payload.reset(); payload.reset();
payload.addButton(0x01); payload.addButton(0x01);
senddata(BUTTONPORT); SendData(BUTTONPORT);
} }
} }
#endif #endif

View File

@ -33,13 +33,16 @@ void doHomework() {
// check free memory // check free memory
if (esp_get_minimum_free_heap_size() <= MEM_LOW) { if (esp_get_minimum_free_heap_size() <= MEM_LOW) {
ESP_LOGW(TAG, ESP_LOGI(TAG,
"Memory full, counter cleared (heap low water mark = %d Bytes / " "Memory full, counter cleared (heap low water mark = %d Bytes / "
"free heap = %d bytes)", "free heap = %d bytes)",
esp_get_minimum_free_heap_size(), ESP.getFreeHeap()); esp_get_minimum_free_heap_size(), ESP.getFreeHeap());
senddata(COUNTERPORT); // send data before clearing counters SendData(COUNTERPORT); // send data before clearing counters
reset_counters(); // clear macs container and reset all counters reset_counters(); // clear macs container and reset all counters
reset_salt(); // get new salt for salting hashes reset_salt(); // get new salt for salting hashes
if (esp_get_minimum_free_heap_size() <= MEM_LOW) // check again
esp_restart(); // memory leak, reset device
} }
} // doHomework() } // doHomework()

View File

@ -97,12 +97,14 @@ void refreshtheDisplay() {
u8x8.setPowerSave(!cfg.screenon); u8x8.setPowerSave(!cfg.screenon);
} }
// if display is switched off we don't need to refresh it and save time // if display is switched off we don't refresh it and save time
if (!DisplayState) if (!DisplayState)
return; return;
uint8_t msgWaiting = 0;
char buff[16]; // 16 chars line buffer
// update counter (lines 0-1) // update counter (lines 0-1)
char buff[16];
snprintf( snprintf(
buff, sizeof(buff), "PAX:%-4d", buff, sizeof(buff), "PAX:%-4d",
(int)macs.size()); // convert 16-bit MAC counter to decimal counter value (int)macs.size()); // convert 16-bit MAC counter to decimal counter value
@ -166,8 +168,21 @@ void refreshtheDisplay() {
// update LMiC event display (line 7) // update LMiC event display (line 7)
u8x8.setCursor(0, 7); u8x8.setCursor(0, 7);
u8x8.printf("%-16s", display_line7); u8x8.printf("%-14s", display_line7);
// update LoRa send queue display (line 7)
msgWaiting = uxQueueMessagesWaiting(LoraSendQueue);
if (msgWaiting) {
sprintf(buff, "%2d", msgWaiting);
u8x8.setCursor(14, 7);
u8x8.setInverseFont(1);
u8x8.printf("%-2s", msgWaiting == SEND_QUEUE_SIZE ? "<>" : buff);
u8x8.setInverseFont(0);
} else
u8x8.printf(" ");
#endif // HAS_LORA #endif // HAS_LORA
} // refreshDisplay() } // refreshDisplay()
void IRAM_ATTR DisplayIRQ() { void IRAM_ATTR DisplayIRQ() {

View File

@ -8,7 +8,7 @@
#include <esp32-hal-log.h> #include <esp32-hal-log.h>
// attn: increment version after modifications to configData_t truct! // attn: increment version after modifications to configData_t truct!
#define PROGVERSION "1.4.1" // use max 10 chars here! #define PROGVERSION "1.4.2" // use max 10 chars here!
#define PROGNAME "PAXCNT" #define PROGNAME "PAXCNT"
// std::set for unified array functions // std::set for unified array functions
@ -47,6 +47,7 @@ extern hw_timer_t *channelSwitch, *sendCycle;
extern portMUX_TYPE timerMux; extern portMUX_TYPE timerMux;
extern volatile int SendCycleTimerIRQ, HomeCycleIRQ, DisplayTimerIRQ, extern volatile int SendCycleTimerIRQ, HomeCycleIRQ, DisplayTimerIRQ,
ChannelTimerIRQ, ButtonPressedIRQ; ChannelTimerIRQ, ButtonPressedIRQ;
extern QueueHandle_t LoraSendQueue, SPISendQueue;
extern std::array<uint64_t, 0xff>::iterator it; extern std::array<uint64_t, 0xff>::iterator it;
extern std::array<uint64_t, 0xff> beacons; extern std::array<uint64_t, 0xff> beacons;

View File

@ -1,4 +1,4 @@
// Hardware related definitions for ebox ESP32-bit with RFM95 LoRa // Hardware related definitions for ebox ESP32-bit with external connected RFM95 LoRa
#define HAS_LORA 1 // comment out if device shall not send data via LoRa #define HAS_LORA 1 // comment out if device shall not send data via LoRa
#define HAS_SPI 1 // comment out if device shall not send data via SPI #define HAS_SPI 1 // comment out if device shall not send data via SPI

View File

@ -108,7 +108,7 @@ void get_hard_deveui(uint8_t *pdeveui) {
#ifdef VERBOSE #ifdef VERBOSE
// Display OTAA keys // Display OTAA keys
void printKeys(void) { void showLoraKeys(void) {
// LMIC may not have used callback to fill // LMIC may not have used callback to fill
// all EUI buffer so we do it here to a temp // all EUI buffer so we do it here to a temp
// buffer to be able to display them // buffer to be able to display them
@ -228,4 +228,51 @@ void lorawan_loop(void *pvParameters) {
} }
} }
// 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_eu868)
LMIC_setDrTxpow(DR_SF11, tx);
cfg.lorasf = sf;
break;
#elif defined(CFG_us915)
LMIC_setDrTxpow(DR_SF11CR, tx);
cfg.lorasf = sf;
break;
#endif
case 12:
#if defined(CFG_eu868)
LMIC_setDrTxpow(DR_SF12, tx);
cfg.lorasf = sf;
break;
#elif defined(CFG_us915)
LMIC_setDrTxpow(DR_SF12CR, tx);
cfg.lorasf = sf;
break;
#endif
default:
break;
}
}
#endif // HAS_LORA #endif // HAS_LORA

View File

@ -13,7 +13,8 @@ void get_hard_deveui(uint8_t *pdeveui);
void os_getDevKey(u1_t *buf); void os_getDevKey(u1_t *buf);
void os_getArtEui(u1_t *buf); void os_getArtEui(u1_t *buf);
void os_getDevEui(u1_t *buf); void os_getDevEui(u1_t *buf);
void printKeys(void); void showLoraKeys(void);
void lorawan_loop(void *pvParameters); void lorawan_loop(void *pvParameters);
void switch_lora(uint8_t sf, uint8_t tx);
#endif #endif

View File

@ -46,10 +46,12 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
char buff[16]; // temporary buffer for printf char buff[16]; // temporary buffer for printf
bool added = false; bool added = false;
int8_t beaconID; // beacon number in test monitor mode int8_t beaconID; // beacon number in test monitor mode
uint16_t hashedmac; // temporary buffer for generated hash value uint16_t hashedmac; // temporary buffer for generated hash value
uint32_t addr2int, uint32_t addr2int; // temporary buffer for shortened MAC
vendor2int; // temporary buffer for shortened MAC and Vendor OUI #ifdef VENDORFILTER
uint32_t vendor2int; // temporary buffer for Vendor OUI
#endif
// only last 3 MAC Address bytes are used for MAC address anonymization // only last 3 MAC Address bytes are used for MAC address anonymization
// but since it's uint32 we take 4 bytes to avoid 1st value to be 0 // but since it's uint32 we take 4 bytes to avoid 1st value to be 0
@ -100,13 +102,13 @@ bool mac_add(uint8_t *paddr, int8_t rssi, bool sniff_type) {
if (cfg.monitormode) { if (cfg.monitormode) {
beaconID = isBeacon(macConvert(paddr)); beaconID = isBeacon(macConvert(paddr));
if (beaconID >= 0) { if (beaconID >= 0) {
ESP_LOGI(TAG, "Beacon ID#d detected", beaconID); ESP_LOGI(TAG, "Beacon ID#%d detected", beaconID);
#if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED) #if (HAS_LED != NOT_A_PIN) || defined(HAS_RGB_LED)
blink_LED(COLOR_WHITE, 2000); blink_LED(COLOR_WHITE, 2000);
#endif #endif
payload.reset(); payload.reset();
payload.addAlarm(rssi, beaconID); payload.addAlarm(rssi, beaconID);
senddata(BEACONPORT); SendData(BEACONPORT);
} }
}; };

View File

@ -41,6 +41,9 @@ hw_timer_t *channelSwitch = NULL, *displaytimer = NULL, *sendCycle = NULL,
volatile int ButtonPressedIRQ = 0, ChannelTimerIRQ = 0, SendCycleTimerIRQ = 0, volatile int ButtonPressedIRQ = 0, ChannelTimerIRQ = 0, SendCycleTimerIRQ = 0,
DisplayTimerIRQ = 0, HomeCycleIRQ = 0; DisplayTimerIRQ = 0, HomeCycleIRQ = 0;
// RTos send queues for payload transmit
QueueHandle_t LoraSendQueue, SPISendQueue;
portMUX_TYPE timerMux = portMUX_TYPE timerMux =
portMUX_INITIALIZER_UNLOCKED; // sync main loop and ISR when modifying IRQ portMUX_INITIALIZER_UNLOCKED; // sync main loop and ISR when modifying IRQ
// handler shared variables // handler shared variables
@ -104,6 +107,30 @@ void setup() {
#endif // verbose #endif // verbose
// initialize send queues for transmit channels
#ifdef HAS_LORA
//--> LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer
//*));
LoraSendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (LoraSendQueue == 0) {
ESP_LOGE(TAG, "Could not create LORA send queue. Aborting.");
exit(0);
} else
ESP_LOGI(TAG, "LORA send queue created, size %d Bytes",
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
#endif
#ifdef HAS_SPI
//--> SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(struct SendBuffer
//*));
SPISendQueue = xQueueCreate(SEND_QUEUE_SIZE, sizeof(MessageBuffer_t));
if (SPISendQueue == 0) {
ESP_LOGE(TAG, "Could not create SPI send queue. Aborting.");
exit(0);
} else
ESP_LOGI(TAG, "SPI send queue created, size %d Bytes",
SEND_QUEUE_SIZE * PAYLOAD_BUFFER_SIZE);
#endif
// read settings from NVRAM // read settings from NVRAM
loadConfig(); // includes initialize if necessary loadConfig(); // includes initialize if necessary
@ -223,7 +250,7 @@ void setup() {
#ifdef HAS_LORA #ifdef HAS_LORA
// output LoRaWAN keys to console // output LoRaWAN keys to console
#ifdef VERBOSE #ifdef VERBOSE
printKeys(); showLoraKeys();
#endif #endif
// initialize LoRaWAN LMIC run-time environment // initialize LoRaWAN LMIC run-time environment
@ -299,7 +326,9 @@ void loop() {
// check housekeeping cycle and if expired do homework // check housekeeping cycle and if expired do homework
checkHousekeeping(); checkHousekeeping();
// check send cycle and send payload if cycle is expired // check send queue and process it
processSendBuffer();
// check send cycle and enqueue payload if cycle is expired
sendPayload(); sendPayload();
// reset watchdog // reset watchdog
vTaskDelay(1 / portTICK_PERIOD_MS); vTaskDelay(1 / portTICK_PERIOD_MS);

View File

@ -45,6 +45,7 @@
#define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit #define PAYLOAD_BUFFER_SIZE 51 // maximum size of payload block per transmit
#define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs #define LORASFDEFAULT 9 // 7 ... 12 SF, according to LoRaWAN specs
#define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy #define MAXLORARETRY 500 // maximum count of TX retries if LoRa busy
#define SEND_QUEUE_SIZE 10 // maximum number of messages in payload send queue
// Ports on which the device sends and listenes on LoRaWAN and SPI // Ports on which the device sends and listenes on LoRaWAN and SPI
#define COUNTERPORT 1 // Port on which device sends counts #define COUNTERPORT 1 // Port on which device sends counts

View File

@ -53,7 +53,7 @@ void PayloadConvert::addConfig(configData_t value) {
} }
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime,
float cputemp) { float cputemp, uint32_t mem) {
uint32_t temp = (uint32_t)cputemp; uint32_t temp = (uint32_t)cputemp;
buffer[cursor++] = highByte(voltage); buffer[cursor++] = highByte(voltage);
buffer[cursor++] = lowByte(voltage); buffer[cursor++] = lowByte(voltage);
@ -69,6 +69,10 @@ void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime,
buffer[cursor++] = (byte)((temp & 0x00FF0000) >> 16); buffer[cursor++] = (byte)((temp & 0x00FF0000) >> 16);
buffer[cursor++] = (byte)((temp & 0x0000FF00) >> 8); buffer[cursor++] = (byte)((temp & 0x0000FF00) >> 8);
buffer[cursor++] = (byte)((temp & 0x000000FF)); buffer[cursor++] = (byte)((temp & 0x000000FF));
buffer[cursor++] = (byte)((mem & 0xFF000000) >> 24);
buffer[cursor++] = (byte)((mem & 0x00FF0000) >> 16);
buffer[cursor++] = (byte)((mem & 0x0000FF00) >> 8);
buffer[cursor++] = (byte)((mem & 0x000000FF));
} }
#ifdef HAS_GPS #ifdef HAS_GPS
@ -124,10 +128,11 @@ void PayloadConvert::addConfig(configData_t value) {
} }
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime,
float cputemp) { float cputemp, uint32_t mem) {
writeUint16(voltage); writeUint16(voltage);
writeUptime(uptime); writeUptime(uptime);
writeTemperature(cputemp); writeTemperature(cputemp);
writeUint32(mem);
} }
#ifdef HAS_GPS #ifdef HAS_GPS
@ -159,6 +164,8 @@ void PayloadConvert::writeLatLng(double latitude, double longitude) {
intToBytes(cursor, longitude, 4); intToBytes(cursor, longitude, 4);
} }
void PayloadConvert::writeUint32(uint32_t i) { intToBytes(cursor, i, 4); }
void PayloadConvert::writeUint16(uint16_t i) { intToBytes(cursor, i, 2); } void PayloadConvert::writeUint16(uint16_t i) { intToBytes(cursor, i, 2); }
void PayloadConvert::writeUint8(uint8_t i) { intToBytes(cursor, i, 1); } void PayloadConvert::writeUint8(uint8_t i) { intToBytes(cursor, i, 1); }
@ -239,7 +246,7 @@ void PayloadConvert::addConfig(configData_t value) {
} }
void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime, void PayloadConvert::addStatus(uint16_t voltage, uint64_t uptime,
float celsius) { float celsius, uint32_t mem) {
uint16_t temp = celsius * 10; uint16_t temp = celsius * 10;
uint16_t volt = voltage / 10; uint16_t volt = voltage / 10;
#ifdef HAS_BATTERY_PROBE #ifdef HAS_BATTERY_PROBE

View File

@ -35,7 +35,7 @@ public:
uint8_t *getBuffer(void); uint8_t *getBuffer(void);
void addCount(uint16_t value1, uint16_t value2); void addCount(uint16_t value1, uint16_t value2);
void addConfig(configData_t value); void addConfig(configData_t value);
void addStatus(uint16_t voltage, uint64_t uptime, float cputemp); void addStatus(uint16_t voltage, uint64_t uptime, float cputemp, uint32_t mem);
void addAlarm(int8_t rssi, uint8_t message); void addAlarm(int8_t rssi, uint8_t message);
#ifdef HAS_GPS #ifdef HAS_GPS
void addGPS(gpsStatus_t value); void addGPS(gpsStatus_t value);
@ -59,6 +59,7 @@ private:
void intToBytes(uint8_t pos, int32_t i, uint8_t byteSize); void intToBytes(uint8_t pos, int32_t i, uint8_t byteSize);
void writeUptime(uint64_t unixtime); void writeUptime(uint64_t unixtime);
void writeLatLng(double latitude, double longitude); void writeLatLng(double latitude, double longitude);
void writeUint32(uint32_t i);
void writeUint16(uint16_t i); void writeUint16(uint16_t i);
void writeUint8(uint8_t i); void writeUint8(uint8_t i);
void writeHumidity(float humidity); void writeHumidity(float humidity);

View File

@ -1,6 +1,3 @@
// remote command interpreter, parses and executes commands with arguments in
// array
// Basic Config // Basic Config
#include "globals.h" #include "globals.h"
#include "rcommand.h" #include "rcommand.h"
@ -8,55 +5,6 @@
// Local logging tag // Local logging tag
static const char TAG[] = "main"; static const char TAG[] = "main";
#ifdef HAS_LORA
// 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_eu868)
LMIC_setDrTxpow(DR_SF11, tx);
cfg.lorasf = sf;
break;
#elif defined(CFG_us915)
LMIC_setDrTxpow(DR_SF11CR, tx);
cfg.lorasf = sf;
break;
#endif
case 12:
#if defined(CFG_eu868)
LMIC_setDrTxpow(DR_SF12, tx);
cfg.lorasf = sf;
break;
#elif defined(CFG_us915)
LMIC_setDrTxpow(DR_SF12CR, tx);
cfg.lorasf = sf;
break;
#endif
default:
break;
}
}
#endif // HAS_LORA
// set of functions that can be triggered by remote commands // set of functions that can be triggered by remote commands
void set_reset(uint8_t val[]) { void set_reset(uint8_t val[]) {
switch (val[0]) { switch (val[0]) {
@ -78,6 +26,11 @@ void set_reset(uint8_t val[]) {
sprintf(display_line6, "Factory reset"); sprintf(display_line6, "Factory reset");
eraseConfig(); eraseConfig();
break; break;
case 3: // reset send queues
ESP_LOGI(TAG, "Remote command: flush send queue");
sprintf(display_line6, "Queue reset");
flushQueues();
break;
default: default:
ESP_LOGW(TAG, "Remote command: reset called with invalid parameter(s)"); ESP_LOGW(TAG, "Remote command: reset called with invalid parameter(s)");
} }
@ -293,7 +246,7 @@ void get_config(uint8_t val[]) {
ESP_LOGI(TAG, "Remote command: get device configuration"); ESP_LOGI(TAG, "Remote command: get device configuration");
payload.reset(); payload.reset();
payload.addConfig(cfg); payload.addConfig(cfg);
senddata(CONFIGPORT); SendData(CONFIGPORT);
}; };
void get_status(uint8_t val[]) { void get_status(uint8_t val[]) {
@ -304,8 +257,9 @@ void get_status(uint8_t val[]) {
uint16_t voltage = 0; uint16_t voltage = 0;
#endif #endif
payload.reset(); payload.reset();
payload.addStatus(voltage, uptime() / 1000, temperatureRead()); payload.addStatus(voltage, uptime() / 1000, temperatureRead(),
senddata(STATUSPORT); ESP.getFreeHeap());
SendData(STATUSPORT);
}; };
void get_gps(uint8_t val[]) { void get_gps(uint8_t val[]) {
@ -314,7 +268,7 @@ void get_gps(uint8_t val[]) {
gps_read(); gps_read();
payload.reset(); payload.reset();
payload.addGPS(gps_status); payload.addGPS(gps_status);
senddata(GPSPORT); SendData(GPSPORT);
#else #else
ESP_LOGW(TAG, "GPS function not supported"); ESP_LOGW(TAG, "GPS function not supported");
#endif #endif
@ -337,27 +291,43 @@ cmd_t table[] = {
{0x80, get_config, 0, false}, {0x81, get_status, 0, false}, {0x80, get_config, 0, false}, {0x81, get_status, 0, false},
{0x84, get_gps, 0, false}}; {0x84, get_gps, 0, false}};
const uint8_t cmdtablesize =
sizeof(table) / sizeof(table[0]); // number of commands in command table
// check and execute remote command // check and execute remote command
void rcommand(uint8_t cmd[], uint8_t cmdlength) { void rcommand(uint8_t cmd[], uint8_t cmdlength) {
if (cmdlength == 0) if (cmdlength == 0)
return; return;
else
cmdlength--; // minus 1 byte for opcode
int i = uint8_t foundcmd[cmdlength], cursor = 0;
sizeof(table) / sizeof(table[0]); // number of commands in command table bool storeflag = false;
while (i--) { while (cursor < cmdlength) {
if ((cmd[0] == table[i].opcode) &&
(table[i].params == cmdlength)) { // lookup command in opcode table
memmove(cmd, cmd + 1,
cmdlength); // strip opcode
table[i].func(cmd); // execute assigned function with given parameters
if (table[i].store) // ceck if function needs to store configuration
saveConfig();
break; // exit while loop, command was found
} // lookup command
} // while
int i = cmdtablesize;
while (i--) {
if (cmd[cursor] == table[i].opcode) { // lookup command in opcode table
cursor++; // strip 1 byte opcode
if ((cursor + table[i].params) <= cmdlength) {
memmove(foundcmd, cmd + cursor,
table[i].params); // strip opcode from cmd array
cursor += table[i].params;
if (table[i].store) // ceck if function needs to store configuration
storeflag = true;
table[i].func(
foundcmd); // execute assigned function with given parameters
} else
ESP_LOGI(
TAG,
"Remote command x%02X called with missing parameter(s), skipped",
table[i].opcode);
break; // exit table lookup loop, command was found
} // command validation
} // command table lookup loop
} // command parsing loop
if (storeflag)
saveConfig();
} // rcommand() } // rcommand()

View File

@ -1,28 +1,27 @@
// Basic Config // Basic Config
#include "globals.h" #include "globals.h"
void senddata(uint8_t port) { // put data to send in RTos Queues used for transmit over channels Lora and SPI
void SendData(uint8_t port) {
MessageBuffer_t MySendBuffer;
MySendBuffer.MessageSize = payload.getSize();
MySendBuffer.MessagePort = PAYLOAD_ENCODER <= 2
? port
: (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT);
memcpy(MySendBuffer.Message, payload.getBuffer(), payload.getSize());
// enqueue message in LoRa send queue
#ifdef HAS_LORA #ifdef HAS_LORA
// Check if there is a pending TX/RX job running if (xQueueSendToBack(LoraSendQueue, (void *)&MySendBuffer, (TickType_t)0))
if (LMIC.opmode & OP_TXRXPEND) { ESP_LOGI(TAG, "%d bytes enqueued to send on LoRa", payload.getSize());
ESP_LOGI(TAG, "LoRa busy, data not sent");
sprintf(display_line7, "LORA BUSY");
// to be done: add queuing here, e.g. use RTos xQueueSend
} else {
LMIC_setTxData2(
PAYLOAD_ENCODER <= 2 ? port
: (PAYLOAD_ENCODER == 4 ? LPP2PORT : LPP1PORT),
payload.getBuffer(), payload.getSize(), (cfg.countermode & 0x02));
ESP_LOGI(TAG, "%d bytes queued to send on LoRa", payload.getSize());
sprintf(display_line7, "PACKET QUEUED");
}
#endif #endif
// enqueue message in SPI send queue
#ifdef HAS_SPI #ifdef HAS_SPI
// to come here: code for sending payload to a local master via SPI if (xQueueSendToBack(SPISendQueue, (void *)&MySendBuffer, (TickType_t)0))
ESP_LOGI(TAG, "%d bytes sent on SPI", payload.getSize()); ESP_LOGI(TAG, "%d bytes enqueued to send on SPI", payload.getSize());
#endif #endif
// clear counter if not in cumulative counter mode // clear counter if not in cumulative counter mode
@ -32,10 +31,10 @@ void senddata(uint8_t port) {
ESP_LOGI(TAG, "Counter cleared"); ESP_LOGI(TAG, "Counter cleared");
} }
} // senddata } // SendData
// cyclic called function to prepare payload to send
void sendPayload() { void sendPayload() {
if (SendCycleTimerIRQ) { if (SendCycleTimerIRQ) {
portENTER_CRITICAL(&timerMux); portENTER_CRITICAL(&timerMux);
SendCycleTimerIRQ = 0; SendCycleTimerIRQ = 0;
@ -64,12 +63,50 @@ void sendPayload() {
ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled"); ESP_LOGD(TAG, "No valid GPS position or GPS data mode disabled");
} }
#endif #endif
senddata(COUNTERPORT); SendData(COUNTERPORT);
} }
} // sendpayload(); } // sendpayload()
// interrupt handler used for payload send cycle timer
void IRAM_ATTR SendCycleIRQ() { void IRAM_ATTR SendCycleIRQ() {
portENTER_CRITICAL(&timerMux); portENTER_CRITICAL(&timerMux);
SendCycleTimerIRQ++; SendCycleTimerIRQ++;
portEXIT_CRITICAL(&timerMux); portEXIT_CRITICAL(&timerMux);
} }
// cyclic called function to eat data from RTos send queues and transmit it
void processSendBuffer() {
MessageBuffer_t RcvBuf;
#ifdef HAS_LORA
// Check if there is a pending TX/RX job running
if ((LMIC.opmode & (OP_JOINING | OP_REJOIN | OP_TXDATA | OP_POLL)) != 0) {
// LoRa Busy -> don't eat data from queue, since it cannot be sent
} else {
if (xQueueReceive(LoraSendQueue, &(RcvBuf), (TickType_t)10)) {
// xMsg now holds the struct MessageBuffer from queue
LMIC_setTxData2(RcvBuf.MessagePort, RcvBuf.Message, RcvBuf.MessageSize,
(cfg.countermode & 0x02));
ESP_LOGI(TAG, "%d bytes sent to LORA", RcvBuf.MessageSize);
sprintf(display_line7, "PACKET QUEUED");
}
}
#endif
#ifdef HAS_SPI
if (xQueueReceive(SPISendQueue, &(RcvBuf), (TickType_t)10)) {
ESP_LOGI(TAG, "%d bytes sent to SPI", RcvBuf.MessageSize);
}
#endif
} // processSendBuffer
void flushQueues() {
#ifdef HAS_LORA
xQueueReset(LoraSendQueue);
#endif
#ifdef HAS_SPI
xQueueReset(SPISendQueue);
#endif
}

View File

@ -1,8 +1,17 @@
#ifndef _SENDDATA_H #ifndef _SENDDATA_H
#define _SENDDATA_H #define _SENDDATA_H
void senddata(uint8_t port); // Struct holding payload for data send queue
typedef struct {
uint8_t MessageSize;
uint8_t MessagePort;
uint8_t Message[PAYLOAD_BUFFER_SIZE];
} MessageBuffer_t;
void SendData(uint8_t port);
void sendPayload(void); void sendPayload(void);
void SendCycleIRQ(void); void SendCycleIRQ(void);
void processSendBuffer(void);
void flushQueues();
#endif // _SENDDATA_H_ #endif // _SENDDATA_H_