Skip to content

Proper working Arduino library for the Heltec ESP32 LoRa v3 board, uses RadioLib

License

Notifications You must be signed in to change notification settings

therealgglggl/heltec_esp32_lora_v3

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Heltec ESP32 LoRa v3

The unofficial library

Introduction

There's this Chinese company named Heltec, and they make a cool little development board that has an Espressif ESP32S3 (which has WiFi and Bluetooth), a 128x64 pixel OLED display and an SX1262 863-928 MHz radio on it. It sells under different names on the internet, but internally they call it HTIT-WB32LA. (They have a 470-510 MHz version also, called HTIT-WB32LAF.) The hardware is cool, the software that comes with it is not so much my taste. There's multiple GitHub repositories, it's initailly unclear what is what, they use some radio stack of unknown origin, code-quality and documentation varies, some examples need tinkering and what could be a cool toy could easily become a very long weekend of frustration before things sort of work.

This library allows you to use that time to instead play with this cool board. The examples are tested, and this library assumes that for all things sub-GHz, you want to use the popular RadioLib.

 

(Click blue headings for more detailed documentation)

Setting up

Make sure your Arduino IDE knows about ESP-32 boards by putting the following URL under "Additional board manager URLs" in the Arduino IDE settings:

https://espressif.github.io/arduino-esp32/package_esp32_index.json

Then under "Settings / Board" select "Heltec WiFi LoRa 32(V3) / Wireless shell (V3) / Wireless stick lite (V3)".

 

Getting started

In your sketch, #include <heltec.h>, this will provide the display, radio and button instances. Then in your setup(), put heltec_setup() to initialize the serial port at 115.200 bps and initialize the display. In the loop part of your sketch, put heltec_loop(). This will make sure the button is scanned, and provides the deep sleep "off" functionality if you set that up.

#include <heltec.h>

void setup() {
  heltec_setup();

  [...]
}

void loop() {
  heltec_loop();

  [...]
}

 

If you #define HELTEC_NO_RADIO_INSTANCE and/or #define HELTEC_NO_DISPLAY_INSTANCE before #include <heltec.h>, you get no instances of radio and/or display, so you can set these up manually. Note that the library then also doesn't turn things off at sleep, etc.

 

This library includes my fork of RadioLib. This is because that fork uses my ESP32_RTC_EEPROM when compiled on ESP32, allowing for much less wear on the ESP32 flash. RadioLib plans to have a more generic mechanism allowing for the retention of state information and as soon as that's in there, this library will depend on (and thus auto-install) the latest version of RadioLib instead of including a copy of it. As long as this uses my fork, make sure the original version of RadioLib is uninstalled to avoid the compiler getting confused.

Next to the radio examples in this library, all RadioLib examples that work with an SX1262 work here. Simply #include <heltec.h> instead of RadioLib and remove any code that creates a radio instance, it already exists when you include this library.

  • It might otherwise confuse you at some point: while Heltec wired the DIO1 line from the SX1262 to the ESP32 (as they should, it is the interrupt line), they labeled it in their pins_arduino.h and much of their own software as DIO0. The SX1262 IO pins start at DIO1.
  • If you place #define HELTEC_NO_RADIOLIB before #include <heltec.h>, RadioLib will not be included and this library won't create a radio object. Handy if you are not using the radio and need the space in flash for something else or if you want to use another radio library or so.

 

Convenience macros: RADIOLIB() and RADIOLIB_OR_HALT()

This library provides convenience macros when calling RadioLib functions. It can be used for those functions that return a status code. When your code calls

RADIOLIB_OR_HALT(radio.setFrequency(866.3));

this gets translated into

  _radiolib_status = radio.setFrequency(866.3);
  _radiolib_status = action;
  Serial.print("[RadioLib] ");
  Serial.print("radio.setFrequency(866.3)");
  Serial.print(" returned ");
  Serial.print(_radiolib_status);
  Serial.print(" (");
  Serial.print(radiolib_result_string(_radiolib_status));
  Serial.println(")");
  if (_radiolib_status != RADIOLIB_ERR_NONE) {
    Serial.println("[RadioLib] Halted");
    while (true) {
        heltec_loop();
    }
  }

In other words, this saves a whole lot of typing if what you want is for RadioLib functions to be called and serial debug output to be generated. Calling RADIOLIB instead of RADIOLIB_OR_HALT does the same thing without the halting.

  • The heltec_loop() part in RADIOLIB_OR_HALT makes sure that if you have set the PRG button to be the power button, it still works when execution is halted.
  • _radiolib_status is an integer that the library provides and that your code can check afterwards to see what happened.
  • radiolib_result_string() returns a textual representation (e.g. CHIP_NOT_FOUND) for a few of the most common errors or a URL to look up the others.

 

The tiny OLED display uses the same library that the original library from Heltec uses, except now the examples work so you don't have to figure out how to make things work. It is included inside this library because the Heltec board needs a hardware reset and I adapted some things to make the Arduino print functionality work better. (The latter change submitted to the original library also.)

There's the primary display library and there's an additinal UI library that allows for multiple frames. The display examples will show you how things work. The library, courtesy of ThingPulse, is well-written and well-documented. Check them out and buy their stuff.

Printing to both Serial and display: both.print()

Instead of using print, println or printf on either Serial or display, you can also print to both. As the name implies, this prints the same thing on both devices. You'll find it used in many of this library's examples.

 

The user button marked 'PRG' on the board is handled by another library this one depends on, called MultiButton. Since we have only one button, it makes sense to have button.isSingleClick(), button.isDoubleClick() and so forth. Just remember to put heltec.loop() in theloop() of your sketch if you use it.

Using it as the power button

If you hook up this board to power, and especially if you hook up a LiPo battery (see below), you'll notice there's no on/off switch. Luckily the ESP32 comes with a very low-power "deep sleep" mode where it draws so little current it can essentially be considered off. Since signals on GPIO pins can wake it back up, we can use the button on the board as a power switch. In your sketch, simply put #define HELTEC_POWER_BUTTON before #include <heltec.h>, make sure heltec_loop() is in your own loop() and then a button press will wake it up and a long press will turn it off. You can still use button.isSingleClick() and button.isDoubleClick() in your loop() function when you use it as a power button.

  • If you use delay() in your code, the power off function will not work during that delay. To fix that, simply use heltec_delay() instead.

 

You can use heltec_deep_sleep(<seconds>) to put the board into this 'off' deep sleep state yourself. This will put the board in deep sleep for the specified number of seconds. After it wakes up, it will run your sketch from the start again. You can use heltec_wakeup_was_button() and heltec_wakeup_was_timer() to find out whether the wakeup was caused by the power button or because your specified time has elapsed. You can even hold on to some data in variables that survive deep sleep by tagging them RTC_DATA_ATTR. More is in this tutorial.

In deep sleep, with this library, according my multimeter power consumption drops to 147 µA if you have defined HELTEC_POWER_BUTTON, or 24 µA if you only use the timer to wake up. Please let me know if you can get it lower than that.

  • If you call heltec_deep_sleep() without a number in seconds when not using the power button feature, you will need to reset it to turn it back on. Note that resetting does reinitialize any RTC_DATA_ATTR variables.

 

LED

The board has a bright white LED, next to the orange power/charge LED. This library provides a function heltec_led that takes the LED brightness in percent. It's really bight, you'll probably find 50% brightness is plenty.

 

Battery

The board is capable of charging a LiPo battery connected to the little 2-pin connector at the bottom. heltec_vbat() gives you a float with the battery voltage, heltec_battery_percent() provides the estimated percentage full.

Note that it takes a single cell (3.7 V) LiPo and that the plus is on the left side when holding the board with the USB-C connector facing up.

  • According to the schematic, the charge current is set to 500 mA. There's a voltage measuring setup where if GPIO37 is pulled low, the battery voltage appears on GPIO1. (Resistor-divided: VBAT - 390kΩ - GPIO1 - 100kΩ - GND)
  • You can optionally provide the float that heltec_vbat() returns to heltec_battery_percent() to make sure both are based on the same measurement.
  • The charging IC used will charge the battery to ~4.2V, then hold the voltage there until charge current is 1/10 the set current (i.e. 50 mA) and then stop and let it discharge to 4.05V (about 90%) and then charge it again, so this is expected.
  • The orange charging LED, on but very dim is no battery is plugged in, is awfully bright when charging, and the IC on the reverse side of the reset switch gets quite hot when the battery is charging but still fairly empty. It's limited to 100 ℃ so nothing too bad can happen, just so you know.

The battery percentage estimate in this library is based on a real LiPo discharge curve.

The library contains all the tools to measure your own curve and use it instead, see heltec.h for details.

 

Ve - external power

There's two pins marked 'Ve' that are wired together and connected to a GPIO-controlled FET that can source 350 mA at 3.3V to power sensors etc. Turn on by calling heltec_ve(true), heltec_ve(false) turns it off.

 

 

Minimal example to show everything:

// Turns the 'PRG' button into the power button, long press is off 
#define HELTEC_POWER_BUTTON   // must be before "#include <heltec.h>"

// creates 'radio', 'display' and 'button' instances 
#include <heltec.h>

void setup() {
  heltec_setup();
  Serial.println("Serial works");
  // Display
  display.println("Display works");
  // Radio
  display.print("Radio ");
  int state = radio.begin();
  if (state == RADIOLIB_ERR_NONE) {
    display.println("works");
  } else {
    display.printf("fail, code: %i\n", state);
  }
  // Battery
  float vbat = heltec_vbat();
  display.printf("Vbat: %.2fV (%d%%)\n", vbat, heltec_battery_percent(vbat));
}

void loop() {
  heltec_loop();
  // Button
  if (button.isSingleClick()) {
    display.println("Button works");
    // LED
    for (int n = 0; n <= 100; n++) { heltec_led(n); delay(5); }
    for (int n = 100; n >= 0; n--) { heltec_led(n); delay(5); }
    display.println("LED works");
  }
}

For a more meaningful demo, especially if you have two of these boards, check out LoRa_rx_tx in the examples. The LoRaWAN_TTN example works, uses The Things Network and goes to deep sleep between sends.

 

Pinout

 

 

 

If you read this far, would you please star this repository? (Not so much for my ego, but it helps other people find it. Thanks!)

About

Proper working Arduino library for the Heltec ESP32 LoRa v3 board, uses RadioLib

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C++ 85.3%
  • C 14.6%
  • Python 0.1%