Tutorial: Heltec ESP32 Board + The Things Network

A few weeks ago we deployed our first LoRaWAN gateways here in Birmingham, grew our group of core contributors, had our first meetup and became an official community of The Things Network. It felt like a huge milestone, but in reality it is the first small step in a long journey. We had a lot of great ideas surface at our inaugural meeting, and we’re on our way to implementing a few of them. In order to do that, we need to get some things connected to the network. We could buy the sensors that we need, but given our initial budget, modest goals, and DIY ethic, we’re going to build a few things instead. I’m not saying commercial LoRaWAN products are off the table, that’s definitely not the case, but as we grow our understanding of the potential of the platform it is in our best interests to develop a deep knowledge of how it all works. For open source folks, that means learning by doing!

I’ve been doing a little digging into development boards for LoRaWAN applications, and there are many options. The Things Network makes their own Arduino based board, The Things Uno. There are also a few options from Adafruit, including a Feather board, a Pycom option for the Pythonistas out there, and a breakout to use with your controller of choice. There are even RaspberryPi hats for building your own gateways, should you choose to go that route (we didn’t, we are using Sentrius RG1xx gateways instead). There are also a ton of cheap ESP32 based options on Amazon from companies like DIYMall, some of which have neat features like an on-board OLED display at price points that might make them worth a look for a hackable project. As an added bonus, you can prototype on those cheap little boards using the Arduino tooling. Sounds like fun, right?! I picked up a couple of the Heltec ESP32 LoRa dev boards to hack around with. There were some tricky bits to getting it all working, so I decided to write up what worked for me in the hopes that it helps somebody else.

Setting up your development environment

I’m doing all this on a 2016 Macbook Pro, using OS 10.13.6. If you are on Windows, Linux, or a different version of Mac, you may have to modify these steps a bit but the basics should be the same.

One of the first challenges is getting everything properly set up for development. The first thing you’ll need to install is the Arduino IDE. I’ve been using version 1.8.5, and at the time of this article, 1.8.7 is the latest and greatest. Once you have the IDE set up, the next step is to add the Espressif ESP32 core. The instructions are in the Github repo, and they work as documented. Make absolutely certain you get the latest Arduino core for the ESP32! Older versions have a bug in the implementation of micros() that will cause the LMIC library (which we’ll cover later) to stop working. Without getting into all the details, with an older version of the core the value returned by micros() will reset to zero periodically, and this causes the LMIC library to schedule actions for a time that is never reached. This Github issue explains a similar issue.

After installing the Espressif core, you’ll also need to install the Silicon Labs CP210x USB to UART Bridge VCP Drivers. This driver is necessary so that the Heltec board will show up as a serial port once it is connected to your computer. On the subject of connections, there were a few things I had to do that deviated from some of the other articles on this subject. First, I had to install both drivers from Silicon Labs. Inside the download package for Mac there are two drivers, a new one and a legacy version. Installing one or the other did not allow the Heltec board to register as a serial port. Once both were installed and the system was restarted, the Arduino IDE still could not see the port. I’ve seen this problem before with some other dev boards, and the fix was simple. I inserted a USB2.0 hub between the board and my Mac, and now the Arduino IDE could see the port. It appears in the “ports” list in the IDE as /dev/cu.SLAB_USBtoUART. This can be confirmed by going to “About This Mac” in the system menu, and clicking the “System Report” button. View the USB device tree, and you should see something that looks like this:


The Heltec board I picked up uses a Semtech sx1276 chip to handle the LoRa communications. This is just a transceiver though, it does not implement the full LoRaWAN stack like some other products (notably those from Microchip, which is used in the The Things Uno). To get LoRaWAN working, there is a port of the IBM LMIC (LoRaMAC-in-C) library for the Arduino platform that provides the stack. Add this to your Arduino library list in the usual manner.  If you want to dig into the dirty details on the LMIC library, there is a handy PDF out there that covers it well.  There is one more step to take before the LMIC library is ready to go.  Find the config.h file for the LMIC library, and open it in your editor of choice.  Near the top of the file there are a couple lines that define the frequency range:

//#define CFG_eu868 1
#define CFG_us915 1

I’m in the US, so I uncommented CFG_us915 and commented out the European option.  While you are in this header file, confirm that the correct Semtech transceiver is configured:

// This is the SX1272/SX1273 radio, which is also used on the HopeRF
// RFM92 boards.
//#define CFG_sx1272_radio 1
// This is the SX1276/SX1277/SX1278/SX1279 radio, which is also used on
// the HopeRF RFM95 boards.
#define CFG_sx1276_radio 1

The Heltec board uses a sx1276 radio, so that’s the one you should have uncommented.  If you are interested, this is also where you can turn up the log levels and define where they are output.  I set the level to debug (2) because I wanted to learn as much as possible about what was going on in there at runtime, and configured the failures to be written out to the Arduino Serial port since that’s where the serial monitor is connected:

#define LMIC_FAILURE_TO Serial

Now that you can connect to your board and add the LMIC library to a sketch, you’re halfway there!

Configuring a device in The Things Network

The next step is to set up an application in The Things Network, and add a couple devices to the application. I’m not going to reproduce the full set of instructions here, since TTN does a great job of explaining it on their own web site. In short, you’ll need to create an account on The Things Network, create an application, and then add a couple of devices to that application.  All of these tasks are accomplished via The Things Network Console.

For testing purposes, I suggest creating two devices in your application.  TTN supports two methods of activating a device.  The first is activation by personalization or ABP.  The second is over the air activation or OTAA.  OTAA is preferred for production, but is a bit more complex.  You can find a more in depth discussion of the differences between ABP and OTAA here.  Following some advice in the TTN forums (a great resource, by the way!), I decided to get ABP working first, and them move on to OTAA.  So, for the purposes of getting this up and running, create two devices.  Configure the first to use ABP, and the second to use OTAA.

An ABP example

The LMIC library comes with two examples that we can use as a starting point, one for each activation type.  In the Arduino IDE, go to File -> Examples -> LMIC-Arduino and open the ttn-abp example.  There are a couple changes that are needed before this sketch will work on the Heltec ESP32 board.  The first change is to adjust the pin mapping used by the LMIC library.  Find the lmic_pinmap constant, and use the following values:

const lmic_pinmap lmic_pins = {
.nss = 18,
.rst = 14,
.dio = {26, 33, 32},

Next, we need to add SPI initialization to the sketch.  Add the following line to the beginning of the setup() function in the example sketch:

SPI.begin(5, 19, 27);

The final step is to copy the network session key, the application session key, and device ID into the sketch.  These can be found on the device page for the device you have configured for ABP.

Screen Shot 2018-09-12 at 8.45.42 PM

And that’s it!  You should be able to upload the modified sketch to the Heltec board and get it talking to The Things Network.  Note that if you restart the LoRaWAN board, you will also need to reset the frame counter or TTN will drop the message.  Frame counter are used to prevent replay attacks on the network.  If the incoming frame is a lower number than the last known frame, the message will be dropped as invalid.  With an ABP device, the frame counter must be tracked on the device across restarts or reset on TTN after a restart.  OTAA does not have this problem, as the frame counter is reset during the activation process.  Alternatively, you could go into the ABP device settings on the TTN console and disable frame counter checks, but this is generally not a good idea for a deployed device.  It is handy for development though.

An OTAA example

Setting up the OTAA example starts out exactly the same as the ABP example.  Find the example sketch, ttn-otaa in this case, and open it in the Arduino IDE.  It is also necessary to set up the lmic_pinmap constant, and add SPI initialization to the setup() function.  After that is complete, the setup diverges a bit.

The ABP example uses a preconfigured device ID and preconfigured app and device session keys.  OTAA is a little bit different.  An OTAA device will activate itself on the network, and to do that it requires a different set of information.  Instead of supplying a device ID and app/device keys, we’ll provide the device and application EUI and an application key.  The activation process will generate the device ID and session keys, and send them down to the device to secure further communications.  The EUIs and app key can be found on the device page for the device you have configured for OTAA:

Screen Shot 2018-09-12 at 8.58.39 PM

Note that the EUIs need to be provided to the sketch in least significant bit format, but the TTN device page provides a handy little button to reverse it for you.  The app key can be provided as-is.  With both EUIs and the key in your sketch, you can upload it to the board and it should start sending data.  As noted earlier, there is no need to disable frame counter checks or reset frame counters when resetting the device as their is for ABP devices.

A few notes and errata

The sketch examples provided will try to send data every 60 seconds or so.  In my testing I ran into some problems with the sketch scheduling the next message via the os_setTimedCallback function.  In my code I’m setting a flag on the EV_TXCOMPLETE event to indicate that the previous message send has completed, and then scheduling the next one in the normal loop() function.  Also, the loop (initiated by os_runloop_once()) used to send the message and handle the subsequent receive windows seems to be timing sensitive (no surprise there) so it is critical to not introduce delays or other long running operations while that loop is running.  There is also a huge thread on the TTN forums about these boards, with a ton of great information.

I hope this helps somebody avoid a few of the things that I tripped on while getting this board running on The Things Network.  The Heltec ESP32 LoRa boards have some drawbacks, but for the price they certainly have some potential!


  1. Thank you for that awesome tutorial.
    Regarding LMIC and TTN, it is describes in many places that it is not enough to just copy the TTN-generated Device Address and Application Id into the example code,
    but it is also necessary to reverse the Byte Order (not the bit order) of these 8 byte sequences.
    Is that true also for the Esp32?

    Mine is stuck at “EV_JOINING”, and I wonder if that is due to wrong credentials or the bad antenna (https://twitter.com/projectgus/status/922781274355351552). Would there be a way to find out from programming / display to the OLED?


    • Yes, you do need to reverse the byte order for those two sequences. That will apply to anything that uses a fork of LMIC, I think.

      If it is stuck joining, you might have some timing issues. Do you see the join attempts on TTN console?


  2. could you please help me? i followed your instructions and i compiled the fttn-otaa file i got this:
    C:\Users\Asus\Documents\Arduino\libraries\LMIC-Arduino\src\hal\hal.cpp: In function ‘void hal_printf_init()’:
    C:\Users\Asus\Documents\Arduino\libraries\LMIC-Arduino\src\hal\hal.cpp:223:54: error: ‘_FDEV_SETUP_WRITE’ was not declared in this scope
    fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
    C:\Users\Asus\Documents\Arduino\libraries\LMIC-Arduino\src\hal\hal.cpp:223:71: error: ‘fdev_setup_stream’ was not declared in this scope
    fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);
    exit status 1
    Erro compilando para a placa WIFI_LoRa_32
    do you know whats wrong?


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s