-
Notifications
You must be signed in to change notification settings - Fork 64
Examples
hello_world
is a simple example app that sends the message "Hello, world" every 30 seconds and displays received messages.
It can be found in GitHub at https://github.com/manuelbl/ttn-esp32/tree/master/examples/hello_world. The relevant code is in main.cpp
.
The start of the code is standard and mainly about configuring the device. There are pin configurations:
// Pins and other resources
#define TTN_SPI_HOST HSPI_HOST
#define TTN_SPI_DMA_CHAN 1
#define TTN_PIN_SPI_SCLK 5
...
#define TTN_PIN_DIO1 33
app_main()
starts with the initialization of all resources that are possibly shared between the TTN code and other parts of the app: GPIO ISR handler service, NVS and SPI bus:
esp_err_t err;
// Initialize the GPIO ISR handler service
err = gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
ESP_ERROR_CHECK(err);
// Initialize the NVS (non-volatile storage) for saving and restoring the keys
err = nvs_flash_init();
ESP_ERROR_CHECK(err);
// Initialize SPI bus
spi_bus_config_t spi_bus_config;
spi_bus_config.miso_io_num = TTN_PIN_SPI_MISO;
spi_bus_config.mosi_io_num = TTN_PIN_SPI_MOSI;
spi_bus_config.sclk_io_num = TTN_PIN_SPI_SCLK;
spi_bus_config.quadwp_io_num = -1;
spi_bus_config.quadhd_io_num = -1;
spi_bus_config.max_transfer_sz = 0;
err = spi_bus_initialize(TTN_SPI_HOST, &spi_bus_config, TTN_SPI_DMA_CHAN);
ESP_ERROR_CHECK(err);
Then the pins of the TTN device are configured (using the defines at the top of the file):
// Configure the SX127x pins
ttn.configurePins(TTN_SPI_HOST, TTN_PIN_NSS, TTN_PIN_RXTX, TTN_PIN_RST, TTN_PIN_DIO0, TTN_PIN_DIO1);
After provisioning the keys and joining the network, a separate task is started to send messages:
xTaskCreate(sendMessages, "send_messages", 1024 * 4, (void* )0, 3, NULL);
The task sends a messages, waits 30 seconds and repeats forever.
To receive messages, a callback function is registered in app_main()
:
ttn.onMessage(messageReceived);
The registered function prints the message:
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");
}
Adding the EUIs and keys to the source code is unfortunate:
- Sensitive data might be put into a source code repository several people have access to
- The same EUIs and keys might accidently be used on a second device and disrupting the communication of the first device
- The source code needs to be changed and recompiled for each device
As an alternative, ttn-esp32
can run a background process that listens for AT commands. The EUIs and keys can then be provisions by AT commands.
The example can be found in GitHub at https://github.com/manuelbl/ttn-esp32/tree/master/examples/provision. The relevant code is in main.cpp
.
Compared to the hello_world
example, it replaces the ttn.provision...
line in the with:
ttn.startProvisioningTask();
ttn.waitForProvisioning();
The first line starts the background task, and the second one waits until the provisioning keys have been provided. It immediately returns, if keys are found in the non-volatile storage.
Compared to the hello_world
example, the provision
example also removes the lines with the EUIs and keys.
When you flash the device and start it for the first time, the following output appears:
# make flash monitor
...
I (273) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (463) ttn_hal: IO initialized
I (473) ttn_hal: SPI initialized
I (473) ttn_hal: Timer initialized
I (513) uart: queue free spaces: 20
I (513) ttn_prov: Provisioning task started
You can then set the EUIs and key via a AT commands:
AT+PROV=0123456789ABCDEF-0123456789ABCDEF-0123456789ABCDEF0123456789ABCDEF
I (77513) ttn_prov: Dev and app EUI and app key saved in NVS storage
I (77513) ttn: event EV_RESET
OK
I (78513) ttn: Device successfully provisioned
Joining...
I (78513) ttn: event EV_JOINING
The next time the device is started, it immediately join TTN and doesn't require any EUIs and keys anymore as they have been saved in NVS.
monitoring
is a simple example app that prints the applied RF settings.
It can be found in GitHub at https://github.com/manuelbl/ttn-esp32/tree/master/examples/monitoring. The relevant code is in main.cpp
.
Every time a message has been sent, the RF settings for the transmission and the receive windows 1 and 2 are printed using the below functions:
void printRFSettings(const char* window, const TTNRFSettings& settings)
{
int bw = (1 << (static_cast<int>(settings.bandwidth) - 1)) * 125;
int sf = static_cast<int>(settings.spreadingFactor) + 5;
if (settings.spreadingFactor == kTTNSFNone)
{
printf("%s: not used\n", window);
}
else if (settings.spreadingFactor == kTTNFSK)
{
printf("%s: FSK, BW %dkHz, %d.%d MHz\n",
window, bw, settings.frequency / 1000000, (settings.frequency % 1000000 + 50000) / 100000);
}
else
{
printf("%s: SF%d, BW %dkHz, %d.%d MHz\n",
window, sf, bw, settings.frequency / 1000000, (settings.frequency % 1000000 + 50000) / 100000);
}
}
void printAllRFSettings()
{
printRFSettings("TX ", ttn.txSettings());
printRFSettings("RX1", ttn.rx1Settings());
printRFSettings("RX2", ttn.rx2Settings());
}
In addition, if a message has been received, the RSSI value is printed:
printf("RSSI: %d dBm\n", ttn.rssi());