From b8221a614eb550003e330893a9f632a891c07737 Mon Sep 17 00:00:00 2001 From: Manuel Bleichenbacher Date: Thu, 19 Jul 2018 16:16:40 +0200 Subject: [PATCH] Receive downlink messages --- component.mk | 2 - examples/send_recv/Makefile | 3 + examples/send_recv/components/ttn-esp32 | 1 + examples/send_recv/main/component.mk | 0 examples/send_recv/main/main.cpp | 87 +++++++++++++++++++++++++ include/TheThingsNetwork.h | 39 +++++++++-- src/TheThingsNetwork.cpp | 38 +++++++++-- src/radio.c | 6 +- 8 files changed, 161 insertions(+), 15 deletions(-) create mode 100644 examples/send_recv/Makefile create mode 120000 examples/send_recv/components/ttn-esp32 create mode 100644 examples/send_recv/main/component.mk create mode 100644 examples/send_recv/main/main.cpp diff --git a/component.mk b/component.mk index ef4f2bd..33f4c60 100755 --- a/component.mk +++ b/component.mk @@ -1,4 +1,2 @@ -CFLAGS += -Wno-error=maybe-uninitialized -Wno-error=unused-value - COMPONENT_ADD_INCLUDEDIRS := include COMPONENT_SRCDIRS := src diff --git a/examples/send_recv/Makefile b/examples/send_recv/Makefile new file mode 100644 index 0000000..1038c2d --- /dev/null +++ b/examples/send_recv/Makefile @@ -0,0 +1,3 @@ +PROJECT_NAME := hello_world + +include $(IDF_PATH)/make/project.mk diff --git a/examples/send_recv/components/ttn-esp32 b/examples/send_recv/components/ttn-esp32 new file mode 120000 index 0000000..f84607d --- /dev/null +++ b/examples/send_recv/components/ttn-esp32 @@ -0,0 +1 @@ +../../../../ttn-esp32/ \ No newline at end of file diff --git a/examples/send_recv/main/component.mk b/examples/send_recv/main/component.mk new file mode 100644 index 0000000..e69de29 diff --git a/examples/send_recv/main/main.cpp b/examples/send_recv/main/main.cpp new file mode 100644 index 0000000..846df2f --- /dev/null +++ b/examples/send_recv/main/main.cpp @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2018 Manuel Bleichenbacher + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Sample program showing how to send a test message every 30 second. + *******************************************************************************/ + +#include "freertos/FreeRTOS.h" +#include "esp_event.h" + +#include "TheThingsNetwork.h" + +// NOTE: +// The LoRaWAN frequency and the radio chip must be configured by running 'make menuconfig'. +// Go to Components / The Things Network, select the appropriate values and save. + +// Copy the below hex string from the "Device EUI" field +// on your device's overview page in the TTN console. +const char *devEui = "????????????????";; + +// Copy the below two lines from bottom of the same page +const char *appEui = "????????????????"; +const char *appKey = "????????????????????????????????"; + + +static TheThingsNetwork ttn; + +const unsigned TX_INTERVAL = 30; +static uint8_t msgData[] = "Hello, world"; + + +void sendMessages(void* pvParameter) +{ + while (1) { + printf("Sending message...\n"); + ttn_response_t res = ttn.sendBytes(msgData, sizeof(msgData) - 1); + if (res == TTN_SUCCESSFUL_TRANSMISSION) + printf("Message sent.\n"); + else if (res == TTN_SUCCESSFUL_RECEIVE) + printf("Message sent and message received\n"); + else + printf("Message transmission failed.\n"); + + vTaskDelay(TX_INTERVAL * 1000 / portTICK_PERIOD_MS); + } +} + +void messageReceived(const uint8_t* message, size_t length, port_t port) +{ + printf("Message of %d bytes received on port %d:", length, port); + for (int i = 0; i < length; i++) + printf(" %02x", message[i]); + printf("\n"); +} + +extern "C" void app_main(void) +{ + gpio_install_isr_service(ESP_INTR_FLAG_IRAM); + + // Initialize SPI bus + spi_bus_config_t spi_bus_config; + spi_bus_config.miso_io_num = 19; + spi_bus_config.mosi_io_num = 27; + spi_bus_config.sclk_io_num = 5; + spi_bus_config.quadwp_io_num = -1; + spi_bus_config.quadhd_io_num = -1; + spi_bus_config.max_transfer_sz = 0; + + esp_err_t ret = spi_bus_initialize(HSPI_HOST, &spi_bus_config, 1); + assert(ret == ESP_OK); + + ttn.configurePins(HSPI_HOST, 18, TTN_NOT_CONNECTED, 14, 26, 33); + + ttn.provision(devEui, appEui, appKey); + + ttn.onMessage(messageReceived); + + printf("Joining...\n"); + ttn.join(); + printf("Joined.\n"); + + xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, NULL); +} diff --git a/include/TheThingsNetwork.h b/include/TheThingsNetwork.h index 36ed55e..d5c112b 100644 --- a/include/TheThingsNetwork.h +++ b/include/TheThingsNetwork.h @@ -15,9 +15,6 @@ #include #include "driver/spi_master.h" -#define TTN_DEFAULT_SF 7 -#define TTN_DEFAULT_FSB 2 - /** * @brief Constant for indicating that a pin is not connected */ @@ -37,6 +34,14 @@ enum ttn_response_t TTN_SUCCESSFUL_RECEIVE = 2 }; +/** + * @brief Callback for recieved messages + * + * @param payload pointer to the received bytes + * @param length number of received bytes + * @param port port the message was received on + */ +typedef void (*message_cb_t)(const uint8_t* payload, size_t length, port_t port); /** * @brief TTN device @@ -114,11 +119,35 @@ public: */ bool join(); + /** + * @brief Send a message + * + * @param payload bytes to be sent + * @param length number of bytes to be sent + * @param port port (default to 1) + * @param confirm flag indicating if a confirmation should be requested. Default to 'false' + * @return TTTN_SUCCESSFUL_TRANSMISSION Successful transmission + * @return TTTN_SUCCESSFUL_RECEIVE Successful transmission and a message has been received + * @return TTN_ERROR_SEND_COMMAND_FAILED Transmission failed + * @return TTTN_ERROR_UNEXPECTED_RESPONSE Unexpected response + */ ttn_response_t sendBytes(const uint8_t *payload, size_t length, port_t port = 1, bool confirm = false); + /** + * @brief Set the function to be called when a message is received + * + * When a message is received, the specified function is called. The + * message, its length and the port is was received on are provided as + * parameters. The values are only valid during the duration of the + * callback. So they must be immediately processed or copied. + * + * @param callback the callback function + */ + void onMessage(message_cb_t callback); + + private: - uint8_t spreadingFactor = TTN_DEFAULT_SF; - uint8_t frequencySubband = TTN_DEFAULT_FSB; + message_cb_t messageCallback; bool decodeKeys(const char *devEui, const char *appEui, const char *appKey); }; diff --git a/src/TheThingsNetwork.cpp b/src/TheThingsNetwork.cpp index b2ac6af..0b1d383 100644 --- a/src/TheThingsNetwork.cpp +++ b/src/TheThingsNetwork.cpp @@ -112,12 +112,16 @@ bool TheThingsNetwork::join(const char *devEui, const char *appEui, const char * bool TheThingsNetwork::join() { + int result = 0; + // empty queue + while (xQueueReceive(result_queue, &result, 0) == pdTRUE) + ; + hal_enterCriticalSection(); LMIC_startJoining(); hal_wakeUp(); hal_leaveCriticalSection(); - int result = 0; while (true) { xQueueReceive(result_queue, &result, portMAX_DELAY); @@ -130,6 +134,11 @@ bool TheThingsNetwork::join() ttn_response_t TheThingsNetwork::sendBytes(const uint8_t *payload, size_t length, port_t port, bool confirm) { + int result = 0; + // empty queue + while (xQueueReceive(result_queue, &result, 0) == pdTRUE) + ; + hal_enterCriticalSection(); if (LMIC.opmode & OP_TXRXPEND) { @@ -141,9 +150,28 @@ ttn_response_t TheThingsNetwork::sendBytes(const uint8_t *payload, size_t length hal_wakeUp(); hal_leaveCriticalSection(); - int result = 0; xQueueReceive(result_queue, &result, portMAX_DELAY); - return result == EV_TXCOMPLETE ? TTN_SUCCESSFUL_TRANSMISSION : TTN_ERROR_SEND_COMMAND_FAILED; + + if (result == EV_TXCOMPLETE) + { + bool hasRecevied = (LMIC.txrxFlags & (TXRX_DNW1 | TXRX_DNW2)) != 0; + if (hasRecevied && messageCallback != NULL) + { + port_t port = 0; + if ((LMIC.txrxFlags & TXRX_PORT)) + port = LMIC.frame[LMIC.dataBeg - 1]; + messageCallback(LMIC.frame + LMIC.dataBeg, LMIC.dataLen, port); + } + + return hasRecevied ? TTN_SUCCESSFUL_RECEIVE : TTN_SUCCESSFUL_TRANSMISSION; + } + + return TTN_ERROR_SEND_COMMAND_FAILED; +} + +void TheThingsNetwork::onMessage(message_cb_t callback) +{ + messageCallback = callback; } @@ -190,9 +218,7 @@ void onEvent (ev_t ev) { if (ev == EV_TXCOMPLETE) { if (LMIC.txrxFlags & TXRX_ACK) - ESP_LOGI(TAG, "Received ack\n"); - if (LMIC.dataLen) - ESP_LOGI(TAG, "Received %d bytes of payload\n", LMIC.dataLen); + ESP_LOGI(TAG, "ACK received\n"); } int result = ev; diff --git a/src/radio.c b/src/radio.c index a66a2ea..faac784 100755 --- a/src/radio.c +++ b/src/radio.c @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "rom/ets_sys.h" #include "lmic.h" // ---------------------------------------- @@ -500,13 +501,14 @@ static void txlora () { // select LoRa modem (from sleep mode) //writeReg(RegOpMode, OPMODE_LORA); opmodeLora(); - // can take a moment to change; so try five times + // can take a moment to change; so try ten times u1_t reg; - for (int i = 0; i < 5; i++) + for (int i = 0; i < 10; i++) { reg = readReg(RegOpMode); if ((reg & OPMODE_LORA) != 0) break; + ets_delay_us(100); } ASSERT((reg & OPMODE_LORA) != 0);