DMD-LoRa Open API
Drive the LoRa module in a DMD device from your own Android app — read & change radio settings and send/receive raw LoRa messages, over a bound service or simple broadcasts.
com.thorkracing.dmdlora.OPEN_API
com.thorkracing.dmdLora
Overview
The DMD-LoRa app owns the LoRa radio module built into a DMD device. The Open API lets any app on the same device use that radio — change its configuration and exchange raw LoRa messages — without touching the hardware directly.
Two ways to integrate:
- Bound service (AIDL) — the primary API. Typed calls with return values plus a push channel for incoming messages.
- Broadcasts — a fire-and-forget veneer for scripted integrations (Tasker, etc.) that don't want to bind.
Open Mode
A DMD device has one radio with one owner, so it runs in one of two modes:
- DMD2 Mode (default) — the radio belongs to the DMD2 / DMD2-Next riding-group features. The Open API is inert.
- Open Mode — the radio is free for general use. The Open API is live, and DMD group features are disabled.
So the first thing your app does is take the radio with setOpenMode(true). While not in Open Mode, readSettingsJson() returns "{}", writeSetting() / sendMessage() return false, and no messages are delivered.
setOpenMode(false) when you're done — that re-applies the group's radio configuration.Quick Start (AIDL)
1. Copy these two .aidl files into your app under src/main/aidl/com/thorkracing/dmdLora/:
// ILoraOpenService.aidl
package com.thorkracing.dmdLora;
import com.thorkracing.dmdLora.ILoraEventListener;
interface ILoraOpenService {
boolean isOpenMode();
boolean setOpenMode(boolean open);
String readSettingsJson();
boolean writeSetting(String key, String value);
boolean sendMessage(String text, int targetAddr, int targetChannel);
void registerListener(ILoraEventListener listener);
void unregisterListener(ILoraEventListener listener);
}
// ILoraEventListener.aidl
package com.thorkracing.dmdLora;
oneway interface ILoraEventListener {
void onMessageReceived(String text, long timestamp);
}
2. Bind to the service:
Intent intent = new Intent("com.thorkracing.dmdlora.OPEN_API");
intent.setPackage("com.thorkracing.dmdLora");
bindService(intent, connection, Context.BIND_AUTO_CREATE);
3. Use it once connected:
ILoraOpenService api = ILoraOpenService.Stub.asInterface(binder);
api.setOpenMode(true); // take the radio
api.sendMessage("hello", -1, -1); // broadcast on the current channel
api.registerListener(myListener); // receive incoming messages
// … later …
api.setOpenMode(false); // hand the radio back to DMD2
false / "{}").Interface Reference
ILoraOpenService
| Method | Returns | Description |
|---|---|---|
isOpenMode() | boolean | True if the radio is currently in Open Mode (API live). |
setOpenMode(boolean open) | boolean | Switch modes. Returns the resulting mode (true = now Open). Passing false re-applies the DMD2 group radio config. |
readSettingsJson() | String | Current module configuration as a JSON object (see Settings). "{}" if unavailable / not in Open Mode. |
writeSetting(key, value) | boolean | Write one setting. false if rejected (unknown key / not in Open Mode). |
sendMessage(text, addr, ch) | boolean | Transmit a raw text message. false if rejected. |
registerListener(cb) | void | Subscribe to incoming messages. |
unregisterListener(cb) | void | Unsubscribe. |
ILoraEventListener
onMessageReceived(String text, long timestamp) | Fired (oneway) for each raw LoRa message received while in Open Mode. timestamp is device-local epoch milliseconds at receipt. |
Mode Control
setOpenMode(boolean) is the gateway. A typical session:
if (!api.isOpenMode()) {
api.setOpenMode(true); // disables DMD group features, frees the radio
}
// … read settings, send/receive …
api.setOpenMode(false); // restores the DMD2 group radio config
You can also flip the mode without binding, via broadcast — see Broadcast API.
Reading & Writing Settings
Reading
readSettingsJson() returns the live register read-back:
{
"deviceType": "...", "address": "0x0000", "channel": 18,
"frequencyMhz": 868, "airRate": "2.4k", "power": "30dBm",
"packetSize": "200", "fixedMode": false, "appendRssi": false,
"worCycle": "2000", "uartBaud": "9600", "parity": "8N1",
"serialTimeout": "...", "lbtParams": "..."
}
Writing
writeSetting(key, value) takes a key and an AT value (an enum index, a number, or "0"/"1" for toggles):
| key | value | Notes |
|---|---|---|
address | 0–65535 | Module network address |
channel | 0–80 | RF channel (maps to frequency) |
power | index 0–3 | 0 = highest power |
airrate / rate | index 0–7 | Air data rate (lower index = longer range) |
packet | index 0–3 | Sub-packet size |
key | 0–65535 | Hardware encryption key (write-only) |
lbt | 0 / 1 | Listen-before-talk |
wtime / worcycle | index 0–7 | WOR wake cycle |
urxt | 1–255 | Serial frame-break timeout |
drssi | 0 / 1 | Append RSSI byte to received data |
trans / fixed | 0 / 1 | 0 = transparent, 1 = fixed (point-to-point) |
Sending & Receiving
Send
api.sendMessage("hello world", -1, -1); // broadcast on current channel
api.sendMessage("to node 5", 5, 18); // fixed-mode: address 5, channel 18
text | The message. Kept short — the LoRa payload is ~180–200 bytes; longer text may be truncated. |
targetAddr | Destination address for fixed mode, or -1 for a transparent broadcast. |
targetChannel | Destination channel for fixed mode, or -1 for the current channel. |
Receive
ILoraEventListener listener = new ILoraEventListener.Stub() {
@Override public void onMessageReceived(String text, long timestamp) {
// handle incoming raw LoRa text
}
};
api.registerListener(listener);
Broadcast API
For integrations that won't bind (scripts, Tasker), the same operations are available as broadcasts. Send them directed to the DMD-LoRa package:
Intent i = new Intent("com.thorkracing.dmdlora.open.SEND_MESSAGE");
i.setPackage("com.thorkracing.dmdLora");
i.putExtra("message", "hello");
i.putExtra("target_addr", -1);
i.putExtra("target_channel", -1);
sendBroadcast(i);
| Action | Extras | Notes |
|---|---|---|
…open.SET_MODE | open_mode (bool) | Switch modes. Works in either mode. |
…open.SEND_MESSAGE | message, target_addr, target_channel | Open Mode only. |
…open.WRITE_SETTING | key, value | Open Mode only. |
To receive messages without binding, register a receiver for the outbound action:
| Action | Extras |
|---|---|
com.thorkracing.dmdlora.open.MESSAGE_RECEIVED | message (String), timestamp (long) |
(All … prefixes above are com.thorkracing.dmdlora.)
Security & Privacy
- No special permission is required to bind or use the API — any app on the device may use the radio while it is in Open Mode.
- DMD riding-group data is never exposed here. The API and its message channel are live only in Open Mode; DMD2 group locations and group chat are private and never delivered through this API.
- Anything you broadcast over LoRa is unencrypted at the app layer and receivable by any DMD-LoRa module on the same channel. Don't send sensitive data in the clear.
- The dangerous radio settings (UART baud, AUX pin, factory reset, reboot) are excluded from the API so a third-party app cannot brick the module.