Building a Custom Drone Controller from Scratch — Part 3: Firmware Architecture (HAL Pattern, Non-Blocking Loop, and the Flight State Machine)

In Part 1 we talked about why this project exists and what hardware it runs on, and in Part 2 we reverse-engineered the UDP protocol that lets an M5Stack AtomS3 talk to a cheap WiFi toy drone. Before going further into flight control and gamepad support, it’s worth stopping to look at how the firmware itself is put together — because the same skeleton ends up driving two completely different drones (Maritaca Force 1, the black E58-protocol drone, and Dr.One, the grey FLOW-WIFI drone) and two completely different input methods (accelerometer tilt and a Bluetooth gamepad) without duplicating the flight logic.

The full source is on GitHub: github.com/popolony2k/maritaca-e88-controller.

A quick tour of the project structure

The firmware is built with PlatformIO, using plain .cpp/.h files (no .ino) so everything can be unit-structured and reasoned about like normal C++. The layout looks like this:

src/
├── main.cpp                  # wiring + non-blocking main loop
├── hal/
│   ├── hal.h                  # BoardHal, DisplayHal, ImuHal, ButtonHal
│   ├── m5atoms3.h
│   └── m5atoms3.cpp           # only file that includes M5Unified.h
├── imu/
│   └── accelerometer.h/.cpp   # ImuData struct, filtering
├── comm/
│   ├── drone_protocol_base.h  # abstract DroneProtocolBase interface
│   ├── drone_protocol.h/.cpp  # WIFI_8K_ black drone (E58 8-byte)
│   ├── flow_wifi_protocol.h/.cpp # FLOW-WIFI grey drone (88-byte)
│   └── wifi_manager.h/.cpp    # WiFi station + auto-detect scan
├── bt/
│   ├── gamepad_axes.h         # normalized axes, no BLE deps
│   └── ble_gamepad.h/.cpp     # BLE HID host (iPega, 8BitDo)
├── control/
│   ├── accel_controller.h/.cpp   # tilt → roll/pitch/yaw/throttle
│   ├── gamepad_controller.h/.cpp # gamepad axes → DroneState
│   ├── flight_controller.h/.cpp  # the state machine
│   └── operation_mode.h
└── ui/
    └── display.h/.cpp         # pure renderer

Three ideas repeat throughout this tree, and they’re the subject of the rest of this article:

  1. A hardware abstraction layer (HAL) that keeps the M5Stack SDK out of almost every file.
  2. A non-blocking main loop built entirely on millis() — no delay(), anywhere.
  3. A flight state machine that’s shared by both drones and both input modes.

The HAL: one file gets to know about M5Unified

M5Unified is a great library — it gives you the display, the IMU, the button and the power management of the AtomS3 behind one consistent API. The problem is that once #include <M5Unified.h> shows up in a file, that file is now tied to this specific board. For a project that already juggles two drone protocols and two control schemes, we didn’t want a third axis of “which file is allowed to touch the hardware.”

So the rule is simple: only src/hal/m5atoms3.cpp includes M5Unified.h. Everything else — the flight controller, the display renderer, the accelerometer filter — talks to a set of plain interfaces defined in hal.h:

struct BoardHal {
    void (*begin)           ();
    void (*update)          ();
    int  (*getBatteryLevel) ();   // 0/25/50/75/100 in %
    bool (*isCharging)      ();   // always false on this hardware
};

struct ButtonHal {
    bool (*wasPressed)  ();
    bool (*wasReleased) ();
    bool (*pressedFor)  (uint32_t ms);
};

These are structs of function pointers, not abstract classes with virtual methods. The implementation, in m5atoms3.cpp, fills them in with non-capturing lambdas:

const BoardHal kBoard {
    .begin           = [] { auto cfg = M5.config(); M5.begin(cfg); },
    .update          = [] { M5.update(); },
    .getBatteryLevel = [] { return batteryLevel(); },
    .isCharging      = [] { return false; },
};

const ButtonHal kButton {
    .wasPressed  = []()            -> bool { return M5.BtnA.wasPressed(); },
    .wasReleased = []()            -> bool { return M5.BtnA.wasReleased(); },
    .pressedFor  = [](uint32_t ms) -> bool { return (bool)M5.BtnA.pressedFor(ms); },
};

A non-capturing lambda — one that doesn’t reference any outside variables — has no state of its own, so it decays to a plain function pointer at compile time. There’s no closure object, no heap allocation, no vtable lookup. kBoard.update() compiles down to exactly the same code as calling M5.update() directly, but every other file in the project now depends on hal.h (a tiny, dependency-free header) instead of the entire M5Unified SDK.

This pays off in a very concrete way: the battery-level logic — reading GPIO8/ADC2 through a voltage divider, averaging samples, applying hysteresis around the 25/50/75% boundaries — lives entirely inside m5atoms3.cpp as a couple of small static functions. The flight controller and the display just call kBoard.getBatteryLevel() and get back a number from 0–100. When the battery calibration changed (we covered that story in an earlier debugging session), not a single line outside m5atoms3.cpp needed to change.

A loop with no delay()

The AtomS3 in this project is doing a lot at once: polling a button, running an IMU filter, talking to a BLE gamepad, maintaining a WiFi connection, sending UDP control packets at a fixed rate, and redrawing a small LCD — all from a single loop(). If any one of those used delay(), everything else would stall for that duration. A drone waiting for its next 40 ms control packet doesn’t care that the display “only” wanted to sleep for 10 ms.

So the project has a hard rule: no delay() in loop(), ever. Every periodic task instead remembers the last time it ran and checks millis() on every pass:

static uint32_t _lastDisplayMs = 0;
static constexpr uint32_t DISPLAY_INTERVAL_MS = 100; // 10 Hz

void loop() {
    kBoard.update();
    wifi.update();
    imu.update(kImu);

    // ... gamepad + flight controller update, every iteration ...

    uint32_t now = millis();
    if (now - _lastDisplayMs >= DISPLAY_INTERVAL_MS) {
        _lastDisplayMs = now;
        display.update(/* ... */);
    }
}

The same pattern shows up at every layer, each with its own cadence chosen for a reason:

  • Drone control packets go out at ~25 Hz (every ~40 ms) — fast enough for responsive flight, matching what the original Android app does.
  • The keepalive packet (AA 80 80 00 80 00 80 55) fires every ~790 ms when the sticks are idle, so the drone doesn’t think the link has died.
  • The display redraws at 10 Hz — plenty for human eyes, and it keeps SPI traffic from competing with the more time-critical UDP and BLE work.
  • BLE scanning uses 5-second timed windows, restarted every 6 seconds while no gamepad is connected.

Because every one of these is just “a timestamp and a constant,” loop() can run thousands of times a second, doing almost nothing on most iterations and exactly the right thing the moment any of those windows elapses. Nothing ever blocks anything else.

One subtlety we ran into directly because of this design: WiFiUDP::begin() needs WiFi.mode() to have been called at least once to initialize the ESP32’s network stack — even on builds where WiFi is never actually connected. Skip it, and the drone protocol driver crashes during setup(), producing a silent boot loop. It’s a good reminder that “non-blocking” and “stateless” aren’t the same thing — some subsystems still have a required initialization order, even if nothing about them looks synchronous.

The flight state machine

All of the above exists to support the actual brain of the firmware: FlightController. It’s a small state machine with six states:

enum class FlightState {
    Idle,        // disarmed, sending keepalive packets
    Calibrating, // sending CaliGyro for 1.5 s before arming
    Arming,      // Unlock, then TakeOff
    Flying,      // active flight, 25 Hz control packets
    Landing,     // sending Land for 2 s, then back to Idle
    Emergency,   // EmergStop, then immediately back to Idle
};

The normal lifecycle is exactly what you’d expect:

Idle ──(double-click + WiFi ok)──▶ Calibrating ──▶ Arming ──▶ Flying
Flying ──(double-click)──▶ Landing ──▶ Idle
any state ──(triple-click)──▶ Emergency ──▶ Idle

Every transition goes through one function, enterState(), which resets all the per-state bookkeeping (button click counters, one-shot command timers, the “is this the first frame in this state” flag) and logs the transition to Serial. runState() then does whatever that state needs to do every frame: send the right command bytes, check elapsed time, and decide whether it’s time to move on.

Gestures, not menus

Remember — the only physical input in ACCEL mode is a single screen button (BtnA). Every gesture has to be encoded in click count and timing:

  • Single click in Flying toggles yaw on/off.
  • Double click in Idle arms and takes off (if WiFi is connected); double click in Flying starts landing.
  • Triple click, from any state, is an immediate emergency stop.
  • Press-and-hold (ACCEL mode, while Flying) drives the throttle — first hold = climb, click-then-hold = descend.

All of this is decided by a small amount of state — _clickCount, _lastReleaseMs, _buttonDown — updated once per frame in handleButton(). A click only “counts” once the double-click window (DOUBLE_CLICK_MS = 1000) has elapsed without a follow-up press, which is what lets a double-click and a hold-after-click (the throttle-down gesture) coexist on the same physical button.

In BLUETOOTH mode, the same state machine is driven by handleGamepadButtons() instead — rising edges on the gamepad’s button bitmask map directly to the same transitions (A = arm, B = land, X = emergency, etc.), so runState() itself doesn’t need to know or care which input method is active.

One state machine, two drones

This is the part that ties back to Part 2. Maritaca Force 1 (the WIFI_8K_ / E58-protocol drone) needs the full Calibrating → Arming(Unlock → TakeOff) → Flying sequence. Dr.One (the FLOW-WIFI drone) auto-arms the moment it receives a single TakeOff toggle — sending it through a multi-second calibration dance would just be wrong for that hardware.

Rather than branch the state machine on “which drone is this,” the abstract DroneProtocolBase interface exposes a single capability flag:

// FlightController::handleDoubleClick()
enterState(_deps.drone.supportsArmSequence()
           ? FlightState::Calibrating   // Maritaca Force 1
           : FlightState::Flying);      // Dr.One — auto-arms on TakeOff

and enterState() fires the one-shot TakeOff command itself when that flag is false:

if (s == FlightState::Flying) {
    _accel.begin();
    _gamepad.begin();
    if (!_deps.drone.supportsArmSequence()) {
        _oneShotCmd   = DroneCmd::TakeOff;
        _oneShotUntil = millis() + 1000;
    }
}

Everything else — gesture handling, throttle hold, the altitude-hold throttle logic, the landing and emergency paths — is shared. Swapping drones at boot is just a matter of which concrete DroneProtocolBase implementation gets injected into FlightController‘s constructor; WifiManager::scanForFirst() figures out which SSID is visible and the rest follows automatically.

Safety net: WiFi loss means emergency stop

Every call to runState() starts with one check, before the big switch statement:

if (!wifiOk && _state != FlightState::Idle && _state != FlightState::Emergency) {
    Serial.println("[Flight] WiFi lost — emergency stop");
    enterState(FlightState::Emergency);
    return;
}

If the AtomS3 ever drops its connection to the drone’s access point while armed, calibrating, flying, or landing, the very next frame forces an emergency stop — regardless of what gesture or gamepad input is happening. The same idea applies to losing the Bluetooth gamepad mid-flight in BLUETOOTH mode. Given that this whole project is one step away from “small spinning blades a meter from your face,” this single guard clause is arguably the most important seven lines in the firmware.

What’s next

With the skeleton in place — HAL, non-blocking loop, and a drone-agnostic state machine — the next two articles get to the fun part: turning physical input into flight commands. Part 4 covers the accelerometer/tilt control path (and the altitude-hold throttle gesture that took some real debugging to get right), and Part 5 covers building a BLE HID host from scratch to support a Bluetooth gamepad.

As always, the full source is on GitHub: github.com/popolony2k/maritaca-e88-controller. You can also see Maritaca Force 1 in action on the project’s YouTube playlist.

Enjoy
[]’s
PopolonY2k

Building a Custom Drone Controller from Scratch — Part 2: Reverse Engineering the UDP Protocol

In Part 1 we introduced the project: a custom WiFi flight controller for Eachine E88/E58 toy drones running on an M5Stack AtomS3. But before writing a single line of firmware, there was a much bigger problem to solve — nobody publishes the protocol these drones speak. No SDK, no documentation, no public spec. If you want to control one of these drones from your own hardware, you have to figure out the protocol yourself.

This is the story of how that happened — entirely through packet capture analysis.

The Tool: PCAPdroid

The drone exposes its own WiFi access point. Its official Android app — the kind of generic white-label app that ships with these E88/E58 clones, often named something like WIFI_CAM or KY UFO — connects to that access point and talks to the drone over plain UDP, no encryption, no authentication. That makes it a perfect target for packet capture.

Using PCAPdroid (a packet capture app for Android that doesn’t require root), it’s possible to record every packet exchanged between the phone and the drone while flying it normally with the official app. The result is a .pcap file that can be opened in Wireshark for analysis.

This single technique — capture with the phone, analyze with Wireshark — was enough to fully reverse-engineer two completely different drone protocols, described below and in Part 6 of this series.

Step 1: Finding the Control Channel

The first question is always: which port is the drone listening on? Standard E58/E88 drones are documented (informally, around hobbyist forums) to use UDP port 50000 or 7099. A quick port scan against this drone showed those ports closed. The actual control channel turned out to be UDP port 8090 — non-standard, and only discoverable by capturing real traffic from the official app.

Filtering the capture by the drone’s IP address (192.168.4.153) and watching for periodic, fixed-size UDP packets immediately revealed the pattern: 8-byte packets sent roughly 25 times per second while the joystick was active.

Step 2: Decoding the Packet Format

Looking at several consecutive packets side by side made the structure obvious almost immediately — most bytes stayed at a constant value (0x80, the “neutral” position for a joystick axis), while one byte changed smoothly as the throttle stick moved:

66 80 80 80 80 00 00 99   ← all axes neutral
66 80 80 26 80 00 a6 99   ← throttle increasing
66 80 80 52 80 00 d2 99   ← throttle higher

From this, the 8-byte structure became clear:

[ 0x66 | Roll | Pitch | Throttle | Yaw | Cmd | XOR | 0x99 ]
  • Byte 0 — fixed header 0x66
  • Bytes 1–4 — Roll, Pitch, Throttle, Yaw, each centered at 0x80 (128)
  • Byte 5 — command flags (take-off, land, flip, etc.)
  • Byte 6 — checksum
  • Byte 7 — fixed footer 0x99

The checksum was the easiest part to confirm — byte 6 is simply the XOR of the four axis bytes. Take the second sample above: 0x80 ^ 0x80 ^ 0x26 ^ 0x80 = 0xA6. Matches exactly.

Step 3: The Missing Piece — App Mode Activation

Here’s where things got interesting. Sending these exact 8-byte packets to port 8090 did nothing. The drone simply ignored them.

The capture held the answer. Right after connecting, before any control packet appeared on port 8090, the app sent two tiny packets to a completely different port — UDP 8080, which is also the drone’s video streaming port:

42 76   → sent on connect: switches drone to WiFi/app control mode
42 77   → sent on disconnect: returns drone to 2.4 GHz RF controller mode

The drone normally boots in 2.4 GHz RF mode (controlled by its physical remote) and completely ignores WiFi commands until this two-byte handshake arrives. Once 42 76 is sent, the drone switches modes, the video stream starts instantly, and port 8090 starts accepting control packets. This single discovery — a two-byte “magic packet” hidden in the connection sequence — was the missing 10% that made the whole protocol work.

Step 4: The Keepalive Packet

One more detail surfaced while analyzing idle periods in the capture — moments where the joystick wasn’t being touched. Instead of going silent, the app kept sending a different 8-byte packet roughly every 790 ms:

AA 80 80 00 80 00 80 55

Notice the header and footer: 0xAA and 0x55 are the bitwise complements of 0x66 and 0x99 — a nice touch of symmetry from whoever designed this protocol. This keepalive almost certainly prevents the drone from timing out the connection and triggering a failsafe landing.

Step 5: Mapping the Command Flags

The command byte (byte 5) only changes when the user presses a button in the app — take off, land, flip, calibrate, etc. By isolating captures around each button press and comparing the command byte before and after, each flag could be matched to its function:

BitValueFunction
00x01Auto take-off
10x02Land
20x04Emergency stop
30x08360° flip
40x10Headless mode
50x20Lock
60x40Unlock motors
70x80Calibrate gyro

The Complete Picture

Putting it all together, the full control sequence for this drone (which we identified as a WIFI_8K_ variant — confirmed via its SSID WIFI_8K_Wf48702) looks like this:

  1. Connect to the drone’s WiFi access point
  2. Send 42 76 to UDP port 8080 — switches the drone into app control mode
  3. Send 8-byte control packets to UDP port 8090 at ~25 Hz while flying
  4. Send the AA…55 keepalive every ~790 ms when the stick is idle
  5. Send 42 77 to UDP port 8080 on disconnect — returns the drone to RF mode

None of this is documented anywhere. It was all extracted, byte by byte, from real traffic captured between a phone and a drone.

Why This Matters

This kind of analysis isn’t just useful for toy drones. The same workflow — capture real traffic, isolate patterns, correlate user actions with byte changes — applies to any closed proprietary protocol: IoT devices, smart home gadgets, obscure consumer electronics. PCAPdroid plus Wireshark plus patience is a surprisingly powerful combination.

The full implementation of this protocol, including the packet encoder, checksum logic, and state machine that drives it, lives in the project’s source code: github.com/popolony2k/maritaca-e88-controller.

What’s Next

In Part 3, we’ll move from protocol to firmware — the architecture decisions behind the AtomS3 codebase: the hardware abstraction layer, the non-blocking control loop, and the flight state machine that turns raw protocol bytes into a real, controllable drone.

Enjoy
[]’s
PopolonY2k

Building a Custom Drone Controller from Scratch — Part 1: Hardware and Motivation

Toy drones are everywhere. You can pick up an Eachine E88 or E58 for under $30, fly it around your living room, and have a decent time. But what happens when the official app stops working, the manufacturer disappears, or you simply want to understand what’s actually happening between your phone and that spinning plastic in the air?

That question is what started this project.

The Goal

Build a fully custom WiFi flight controller for Eachine E88/E58 toy drones, running on an M5Stack AtomS3 attached to an M5Stack Atomic Battery Base — a compact ESP32-S3 assembly with a built-in LCD, RGB LED, LiPo battery, and a single face button. No phone required. No official app. Full control over every byte sent to the drone.

The complete build process for the black drone — which we called Maritaca Force 1 — is documented in a YouTube playlist where you can follow the project from the very beginning, including hardware assembly, initial tests, and first flights. Watch the Maritaca Force 1 build playlist on YouTube.

The Hardware

M5Stack AtomS3 + Atomic Battery Base

The AtomS3 is a compact development board built around the ESP32-S3 (240 MHz dual-core, 8 MB Flash, 2 MB PSRAM), attached to the M5Stack Atomic Battery Base which adds a LiPo battery and an IP5306 power management IC. Together they form the controller unit used in this project:

  • Built-in 0.85″ 128×128 LCD (GC9107 driver, SPI) — small but enough for a real flight HUD
  • Built-in RGB LED via M5Unified
  • One face button (BtnA, GPIO 41) — the entire UI runs through this single input
  • WiFi 2.4 GHz built in — connects directly to the drone’s access point
  • LiPo battery via Atomic Battery Base — USB-C charging, fits in one hand
  • Battery level read directly via I2C (0x75) — no extra library needed, just two register reads

The Drones — Eachine E88 Clones

Both drones are E88 clones that expose a WiFi access point, stream MJPEG video over UDP, and accept flight commands via UDP packets using a protocol derived from the Eachine E58 family.

We have two drones in this project:

  • Maritaca Force 1 (black drone, WIFI_8K_ variant) — 8-byte UDP packets on port 8090
  • Dr.One (grey drone, FLOW-WIFI variant) — 88-byte wrapped packets on port 8800, with optical flow altitude hold

Both were fully reverse-engineered from packet captures. No SDK, no documentation.

The Build System

The firmware is written in C++ using PlatformIO and the Arduino framework, targeting the m5stack-atoms3 board. The only external library dependency is M5Unified — everything else (WiFi, BLE, UDP) comes from the ESP32 Arduino core.

Why Not Just Use the App?

  1. The official apps are garbage. Laggy, ad-infested, and they break with OS updates.
  2. Learning. Reverse engineering a proprietary UDP protocol from packet captures is a genuinely interesting exercise.
  3. Control. Once you own the protocol, you can do things the app never supported — like controlling the drone with a physical BLE gamepad, or using the board’s accelerometer as a tilt controller.
  4. The AtomS3 fits in your pocket. A dedicated hardware controller with a real display is more satisfying than a phone app.

What’s Coming in This Series

  • Part 1 (this post) — Hardware and motivation
  • Part 2 — Reverse engineering the UDP protocol with packet captures
  • Part 3 — Firmware architecture: HAL pattern, non-blocking loop, flight state machine
  • Part 4 — Accelerometer tilt control
  • Part 5 — BLE HID gamepad host on ESP32 (iPega PG-9021S)
  • Part 6 — Second drone: FLOW-WIFI reverse engineering

Enjoy

[]’s
PopolonY2k

MSX LoRa – First contact

Finalmente após algum período longe do desenvolvimento de software para MSX, decidi voltar ao game unindo o útil mundo do IoT ao agradável universo de retrocomputing através do nosso querido e amado MSX.

Como se trata de um tema atual, rico de informações e possibilidades, decidi escrever dois artigos sendo esse aqui o primeiro que é mais introdutório e o segundo será mais prático, descrevendo o passo-a-passo de como usar toda a tecnologia envolvida nesse pequeno projeto.

Não é a primeira vez que me aventuro na área de comunicação no MSX, pois há 10 anos atrás (passou o tempo e nem percebi) escrevi sobre Networking no MSX através de 2 artigos intitulados MSX Networking e MSX Networking – Developers, developers, developers, developers, … respectivamente e que na época tiveram boa receptividade na comunidade MSX.

Nesses posts apresentei tecnologias como a interface de rede para MSX denominada ObsoNET e a interface que estava sendo desenvolvida na comunidade MSX na época e que não tinha nome e que eu intitulei como OptoNET e desde então é reconhecida como OptoNET, inclusive teve uma versão Wi-Fi da mesma e que usa ESP8266.

No ultimo artigo inclusive liberei a implementação de uma abstração de Network Socket para uso em aplicações escritas em Turbo Pascal 3 no MSX, bem como a implementação de um driver para OptoNET sendo assim possível a sua programação em Pascal usando essa abstração.

Nesse interim adquiri quase tudo o que pude referente a comunicação, desde as diversas placas de rede existentes para MSX como a excelente Gr8Net de Eugeny Brychkov (from Russia with love), DenYoNet da Sunrise, até interfaces RS-232 como a Fast Harukaze e a CT-80NET da Gradiente.

Comecei a desenvolver muita coisa para RS-232 utilizando a BIOS padronizada pelo consorcio MSX mas infelizmente paralisei seu desenvolvimento em um estágio bem avançado, o mesmo tendo acontecido com o desenvolvimento das rotinas para uso da ObsoNET que também está quase no mesmo estágio de conclusão das rotinas de RS-232 e que esse ano, ambas as implementações certamente serão concluídas e assim teremos mais uma importante feature na PopolonY2k Framework Library.

Pois bem, há quase 10 anos tenho essas duas interfaces RS-232, Fast Harukaze e a Gradiente CT-80NET e arrisco a dizer que a possibilidade de programar softwares de comunicação para MSX foi o que me fez a voltar a ser bastante atuante na comunidade (isso lá no início dos anos 2000) e que principalmente foi um tópico que me fez a querer ter um MSX real (agora também os sintetizados via FPGA) para colocar meus planos em prática.

RS-232 – O inicio de tudo

O RS-232, também conhecido como RS-232C, é um protocolo padrão de mercado criado no final dos anos 60, mais especificamente em 1969 e que teve seu uso amplamente difundido para o público técnico e de uso geral principalmente nos anos 80 com o advento da computação pessoal difundida pelos mais diversos PC`’s de 8, 16 e 32 Bits da época, atravessando o tempo e chegando aos dispositivos IoT dos dias atuais, como Arduino e Raspberry Pi.

LoRa – Um protocolo serial via wireless

A comunicação avançou muito nos últimos anos e com todo esse avanço tivemos diversas tecnologias criadas que vão desde comunicação via Ethernet cabeada, até WiFi, o que chamamos de comunicação Wireless ou sem fio.

Apesar do bom e velho RS-232C e suas variantes como a RS-485 terem sido desenvolvidos principalmente para operar utilizando cabos, não é recente a sua utilização via wireless através de dispositivos especialmente projetados para lidar com as caraterísticas especiais de uma comunicação sem fio.

Nesse universo e principalmente devido ao grande avanço das tecnologias de Internet Of Things (IoT), muita coisa nova tem sido desenvolvida, principalmente através da união de grandes players de tecnologia, que por sua vez tem ajudado a criar padrões para utilização no universo IoT.

É nesse cenário nasce a Lora Alliance, que é uma organização sem fins lucrativos responsável pela criação, especificação de tecnologias baseadas em LoRa.

Mas que raios é esse LoRa afinal ?

LoRa é um protocolo de comunicação via rádio que significa Long Range (Longo alcance), ou seja, é um protocolo para envio de informações a longas distâncias, onde se tem relatos de comunicação entre 2 pontos a uma distância de até 15Km.

É lógico que depende de vários fatores, como visada (Line of Sight) dentre outros que podem interferir no alcance, porém só o fato de existir essa possibilidade de comunicação a longas distâncias já torna LoRa muito interessante para uso em diversas aplicações como já vem sendo utilizado pelo agronegócio para monitoria de equipamentos em fazendas que na maioria dos casos é um ambiente propício para esse tipo de aplicação

Apesar da grande distância que um dispositivo LoRa pode proporcionar, nem tudo são flores sendo a principal desvantagem a baixa velocidade pois a comunicação entre dispositivos LoRa é extremamente lenta, impossibilitando o seu uso em transmissões com grande volume de dados como áudio e vídeo por exemplo.

Esse é um dos principais motivos pelos quais as informações trafegadas entre dispositivos LoRa, estar restrita a envio de dados de telemetria ou aplicações onde baixa latência não é o principal requisito.

LoRa possui duas topologias de operação, sendo elas Point-to-Point e Network, essa última de fato é uma espécie de Broadcasting, além de variantes como a LoRaWan capaz de colocar um dispositivo LoRa na internet, o que foge um pouco do escopo desse post portanto vamos explorá-la em um post futuro.

Dispositivos LoRa na prática

Na prática temos uma infinidade de dispositivos LoRa desenvolvidos por diversos fabricantes e embora exista uma certa padronização no nível de sinal, frequência de rádio e afins, não há uma padronização na interface de software com LoRa, com isso cada fabricante de dispositivos LoRa está criando sua própria abstração de comunicação com seus dispositivos.

A fabricante REYAX, é a que melhor se adaptou e está usando comandos AT, padrão da Hayes criado para comunicação via modem que se tornou padrão de facto sendo amplamente utilizado pela industria desde os anos 80 bem como pela própria REYAX em seu RYLR998, o que torna esse dispositivo um dos mais “padronizados” em nível de software no mercado.

Dentre os diversos fabricantes, um dos mais “famosos” é a chinesa EBYTE, que possui diversos modelos de módulos LoRa, todos bem parecidos visualmente e operacionalmente entre si, apenas mudando especificações técnicas talvez relacionadas a potência e consumo por exemplo.
O módulo que utilizo nos experimentos com MSX e IoT é exatamente esse da imagem, o E220-900T22D.

Tanto o módulo LoRa da REYAX, quanto o da EBYTE, tem sua interface física de comunicação baseada em UART TTL serial e no caso dos módulos da EBYTE eles tem um protocolo binário bem simples para setup do módulo LoRa e que está disponível quando o módulo está operando em modo de setup/configuração.

A documentação do módulo LoRa da EBYTE pode ser encontrada nesse link aqui, ou em nosso repositório local aqui.

Infelizmente a documentação do módulo da EBYTE não é muito clara e gera algumas duvidas que nos induz a pensar que a documentação está errada ou o módulo não funciona, entretanto após pesquisar outros sites e vídeos sobre esses módulos da EBYTE percebi mesmo que essa documentação é só mau escrita mesmo e que o módulo funciona corretamente e de fato ele é mais simples do que eu imaginava, não necessitando conhecer algum protocolo específico para sua operação (exceto quando você está em modo de setup/configuração do módulo).

Bom, acredito que após essa longa introdução sobre comunicação, dispositivos seriais e LoRa, podemos começar a colocar a mão na massa e ver como usar os dispositivos LoRa e principalmente como utilizá-los através de um computador MSX.

Como eu já citei inicialmente, esse é o primeiro artigo introdutório e no próximo descreveremos tecnicamente os detalhes e possibilidades de integração de dispositivos LoRa no MSX ou em outras tecnologias como Arduino, Raspberry Pi, etc.

Enjoy

[]’s
PopolonY2k

MSX ARM – Primeiro teste de hardware.

Muitos já devem saber, através das listas GDMSX, MSXBR-L e MSX-All, além do blog do Retrocomputaria Plus e das comunidades MSX Brasil Oficial no FaceBook e no Orkut, que o primeiro vídeo de testes do MSX ARM foi liberado.

Nesse vídeo eu e Rogério Matte Machado, testamos o hardware e sua integração com  um MSX Real, no caso um MSX2+ Sony HitBit F1 XDJ.

Alguns “testes básicos” foram feitos e apesar de chamá-los de “testes básicos”, posso garantir que o esforço e a quantidade de horas de pesquisa e desenvolvimento já utilizados até agora, nos deixam com um produto próximo a estabilidade de um hardware MSX convencional com um SD embutido e RS232, faltando ainda a camada de ethernet, portas USB, MIDI In/OUT além de uma nova capacidade inserida no hardware desde o dia em que o vídeo foi feito, que é uma saída S/PDIF para áudio digital 5.1.

Com isso estamos próximos de um passo importante, que é o desenvolvimento de todo o software embarcado necessário para a integração dos periféricos extras, como ethernet, USB, MIDI IN/OUT, dentre outros.

Teremos um grande trabalho pela frente no próximo ano.

Segue o demo abaixo.

MSX ARM primeiro vídeo – Teste de hardware

[]’s
PopolonY2k

Referência na internet

Retrocomputaria Plus (anúncio do primeiro vídeo do MSX ARM)
http://www.cupulablogs.com/retrocomputaria/?p=2038

 GDMSX (Grupo de desenvolvimento de software e coisas legais para MSX)
 http://groups.google.com/group/gdmsx

MSXBR-L (Lista de usuários de MSX)
http://listas.amplus.com.br/mailman/listinfo/msxbr-l

MSX-All
http://br.groups.yahoo.com/group/MSX-All/

MSX Brasil (Orkut)
http://www.orkut.com.br/Main#Community?cmm=98375914 

MSX Brasil (FaceBook)
http://www.facebook.com/home.php?sk=group_182223775136806#!/home.php?sk=group_182223775136806

MSX2+ Sony HitBit F1 XDJ
http://www.1000bit.it/scheda.asp?id=1682 

S/PDIF (Wikipedia)
http://en.wikipedia.org/wiki/S/PDIF