Introduction
For some time, actually some years, I am pondering with ideas how to create a wide area low bandwidth data network. The regulatory situation is a bit limiting, you only have few license free bands at hand, the so called ISM bands. The most well know are the 2.4GHz and 5GHz bands used by WiFi and Bluetooth. But these have practical limits, signals in these band do not propagate very well and far, basically everything is in its path causes significant loss, which is why WiFi usually does not go much farther than your own apartment. What I am looking for is rather hundreds of meters range, or better even, kilometers.
Given the limitation in ISM bands the currently best and most easily accessible radio is probably LoRa. LoRa can operate in the 433MHz, 868MHz and 900MHz ISM bands. The LoRa radios adhere to the ISM limitations, especially the (low) maximum transmit power, but still reach comparably long range. They do this by using a spread spectrum technology, which is sadly proprietary to LaRa by Semtech. But well, these exist and hardware with LoRa radio chips is abundantly available, there are dozens of different easily accessible development boards with LoRa radio chips like SX1262 or SX1278 plus some Espressif ESP microcontroller, for example:

These boards are nice to build autonomous devices, like a sensor node or mobile messaging devices, or gateway devices that can be attached e.g. vie Bluetooth Low Energy (BLE) to your mobile phone. Probably one of the most well know projects doing this is Meshtastic, another super interesting one is the Reticulum network.
Intermission
Maybe a small intermission, a word about LoRa. I always find discussions about LoRa super confusing because the word LoRa can have different meanings in different contexts. On the lower level there is the LoRa radio, which is what I described above. The LoRa radio and chips implementing it are protocol agnostic, i.e. you can use the radio chips as arbitrary data radios and transmit whatever you like, like Meshtastic and Reticulum do. Then there is also the LoRa protocol, which is a proprietary standard for creating mesh networks between different node types. The protocol can be used kind of freely but it is proprietary to the Semtech company. And then there is LoRa WAN, which is the extension of local mesh networks into larger networks using internet gateway nodes. The LoRa WAN network is a proprietary service by Semtech with low bandwidth / usage free tier options. What I am talking about and using here is just the LoRa radio.
Goal
Back to LoRa enabled hardware, the development board with some ESP controllers on them are very nice, but for protocol tinkering a bit cumbersome. You need to implement everything for some ESP controller and every time you want to try something, you need to flash one or two boards with a new firmware.
I am looking for generic LoRa radio which I can attach to my PC and develop software on my PC that directly use the attached radio to send / receive data packets over LoRa. It should ideally be small so that it can be carried around easily. Then I came across the E22-900T22U by Ebyte:

Very compact, USB interface, SMA connector for an external antenna – nice! So I ordered one. The problem with these is that they embed a small microcontroller to interface with the LoRa radio chip which implements a proprietary protocol, which is more geared towards to point to point data transmission, like a serial data bridge. This is not what I need. So I cracked it open to figure out if this can be changed:

Ebyte E22-900T22U PCB, side by side, one side flipped so that connections can be traced more easily
Inside you can find a Semtech LLCC68 LoRa radio chip, a ZBit Semi CX32L003F8 ARM Cortex M0 microcontroller, a CH340 USB to serial UART bridge, a red and green LED and a push button. Ebyte was nice enough to also embed four free solder vias for the SWD in system debugging interface (top right). Cool! For all these chips datasheets can be kind of easily found on the internet. With some poking around I found most connections.
Development, Tools, SDK
The ZBit Semi CX32L003 is not a very common microcontroller. But since it is a ARM Cortex M0 implementation we can be hopeful. On Debian Linux you can easily install a cross toolchain, which is part of Debian:
sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi
ZBit Semi was kind enough to make an ARM CMSIS based SDK available as open source on Github. So far so good. But how to get a program into the flash of the controller? The CX32L003 has a built-in bootloader that can be activated by holding the push button during power-on (i.e. plugging the dongle into USB). ZBit Semi offers an In System Programming (ISP) tool for the CX32L003 on their website, but that’s Windows only and the serial protocol is not documented anywhere. And even if we had the protocol then you probably also want some debugging means for development.
After some hours of internet research I came across the work of Benjamin Vernoux! Benjamin has worked a lot on a similar Ebyte device and put a lot of effort into getting a cross development environment working. One part of his work is a patch for OpenOCD for the CX32L003! That was already back in 2023 and the patch is not merged until today, so it does not come with the OpenOCD package e.g. found in Debian. But the patch still kind of cleanly applies to current OpenOCD. For my own and probably also your convenience I created a fork on my own code hosting server and applied the patch: https://source.haeckserei.de/nica/openocd-code
Then I soldered wired to the SWD pins and attached a cheap ST-Link SWD adapter:

Ttogether with the patched OpenOCD I get this:
$ openocd -f interface/stlink.cfg -f target/cx32l003.cfg Open On-Chip Debugger 0.12.0+dev-01248-g22afaae7f-dirty (2025-11-02-14:15) Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Warn : DEPRECATED: auto-selecting transport "swd (dapdirect)". Use 'transport select swd' to suppress this message. Info : using default value max bank size Info : Listening on port 6666 for tcl connections Info : Listening on port 4444 for telnet connections Info : STLINK V2J29S7 (API v2) VID:PID 0483:3748 Info : Target voltage: 3.282518 Info : Unable to match requested speed 2000 kHz, using 1800 kHz Info : Unable to match requested speed 2000 kHz, using 1800 kHz Info : clock speed 1800 kHz Info : SWD DPIDR 0x0bc11477 Info : [cx32l003.cpu] Cortex-M0+ r0p1 processor detected Info : [cx32l003.cpu] target has 4 breakpoints, 2 watchpoints Info : [cx32l003.cpu] Examination succeed Info : [cx32l003.cpu] starting gdb server on 3333 Info : Listening on port 3333 for gdb connections
We are in business!
Next the firmware. The CMSIS code base is not directly tailored for the GNU toolchain, rather some Eclipse based more or less commercial development environment. But also here Benjamin already laid out the groundwork, which can be found, along with a lot more information, on his server: https://hydrabus.com/openocd_cx32l003/ In there you can find a project called ‚Test_E220-900T22D_llcc68_ping_pong_EEZBB3.7z‚ which has a customized version of the SDK and a demo program for the Ebyte E220 LoRa module, which can be compile with the Linux GNU cross toolchain and simple make!
I have modified it for the E22 dongle, the pinout is a bit different, additional LEDs etc. Right now it is still called very creatively „hello_world“ 🙂 And that’s about what it looks like compiling:
$ make ... Memory region Used Size Region Size %age Used RAM: 2440 B 4 KB 59.57% FLASH: 25008 B 64 KB 38.16% Invoking: GNU ARM Cross Create Flash Image arm-none-eabi-objcopy -O ihex "./build/hello_world.elf" "./build/hello_world.hex" Create Flash Image BIN arm-none-eabi-objcopy -O binary "./build/hello_world.elf" "./build/hello_world.bin" Invoking: GNU ARM Cross Create Listing arm-none-eabi-objdump --source --all-headers --demangle --line-numbers --wide "./build/hello_world.elf" > "./build/hello_world.lst" Invoking: GNU ARM Cross Print Size arm-none-eabi-size --format=berkeley "./build/hello_world.elf" text data bss dec hex filename 24884 124 2324 27332 6ac4 ./build/hello_world.elf
Pretty cool! So we now have a linked ELF binary. Now flashing it via OpenOCD:
$ telnet localhost 4444 Trying ::1... Connection failed: Verbindungsaufbau abgelehnt Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. Open On-Chip Debugger > reset halt [cx32l003.cpu] halted due to debug-request, current mode: Thread xPSR: 0x61000000 pc: 0x00004cb4 msp: 0x20001000 > program hello_world.elf [cx32l003.cpu] halted due to debug-request, current mode: Thread xPSR: 0x61000000 pc: 0x00004cb4 msp: 0x20001000 ** Programming Started ** Adding extra erase range, 0x000061b0 .. 0x000061ff cx32l003_write buffer=0x55a9bef4a700 offset=0x00000000 count=25008 ** Programming Finished ** > reset run
If everything worked out according to plan then the new program is now running on the controller, in my case I get some debug log output on the UART, I am using the very simple but handy tool ‚tio‘ to connect to it:
# tio -b 115200 -f none -m INLCRNL /dev/ttyUSB0 LLCC68 Ping Pong logs UART1, PA2-TXD, PA1-RXD INFO: ===== LLCC68 Ping Pong ===== INFO: SDK version: 2.0.1-4-gc964966 INFO: INFO: LLCC68 get default lora syncword=0x1424 (expected 0x1424) INFO: Common RF parameters: INFO: Packet type = LLCC68_PKT_TYPE_LORA INFO: RF Freq = 869525000 Hz INFO: Output power = 0 dBm INFO: Fallback mode = LLCC68_FALLBACK_STDBY_RC INFO: Rx boost mode deactivated INFO: INFO: LoRa modulation parameters: INFO: Spreading Factor = LLCC68_LORA_SF10 INFO: Bandwidth = LLCC68_LORA_BW_250 INFO: Coding rate = LLCC68_LORA_CR_4_5 INFO: INFO: LoRa packet parameters: INFO: Preamble length = 8 symbol(s) INFO: Header mode = LLCC68_LORA_PKT_EXPLICIT INFO: Payload length = 128 INFO: INFO: LoRa sync word = 0x2b
As you can see I still have not renamed everything, the „Ping Pong“ demo strings are still in there 🙂 But I did change the program quite a bit by now. The radio is now in continuous receive mode, I also changed the frequency and LoRa parameters sync word, spreading factor, bandwidth and coding rate to the Meshtastic Medium-Slow defaults, the default Long-Fast uses 250kBit and spreading factor 11, which does not seem to be supported by the LLCC68. Anyway, using the Medium-Slow default and a Meshtastic radio I can get something like this:
INFO: Packet status: INFO: - RSSI packet = -39 dBm INFO: - Signal RSSI packet = -39 dBm INFO: - SNR packet = 8 dB INFO: Preamble detected INFO: on_preamble_detected INFO: Header valid INFO: on_header_valid INFO: Rx done Packet content - (58 bytes): C0 85 52 EC 8B C2 04 C3 94 99 33 AD DF C0 DB 14 C8 DA 6A 47 1C 2D FF FF FF FF 54 FC A5 A0 A8 2D CC 65 63 18 00 54 D6 E4 2D 14 06 D8 35 89 25 E2 24 79 5F 42 B4 48 6F 38 20 30
Wohoo! That should be some Meshtastic protocol packet!
So we can safely say, we have a working toolchain, compiler + linker, a working BSP and an in system debugger up and running perfectly fine!
Outlook
I still need to clean up my testing mess a bit and will soon upload my WIP code to my server. I am currently working on integrating an AT command parser for configuration and switching modes (between AT and ‚online‘ mode). There is also some storage space in flash, maybe I can make settings persistent. I also need to think about a proper protocol to send / receive data, the ASCII hexdump shown above is for sure not ideal.
I would also be very interested in help reverse engineering the serial ISP protocol so that these dongles could be more easily reflashed in the field – hint: The ISP Windows application is a .NET application which probably can be reverse translated to read the protocol from it? It would be very cool to have a small Linux commandline tool for that so that everyone with these dongles can flash an alternative firmware easily.