DIY Mioty LPWAN Node Under $10 With AI Setup – Easy LoRaWAN Alternative

by phaseshifted in Circuits > Wireless

52 Views, 1 Favorites, 0 Comments

DIY Mioty LPWAN Node Under $10 With AI Setup – Easy LoRaWAN Alternative

20250805_131210.jpg
routing.png

This project shows how to build a mioty Class Z end-point (unidirectional) using a Raspberry Pi Pico and a HopeRF radio module.

mioty is an LPWAN protocol similar to LoRaWAN, but it uses a different, more robust modulation technique that works especially well in crowded environments with interference.

One of mioty’s advantages for us is that it’s a software-defined radio protocol. That means it can run on almost any chip capable of FSK modulation. So in theory, we can build a mioty end point using just a simple HopeRF module—for less than $10 in parts.

This Instructable is about testing that idea:

👉 Can we build a low-cost mioty LPWAN node as a LoRaWAN alternative?

👉 And can we even make it easier by using AI-assisted programming to set it up?

Let’s find out!

Supplies

Screenshot 2025-09-15 153654.png
hoperf-overview.jpg

About the HopeRF radio modules

First, let's clarify some differences between the many HopeRF modules, as this wasn't exactly clear to me.

We are going to need the generic RFM69 module, not the RFM95 module which is specifically for LoRa. (Should work too over the LoRa chip FSK mode, but didn't test it yet). There are different footprints available:

  1. Large form factors, model without "C": Supposed to be for breadboard prototyping
  2. Compact version, with a "C": Smaller footprint for a more compact design to add to another PCB as a module, for example

Then, there are different power variants:

  1. Without "H": Output power up to 13 dBm
  2. High Power, with "H": More components to boost for an output power up to 20dBm.

For our project, it doesn't really matter which form factor and power version we choose, but be careful because the fooprint and order of the pins is not the same for the compact and normal version! Most ISM frequency bands like EU868 allow for an output power of up to 14 dBm, but there will always be some coupling losses. It might be a good choice to go for the 20dBm version and depending on the losses, set only 16 dBm or 18 dBm, just to end up with an EIRP of 14 dBm at the antenna, to be most optimized for range.


Components

  1. Raspberry Pi Pico 2 (RP2040 chip) – about $6
  2. HopeRF RFM69W, HW or HCW module with coil antenna – about $2
  3. 100 Ω through-hole resistor (THD) – a few cents
  4. 868MHz coil antenna – a few cents
  5. Jumper wires / cables for connections – a few cents each
  6. Optional: USB Power Bank (any small, portable one will do)

Tools & Supplies

  1. Soldering iron (basic entry-level iron is fine)
  2. Micro USB cable
  3. Soldering wire

Wire Everything Up

20250805_131210.jpg
routing.png

Now let's do the wiring part of the boards. My RFM69HCW already came with a coil antenna pre-soldered, other wise you will have to solder that part directly to the ANT pin.

Carefully check the wiring diagram to connect the correct GPIO pins on the Raspberry Pi with the module for the power supply (3.3V!) and SPI communication.

Optional: Adding a Dummy Load

Because I want to use this device with a portable power bank to do some range testing, I will be adding a 100 Ohms resistor as a dummy load between one of the GPIO pins and the ground. As the RPI2040 is too low power, it makes my power bank go to sleep after a minute, which I will prevent by drawing some current pulses from time to time on the resistor. For my power bank, it worked fine, you may have to tweak the values for the duration and interval of the current peaks slightly if it doesn't work for your power bank. Or maybe you don't need the resistor at all - if your power bank doesn't have a shutoff feature.

Defining the Payload for Our Mioty Device


In this step, we are designing the payload that our Pico-based mioty endpoint will transmit. Defining a payload is about answering two key questions:

  1. What information do we want to send over the air?
  2. What is the most efficient format to represent it?

Since we have no external sensors connected, we will use the internal temperature sensor of the RP2040 MCU as our first data source. This keeps things simple but still illustrates how a real-world mioty payload is structured and decoded.

1. Why Payload Design Matters

Every byte we send consumes airtime and energy, so payload design is critical. A good payload should be:

  1. Compact → shorter messages = lower power consumption and higher update interval possible within the allowed duty cycle
  2. Structured → a consistent format makes decoding easier
  3. Extensible → new sensors can be added later without breaking compatibility

Our example follows these principles by creating a header + sensor data structure.

2. Example Payload Structure

We will define a 10-byte payload for this project. Here’s the breakdown of each field:

  1. Byte 0 — Payload Version: Identifies the structure version. Example: 0x01.
  2. Bytes 1–2 — Firmware Version: Major.Minor (2 bytes). Example: 0x01 0x00 (v1.0).
  3. Byte 3 — Hardware Version: Board revision. Example: 0x01.
  4. Byte 4 — TX Power: Transmission power in dBm. Example: 0x14 (20 dBm).
  5. Byte 5 — Trigger Type: What caused the transmission. Example: 0x01 (Timer).
  6. Bytes 6–7 — Reserved: Reserved for future use. Example: 0x00 0x00.
  7. Bytes 8–9 — Temperature: Sensor data scaled by 100. Example: 0x29 0x09, which encodes 23.45 °C as 2345 in big-endian format.

So a full transmission might look like this:


01 01 00 01 14 01 00 00 29 09

Here, the temperature 23.45 °C is represented as 2345 → 0x2909 (big-endian).

3. End-to-End mioty Data Flow

With this payload, every transmission follows the sequence:

  1. Sensor Reading → acquire clean MCU temperature data
  2. Payload Composition → format into binary payload with scaling
  3. mioty Transmission → send using the TS-UNB protocol and recieve message on mioty Service Center (SC)
  4. Blueprint Decoding → mioty Application Center (AC) interprets the payload and displays structured data

4. mioty Blueprint Definition

To make the base station decode our payload, we create a mioty blueprint. This JSON definition tells the network how to interpret each field in our payload:


{
"version": "1.0",
"typeEui": "70b3d56770000001",
"meta": {
"name": "mioty End-Point Example - Temperature Only",
"vendor": "yourcompany",
"description": "RP2040 + RFM69HW with internal temperature sensor"
},
"uplink": [
{
"id": 0,
"crypto": 0,
"payload": [
{ "name": "payload_version", "component": "8bitUnsigned" },
{ "name": "firmware_major", "component": "8bitUnsigned" },
{ "name": "firmware_minor", "component": "8bitUnsigned" },
{ "name": "hardware_version","component": "8bitUnsigned" },
{ "name": "tx_power_dbm", "component": "8bitSigned" },
{ "name": "trigger_type", "component": "8bitUnsigned" },
{ "name": "reserved1", "component": "8bitUnsigned" },
{ "name": "reserved2", "component": "8bitUnsigned" },
{ "name": "temperature", "component": "16bitTemperature" }
]
}
],
"component": {
"8bitUnsigned": {
"size": 8,
"type": "int",
"func": "$",
"unit": "",
"littleEndian": false
},
"8bitSigned": {
"size": 8,
"type": "int",
"func": "$",
"unit": "dBm",
"littleEndian": false
},
"16bitTemperature": {
"size": 16,
"type": "int",
"func": "$/100",
"unit": "°C",
"littleEndian": false
}
}
}

With this blueprint, the AC knows exactly how to parse our 10-byte payload and present it as human-readable data, and forward it to another backend service.

5. Extending the Payload

The real power of this design is extensibility. If we want to add a humidity sensor later, we simply:

  1. Connect the sensor (I²C/SPI)
  2. Implement the driver following the sensor template
  3. Append the humidity data field in the payload after the header
  4. Update the blueprint with a new component definition

No existing code breaks, and our system scales naturally.

✅ We now have a complete payload + blueprint definition for our Pico-based mioty endpoint. This provides a foundation for adding more sensors and building full-featured IoT devices.

Programming the Raspberry Pi Pico As a Mioty Endpoint

pico-extension.png


In this step, we are setting up our development environment and programming the Pico to act as a mioty node. We’ll use Visual Studio Code along with the Raspberry Pi Pico Extension, which gives us a smooth workflow for compiling and flashing code.

1. Get the Source Code

We start by cloning the project repository and moving into the folder:


git clone https://github.com/phaseshifted-iot/mioty-pico-endpoint.git
cd mioty-pico-endpoint

2. Open the Project in VS Code

Now we open the project folder in Visual Studio Code.

  1. Install the “Raspberry Pi Pico” extension (official one).
  2. The extension will guide us through the required SDK setup.

3. Build the Project

With everything set up, we are ready to build:

  1. Press Ctrl+Shift+P and select:
  2. Raspberry Pi Pico: Configure CMake
  3. Press Ctrl+Shift+P again and select:
  4. Raspberry Pi Pico: Compile

This will generate the binary we need for flashing.

4. Flash the Pico

Next, we put the Pico into bootloader mode and upload our program:

  1. Hold the BOOTSEL button on the Pico.
  2. Connect the USB cable.
  3. Release BOOTSEL — the Pico will appear as a USB drive.
  4. Copy the generated .uf2 file to the Pico drive, or simply use the Run function of the extension.

The Pico will automatically reboot and run our code.

5. Monitor the Device

To verify that our node is alive and transmitting, we set up a serial monitor.

  1. In VS Code, open Extensions (Ctrl+Shift+X).
  2. Install “Serial Monitor” by Microsoft.
  3. Start monitoring via Ctrl+Shift+P → Serial Monitor: Start Monitoring.
  4. Select the Pico’s COM port (it usually appears as Board CDC or similar).
  5. Set the baud rate to 115200.

Now we’ll see live logs showing:

  1. Sensor readings
  2. mioty transmission attempts
  3. System status and error messages
  4. Power management events and sleep cycles

6. Debugging Tips

The serial logs are our main debugging tool. If the device has trouble transmitting or reading from sensors, we’ll see it here. Pay attention to:

  1. Successful transmission confirmations
  2. Any error codes or system warnings

✅ That’s it — we now have a working mioty endpoint on the Raspberry Pi Pico, transmitting temperature data every 60 seconds.

Setting Up a Mioty Base Station


This project is about building a mioty End Point, but in order to test the entire data transmittion chain, you will need a base station too. You can either buy one off the shelf - or build your own. It should merit its own guide, but here's a quick overview of how this can be achieved:

1. Hardware Requirements

  1. Device: Raspberry Pi 3, 4, or 5, or an x86_64 Linux-based computer.
  2. Receiver: SDRPlay RSP1A or RSP1B.
  3. Antenna: Appropriate for the frequency band you're using (e.g., EU868).

2. Software Installation

  1. Clone the miotyGO repository:

git clone https://github.com/loriot/miotyGO.git
cd miotyGO
  1. Run the installation script:

sudo ./install.sh
  1. This installs the base station software and sets up necessary directories.

3. Service Center Configuration

  1. Access your Service Center (e.g., eu3.loriot.io).
  2. Create a new mioty network.
  3. Add a base station to the network using the Base Station EUI provided during installation.
  4. Download the required certificates:
  5. Root CA certificate
  6. TLS certificate
  7. TLS private key
  8. Place these certificates in /etc/miotyGO/certificates/ on your device.

4. Configuration File

  1. Edit the configuration file at /etc/miotyGO/config.json:
  2. Set the Service Center host and port.
  3. Specify the RX power gain (default is 100).
  4. Choose the mioty region (e.g., eu868).

5. Starting the Base Station

  1. Start the base station service:

sudo mioty_base_station start
  1. Monitor the logs:

tail -f /var/log/miotyGO.log

6. Attaching Your Endpoint

  1. In the Service Center, create a new application.
  2. Add a new device with the parameters matching your endpoint.
  3. Attach the device to the base station.
  4. Once attached, your endpoint's data will be received by the base station and can be processed accordingly.

For detailed instructions and troubleshooting, refer to the miotyGO GitHub repository.

Use AI Tools to Add New Sensors

This example is built with a flexible setup where each sensor has its own driver. That design makes it easy to extend the project, and now we can even leverage AI tools like GitHub Copilot to speed up development. Here’s how we can add new sensors using this approach:

  1. Create a new sensor driver
  2. Use the existing templates as a guide. Copilot can help generate the boilerplate code for reading the new sensor, handling errors, and formatting the output.
  3. Adapt your board configuration
  4. Update src/app/board_config.hpp to define the pins and wiring for your new sensor. This tells the firmware where to read the new sensor data from (I²C, SPI, or analog).
  5. Update the payload configuration
  6. Edit src/app/payload_config.hpp to include your new sensor in the payload. Make sure the sensor data is added in sequence after existing sensors so the base station decodes it correctly.
  7. Rebuild and flash your firmware
  8. Compile the project and flash it to your Pico. Your new sensor is now part of the system and will transmit data according to the payload structure you defined.
  9. Update the mioty blueprint
  10. Add a new component definition for the sensor in your blueprint JSON, ensuring the base station can decode and display the new sensor data properly.

Example: Adding a Humidity Sensor

  1. In payload_config.hpp:

HUMIDITY = 0x03, // New sensor type
  1. In your application code:

humidity_sensor.read();
payload_builder.addSensorData(SensorType::HUMIDITY, humidity_value);

Everything else (scaling, big-endian formatting, and inclusion in the binary payload) is handled automatically.

This modular design, combined with AI-assisted code suggestions, makes it fast and reliable to expand your mioty node. You can add multiple sensors—humidity, pressure, battery voltage, and more—without breaking the existing system.

Happy building!