Ebyte LoRa E32 & MicroPython: exploring library – 2


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 library
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:

pip install ebyte-lora-e32

On Thonny IDE, you can use Tools --> Manage plug-ins... .

EByte LoRa E32 MicroPython: pypi, pip library Thonny
EByte LoRa E32 MicroPython: PyPi, pip library Thonny

Pinout

EByte LoRa Exx pinout
EByte LoRa Exx pinout
Pin No.Pin itemPin directionPin application
1M0Input(weak pull-up)Work with M1 & decide on the four operating modes. Floating is not allowed, it can be ground.
2M1Input(weak pull-up)Work with M1 & decide on the four operating modes. Floating is not allowed. It can be ground.
3RXDInputTTL UART inputs connect to external (MCU, PC) TXD output pin. It can be configured as open-drain or pull-up input.
4TXDOutputWork with M0 & decide on the four operating modes. Floating is not allowed. It can be ground.

5

AUX

Output
TTL UART outputs connect to external RXD (MCU, PC) input pin. It can be configured as open-drain or push-pull output.
6VCCPower supply 2.3V~5.5V DC
7GNDGround

As you can see, you can set various modes via M0 and M1 pins.

ModeM1M0Explanation
Normal00UART and the wireless channel are good to go
Wake-Up01Used in setting parameters. Transmitting and receiving are disabled.
Power-Saving10Same as normal, but a preamble code is added to transmitted data for waking-up the receiver.
Sleep11Used 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
ESP32 DOIT DEV KIT v1 pinout
Ebyte LoRa E22 device esp32 dev kit v1 breadboard full connection
Ebyte LoRa E22 device esp32 dev kit v1 breadboard full connection
M0D21
M1D19
TXPIN RX2 (PullUP 4,7KΩ)
RXPIN TX3 (PullUP 4,7KΩ)
AUXPIN D18 (PullUP 4,7KΩ) (D15 to wake up)
VCC5V
GNDGND
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
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
Raspberry Pi Pico EByte LoRa Exx fully connected MicroPython
E32Raspberry Pi Pico
M010
M111
RXTX1 GPIO4 (PullUP 4,7KΩ)
TXRX1 GPIO5 (PullUP 4,7KΩ)
AUX2 (PullUP 4,7KΩ)
VCC5v
GNDGND

Pay attention UART(1) use different pin in MicroPython than Arduino environment Serial1

# 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
Raspberry Pi Pico EByte LoRa Exx fully connected
E32Raspberry Pi Pico
M010
M111
RXTX1 GPIO8 (PullUP 4,7KΩ)
TXRX1 GPIO9 (PullUP 4,7KΩ)
AUX2 (PullUP 4,7KΩ)
VCC5v
GNDGND

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.

# Create a UART object to communicate with the LoRa module with ESP32
# uart2 = UART(2)
# Create a LoRaE32 object, passing the UART object and pin configurations
# lora = LoRaE32('433T20D', uart2, aux_pin=15, m0_pin=21, m1_pin=19)

# Create a UART object to communicate with the LoRa module with Raspberry Pi Pico
#uart2 = UART(1)
# Use the Serial1 pins of Arduino env on the Raspberry Pi Pico
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:

  1. The model name of the Ebyte LoRa E32 module, in this case, ‘433T20D’.
  2. The UART object (uart2), which enables serial communication with the LoRa module.
  3. 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.
  4. 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.

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.

  1. 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.
  2. 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.

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.

----------------------------------------
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.

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.

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

ADDHHigh address byte of the module (the default 00H)00H-FFH
ADDLLow address byte of the module (the default 00H)00H-FFH
SPEDInformation about data rate parity bit and Air data rate
CHANCommunication channel(410M + CHAN*1M), default 17H (433MHz), valid only for 433MHz device check below to check the correct frequency of your device00H-1FH
OPTIONType 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
008N1 (default)MODE_00_8N1
018O1MODE_01_8O1
108 E1MODE_10_8E1
118N1 (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.

543 TTL UART baud rate(bps) Constant value
0001200UART_BPS_1200
0012400UART_BPS_2400
0104800UART_BPS_4800
0119600 (default)UART_BPS_9600
10019200UART_BPS_19200
10138400UART_BPS_38400
11057600UART_BPS_57600
111115200UART_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.

210 Air data rate(bps) Constant value
0000.3kAIR_DATA_RATE_000_03
0011.2kAIR_DATA_RATE_001_12
0102.4k (default)AIR_DATA_RATE_010_24
0114.8kAIR_DATA_RATE_011_48
1009.6kAIR_DATA_RATE_100_96
10119.2kAIR_DATA_RATE_101_192
11019.2k (same to 101)AIR_DATA_RATE_110_192
11119.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
0Transparent transmission modeFT_TRANSPARENT_TRANSMISSION
1Fixed transmission modeFT_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
1TXD and AUX push-pull outputs, RXD pull-up inputsIO_D_MODE_PUSH_PULLS_PULL_UPS
0TXD、AUX open-collector outputs, RXD open-collector inputsIO_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.

543 Wireless wake-up time Constant value
000250ms (default)WAKE_UP_250
001500msWAKE_UP_500
010750msWAKE_UP_750
0111000msWAKE_UP_1000
1001250msWAKE_UP_1250
1011500msWAKE_UP_1500
1101750msWAKE_UP_1750
1112000msWAKE_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
0Turn off FECFEC_0_OFF
1Turn on FEC (default)FEC_1_ON

Transmission power

Exists difference from power devices, for example the 20dBm modules have:

10 Transmission power (approximation) Constant value
0020dBm (default)POWER_20
0117dBmPOWER_17
1014dBmPOWER_14
1110dBmPOWER_10

The 27dBm modules have:

10 Transmission power (approximation) Constant value
0027dBm (default)POWER_27
0124dBmPOWER_24
1021dBmPOWER_21
1118dBmPOWER_18

And the 30dBm modules:

10 Transmission power (approximation) Constant value
0030dBm (default)POWER_30
0127dBmPOWER_27
1024dBmPOWER_24
1121dBmPOWER_21

Also, the frequencies change with the model version.

Model freq.Start value to add Channel
433410
170130
470370
868862
900862
915900

Send receive message

First, we must introduce a simple but useful method to check if something is in the receiving buffer.

if lora.available() > 0:

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.

message = 'Hello, world!'
code = lora.send_transparent_message(message)
print("Send message: {}", ResponseStatusCode.get_description(code))

The other device simply does on the loop.

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.

lora.send_transparent_dict({'pippo': 'fixed', 'pippo2': 'fixed2'})

And the respective receiver method.

        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.

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.

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.

# Set the configuration to default values and print the updated configuration to the console
# Not needed if already configured
configuration_to_set = Configuration('433T20D')
# With BROADCASS ADDRESS we receive all message
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.

  1. EByte LoRa E32 & MicroPython: specifications, overview and first use
  2. EByte LoRa E32 & MicroPython: exploring MicroPython library
  3. EByte LoRa E32 & MicroPython: detailed look at the configuration
  4. EByte LoRa E32 & MicroPython: a deep dive into transmission types

Leave a Reply

Your email address will not be published. Required fields are marked *