Ever since I started out with poking around with ESP32's trying to do something with LoRa ¹ has been on my list. And because my doorbell kept breaking CONSTANTLY, I thought that I would just make my own... and if it could have kilometres of range, well, why shouldn't it?! So I went on AliExpress and bought some parts...
I have gone through a lot of trial and error to get to where I am at the moment. The biggest problem was that I bought the LoRa modules before I stopped using Arduino and switched over to MicroPython for development on the ESP32. I did so because of a few reasons: 1) I never actually learned C++ so Arduino wasn't HUGELY familiar, albeit some secondary-school C and some JavaScript mostly got me there, but there came a point where even writing to an SD Card seemed to be such a huge challenge that I just gave up on it; and 2) I was learning Python anyway and my good friend Iohannes could help me with it, as he is fluent in French, Latin, and (u)Python; 3) It's so much faster to work with uPython; it's very rewarding.
The biggest problem I think I faced was that I ordered the wrong chipset tho for uPython + ESP32. Arduino supports the RFM95W cards I bought, but MicroPython, as I discovered, likes working with SX127x cards most. This really did cause quite a lot of problems, because there were SO many times when I thought I hooked up everything correctly and the code ought to have worked, but didn't. I should have been more suspicious really: for example there were a number of boards where I saw mysterious things like "DIO0" pins and "NSS" pins, but at times the code made no mention of these at all... I went through about 4-5 libraries that promised to make it function but I had no success with any of them. Until I discovered that there's an "official" uPython project called "micropython-lib" ² that has a "lora" library. But micropython-lib/lora
only really supports SX126x and SX127x chipsets; so I had to go online and buy some again... Fortunately they were not very expensive at all.
Another slightly frustrating thing was working with cheap led-free solder. Gracious me, it was HORRIBLE. It just kept crumbling and crumbling -- what ought to have taken 3-4 minutes to connect up often took more like 10. There were times when I had to entirely re-do things because I was getting failed solder joints, etc. So I ordered some 60/40 solder from China, and, while I do think I was sold more like a 40/60 (the tin content is probably much higher than it ought to be), it's still an upgrade to work with it.
It's also a work in progress. I am learning as I go!
anything, check on what frequencies you are allowed to use. LoRa is available over 3 frequencies: 915mHz, 863mHz, and 433mHz. There is a list on TheThingsNetwork's website of what country allows which frequencies. ³ Where I am, 863 and 433 are available for the public, with some caveats.
I chose Wemos' Lolin ESP32 boards beacuse they are great value for money, plus there are also so many little add-ons for the Mini, they seem quite useful (like prototype shields).
=> Wemos ESP32 S3 Pro (I was planning for this to become the receiver, as it's more advanced and has things like an SD card slot) | S3 Pro documentation
=> Wemos ESP32 C3 Pico (This is nice and small and thus really quite convenient for a small doorbell) | C3 Pico documentation
=> Prototype boards
=> Some t̶i̶n̶y̶ ̶r̶o̶b̶o̶t̶ ̶p̶u̶p̶p̶i̶e̶s̶ tactile momentary buttons
These LoRa modules communicate over SPI, so you need to consult your board's schematics to see which pin is what exactly. Another lesson hard learned: always go to your manufacturer's website and DO NOT JUST GOOGLE ABOUT, because you will get wrong information. Wiring up your board to the wrong pins can sometimes have hilarious effects, like, when I accidentally hooked one data line to the LED, that was quite interesting. Sometimes if you end up just using random GPIO pins, you can use SoftSPI to still make it work; but it's much much better to use the actual SPI pins.
If you do not know the SPI pins, you can write a little script in MicroPython to help:
from machine import Pin, SPI ### # Enumerate pins and print their state ### for i in range(50): try: currentpin = Pin(i) print(f"{currentpin}: {currentpin.value()}") except: pass ### # Enumerate SPI interfaces and print their pins ### for i in range(0,3): try: print(SPI(i)) except: pass
For reference, my SPI pins are:
ESP32C3 Pico:
ESP32S3 Pro:
On top of these, you will need to connect the following LoRa pins to some available GPIO pins:
These can be connected to any GPIO.
=> Here are some wiring diagrams. (Look at it, it has ASCII art and all).
!! IF YOU ARE ON MACOS, then there is a set of drivers you will need to install to be able to access the serial ports over USB. I don't know much more. !! IF YOU ARE ON WINDOWS, then you are on your own, sorry. I just haven't used Windows in over a decade. (i) On Linux / Unix systems, make sure that your user is a member of the `dialout` (Linux) or `dialer` (BSD) group.
First and foremost you will need esptool.py
for flashing, ⁴ and the correct firmware images.
Esptool.py is a pretty well-known tool and so it's often available in standard package managers.
$ doas pkg_add py3-esptool
But failing that, it's also available in pip
/ pipx
:
$ pipx install esptool
You will also want to use something to copy files across to your board. There are various choices: from the MicroPython-specific Thonny IDE ⁵. I use rshell
and I will be using that in any examples. ⁶
Finally, you will also want to have mpremote
⁷ installed because it helps with installing libraries onto the board later:
$ pipx install mpremote
On first try I downloaded the "stock" ESP32C3 / S3 firmware .bin files from the MicroPython website ⁸ but I discovered that Wemos has published their own MicroPython flash binaries on GitHub, which is amazing. ⁹ So get the latter -- go specific if you can avoid going generic. At time of writing the filenames are:
firmware-LOLIN_C3_PICO-v1.23.0-220-g692763989.bin firmware-LOLIN_S3_PRO-v1.23.0-220-g692763989.bin
Micropython-lib has a specific LoRa library. Which is absolutely fantastic news, because honestly, it is HARD to find reliable stuff for uPython and LoRa, as I already hinted above. You can get the LoRa library on GitHub; ¹⁰ but if you have mpremote
installed, then it is easier to directly install the libraries on the board using mip
:
$ mpremote mip install lora lora-sync lora-sx127x
Of course you can also install lora-async
if that is how you want to go.
It's a good idea to start monitoring your USB devices at this stage. You will see changes to the device tree as the ESP32 enters flashing mode.
$ tail -f /var/log/messages
To start messing with the flash memory and firmware and stuff, put the board in DFU / flashing mode. To do this, unplug the board, hold down the BOOT button, plug in the board while holding BOOT, hold down RESET briefly alongside BOOT, release RESET, release BOOT. Your messages should give you something like this:
Oct 16 22:31:59 sul4co /bsd: umodem0 detached Oct 16 22:32:04 sul4co /bsd: umodem0 at uhub1 port 3 configuration 1 interface 0 "Espressif USB JTAG/serial debug unit" rev 2.00/1.01 addr 5 Oct 16 22:32:04 sul4co /bsd: umodem0: data interface 1, has CM over data, has no break Oct 16 22:32:04 sul4co /bsd: umodem0: status change notification available Oct 16 22:32:04 sul4co /bsd: ucom0 at umodem0: usb0.1.00003.1 Oct 16 22:32:04 sul4co /bsd: ugen2 at uhub1 port 3 configuration 1 "Espressif USB JTAG/serial debug unit" rev 2.00/1.01 addr 5 Oct 16 22:32:05 sul4co /bsd: ucom0 detached Oct 16 22:32:05 sul4co /bsd: umodem0 detached Oct 16 22:32:05 sul4co /bsd: ugen2 detached Oct 16 22:32:07 sul4co /bsd: umodem0 at uhub1 port 3 configuration 1 interface 0 "Espressif USB JTAG/serial debug unit" rev 2.00/1.01 addr 5 Oct 16 22:32:07 sul4co /bsd: umodem0: data interface 1, has CM over data, has no break Oct 16 22:32:07 sul4co /bsd: umodem0: status change notification available Oct 16 22:32:07 sul4co /bsd: ucom0 at umodem0: usb0.1.00003.1 Oct 16 22:32:07 sul4co /bsd: ugen2 at uhub1 port 3 configuration 1 "Espressif USB JTAG/serial debug unit" rev 2.00/1.01 addr 5
If you can see "serial debug unit" rather than just "Espressif Systems Espressif Device", you're golden.
Then run:
$ esptool.py --chip esp32s3 --port /dev/ttyU0 erase_flash
See the help page of esptool.py for details and chipsets and stuff.
You should still be in DFU mode after successfully erasing the flash, but if not, repeat the above boot procedure. Then you want to run esptool.py again and tell it to write_flash
:
$ esptool.py --chip esp32s3 --port /dev/ttyU0 write_flash -z 0x0 ~/path/to/firmware-LOLIN_S3_PRO-v1.23.0-220-g692763989.bin
Don't forget the -z 0x0
part; that specifies that we want to start writing the firmware from the 0th block -- replacing the bootloader.
Finally!
$ rshell --port /dev/ttyU0 [...snip...] Welcome to rshell. Use Control-D (or the exit command) to exit rshell.
Here you can issue various commands. Try running repl
and issue some Python commands for fun. Use Ctrl-X to exit the Python prompt.
In rshell
the prompt is showing you the local directory on your computer that you are working in; to copy stuff over to the ESP you need to copy them to /pyboard/:
/home/kls> cp ~/foo.py /pyboard/bar.py
Press reset if need be on the board, then install the libraries you will need using mip
and mpremote
running the command above. Mpremote will then download the needed libaries for you and copy them over to the board.
Among the MicroPython-lib files on GitHub there is an example file, called simple_rxtx.py
. ¹¹ Give it a go. Uncomment the stuff in the get_modem()
function, comment out the raise
line, specify correct Pin numbers abd SPI interface.
Copy the file over to your ESP32 using rshell. (See above).
For testing I like to run things directly in the repl:
/home/kls> repl Entering REPL. Use Control-X to exit. > MicroPython v1.23.0-220.g6927639898 on 2024-07-11; LOLIN S3 PRO with ESP32S3 Type "help()" for more information. >>> >>> import simple_rxtx >>> simple_rxtx.main()
If you get to "Sending..." and it doesn't crash after "Initializing" then you should be happy!
Now, if you wire up ANOTHER board and install stuff the same way, then you will be able to send messages from one to another!
=> Like so!
As you can see from the image above, it WORKS, but it's sending a lot of garbage. It is recognisably trying to send "Hello world from MicroPython #42" but there seems to be quite a lot of noise.
Go and read up a little in the GitHub page for the micropython-lib
LoRa library. There are some really good parameters to tweak here, such as bandwidth, SF, etc. I tried to optimise for range and reliability over transmission speed, so here's what I came up with:
def get_modem(): from lora import SX1276 lora_cfg = { "freq_khz": 868000, "sf": 12, "bw": "62.5", # kHz "coding_rate": 8, "preamble_len": 12, "output_power": 20, # dBm "auto_image_cal" : True, "syncword": 0x12, "tx_ant": "PA_BOOST", "rx_boost": True, }
As you can see, I set the frequency to 868, because that's what the law says over here.
I set "SF" to the maximum because
Setting sf to maximum (higher "Spreading Factor") means each LoRa "chirp" takes longer, for more range but lower data rate.
BandWidth was dropped from initial 500 to 62.5 as:
Setting bw bandwidth setting lower makes the signal less susceptible to noise, but again slower. 62.5kHz is the lowest setting recommended by Semtech unless the modem uses a TCXO for high frequency stability, rather than a cheaper crystal.
NB: when specifying "floats" here the devs recommend specifying them as strings and not as floats / numbers! (You know why...) ¹
So, here we are at the moment. Some further work will be necessary to make sure transmission doesn't randomly fail. But I can send packets over the air now, and hopefully, so can you!
TBC as and when...
=> ² LoRa Alliance | ³ micropython-lib on GitHub | ⁴ The Things Network: Frequency Plans by Country | ⁵ Esptool.py Installation | ⁶ Thonny | ⁷ rshell on GitHub | ⁸ mpremote | ⁹ Generic MicroPython firmware files for the ESP32 | ¹⁰ WeMos Lolin ESP32 MicroPython firmware files on GitHub | Micropython-lib: LoRa on GitHub | simple_rxtx.py | ¹¹ The famous Python Floating Point weirdness This content has been proxied by September (3851b).Proxy Information
text/gemini; charset=utf-8; lang=en-GB