The Ebyte LoRa E32 library is a comprehensive tool designed to facilitate seamless communication with LoRa wireless modules. Developed for programmers and tech enthusiasts alike, the library enables a smooth interfacing with Ebyte’s LoRa E32 devices using MicroPython, a lean and efficient implementation of the Python 3 programming language.
Ebyte LoRa E32 & MicroPython: exploring the library
LoRa E32-TTL-100
Here my device selection AliExpress (433MHz 5Km) - AliExpress (433MHz 8Km) - AliExpress (433MHz 16Km) - AliExpress (868MHz 915MHz 5.5Km) - AliExpress (868MHz 915MHz 8Km)
They can work over a distance of 3000m to 8000m and have many features and parameters.
So I created this library to simplify the usage.
Library
To install the library, you can download It from this GitHub repository:
Or you can install it via Pypi with the command:
1
pip
install
ebyte-lora-e32
On Thonny IDE, you can use Tools --> Manage plug-ins...
.
EByte LoRa E32 MicroPython: PyPi, pip library Thonny
Pinout
EByte LoRa Exx pinout
Pin No. Pin item Pin direction Pin application 1 M0 Input(weak pull-up) Work with M1 & decide on the four operating modes. Floating is not allowed, it can be ground. 2 M1 Input(weak pull-up) Work with M1 & decide on the four operating modes. Floating is not allowed. It can be ground. 3 RXD Input TTL UART inputs connect to external (MCU, PC) TXD output pin. It can be configured as open-drain or pull-up input. 4 TXD Output Work with M0 & decide on the four operating modes. Floating is not allowed. It can be ground. 5 AUX OutputTTL UART outputs connect to external RXD (MCU, PC) input pin. It can be configured as open-drain or push-pull output. 6 VCC Power supply 2.3V~5.5V DC 7 GND Ground
As you can see, you can set various modes via M0 and M1 pins.
Mode M1 M0 Explanation Normal 0 0 UART and the wireless channel are good to go Wake-Up 0 1 Used in setting parameters. Transmitting and receiving are disabled. Power-Saving 1 0 Same as normal, but a preamble code is added to transmitted data for waking-up the receiver. Sleep 1 1 Used in setting parameters. Transmitting and receiving disabled.
For the next simple test, we are going to use Normal mode.
Some pins can be used statically, but If you connect Them to the microcontroller and configure them in the library, you gain in performance, and you can control all modes via software, but we will explain better next.
Fully connected schema
As I already said, It’s not essential to connect all pins to the output of the microcontroller, you can put M0 and M1 pins to HIGH or LOW to get the desired configuration, and if you don’t connect AUX, the library sets a reasonable delay to be sure that the operation is complete .
AUX pin
When transmitting, data can be used to wake up external MCU and return HIGH on data transfer finish.
LoRa E32 AUX Pin on transmission
When receiving, AUX goes LOW and returns HIGH when the buffer is empty.
LoRa e32 AUX pin on reception
It’s also used for self-checking to restore regular operation (on power-on and sleep/program mode).
LoRa e32 AUX pin on self-check
Wiring
esp32
ESP32 DOIT DEV KIT v1 pinout
Ebyte LoRa E22 device esp32 dev kit v1 breadboard full connection
M0 D21 M1 D19 TX PIN RX2 (PullUP 4,7KΩ) RX PIN TX3 (PullUP 4,7KΩ) AUX PIN D18 (PullUP 4,7KΩ) (D15 to wake up) VCC 5V GND GND
1
2
uart2
=
UART(
2
)
lora
=
LoRaE32(
'433T20D'
, uart2, aux_pin
=
15
, m0_pin
=
21
, m1_pin
=
19
)
Raspberry Pi Pico
Raspberry Pi Pico rp2040 pinout low resolution
And here is the connection diagram. For this test, you can remove the AUX pin connection. You can also see that I use a different default Serial port because It differs from the Arduino environment.
MicroPython default setting
Raspberry Pi Pico EByte LoRa Exx fully connected MicroPython
E32 Raspberry Pi Pico M0 10 M1 11 RX TX1 GPIO4 (PullUP 4,7KΩ) TX RX1 GPIO5 (PullUP 4,7KΩ) AUX 2 (PullUP 4,7KΩ) VCC 5v GND GND
Pay attention UART(1) use different pin in MicroPython than Arduino environment Serial1
1
2
3
# Create a UART object to communicate with the LoRa module with Raspberry Pi Pico
uart2 = UART(1)
lora = LoRaE32('433T20D', uart2, aux_pin=2, m0_pin=10, m1_pin=11)
Arduino default Serial
Arduino default Serial is different from MicroPython. Here is a schema to use the same interface.
Raspberry Pi Pico EByte LoRa Exx fully connected
E32 Raspberry Pi Pico M0 10 M1 11 RX TX1 GPIO8 (PullUP 4,7KΩ) TX RX1 GPIO9 (PullUP 4,7KΩ) AUX 2 (PullUP 4,7KΩ) VCC 5v GND GND
For the Arduino standard pinout, you must change the UART declaration so uart2 = UART(1, rx=Pin(9), tx=Pin(8))
Constructor
I made a set of numerous constructors because we can have more options and situations to manage.
1
2
3
4
5
6
7
8
9
10
11
uart2
=
UART(
1
, rx
=
Pin(
9
), tx
=
Pin(
8
))
lora
=
LoRaE32(
'433T20D'
, uart2, aux_pin
=
2
, m0_pin
=
10
, m1_pin
=
11
)
The provided code segment demonstrates how to set up a UART (Universal Asynchronous Receiver/Transmitter) connection with a LoRa module, specifically an Ebyte LoRa E32 device, using either the ESP32 or Raspberry Pi Pico microcontrollers. UART is a hardware device or a protocol that helps in serial communication between two devices.
Initially, the code sets up the UART interface, which is used for serial communication between the microcontroller (either ESP32 or Raspberry Pi Pico) and the LoRa module.
In the case of ESP32, the UART object would be initialized as uart2 = UART(2)
. This line is commented out in the given code because the setup is demonstrated for the Raspberry Pi Pico.
For the Raspberry Pi Pico, there are two alternatives presented. The first one is a simple initialization: uart2 = UART(1)
. The second one specifies the particular pins for RX (receiving data) and TX (transmitting data): uart2 = UART(1, rx=Pin(9), tx=Pin(8))
. This line sets the UART channel to 1 and specifies that Pin 9 is for receiving data (rx) and Pin 8 is for transmitting data (tx).
The next step is to create an instance of the LoRaE32 class. The LoRaE32()
constructor is used to initialize the object named lora
. It takes four parameters:
The model name of the Ebyte LoRa E32 module, in this case, ‘433T20D’.
The UART object (uart2
), which enables serial communication with the LoRa module.
The AUX pin number, which is used for auxiliary functions like indicating the operational status of the module or for handshaking. Here, it’s set to 2 for Raspberry Pi Pico.
The M0 and M1 pin numbers, which are used to switch the module between different operating modes. In this case, they are set to 10 and 11, respectively, for the Raspberry Pi Pico.
By initializing the lora
object with these parameters, the microcontroller is ready to interact with the LoRa module, and you can start configuring and using the module for wireless communication.
Remember to uncomment or modify the code according to the microcontroller you are using, whether it’s ESP32 or Raspberry Pi Pico.
begin()
The begin
command is used to startup Serial and pins in input and output mode.
1
2
code
=
lora.begin()
print
(ResponseStatusCode.get_description(code))
The provided lines of code are used to start the LoRa module and print a description of the response status code obtained during the initialization process.
code = lora.begin()
: This line of code calls the begin()
function on the lora
object, which was previously instantiated as an instance of the LoRaE32
class. The begin()
function is typically used to initialize the LoRa module and prepare it for communication. This function might include steps like setting the LoRa module to the correct mode, checking that it is responding correctly, or even performing some initial configuration. The function returns a status code that indicates whether the initialization process was successful or if any errors occurred.
print(ResponseStatusCode.get_description(code))
: This line is used to print a human-readable description of the response status code that was returned by the begin()
function. The get_description()
function is a method of the ResponseStatusCode
class, which is designed to map status code values to their corresponding descriptions. By passing the code
returned by begin()
to get_description()
, you can get a text description of the initialization result, which can be very helpful for debugging. For example, if the initialization was successful, you might get a description like “Success”, while if an error occurred, you might get a message indicating the type of error.
Configuration and information method
There are many methods for managing configuration and getting information about the device.
getConfiguration
In the context of a LoRa E32 module, maintaining a keen awareness of its current configuration can be crucial in managing its operations effectively. In this regard, the ability to retrieve and understand the module’s configuration is fundamental. The Python code provided above demonstrates a straightforward method to achieve this task using the lora_e32
and lora_e32_operation_constant
libraries.
1
2
3
4
5
6
7
from
lora_e32
import
LoRaE32, print_configuration, Configuration
from
lora_e32_operation_constant
import
ResponseStatusCode
code, configuration
=
lora.get_configuration()
print
(ResponseStatusCode.get_description(code))
print_configuration(configuration)
The LoRaE32
class, which is central to interacting with the LoRa module, and the print_configuration
function, which translates the configuration data into a human-readable format. It also imports the Configuration
class that represents the module’s configuration and the ResponseStatusCode
class to interpret the response of the get_configuration()
function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
----------------------------------------
HEAD : 0b11000000 192
AddH : 0
AddL : 2
Chan : 23 -> 433
SpeedParityBit : 0b0 -> 8N1 (Default)
SpeedUARTDatte : 0b11 -> 9600bps (default)
SpeedAirDataRate : 0b10 -> 2.4kbps (default)
OptionTrans : 0b1 -> Fixed transmission (first three bytes can be used a
s high
/low
address and channel)
OptionPullup : 0b1 -> TXD, RXD, AUX are push-pulls
/pull-ups
(default)
OptionWakeup : 0b0 -> 250ms (default)
OptionFEC : 0b1 -> Turn on Forward Error Correction Switch (Default)
OptionPower : 0b0 -> 20dBm (Default)
----------------------------------------
The final part of the code prints the configuration using the print_configuration()
function. This function provides a structured overview of the current LoRa module’s settings, offering a snapshot of its operational parameters. These parameters include various details, such as the header of the configuration message, the device address, the operating channel, the speed settings, and the transmission settings, including the mode, I/O mode, wireless wake-up time, error correction, and transmission power. Understanding these parameters is pivotal in diagnosing operational issues, fine-tuning performance, and ensuring successful data transmission over the LoRa network.
setConfiguration
Configuring the LoRa E32 module to meet specific operational requirements is an essential aspect of using it effectively. This Python code illustrates how to define and set the configuration of a LoRa module, offering a clear demonstration of the flexibility and control provided by the lora_e32
library.
1
2
3
4
5
configuration_to_set
=
Configuration(
'433T20D'
)
configuration_to_set.ADDL
=
0x02
configuration_to_set.OPTION.fixedTransmission
=
FixedTransmission.FIXED_TRANSMISSION
code, confSetted
=
lora.set_configuration(configuration_to_set)
The process begins with the creation of a Configuration
object. This object represents the entire configuration of the LoRa module. The user sets the parameters of the configuration according to their specific needs. In this example, the configuration is tailored to the ‘433T20D’ model of the LoRa module, and the lower part of the address (ADDL) is set to 0x02. The fixed transmission mode is also set using a constant from the FixedTransmission
class.
Once the configuration object has been suitably defined, it’s passed to the set_configuration()
function of the LoRaE32
object. This function uploads the defined configuration to the LoRa module, effectively changing its operation according to the new settings.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class
Configuration:
class
Speed:
def
__init__(
self
, model):
self
.model
=
model
self
.airDataRate
=
AirDataRate.AIR_DATA_RATE_010_24
self
.uartBaudRate
=
UARTBaudRate.BPS_9600
self
.uartParity
=
UARTParity.MODE_00_8N1
class
Option:
def
__init__(
self
, model):
self
.model
=
model
self
.transmissionPower
=
TransmissionPower(
self
.model).get_transmission_power().get_default_value()
self
.fec
=
ForwardErrorCorrectionSwitch.FEC_1_ON
self
.wirelessWakeupTime
=
WirelessWakeUpTime.WAKE_UP_250
self
.ioDriveMode
=
IODriveMode.PUSH_PULLS_PULL_UPS
self
.fixedTransmission
=
FixedTransmission.TRANSPARENT_TRANSMISSION
class
Configuration:
def
__init__(
self
, model):
self
.HEAD
=
0
self
.ADDH
=
0
self
.ADDL
=
0
self
.SPED
=
Speed(model)
self
.CHAN
=
23
self
.OPTION
=
Option(model)
The code also gives a glimpse into the structure of the Configuration
class, showcasing its nested classes Speed
and Option
. These classes encapsulate different aspects of the LoRa module’s configuration, such as the speed of data transmission and various operating options. It’s worth noting that the classes contain a multitude of parameters, each with predefined constants for easier and more reliable configuration. These parameters include air data rate, UART baud rate, UART parity, transmission power, forward error correction switch, wireless wake-up time, I/O drive mode, and fixed transmission.
The nested structure of the Configuration
class and the use of predefined constants underscore the organized and intuitive approach of the lora_e32
library in managing the LoRa module’s configuration. This modular design allows developers to tailor the LoRa module’s operation to their specific needs, enabling more efficient and effective use of the technology.
Basic configuration option
ADDH High address byte of the module (the default 00H) 00H-FFH ADDL Low address byte of the module (the default 00H) 00H-FFH SPED Information about data rate parity bit and Air data rate CHAN Communication channel(410M + CHAN*1M), default 17H (433MHz), valid only for 433MHz device check below to check the correct frequency of your device 00H-1FH OPTION Type of transmission, pull-up settings, wake-up time, FEC, Transmission power
SPED detail
UART Parity bit: UART mode can be different between communication parties
7 6 UART parity bit Constant value 0 0 8N1 (default) MODE_00_8N1 0 1 8O1 MODE_01_8O1 1 0 8 E1 MODE_10_8E1 1 1 8N1 (equal to 00) MODE_11_8N1
UART baud rate: UART baud rate can be different between communication parties. The UART baud rate has nothing to do with wireless transmission parameters & won’t affect the wireless transmit/receive features.
5 4 3 TTL UART baud rate(bps) Constant value 0 0 0 1200 UART_BPS_1200 0 0 1 2400 UART_BPS_2400 0 1 0 4800 UART_BPS_4800 0 1 1 9600 (default) UART_BPS_9600 1 0 0 19200 UART_BPS_19200 1 0 1 38400 UART_BPS_38400 1 1 0 57600 UART_BPS_57600 1 1 1 115200 UART_BPS_115200
Air data rate: The lower the air data rate, the longer the transmitting distance, better anti-interference performance, and longer transmitting time; the air data rate must be constant for both communication parties.
2 1 0 Air data rate(bps) Constant value 0 0 0 0.3k AIR_DATA_RATE_000_03 0 0 1 1.2k AIR_DATA_RATE_001_12 0 1 0 2.4k (default) AIR_DATA_RATE_010_24 0 1 1 4.8k AIR_DATA_RATE_011_48 1 0 0 9.6k AIR_DATA_RATE_100_96 1 0 1 19.2k AIR_DATA_RATE_101_192 1 1 0 19.2k (same to 101) AIR_DATA_RATE_110_192 1 1 1 19.2k (same to 101) AIR_DATA_RATE_111_192
OPTION detail
Transmission mode: The first three bytes of each user’s data frame can be used as high/low address and channel in fixed transmission mode. The module changes its address and channel when transmitted. It will revert to the original setting after completing the process.
7 Fixed transmission enabling bit(similar to MODBUS) Constant value 0 Transparent transmission mode FT_TRANSPARENT_TRANSMISSION 1 Fixed transmission mode FT_FIXED_TRANSMISSION
IO drive mode: this bit is used for the module’s internal pull-up resistor. It also increases the level of adaptability in case of an open drain. But in some cases, it may need an external pull-up resistor.
6 IO drive mode ( default 1) Constant value 1 TXD and AUX push-pull outputs, RXD pull-up inputs IO_D_MODE_PUSH_PULLS_PULL_UPS 0 TXD、AUX open-collector outputs, RXD open-collector inputs IO_D_MODE_OPEN_COLLECTOR
Wireless wake-up time: the transmit & receive module work in mode 0, whose delay time is invalid & can be an arbitrary value; the transmitter works in mode one can send the preamble code of the corresponding time continuously when the receiver operates in mode 2, the time means the monitor interval time (wireless wake-up). Only the data from the transmitter that works in mode one can be received.
5 4 3 Wireless wake-up time Constant value 0 0 0 250ms (default) WAKE_UP_250 0 0 1 500ms WAKE_UP_500 0 1 0 750ms WAKE_UP_750 0 1 1 1000ms WAKE_UP_1000 1 0 0 1250ms WAKE_UP_1250 1 0 1 1500ms WAKE_UP_1500 1 1 0 1750ms WAKE_UP_1750 1 1 1 2000ms WAKE_UP_2000
FEC: after turning off FEC, the actual data transmission rate increases while the anti-interference ability decreases. Also, the transmission distance is relatively short, and both communication parties must keep on the same pages about turn-on or turn-off FEC.
2 FEC switch Constant value 0 Turn off FEC FEC_0_OFF 1 Turn on FEC (default) FEC_1_ON
Transmission power
Exists difference from power devices, for example the 20dBm modules have:
1 0 Transmission power (approximation) Constant value 0 0 20dBm (default) POWER_20 0 1 17dBm POWER_17 1 0 14dBm POWER_14 1 1 10dBm POWER_10
The 27dBm modules have:
1 0 Transmission power (approximation) Constant value 0 0 27dBm (default) POWER_27 0 1 24dBm POWER_24 1 0 21dBm POWER_21 1 1 18dBm POWER_18
And the 30dBm modules:
1 0 Transmission power (approximation) Constant value 0 0 30dBm (default) POWER_30 0 1 27dBm POWER_27 1 0 24dBm POWER_24 1 1 21dBm POWER_21
Also, the frequencies change with the model version.
Model freq. Start value to add Channel 433 410 170 130 470 370 868 862 900 862 915 900
Send receive message
First, we must introduce a simple but useful method to check if something is in the receiving buffer.
It’s simple to return how many bytes you have in the current stream.
Normal transmission mode
Normal/Transparent transmission mode sends messages to all devices with the same address and channel.
LoRa E32 transmitting scenarios, lines are channels.
The first method is sendMessage and is used to send a String to a device in Normal mode .
1
2
3
message
=
'Hello, world!'
code
=
lora.send_transparent_message(message)
print
(
"Send message: {}"
, ResponseStatusCode.get_description(code))
The other device simply does on the loop.
1
2
3
4
5
6
7
while
True
:
if
lora.available() >
0
:
code, value
=
lora.receive_message()
print
(ResponseStatusCode.get_description(code))
print
(value)
utime.sleep_ms(
2000
)
Pay attention if you receive multiple messages in the buffer, and you don’t want reading all in one time you must use a delimiter
parameter
Use dictionary
There is an alternative method that allows sending a dictionary.
1
lora.send_transparent_dict({
'pippo'
:
'fixed'
,
'pippo2'
:
'fixed2'
})
And the respective receiver method.
1
code, value
=
lora.receive_dict()
Fixed mode instead of normal mode
In some manner, I create a set of methods to use with fixed transmission
Fixed transmission
You need to change only the sending method because the destination device doesn’t receive the preamble with Address and Channel when setting the fixed mode.
Fixed transmission has more scenarios
LoRa E32 transmitting scenarios, lines are channels
If you send it to a specific device (second scenario Fixed transmission), you must add ADDL, ADDH, and CHAN to identify It directly.
1
code
=
lora.send_fixed_message(
0
,
0x01
,
23
, message)
If you want to send a message to all devices in a specified Channel, you can use this method.
1
code
=
lora.send_broadcast_message(
23
, message)
If you wish to receive all broadcast messages in the network, you must set your ADDH
and ADDL
with BROADCAST_ADDRESS
.
1
2
3
4
5
6
7
8
9
configuration_to_set
=
Configuration(
'433T20D'
)
configuration_to_set.ADDL
=
BROADCAST_ADDRESS
configuration_to_set.ADDH
=
BROADCAST_ADDRESS
configuration_to_set.OPTION.fixedTransmission
=
FixedTransmission.FIXED_TRANSMISSION
code, confSetted
=
lora.set_configuration(configuration_to_set)
print
(
"Set configuration: {}"
, ResponseStatusCode.get_description(code))
Thanks
Now you have all the information to do your work, but I think It’s important to show some real examples to understand better all the possibilities.
EByte LoRa E32 & MicroPython: specifications, overview and first use
EByte LoRa E32 & MicroPython: exploring MicroPython library
EByte LoRa E32 & MicroPython: detailed look at the configuration
EByte LoRa E32 & MicroPython: a deep dive into transmission types