diff --git a/src/TTNLogging.cpp b/src/TTNLogging.cpp index d03912a..35163ad 100644 --- a/src/TTNLogging.cpp +++ b/src/TTNLogging.cpp @@ -21,9 +21,15 @@ #include "TTNLogging.h" +#define NUM_RINGBUF_MSG 50 static const char* const TAG = "lmic"; static TTNLogging ttnLog; +/** + * @brief Message structure used in ring buffer + * + * The structure is sent from the LMIC task to the logging task. + */ struct TTNLogMessage { const char* message; uint32_t datum; @@ -43,6 +49,7 @@ struct TTNLogMessage { u1_t saveIrqFlags; }; +// Constants for formatting LORA values static const char* const SF_NAMES[] = { "FSK", "SF7", "SF8", "SF9", "SF10", "SF11", "SF12", "SFrfu" }; static const char* const BW_NAMES[] = { "BW125", "BW250", "BW500", "BWrfu" }; static const char* const CR_NAMES[] = { "CR 4/5", "CR 4/6", "CR 4/7", "CR 4/8" }; @@ -54,15 +61,17 @@ static void printEvtJoinFailed(TTNLogMessage* log); static void bin2hex(const uint8_t* bin, unsigned len, char* buf, char sep = 0); +// Create singleton instance TTNLogging* TTNLogging::initInstance() { ttnLog.init(); return &ttnLog; } +// Initialize logging void TTNLogging::init() { - ringBuffer = xRingbufferCreate(50 * sizeof(TTNLogMessage), RINGBUF_TYPE_NOSPLIT); + ringBuffer = xRingbufferCreate(NUM_RINGBUF_MSG * sizeof(TTNLogMessage), RINGBUF_TYPE_NOSPLIT); if (ringBuffer == nullptr) { ESP_LOGE(TAG, "Failed to create ring buffer"); ASSERT(0); @@ -72,6 +81,7 @@ void TTNLogging::init() hal_set_failure_handler(logFatal); } +// Record a logging event for later output void TTNLogging::logEvent(int event, const char* message, uint32_t datum) { if (ringBuffer == nullptr) @@ -100,20 +110,22 @@ void TTNLogging::logEvent(int event, const char* message, uint32_t datum) xRingbufferSend(ringBuffer, &log, sizeof(log), 0); } - +// record a fatal event (failed assert) for later output void TTNLogging::logFatal(const char* file, uint16_t line) { ttnLog.logEvent(-3, file, line); } - - +// Record an informational message for later output +// The message must not be freed. extern "C" void LMICOS_logEvent(const char *pMessage) { ttnLog.logEvent(-1, pMessage, 0); } +// Record an information message with an integer value for later output +// The message must not be freed. extern "C" void LMICOS_logEventUint32(const char *pMessage, uint32_t datum) { ttnLog.logEvent(-2, pMessage, datum); @@ -123,6 +135,7 @@ extern "C" void LMICOS_logEventUint32(const char *pMessage, uint32_t datum) // --------------------------------------------------------------------------- // Log output +// Tasks that receiveds the recorded messages, formats and outputs them. void TTNLogging::loggingTask(void* param) { RingbufHandle_t ringBuffer = (RingbufHandle_t)param; @@ -140,6 +153,7 @@ void TTNLogging::loggingTask(void* param) } +// Format and output a log message void printMessage(TTNLogMessage* log) { switch((int)log->event) @@ -173,6 +187,7 @@ void printMessage(TTNLogMessage* log) } +// Format and output the detail of a successful network join void printEvtJoined(TTNLogMessage* log) { ESP_LOGI(TAG, "%s: ch=%d", log->message, (unsigned)log->txChnl); @@ -196,6 +211,7 @@ void printEvtJoined(TTNLogMessage* log) } +// Format and output the detail of a failed network join void printEvtJoinFailed(TTNLogMessage* log) { rps_t rps = log->rps; @@ -215,6 +231,14 @@ void printEvtJoinFailed(TTNLogMessage* log) static const char* HEX_DIGITS = "0123456789ABCDEF"; +/** + * @brief Convert binary data to hexadecimal representation. + * + * @param bin start of binary data + * @param len length of binary data (in bytes) + * @param buf buffer for hexadecimal result + * @param sep separator used between bytes (or 0 for none) + */ void bin2hex(const uint8_t* bin, unsigned len, char* buf, char sep) { int tgt = 0; diff --git a/src/TTNLogging.h b/src/TTNLogging.h index b5c267a..2295603 100644 --- a/src/TTNLogging.h +++ b/src/TTNLogging.h @@ -20,6 +20,22 @@ #include +/** + * @brief Logging class. + * + * Logs internal information from LMIC in an asynchrnous fashion in order + * not to distrub the sensitive LORA timing. + * + * A ring buffer and a separate logging task is ued. The LMIC core records + * relevant values from the current LORA settings and writes them to a ring + * buffer. The logging tasks receives the message and the values, formats + * them and outputs them via the regular ESP-IDF logging mechanism. + * + * In order to activate the detailed logging, set the macro + * `LMIC_ENABLE_event_logging` to 1. + * + * This class is not to be used directly. + */ class TTNLogging { public: static TTNLogging* initInstance(); diff --git a/src/TTNProvisioning.cpp b/src/TTNProvisioning.cpp index ccadbd6..b0b0aa4 100644 --- a/src/TTNProvisioning.cpp +++ b/src/TTNProvisioning.cpp @@ -286,7 +286,7 @@ void TTNProvisioning::processLine() ttn_hal.enterCriticalSection(); LMIC_reset(); ttn_hal.leaveCriticalSection(); - onEvent(EV_RESET); + LMIC.client.eventCb(LMIC.client.eventUserData, EV_RESET); } uart_write_bytes(UART_NUM, is_ok ? "OK\r\n" : "ERROR\r\n", is_ok ? 4 : 7); diff --git a/src/TheThingsNetwork.cpp b/src/TheThingsNetwork.cpp index 86c902c..b6c6c08 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -20,36 +20,45 @@ #include "TTNLogging.h" -enum TTNClientAction +/** + * @brief Reason the user code is waiting + */ +enum TTNWaitingReason { - eActionUnrelated, - eActionJoining, - eActionTransmission + eWaitingNone, + eWaitingForJoin, + eWaitingForTransmission }; +/** + * @brief Event type + */ enum TTNEvent { - EvtNone, - EvtJoinCompleted, - EvtJoinFailed, - EvtMessageReceived, - EvtTransmissionCompleted, - EvtTransmissionFailed + eEvtNone, + eEvtJoinCompleted, + eEvtJoinFailed, + eEvtMessageReceived, + eEvtTransmissionCompleted, + eEvtTransmissionFailed }; -struct TTNResult { - TTNResult(TTNEvent ev = EvtNone): event(ev) { } +/** + * @brief Event message sent from LMIC task to waiting client task + */ +struct TTNLmicEvent { + TTNLmicEvent(TTNEvent ev = eEvtNone): event(ev) { } TTNEvent event; uint8_t port; - const uint8_t *message; + const uint8_t* message; size_t messageSize; }; static const char *TAG = "ttn"; static TheThingsNetwork* ttnInstance; -static QueueHandle_t resultQueue; -static TTNClientAction clientAction = eActionUnrelated; +static QueueHandle_t lmicEventQueue = nullptr; +static TTNWaitingReason waitingReason = eWaitingNone; static TTNProvisioning provisioning; #if LMIC_ENABLE_event_logging static TTNLogging* logging; @@ -66,7 +75,6 @@ TheThingsNetwork::TheThingsNetwork() #if defined(TTN_IS_DISABLED) ESP_LOGE(TAG, "TTN is disabled. Configure a frequency plan using 'make menuconfig'"); ASSERT(0); - esp_restart(); #endif ASSERT(ttnInstance == nullptr); @@ -93,8 +101,8 @@ void TheThingsNetwork::configurePins(spi_host_device_t spi_host, uint8_t nss, ui os_init_ex(nullptr); reset(); - resultQueue = xQueueCreate(4, sizeof(TTNResult)); - ASSERT(resultQueue != nullptr); + lmicEventQueue = xQueueCreate(4, sizeof(TTNLmicEvent)); + ASSERT(lmicEventQueue != nullptr); ttn_hal.startBackgroundTask(); } @@ -102,6 +110,11 @@ void TheThingsNetwork::reset() { ttn_hal.enterCriticalSection(); LMIC_reset(); + waitingReason = eWaitingNone; + if (lmicEventQueue != nullptr) + { + xQueueReset(lmicEventQueue); + } ttn_hal.leaveCriticalSection(); } @@ -181,26 +194,26 @@ bool TheThingsNetwork::joinCore() } ttn_hal.enterCriticalSection(); - clientAction = eActionJoining; + waitingReason = eWaitingForJoin; LMIC_startJoining(); ttn_hal.wakeUp(); ttn_hal.leaveCriticalSection(); - TTNResult result; - xQueueReceive(resultQueue, &result, portMAX_DELAY); - return result.event == EvtJoinCompleted; + TTNLmicEvent event; + xQueueReceive(lmicEventQueue, &event, portMAX_DELAY); + return event.event == eEvtJoinCompleted; } TTNResponseCode TheThingsNetwork::transmitMessage(const uint8_t *payload, size_t length, port_t port, bool confirm) { ttn_hal.enterCriticalSection(); - if (LMIC.opmode & OP_TXRXPEND) + if (waitingReason != eWaitingNone || (LMIC.opmode & OP_TXRXPEND) != 0) { ttn_hal.leaveCriticalSection(); return kTTNErrorTransmissionFailed; } - clientAction = eActionTransmission; + waitingReason = eWaitingForTransmission; LMIC.client.txMessageCb = messageTransmittedCallback; LMIC.client.txMessageUserData = nullptr; LMIC_setTxData2(port, (xref2u1_t)payload, length, confirm); @@ -209,20 +222,20 @@ TTNResponseCode TheThingsNetwork::transmitMessage(const uint8_t *payload, size_t while (true) { - TTNResult result; - xQueueReceive(resultQueue, &result, portMAX_DELAY); + TTNLmicEvent result; + xQueueReceive(lmicEventQueue, &result, portMAX_DELAY); switch (result.event) { - case EvtMessageReceived: + case eEvtMessageReceived: if (messageCallback != nullptr) messageCallback(result.message, result.messageSize, result.port); break; - case EvtTransmissionCompleted: + case eEvtTransmissionCompleted: return kTTNSuccessfulTransmission; - case EvtTransmissionFailed: + case eEvtTransmissionFailed: return kTTNErrorTransmissionFailed; default: @@ -260,7 +273,7 @@ const char *eventNames[] = { LMIC_EVENT_NAME_TABLE__INIT }; #endif - +// Called by LMIC when an LMIC event (join, join failed, reset etc.) occurs void eventCallback(void* userData, ev_t event) { #if LMIC_ENABLE_event_logging @@ -269,45 +282,42 @@ void eventCallback(void* userData, ev_t event) ESP_LOGI(TAG, "event %s", eventNames[event]); #endif - if (event == EV_TXCOMPLETE) { - if (LMIC.txrxFlags & TXRX_ACK) - ESP_LOGI(TAG, "ACK received\n"); - } + TTNEvent ttnEvent = eEvtNone; - TTNEvent ttnEvent = EvtNone; - - if (clientAction == eActionJoining) + if (waitingReason == eWaitingForJoin) { if (event == EV_JOINED) { - ttnEvent = EvtJoinCompleted; + ttnEvent = eEvtJoinCompleted; } else if (event == EV_REJOIN_FAILED || event == EV_RESET) { - ttnEvent = EvtJoinFailed; + ttnEvent = eEvtJoinFailed; } } - if (ttnEvent == EvtNone) + if (ttnEvent == eEvtNone) return; - TTNResult result(ttnEvent); - clientAction = eActionUnrelated; - xQueueSend(resultQueue, &result, 100 / portTICK_PERIOD_MS); + TTNLmicEvent result(ttnEvent); + waitingReason = eWaitingNone; + xQueueSend(lmicEventQueue, &result, 100 / portTICK_PERIOD_MS); } +// Called by LMIC when a message has been received void messageReceivedCallback(void *userData, uint8_t port, const uint8_t *message, size_t nMessage) { - TTNResult result(EvtMessageReceived); + TTNLmicEvent result(eEvtMessageReceived); result.port = port; result.message = message; result.messageSize = nMessage; - xQueueSend(resultQueue, &result, 100 / portTICK_PERIOD_MS); + xQueueSend(lmicEventQueue, &result, 100 / portTICK_PERIOD_MS); } +// Called by LMIC when a message has been transmitted (or the transmission failed) void messageTransmittedCallback(void *userData, int success) { - clientAction = eActionUnrelated; - TTNResult result(success ? EvtTransmissionCompleted : EvtTransmissionFailed); - xQueueSend(resultQueue, &result, 100 / portTICK_PERIOD_MS); + waitingReason = eWaitingNone; + TTNLmicEvent result(success ? eEvtTransmissionCompleted : eEvtTransmissionFailed); + xQueueSend(lmicEventQueue, &result, 100 / portTICK_PERIOD_MS); }