Skip to content

Board Addressing & Devices

Each Segway-Ninebot vehicle consists of multiple boards (modules), each with its own address. Commands are routed to specific boards using the TARGET_ID field in the frame.


Board types

Board String ID Hex Description
DIS dis varies Display / Dashboard controller
CTRL ctrl varies Main controller
BLE ble varies Bluetooth module
BMS bms varies Battery Management System (primary)
BMS2 bms2 varies Battery Management System (secondary)
BMS3 bms3 varies Battery Management System (tertiary)
BMS4 bms4 varies Battery Management System (quaternary)
MCU mcu varies Microcontroller unit
ECU ecu varies Electronic Control Unit
SUB_CTRL sub_ctrl varies Secondary controller
DRIVER driver varies Motor driver
AHRS ahrs varies Attitude & Heading Reference System
UPC upc varies Uninterruptible Power Controller
ANCHOR anchor varies Anchor/dock module
DBOARD dboard varies Dashboard board
MOTOR1 motor1 varies Motor 1
MOTOR2 motor2 varies Motor 2
TAG tag varies Tag/tracking module
PTZ ptz varies Pan-Tilt-Zoom (camera)
BFG bfg varies BLE Firmware Gateway

Protocol identifier

The BT_ID field (byte position 3 in Encryption2 frames) is always 0x3E (62). This identifies the Bluetooth protocol layer — it is not a board address. Frames with BT_ID != 0x3E are silently discarded.


Board ID routing

Each board has two IDs:

  • sendId: Used as TARGET_ID when sending commands to this board
  • receiveId: Expected in response frames from this board

These IDs are defined in the device configuration and vary by device model.


Example: Segway eScooter E (E125S) module map

Modules referenced by the Segway eScooter E config (serverId 81, HW 66 — the E125S). The E150S / E250S (serverId 14102, HW 4102) are a separate config family. The module list is generated from the config package; the numeric target bytes come from the app's BoardConfig (see board_ids.json):

Modules referenced by this family's config, joined to the numeric BLE target byte (from board_ids.json — the numeric IDs are the one thing not carried in the config package).

Module Target In config Live-confirmed Note
abs 0x1B
abs-f 0x1B
abs-r 0x1C
ble 0x04 handshake target
bms-ble1 0x0D
bms-ble2 0x0E
bms-ble3 0x0F
bms1 0x07 ~232 registers
bms2 0x06 ~231 registers
bms3 0x05 absent on 2-battery units
chg 0x20 16 registers respond at 0x20
dis 0x01 answers every index (256)
dvr-f 0x31
ecu 0x09 power state + vehicle actions + config block
fl-apl 0x22
gl-apl 0x34
hep 0x11
htg 0x35
light 0x15
mcu 0x02 E125S: part number only, no gear registers
tft 0x23

Board availability by power state

Not all boards are accessible when the vehicle is powered off. Only boards with independent power (BLE module, dashboard) respond to register reads when the ignition is off. Commands sent to sleeping boards time out silently with no response.

Confirmed on E125S (live BLE full-register sweep):

Board Target Off On Notes
DIS (Dashboard) 0x01 yes yes Always powered — answers every index; aggregates other boards
BLE 0x04 yes yes Battery-powered radio module
ECU / VCU 0x09 yes yes Power state via rMainPower (0x03); config block at 0xDF
MCU 0x02 no yes* *Even powered on, answers only its part number (0x00/0x10) — this family has no MCU gear registers
BMS1 0x07 no yes ~232 registers when awake
BMS2 0x06 no yes ~231 registers when awake
BMS3 0x05 no no Absent on 2-battery units
CHG 0x20 no charging 16 registers respond

Addresses come from the config, not the old 0x20/0x22/0x25 scheme

On the E-series the motor controller is 0x02, the batteries 0x06/0x07, and the ECU 0x09 — matching this family's config (and live-confirmed). There is no board at 0x25. Earlier drafts used a different family's 0x20/0x22/0x23/0x25 convention.

Reading power state (rMainPower, ECU 0x03)

Bit 3 (0x08) = powered on. Observed 0x01F7 (503) = off; 0x05FF (1535) or 0x01FF (511) = on — bit 3 is the reliable cross-unit indicator. Bit 2 (0x04) = main battery present (set even in standby, so not an on/off signal).

Dashboard as proxy

When the vehicle is off, read battery level (DIS:0xB5), range (DIS:0x25), and odometer (DIS:0xB7) from the DIS board (0x01) rather than directly from BMS. DIS aggregates data from sleeping boards and keeps cached values available.


Supported device families

Every device family in the manifest, with its protocol/encryption values, is generated from the pulled config packages + app scan config — so it never drifts:

All 66 device families from manifest.json, joined to the scan-config protocol values (backup_config). ble_protocol and encrypt are the static config values; - = field absent (runtime default applies).

Kick scooters

Family serverId HW id Commands ble_protocol encrypt Live-confirmed
Ninebot KickScooter Air 58 35 55 2 -
Ninebot KickScooter E 55 39 106 2 -
Ninebot KickScooter ES 52 33 84 2 0
Ninebot KickScooter Max 54 36 68 2 0
Ninebot Kickscooter C2 Pro 535 124 70 2 -
Ninebot Kickscooter D18 525 116 46 - 2
Ninebot Kickscooter D28 526 114 47 - 2
Ninebot Kickscooter D38 527 115 46 - 2
Ninebot Kickscooter E2/E2 Plus 536 125 69 2 -
Ninebot Kickscooter F 521 44 63 2 -
Ninebot Kickscooter F 534 123 55 2 -
Ninebot Kickscooter F2 538 127 104 2 -
Ninebot Kickscooter F2 Plus 539 128 104 2 -
Ninebot Kickscooter F2 Pro 540 129 104 2 -
Ninebot Kickscooter F65 530 45 59 - 2
Ninebot Kickscooter G65 531 120 63 - 2
Ninebot Kickscooter MAX G2 537 131 121 - 2
Ninebot Kickscooter UiFi 1 532 121 74 - 2
Ninebot Kickscooter UiFi 1 Pro 533 122 74 - 2
Ninebot eKickScooter E2 Pro 541 141 107 2 -
Segway ST2 Pro 544 136 83 - 2
Segway GT1 523 112 86 - 2
Segway GT2 524 113 83 - 2
Segway P100S 529 119 91 - 2
Segway P65 528 118 91 - 2
Segway SuperScooter GT3 10257 257 192 2 2
Segway ZT3 Pro 10256 256 191 2 2
Segway eKickscooter Ninebot MAX G3/MAX G3 Plus 10258 258 192 2 2
eKickScooter E3 Series 10261 261 145 2 -
eKickScooter F3 10259 259 192 2 2

Mopeds & motorcycles

Family serverId HW id Commands ble_protocol encrypt Live-confirmed
SEGWAY E150S / SEGWAY E250S 14102 4102 512 2 2
Segway eMoped B 110 73 511 2 -
Segway eMoped B 111 86 511 2 -
Segway eMoped C 105 67 511 2 -
Segway eScooter E 81 66 512 2 - Enc2/gen2
Segway eScooter N 820 89 512 2 -

Self-balancing

Family serverId HW id Commands ble_protocol encrypt Live-confirmed
Ninebot S 2 33 30 98 1 -
Ninebot S L 39 28 96 2 -
Ninebot S Nano 37 25 83 2 -
Ninebot S Nano 38 27 82 2 -
Ninebot S-Max 35 24 85 1 -
Ninebot S-Max 351 26 92 1 -
Ninebot S-Plus 6 20 98 2 -
Ninebot-S 3 3 84 1 -
Segway miniLITE 32 22 73 2 -

GoKarts

Family serverId HW id Commands ble_protocol encrypt Live-confirmed
Ninebot Gokart 7 48 88 2 -
Ninebot Gokart Pro 72 49 93 2 -
Ninebot Gokart Pro Automobili Lamborghini Edition 73 50 100 2 -
Segway Gokart Kit 2 77 57 102 2 -
Segway Gokart Pro 2 76 56 116 2 -
Segway Gokart Pro Bumblebee Limited Edition 74 54 94 2 -
Segway Gokart Pro Optimus Prime Limited Edition 75 55 95 2 -

Unicycles

Family serverId HW id Commands ble_protocol encrypt Live-confirmed
Ninebot One 2 2 52 1 -
Ninebot One A1 4 19 72 1 -
Ninebot One Z 42 18 69 2 -

E-bikes

Family serverId HW id Commands ble_protocol encrypt Live-confirmed
Segway E-bike Muxi 27153 17153 166 2 2
Segway E-bike Myon 26384 16384 165 2 2
Segway Xyber 27152 17152 166 2 2
Xafari 26640 16640 159 2 2

Speakers

Family serverId HW id Commands ble_protocol encrypt Live-confirmed
Ninebot Engine Speaker 1110 242 5 2 0
Segway-Ninebot Engine Speaker 2 1111 244 5 2 0
九号引擎音箱II 27410 17410 5 2 0

Power stations

Family serverId HW id Commands ble_protocol encrypt Live-confirmed
Lumina-500 20752 10752 104 2 2
Segway Portable Power Station Cube 1112 208 104 2 2

Armor kits

Family serverId HW id Commands ble_protocol encrypt Live-confirmed
Ninebot Mecha Kit 107 51 104 2 -
Ninebot Mecha Kit 1071 52 104 2 -

Encryption is negotiated at runtime

The encrypt column is the static scan-config default. A device can advertise a higher encryption version, which the app honours at connect time. Segway eScooter E (HW 66 / serverId 81 — the E125S family) carries no static encrypt value yet completes a full Encryption2/gen2 AES handshake on real hardware. Do not infer the wire encryption from this column alone. Note also that E125S (HW 66) and E150S/E250S (HW 4102 / serverId 14102) are separate config families.

Protocol selection

Each device's configuration specifies two values that determine the BLE protocol class:

  • ble_protocol — frame format: 1 = Protocol 1 (0x55AA), 2 = Protocol 2 (0x5AA5)
  • encrypt — encryption version: 0 = XOR/none, 2 = Encryption2 (AES-CCM), 3 = Encryption3 (V3Auth)

The app's DynamicDevice.createBleProtocol() selects the protocol class:

ble_protocol + encrypt → Protocol class

  protocol=2, encrypt=2  →  BleEncryption2Protocol2  (most devices)
  protocol=2, encrypt=3  →  BleEncryption3Protocol2  (newest devices)
  protocol=2, encrypt=0  →  BleProtocol2             (XOR only)
  protocol=1, encrypt=2  →  BleEncryption2Protocol1
  protocol=1, encrypt=0  →  BleProtocol1             (no encryption)

All BLE protocols use 0x5A 0xA5

Every BLE protocol variant (Protocol 2, Encryption2, Encryption3) uses the same 0x5A 0xA5 header. The 0x5A 0xB5 header is exclusively used by WiFi v2 transport (WifiEncryptionProtocol2), which supports 2-byte length fields for payloads up to 65,535 bytes.

The encrypt value from the config is a default — the actual encryption version can also be determined at runtime from BLE advertisement manufacturer data (manufacturer ID 0x4E42 or 0x4E43).

Protocol class hierarchy

BleProtocol1           — 0x55AA, optional XOR
BleProtocol2           — 0x5AA5, optional XOR
BleEncryption2Protocol — 0x5AA5, AES-128 CTR + CBC-MAC (abstract)
  ├── BleEncryption2Protocol1 — Protocol 1 framing + AES
  ├── BleEncryption2Protocol2 — Protocol 2 framing + AES (most common)
  └── BleEncryption3Protocol2 — Protocol 2 framing + V3Auth
WifiEncryptionProtocol  — 0x5AA5, WiFi transport
WifiEncryptionProtocol2 — 0x5AB5, WiFi v2 (extended length)

Encryption3 / V3Auth

No devices in the current scan config have encrypt=3 set statically. Encryption3 support is determined at runtime from the device's BLE advertisement data (encryptProtocolVersion == 3). Devices that support V3Auth use BleEncryption3Protocol2 with a different handshake flow (conectV3() instead of connectGetSN()).