Integration Guide
Overview
This guide covers the integration of the miro EdgeCard mioty into a host system. It is intended for system integrators embedding the card into a product or gateway device.
Current firmware revisions are fully locked down. There is no SSH or serial console access. All configuration and management is performed through an HTTP API exposed by the card over its USB network interface.
Legacy firmware
If you are working with an older firmware revision that provides SSH access, refer to the legacy integration guide.
Host System Networking
The miro EdgeCard mioty exposes a USB CDC ECM network interface with a static IP address of 172.30.1.2/24. The host system must be configured with an address in the same subnet (e.g. 172.30.1.1) and must forward traffic on behalf of the card so it can reach the mioty service center.
The host network stack SHALL restrict access to 172.30.1.2 to localhost only. The Config API is unauthenticated; network-level isolation is the only access control in place.
The card requires the following outbound connections. The host SHALL permit these through its firewall or routing policy:
| Service | Protocol | Notes |
|---|---|---|
| BSSCI or MQTT | TCP | Port configurable via Config API |
| NTP | UDP 123 | Time synchronisation |
| Firmware updates | HTTPS or HTTP | RAUC bundle download |
The card does not require any inbound connections from the internet.
Replace eth1 with the host's uplink interface and eth2 with the USB CDC ECM interface.
Save to /etc/iptables/iptables.rules to survive reboots:
All subsequent API calls use the base address http://172.30.1.2/cgi-bin.
Security Requirements
The following requirements apply to all deployments. They are mandatory for compliance with EN 18031-1:2024 under EU Radio Equipment Directive 2022/30, Article 3(3)(d). Requirements use normative language: SHALL denotes a mandatory requirement; SHOULD denotes a strong recommendation.
Config API Access Control
The Config API at 172.30.1.2 is unauthenticated by design. The host network stack SHALL restrict access to 172.30.1.2 to localhost only. The Config API SHALL NOT be exposed to any external network interface, including any interface reachable from the internet or from other devices on the local network.
The iptables configuration in Host System Networking provides a forwarding baseline. The integrator is responsible for ensuring no rule exposes the Config API beyond the host.
BSSCI Transport Security
Unencrypted BSSCI connections are permitted only to a mioty Service Center running on the host system (loopback or isolated LAN segment). For any BSSCI connection to a Service Center reachable over an untrusted network, TLS SHALL be enabled. mTLS is strongly recommended. Certificate and key management is the responsibility of the system integrator.
See TLS Configuration for setup instructions.
MQTT Transport Security (NM Mode)
This requirement applies to the GWC-62-MY-868-NM variant only.
Unencrypted MQTT connections are permitted only to a broker running on the host system. TLS SHALL be enabled for any connection to an external broker. mTLS is strongly recommended.
Physical Access Control
The host system enclosure SHALL prevent unauthorised physical access to the installed gateway card. Physical access to the card provides direct access to the Config API. See the Overview for mechanical requirements.
Firmware Updates
The system integrator SHALL apply Miromico security firmware updates within a timeframe appropriate to the severity of the vulnerability, as stated in the Miromico security advisory. Miromico provides security updates for a minimum of 5 years from the last date of manufacture.
System Time at Boot
The host system SHOULD set the card system time via /cgi-bin/time after each boot, before NTP
synchronisation is established. This ensures correct certificate validation and license expiry
checking from first startup. See Set System Time.
Initial Setup
Boot Sequence
Complete the following steps in order on boot:
- Verify the card is reachable:
GET /cgi-bin/status - Set system time:
POST /cgi-bin/time - Configure BSSCI or MQTT endpoint:
POST /cgi-bin/config - Verify operation:
GET /cgi-bin/status
Check Device Status
Before configuring the card, verify it is reachable and note the device ID. The device ID is required when requesting a license.
Set System Time
The card does not have a hardware real-time clock. Set the system time on every boot before performing any operation that involves certificate validation (TLS connections, firmware updates).
License
The card ships with a license for the BS (Base Station) feature pre-installed. No license installation is required for initial operation.
Check License Status
Use GET /cgi-bin/license to query the installed licenses and their current feature validity.
expiry: null means the feature has no expiry date. valid: false on a feature means it has
expired. An empty licenses array means no license is installed.
In-field license changes are provided by Miromico as a separate process; the integrator does not call the license API directly to install or upgrade a license.
Operating Modes
The card operates in exactly one of three mutually exclusive modes at a time: BS, SC, or
IQ. POST /cgi-bin/config selects the active mode by which top-level block (bs, sc, or
iq) is present in the request body; applying one mode's configuration stops the previously
active mode's services and starts the new one. Each mode requires the corresponding feature to be
licensed (see License).
| Mode | Block | Description |
|---|---|---|
| BS | bs |
Base Station only. Connects to an external mioty Service Center over BSSCI. |
| SC | sc |
Service Center + co-located Base Station, connected over loopback. This is the mode referenced as "NM Mode" in MQTT Transport Security. |
| IQ | iq |
IQ stream tunneling server only. No Base Station or Service Center runs in this mode. |
GET /cgi-bin/config returns the configuration for whichever mode is currently active.
Configuration Merge Behavior
Configuration is always persistent (survives reboot) and is merged field-by-field into whatever is already stored, rather than being replaced wholesale:
- A parameter omitted from the request keeps its last stored value.
- A parameter present with a value overwrites the stored value.
- A parameter explicitly set to
""(empty string) clears it. - An empty mode block (e.g.
"bs": {}) is therefore a no-op on stored configuration -- it just activates that mode without changing anything already configured.
This applies to every mode block (bs, sc, iq) and to the feature flags described below.
BS Configuration
The BS is configured via POST /cgi-bin/config. The request body is a JSON object with a bs
block containing the configuration and top-level control flags.
Configuration is merged into whatever is already stored -- see Configuration Merge Behavior. Omitting a parameter leaves its current value unchanged; submit only the fields you want to change.
Parameter Reference
Top-level request fields
| Field | Type | Default | Description |
|---|---|---|---|
bs |
object | Configuration block. Contains bsi, mps, and optional TLS fields. Merged into whatever is already stored. |
|
restart |
boolean | true |
true restarts the BS service immediately after applying the configuration. Required for changes to take effect without a full reboot. |
reset |
boolean | false |
true erases the stored BS configuration back to factory defaults and activates BS mode. Mode block must be empty ("bs": {}). |
bs.bsi Base Station Identity
| Field | Type | Default | Description |
|---|---|---|---|
uniqueBaseStationId |
string | 00-00-00-00-00-00-00-00 |
EUI-64 identifier. Accepts 16 plain hex digits, hyphen-separated, or colon-separated (case-insensitive) -- normalized to lowercase hyphen-separated form. Must be unique across all base stations connected to the same service center. See Base Station ID. |
baseStationName |
string | mioty-bsm |
Human-readable name reported to the service center. Used for display and logging. |
baseStationInfo |
string | "" |
Optional free-text description of the installation site or deployment context. |
baseStationVendor |
string | "" |
Optional vendor identifier reported to the service center. |
baseStationModel |
string | "" |
Optional model identifier reported to the service center. |
serviceCenterAddr |
string | 172.30.1.1 |
Hostname or IP address of the mioty Service Center (BSSCI endpoint). |
serviceCenterPort |
string | 16017 |
TCP port of the mioty Service Center. |
tlsAuthRequired |
string | "false" |
Set to "true" to enable TLS for the BSSCI connection. Requires tlsCaCert, tlsCert, and tlsKey. See TLS Configuration. |
bs.mps Modulation and Physical Layer Settings
| Field | Type | Default | Description |
|---|---|---|---|
profile |
string | eu868 |
Radio frequency profile. Determines the operating band and channel plan. Supported value: eu868 (863-870 MHz, EU). |
bs TLS fields (required when tlsAuthRequired is "true")
| Field | Type | Description |
|---|---|---|
tlsCaCert |
string | PEM-encoded CA certificate used to verify the service center's server certificate. |
tlsCert |
string | PEM-encoded client certificate presented by the base station during mTLS handshake. |
tlsKey |
string | PEM-encoded private key corresponding to tlsCert. |
PEM values must be passed as single-line strings with literal \n escape sequences.
See TLS Configuration for the conversion procedure.
bs Feature Flags
These flags enable optional BS capabilities. Each flag is omitted from the default configuration
(equivalent to disabled). Set to true to enable. Flags merge the same way as everything else:
omitting a flag keeps its last stored value, and only an explicitly-present flag changes it. There
is no way to "unset" a flag back to a default state distinct from false -- send false to turn
it off.
| Field | Type | Description |
|---|---|---|
varmac |
boolean | Enable variable MAC ID mode. When enabled, the BS accepts endpoint frames with variable MAC addresses. |
ulp |
boolean | Enable ULP (Ultra Low Power) physical layer. |
hdr |
boolean | Enable HDR (High Data Rate) physical layer. |
emu |
boolean | Enable emulator mode with virtual endpoints for testing without physical devices. |
plmli |
boolean | Enable PHY MAC layer interface (PLMLI). |
recon |
boolean | Enable debug and measurement interfaces. Use during integration and diagnostics only. |
Factory Default Configuration
The default persistent configuration applied at the factory is equivalent to:
<module name="root">
<module name="bsi">
<parameter name="uniqueBaseStationId">00-00-00-00-00-00-00-00</parameter>
<parameter name="baseStationName">mioty-bsm</parameter>
<parameter name="baseStationInfo"/>
<parameter name="baseStationVendor"></parameter>
<parameter name="baseStationModel"></parameter>
<parameter name="serviceCenterAddr">172.30.1.1</parameter>
<parameter name="serviceCenterPort">16017</parameter>
<parameter name="tlsAuthRequired">false</parameter>
</module>
<module name="mps">
<parameter name="profile">eu868</parameter>
</module>
</module>
Base Station ID
uniqueBaseStationId must be unique across all base stations connected to the service center.
It may be submitted as 16 plain hex digits, hyphen-separated, or colon-separated octets
(case-insensitive) -- the device normalizes it to lowercase hyphen-separated form before storing
it. Any other form (wrong length, non-hex characters) is rejected with HTTP 400.
The recommended approach is to derive it from the host system's MAC address by inserting ff-fe
after the third octet.
For example, if the host MAC address is 9c:65:f9:61:07:c7, the base station ID becomes
9c-65-f9-ff-fe-61-07-c7 (any of 9c65f9fffe6107c7, 9C:65:F9:FF:FE:61:07:C7, or
9c-65-f9-ff-fe-61-07-c7 are equally accepted).
Read Current Configuration
Use GET /cgi-bin/config to read back the active BS configuration -- either what's been
explicitly configured, or the factory default if BS has never been configured.
Warning
If bsi.tlsKey is set, GET never returns it in full -- it's replaced with a short
prefix...suffix fingerprint (e.g. MIIJQQ...VUmZncz), enough to confirm the right key is
loaded without exposing it over the API. Re-POSTing a GET response verbatim without editing it
would therefore overwrite the real stored key with that fingerprint -- omit tlsKey from a
request entirely unless you are actually setting a new one.
Apply Configuration
Configuration is stored on the card and survives reboots. Submit only the fields you want to set or change -- anything omitted keeps its current value (see Configuration Merge Behavior).
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{
"bs": {
"bsi": {
"uniqueBaseStationId": "9c-65-f9-ff-fe-61-07-c7",
"baseStationName": "my-gateway-01",
"serviceCenterAddr": "eu3.loriot.io",
"serviceCenterPort": "727",
"tlsAuthRequired": "false"
},
"mps": {
"profile": "eu868"
}
},
"restart": true
}' \
http://172.30.1.2/cgi-bin/config | jq .
Resetting Configuration
To erase the stored BS configuration and revert to factory defaults:
TLS Configuration
When tlsAuthRequired is "true", the BSSCI connection to the service center is authenticated
and encrypted using TLS. mTLS is strongly recommended; provide all three of the following files:
| File | Description |
|---|---|
root_ca.cer |
CA certificate used to verify the service center |
bstation.cer |
Client certificate presented by the base station |
bstation.key |
Private key corresponding to the client certificate |
PEM files must be converted to single-line strings with literal \n sequences before embedding
in JSON.
CA=$(awk '{printf "%s\\n", $0}' root_ca.cer)
CERT=$(awk '{printf "%s\\n", $0}' bstation.cer)
KEY=$(awk '{printf "%s\\n", $0}' bstation.key)
curl -s -X POST \
-H "Content-Type: application/json" \
-d "{
\"bs\": {
\"bsi\": {
\"uniqueBaseStationId\": \"9c-65-f9-ff-fe-61-07-c7\",
\"serviceCenterAddr\": \"eu3.loriot.io\",
\"serviceCenterPort\": \"727\",
\"tlsAuthRequired\": \"true\",
\"tlsCaCert\": \"$CA\",
\"tlsCert\": \"$CERT\",
\"tlsKey\": \"$KEY\"
},
\"mps\": {
\"profile\": \"eu868\"
}
},
\"restart\": true
}" \
http://172.30.1.2/cgi-bin/config | jq .
Important
The system time must be valid before applying TLS configuration. Set the time using
/cgi-bin/time as described in Initial Setup.
SC Configuration
SC mode runs a mioty Service Center together with a co-located Base Station that connects to it over loopback, forwarding endpoint data to an application via MQTT. Selecting SC mode stops BS mode (and vice versa) -- see Operating Modes.
The SC is configured via POST /cgi-bin/config. The request body is a JSON object with an sc
block containing the configuration and the same top-level control flags (restart, reset) as
BS Configuration. The bs feature flags (varmac, ulp, hdr, emu,
plmli, recon) also apply here, controlling the co-located BS instance -- they share storage
with standalone BS mode, so setting a flag from either mode affects the other.
Parameter Reference
sc.ac Application Center
| Field | Type | Default | Description |
|---|---|---|---|
mqttBrokerAddr |
string | 127.0.0.1 |
Hostname or IP address of the MQTT broker. |
mqttBrokerPort |
string | 1883 |
TCP port of the MQTT broker. |
uniqueAppCenterId |
string | 00-00-00-00-00-00-00-00 |
EUI-64 identifier for the application center, hyphen-separated. |
sc.sc Service Center Settings
| Field | Type | Default | Description |
|---|---|---|---|
tlsAuthRequired |
string | "false" |
Set to "true" to enable TLS for the MQTT connection. |
Local vs. Remote MQTT Broker
Whether the card runs its own MQTT broker or connects to an external one is inferred from the
merged sc.ac.mqttBrokerAddr -- i.e. after the request is merged into whatever is already
stored -- not from whether the request's sc block happens to be empty:
- Empty or loopback (
mqttBrokerAddrunset,"", or127.0.0.1): the card runs a local MQTT broker, reachable at172.30.1.2:1883from the host. Use this when the application consuming endpoint data runs on the host system. - Any other address: the card connects to the external broker configured under
sc.ac. Use this to forward endpoint data directly to a broker elsewhere on the network.
Because this is based on the merged result, "sc": {} after a remote broker has already been
configured does not revert to local -- it's a no-op that leaves the remote configuration in
place (see Configuration Merge Behavior). To revert to a local
broker, explicitly clear the ac fields:
Read Current Configuration
Use GET /cgi-bin/config while SC mode is active to read back the current configuration.
Local Broker
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{
"sc": {},
"restart": true
}' \
http://172.30.1.2/cgi-bin/config | jq .
Remote Broker
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{
"sc": {
"ac": {
"mqttBrokerAddr": "mqtt.example.com",
"mqttBrokerPort": "8883",
"uniqueAppCenterId": "9c-65-f9-ff-fe-61-07-c7"
}
},
"restart": true
}' \
http://172.30.1.2/cgi-bin/config | jq .
Revert to Local Broker
After a remote broker has been configured, "sc": {} alone is a no-op (see
Local vs. Remote MQTT Broker) -- explicitly clear the ac fields
to revert:
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{
"sc": {
"ac": {
"mqttBrokerAddr": "",
"mqttBrokerPort": "",
"uniqueAppCenterId": ""
}
}
}' \
http://172.30.1.2/cgi-bin/config | jq .
Resetting Configuration
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"sc": {}, "reset": true}' \
http://172.30.1.2/cgi-bin/config | jq .
IQ Configuration
IQ mode runs only the IQ stream tunneling server -- no Base Station or Service Center. Selecting IQ mode stops BS/SC mode (and vice versa) -- see Operating Modes.
The IQ stream server is configured via POST /cgi-bin/config with an iq block.
Parameter Reference
| Field | Type | Default | Description |
|---|---|---|---|
port |
integer | 9000 |
TCP port the IQ stream server listens on. |
port merges like every other field: omitting it (posting {}) keeps whatever port is already
stored, rather than resetting to 9000. To explicitly reset the port back to 9000, send it as
"".
Read Current Configuration
Configure Custom Port
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{
"iq": { "port": 9010 },
"restart": true
}' \
http://172.30.1.2/cgi-bin/config | jq .
Resetting Configuration
curl -s -X POST \
-H "Content-Type: application/json" \
-d '{"iq": {}, "reset": true}' \
http://172.30.1.2/cgi-bin/config | jq .
Firmware Update
The card firmware can be updated in the field. The card downloads a RAUC bundle from an HTTP server and installs it. The bundle URL is passed in the update request and can point to any server reachable by the card.
If the host has internet access and NAT is configured as described above:
Trigger the Update
Set the system time before triggering the update, then start the install:
Monitor Progress
The install runs in the background. Poll the status endpoint until the state is no longer
installing:
Possible states are idle, installing, success, failed, and timeout. On failure, the
error and log fields contain details from the installer. progress and step are empty
strings ("") whenever no percentage is available yet -- not a placeholder "0%" -- which
includes every state other than installing, and briefly during installing itself before the
first percentage is reported.
Reboot
After a successful update, reboot the card to activate the new firmware:
The USB connection will drop during reboot. Wait 30-60 seconds, then use GET /cgi-bin/status
to confirm the card is back online.
Diagnostics
The card exposes recent system journal entries via the errors endpoint. This is useful for verifying that the BS service has started and connected to the service center, or for diagnosing connection issues.
Fetch last 100 lines and show only entries with priority 0-3 (error and above):
Antenna Recommendation
The miro EdgeCard mioty has been certified using the 2JW1115-C952B 868/915 MHz ISM antenna from 2J Antennas. This antenna is ground-plane independent and suitable for enclosure mounting without a metal backplane.
The card exposes a U.FL connector. Connect the antenna using a U.FL to SMA adapter cable.
Antenna Selection
Reads or sets the antenna selection for RX and TX.
Note
This endpoint requires the recon interface to be enabled. Set "recon": true in the
POST /cgi-bin/config request before using this endpoint.
Read
No request body required.
| Field | Type | Description |
|---|---|---|
rxant |
string | Antenna selected for RX ("0" or "1") |
txant |
string | Antenna selected for TX ("0" or "1") |
Set
| Field | Type | Description |
|---|---|---|
rxant |
integer | Antenna selected for RX (0 or 1) |
txant |
integer | Antenna selected for TX (0 or 1) |
At least one of rxant or txant must be provided.
Returns HTTP 200 with an empty JSON object {} on success.
Example Command