commit
20732f13dd
@ -158,7 +158,7 @@ You can add up to 3 user defined sensors. Insert sensor's payload scheme in [*se
|
|||||||
|
|
||||||
Output of user sensor data can be switched by user remote control command 0x14 sent to Port 2.
|
Output of user sensor data can be switched by user remote control command 0x14 sent to Port 2.
|
||||||
|
|
||||||
Output of sensor and peripheral data is internally switched by a bitmask register. Default mask (0xFF) can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](src/configmanager.cpp) following this scheme:
|
Output of sensor and peripheral data is internally switched by a bitmask register. Default mask can be tailored by editing *cfg.payloadmask* initialization value in [*configmanager.cpp*](src/configmanager.cpp) following this scheme:
|
||||||
|
|
||||||
| Bit | Sensordata |
|
| Bit | Sensordata |
|
||||||
| --- | ------------- |
|
| --- | ------------- |
|
||||||
|
@ -23,6 +23,13 @@
|
|||||||
extern QueueHandle_t LoraSendQueue;
|
extern QueueHandle_t LoraSendQueue;
|
||||||
extern TaskHandle_t lmicTask, lorasendTask;
|
extern TaskHandle_t lmicTask, lorasendTask;
|
||||||
|
|
||||||
|
// table of LORAWAN MAC commands
|
||||||
|
typedef struct {
|
||||||
|
const uint8_t opcode;
|
||||||
|
const char cmdname[20];
|
||||||
|
const uint8_t params;
|
||||||
|
} mac_t;
|
||||||
|
|
||||||
esp_err_t lora_stack_init();
|
esp_err_t lora_stack_init();
|
||||||
void lmictask(void *pvParameters);
|
void lmictask(void *pvParameters);
|
||||||
void onEvent(ev_t ev);
|
void onEvent(ev_t ev);
|
||||||
@ -37,6 +44,11 @@ void switch_lora(uint8_t sf, uint8_t tx);
|
|||||||
void lora_send(void *pvParameters);
|
void lora_send(void *pvParameters);
|
||||||
void lora_enqueuedata(MessageBuffer_t *message);
|
void lora_enqueuedata(MessageBuffer_t *message);
|
||||||
void lora_queuereset(void);
|
void lora_queuereset(void);
|
||||||
|
void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
||||||
|
size_t nMsg);
|
||||||
|
void myTxCallback(void *pUserData, int fSuccess);
|
||||||
|
void mac_decode(const uint8_t cmd[], const uint8_t cmdlength);
|
||||||
|
|
||||||
#if (TIME_SYNC_LORAWAN)
|
#if (TIME_SYNC_LORAWAN)
|
||||||
void user_request_network_time_callback(void *pVoidUserUTCTime,
|
void user_request_network_time_callback(void *pVoidUserUTCTime,
|
||||||
int flagSuccess);
|
int flagSuccess);
|
||||||
|
@ -19,11 +19,11 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
const uint8_t opcode;
|
const uint8_t opcode;
|
||||||
void (*func)(uint8_t []);
|
void (*func)(uint8_t []);
|
||||||
uint8_t params;
|
const uint8_t params;
|
||||||
const bool store;
|
const bool store;
|
||||||
} cmd_t;
|
} cmd_t;
|
||||||
|
|
||||||
void rcommand(uint8_t cmd[], uint8_t cmdlength);
|
void rcommand(const uint8_t cmd[], const uint8_t cmdlength);
|
||||||
void do_reset();
|
void do_reset();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
void timesync_init(void);
|
void timesync_init(void);
|
||||||
void send_timesync_req(void);
|
void send_timesync_req(void);
|
||||||
int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len);
|
int recv_timesync_ans(const uint8_t seq_no, const uint8_t buf[], const uint8_t buf_len);
|
||||||
void process_timesync_req(void *taskparameter);
|
void process_timesync_req(void *taskparameter);
|
||||||
void store_time_sync_req(uint32_t t_millisec);
|
void store_time_sync_req(uint32_t t_millisec);
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ description = Paxcounter is a device for metering passenger flows in realtime. I
|
|||||||
|
|
||||||
[common]
|
[common]
|
||||||
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
; for release_version use max. 10 chars total, use any decimal format like "a.b.c"
|
||||||
release_version = 1.7.974
|
release_version = 1.7.979
|
||||||
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
; DEBUG LEVEL: For production run set to 0, otherwise device will leak RAM while running!
|
||||||
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
; 0=None, 1=Error, 2=Warn, 3=Info, 4=Debug, 5=Verbose
|
||||||
debug_level = 3
|
debug_level = 3
|
||||||
|
@ -144,7 +144,7 @@ int checkIaqSensorStatus(void) {
|
|||||||
// store current BME sensor data in struct
|
// store current BME sensor data in struct
|
||||||
void bme_storedata(bmeStatus_t *bme_store) {
|
void bme_storedata(bmeStatus_t *bme_store) {
|
||||||
|
|
||||||
if ((cfg.payloadmask && MEMS_DATA) &
|
if ((cfg.payloadmask & MEMS_DATA) &&
|
||||||
(I2C_MUTEX_LOCK())) { // block i2c bus access
|
(I2C_MUTEX_LOCK())) { // block i2c bus access
|
||||||
|
|
||||||
#ifdef HAS_BME680
|
#ifdef HAS_BME680
|
||||||
|
@ -138,7 +138,7 @@ void gps_loop(void *pvParameters) {
|
|||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
|
||||||
if (cfg.payloadmask && GPS_DATA) {
|
if (cfg.payloadmask & GPS_DATA) {
|
||||||
#ifdef GPS_SERIAL
|
#ifdef GPS_SERIAL
|
||||||
// feed GPS decoder with serial NMEA data from GPS device
|
// feed GPS decoder with serial NMEA data from GPS device
|
||||||
while (GPS_Serial.available()) {
|
while (GPS_Serial.available()) {
|
||||||
|
@ -19,9 +19,14 @@
|
|||||||
// LMIC LORAWAN STACK SETTINGS
|
// LMIC LORAWAN STACK SETTINGS
|
||||||
// --> adapt to your device only if necessary
|
// --> adapt to your device only if necessary
|
||||||
|
|
||||||
|
// 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
|
//#define LMIC_USE_INTERRUPTS 1
|
||||||
|
|
||||||
//time sync via LoRaWAN network, is not yet supported by TTN (LoRaWAN spec v1.0.3)
|
// needed for paxcounter code
|
||||||
|
#define LMIC_ENABLE_user_events 1
|
||||||
|
|
||||||
|
// time sync via LoRaWAN network, note: not supported by TTNv2
|
||||||
// #define LMIC_ENABLE_DeviceTimeReq 1
|
// #define LMIC_ENABLE_DeviceTimeReq 1
|
||||||
|
|
||||||
// 16 μs per tick
|
// 16 μs per tick
|
||||||
|
160
src/lorawan.cpp
160
src/lorawan.cpp
@ -244,38 +244,6 @@ void onEvent(ev_t ev) {
|
|||||||
strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED ACK")
|
strcpy_P(buff, (LMIC.txrxFlags & TXRX_ACK) ? PSTR("RECEIVED ACK")
|
||||||
: PSTR("TX COMPLETE"));
|
: PSTR("TX COMPLETE"));
|
||||||
sprintf(display_line6, " "); // clear previous lmic status
|
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] >= TIMEANSWERPORT_MIN) &&
|
|
||||||
(LMIC.frame[LMIC.dataBeg - 1] <= TIMEANSWERPORT_MAX)) {
|
|
||||||
recv_timesync_ans(LMIC.frame[LMIC.dataBeg - 1],
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EV_LOST_TSYNC:
|
case EV_LOST_TSYNC:
|
||||||
@ -402,14 +370,15 @@ void lora_send(void *pvParameters) {
|
|||||||
// attempt to transmit payload
|
// attempt to transmit payload
|
||||||
else {
|
else {
|
||||||
|
|
||||||
switch (LMIC_setTxData2(SendBuffer.MessagePort, SendBuffer.Message,
|
switch (LMIC_sendWithCallback(
|
||||||
SendBuffer.MessageSize,
|
SendBuffer.MessagePort, SendBuffer.Message, SendBuffer.MessageSize,
|
||||||
(cfg.countermode & 0x02))) {
|
(cfg.countermode & 0x02), myTxCallback, NULL)) {
|
||||||
|
|
||||||
case 0:
|
case 0:
|
||||||
ESP_LOGI(TAG, "%d byte(s) delivered to LMIC", SendBuffer.MessageSize);
|
ESP_LOGI(TAG, "%d byte(s) sent to LORA", SendBuffer.MessageSize);
|
||||||
break;
|
break;
|
||||||
case -1: // LMIC already has a tx message pending
|
case -1: // LMIC already has a tx message pending
|
||||||
ESP_LOGD(TAG, "LMIC busy, message re-enqueued");
|
// ESP_LOGD(TAG, "LMIC busy, message re-enqueued");
|
||||||
vTaskDelay(pdMS_TO_TICKS(1000 + random(500))); // wait a while
|
vTaskDelay(pdMS_TO_TICKS(1000 + random(500))); // wait a while
|
||||||
lora_enqueuedata(&SendBuffer); // re-enqueue the undeliverd message
|
lora_enqueuedata(&SendBuffer); // re-enqueue the undeliverd message
|
||||||
break;
|
break;
|
||||||
@ -554,6 +523,9 @@ void lmictask(void *pvParameters) {
|
|||||||
// rate for 125 kHz channels, and it minimizes air time and battery power.
|
// rate for 125 kHz channels, and it minimizes air time and battery power.
|
||||||
// Set the transmission power to 14 dBi (25 mW).
|
// Set the transmission power to 14 dBi (25 mW).
|
||||||
LMIC_setDrTxpow(DR_SF7, 14);
|
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);
|
||||||
|
|
||||||
#if defined(CFG_US915) || defined(CFG_au921)
|
#if defined(CFG_US915) || defined(CFG_au921)
|
||||||
// in the US, with TTN, it saves join time if we start on subband 1
|
// in the US, with TTN, it saves join time if we start on subband 1
|
||||||
@ -569,4 +541,118 @@ void lmictask(void *pvParameters) {
|
|||||||
}
|
}
|
||||||
} // lmictask
|
} // lmictask
|
||||||
|
|
||||||
|
// receive message handler
|
||||||
|
void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
|
||||||
|
size_t nMsg) {
|
||||||
|
|
||||||
|
// tell the compiler that pUserData is required by the API, but we don't
|
||||||
|
// happen to use it.
|
||||||
|
LMIC_API_PARAMETER(pUserData);
|
||||||
|
|
||||||
|
// display type of received data
|
||||||
|
if (nMsg)
|
||||||
|
ESP_LOGI(TAG, "Received %u bytes 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, "Received %u MAC messages:", nMac);
|
||||||
|
// NOT WORKING YET
|
||||||
|
// whe need to strip some protocol overhead from LMIC.frame to unwrap the
|
||||||
|
// MAC command
|
||||||
|
mac_decode(LMIC.frame, nMac);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
/* currently no code here */
|
||||||
|
|
||||||
|
// tell the compiler that pUserData is required by the API, but we don't
|
||||||
|
// happen to use it.
|
||||||
|
LMIC_API_PARAMETER(pUserData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// LORAWAN MAC interpreter
|
||||||
|
|
||||||
|
// 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 mac_t 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}};
|
||||||
|
|
||||||
|
static const uint8_t cmdtablesize =
|
||||||
|
sizeof(table) / sizeof(table[0]); // number of commands in command table
|
||||||
|
|
||||||
|
// decode mac message
|
||||||
|
void mac_decode(const uint8_t cmd[], const uint8_t cmdlength) {
|
||||||
|
|
||||||
|
if (!cmdlength)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint8_t foundcmd[cmdlength], cursor = 0;
|
||||||
|
|
||||||
|
while (cursor < cmdlength) {
|
||||||
|
|
||||||
|
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;
|
||||||
|
ESP_LOGI(TAG, "Network command %s", table[i].cmdname);
|
||||||
|
} else
|
||||||
|
ESP_LOGI(TAG, "MAC message 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_LOGI(TAG, "Unknown MAC message 0x%02X", cmd[cursor]);
|
||||||
|
cursor++;
|
||||||
|
}
|
||||||
|
} // command parsing loop
|
||||||
|
|
||||||
|
} // mac_decode()
|
||||||
|
|
||||||
#endif // HAS_LORA
|
#endif // HAS_LORA
|
@ -85,6 +85,7 @@
|
|||||||
|
|
||||||
// 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 // counts
|
#define COUNTERPORT 1 // counts
|
||||||
|
#define MACPORT 0 // network commands
|
||||||
#define RCMDPORT 2 // remote commands
|
#define RCMDPORT 2 // remote commands
|
||||||
#define STATUSPORT 2 // remote command results
|
#define STATUSPORT 2 // remote command results
|
||||||
#define CONFIGPORT 3 // config query results
|
#define CONFIGPORT 3 // config query results
|
||||||
|
@ -127,7 +127,7 @@ void set_gps(uint8_t val[]) {
|
|||||||
if (val[0]) {
|
if (val[0]) {
|
||||||
cfg.payloadmask |= (uint8_t)GPS_DATA; // set bit in mask
|
cfg.payloadmask |= (uint8_t)GPS_DATA; // set bit in mask
|
||||||
} else {
|
} else {
|
||||||
cfg.payloadmask &= ~(uint8_t)GPS_DATA; // clear bit in mask
|
cfg.payloadmask &= (uint8_t)~GPS_DATA; // clear bit in mask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ void set_bme(uint8_t val[]) {
|
|||||||
if (val[0]) {
|
if (val[0]) {
|
||||||
cfg.payloadmask |= (uint8_t)MEMS_DATA; // set bit in mask
|
cfg.payloadmask |= (uint8_t)MEMS_DATA; // set bit in mask
|
||||||
} else {
|
} else {
|
||||||
cfg.payloadmask &= ~(uint8_t)MEMS_DATA; // clear bit in mask
|
cfg.payloadmask &= (uint8_t)~MEMS_DATA; // clear bit in mask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +146,7 @@ void set_batt(uint8_t val[]) {
|
|||||||
if (val[0]) {
|
if (val[0]) {
|
||||||
cfg.payloadmask |= (uint8_t)BATT_DATA; // set bit in mask
|
cfg.payloadmask |= (uint8_t)BATT_DATA; // set bit in mask
|
||||||
} else {
|
} else {
|
||||||
cfg.payloadmask &= ~(uint8_t)BATT_DATA; // clear bit in mask
|
cfg.payloadmask &= (uint8_t)~BATT_DATA; // clear bit in mask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,7 +332,7 @@ void set_flush(uint8_t val[]) {
|
|||||||
// format: opcode, function, #bytes params,
|
// format: opcode, function, #bytes params,
|
||||||
// flag (true = do make settings persistent / false = don't)
|
// 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},
|
{0x01, set_rssi, 1, true}, {0x02, set_countmode, 1, true},
|
||||||
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true},
|
{0x03, set_gps, 1, true}, {0x04, set_display, 1, true},
|
||||||
{0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 1, true},
|
{0x05, set_lorasf, 1, true}, {0x06, set_lorapower, 1, true},
|
||||||
@ -349,11 +349,11 @@ cmd_t table[] = {
|
|||||||
{0x85, get_bme, 0, false}, {0x86, get_time, 0, false},
|
{0x85, get_bme, 0, false}, {0x86, get_time, 0, false},
|
||||||
{0x87, set_time, 0, false}, {0x99, set_flush, 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
|
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(const uint8_t cmd[], const uint8_t cmdlength) {
|
||||||
|
|
||||||
if (cmdlength == 0)
|
if (cmdlength == 0)
|
||||||
return;
|
return;
|
||||||
|
@ -154,7 +154,7 @@ void store_time_sync_req(uint32_t timestamp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// process timeserver timestamp answer, called from lorawan.cpp
|
// process timeserver timestamp answer, called from lorawan.cpp
|
||||||
int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) {
|
int recv_timesync_ans(const uint8_t seq_no, const uint8_t buf[], const uint8_t buf_len) {
|
||||||
|
|
||||||
// if no timesync handshake is pending then exit
|
// if no timesync handshake is pending then exit
|
||||||
if (!timeSyncPending)
|
if (!timeSyncPending)
|
||||||
|
Loading…
Reference in New Issue
Block a user