Command Reference¶
Commands are sent as register read/write operations within encrypted frames. Each command targets a specific board (module) and register address.
Command types¶
| Value | Constant | Direction | Response? |
|---|---|---|---|
0x01 |
CMD_READ |
App → Device | Yes — read request |
0x02 |
CMD_WRITE |
App → Device | Yes — write with ACK |
0x03 |
CMD_WRITE_NR |
App → Device | No — write, no response |
0x04 |
CMD_READ_RESP |
Device → App | Read response |
0x05 |
CMD_WRITE_RESP |
Device → App | Write acknowledgment |
0x06 |
CMD_WR_BIT |
App → Device | Bitfield write |
Read operation¶
Request (cmd = 0x01):
Response (cmd = 0x04):
Write operation¶
Request (cmd = 0x02 or 0x03):
Write ACK (cmd = 0x05):
CMD_WRITE_NR (0x03) does not expect a response.
Special command IDs¶
These commands use a dedicated command byte instead of the standard read/write types:
| Cmd | Hex | Purpose |
|---|---|---|
| 87 | 0x57 |
Vehicle activation |
| 91 | 0x5B |
PRE_COMM (handshake phase 1) |
| 92 | 0x5C |
SET_PWD (handshake phase 2) |
| 93 | 0x5D |
AUTH (handshake phase 3) |
| 99 | 0x63 |
Vehicle unlock |
| 100 | 0x64 |
Trunk/ACC open (requires auth) |
| 102 | 0x66 |
Set password |
| 104 | 0x68 |
NFC operations |
| 110 | 0x6E |
HID bond management |
| 111 | 0x6F |
BLE config (connect mode, RSSI) |
| 113 | 0x71 |
Navigation data (to TFT) |
| 118-120 | 0x76-78 |
File transfer (info/write/CRC) |
| 129 | 0x81 |
TFT theme/gear name |
| 171 | 0xAB |
Handlebar calibration |
| 175 | 0xAF |
Set password (newer) |
Complete s_cmd → command mapping, generated from the config package:
Commands whose wire cmd byte is overridden by s_cmd (everything else uses the ops default: read=1, write=2, writeNR=3).
| s_cmd | hex | commands |
|---|---|---|
| 1 | 0x01 |
rHtgAllSetInfo, rHtgBindState, rHtgErrorInfo, rHtgPN, rHtgStartAndTempMode, rHtgTempMode … |
| 2 | 0x02 |
wHtgAppt1, wHtgAppt2 |
| 6 | 0x06 |
setBatteryHeat, setDVRFBool3Bit, setDVRFBoolBit, setDashNaviTheme, setFL-APLColorEnable1Bit, setFL-APLColorEnable2Bit … |
| 7 | 0x07 |
iapBegin |
| 8 | 0x08 |
iapWrite |
| 9 | 0x09 |
iapCRC |
| 10 | 0x0A |
firmwareReset |
| 13 | 0x0D |
rPowerData |
| 17 | 0x11 |
playLight, playLight2, playSound |
| 40 | 0x28 |
thirdPartsBind, thirdPartsPair, thirdPartsUnbind |
| 87 | 0x57 |
active |
| 99 | 0x63 |
unlock |
| 100 | 0x64 |
closeAcc, closeAcc_os, openAcc, openAcc_os, openTrunk, openTrunk_os |
| 102 | 0x66 |
setPwd |
| 104 | 0x68 |
writeNfcDelete, writeNfcEnter, writeNfcExit |
| 110 | 0x6E |
clearAllHIDBond, clearHIDBond, getHIDRSSI, resetHID, setHIDRSSI, triggerHidBond … |
| 111 | 0x6F |
getConnectMode, getInfo, mediaEnable, rssiCali, rssiGet, rssiSet … |
| 113 | 0x71 |
rAppNaviState, setNaviDistance, setNaviDriveInfo, setNaviExit, setNaviInfo, setNaviRoad … |
| 114 | 0x72 |
writeLongString |
| 118 | 0x76 |
fileInfo |
| 119 | 0x77 |
fileWrite |
| 120 | 0x78 |
fileCRC |
| 129 | 0x81 |
rGearNameM1, rGearNameM2, rTftTheme1, rTftTheme2, setGearNameM1, setGearNameM2 … |
| 130 | 0x82 |
file_read_begin |
| 131 | 0x83 |
file_read_data |
| 132 | 0x84 |
file_read_verify |
| 137 | 0x89 |
fileDelete |
| 138 | 0x8A |
fileRetrieve |
| 139 | 0x8B |
fileSet |
| 144 | 0x90 |
wMediaControlAck, wMediaInfo, wMediaState |
| 146 | 0x92 |
rNtfStatus, readNtfSwitch, sNtfMsg, setNtfStatus, setNtfSwitch |
| 147 | 0x93 |
rOPPOFind, sOPPOFind |
| 171 | 0xAB |
handleCalibrateCheck, handleCalibrateEnd, handleCalibrateStart |
| 175 | 0xAF |
setPassWord |
| 177 | 0xB1 |
setWhiteList |
Speed & drive¶
Speed limit¶
Speed safety lock — setLimitSpeed / rLimitSpeed, DIS register 0x93. Values are whole km/h (the config writes the km/h value directly — not 0.1 km/h units).
| Setting | Value written | On-wire payload (u16 LE) |
|---|---|---|
| off / no limit | 0 | 00 00 |
| 25 km/h | 25 | 19 00 |
| 35 km/h | 35 | 23 00 |
| 45 km/h | 45 | 2D 00 |
| 60 km/h | 60 | 3C 00 |
Read back with rLimitSpeed (DIS 0x93, 2 bytes). The rated/factory cap is a separate, read-only register rRatedSpeed/rMaxSpeed (DIS 0x48, stored ×10 — e.g. 450 = 45.0 km/h). Effective top speed = min(rated, lock).
Speed readings¶
| Command | Board | Index | Size | Description |
|---|---|---|---|---|
| Current speed | DIS (0x01) | 38 (0x26) | 2B | Speed in 0.1 km/h units |
| Rated max speed | DIS (0x01) | 72 (0x48) | 2B | Factory max speed (0.1 km/h units) |
| Trip max speed | DIS (0x01) | 36 (0x24) | 2B | Trip maximum (x0.1 km/h) |
| Average speed | DIS (0x01) | 39 (0x27) | 2B | Trip average (x0.1 km/h) |
Riding mode¶
| Command | Board | Index | Data | Purpose |
|---|---|---|---|---|
| Read mode | DIS (0x01) | 116 (0x74) | 2B read | Current riding mode |
| Set mode | DIS (0x01) | 116 (0x74) | write | Set riding mode |
Gear profiles (MCU board)¶
| Command | Board | Index | Data | Purpose |
|---|---|---|---|---|
| Read gear 1 | MCU (0x02) | 34 | 32B | Gear profile 1 |
| Write gear 1 | MCU (0x02) | 34 | write | Write gear profile 1 |
| Gear top speed | MCU (0x02) | 49 | write | Set gear top speed |
| Gear acceleration | MCU (0x02) | 45 | write | Set acceleration rate |
| Gear TCS | MCU (0x02) | 47 | write | Set traction control |
| Gear nitro | MCU (0x02) | 48 | write | Set nitro speed |
| Read gear 2 | MCU (0x02) | 58 | 32B | Gear profile 2 |
| Gear 2 top speed | MCU (0x02) | 73 | write | Set gear 2 top speed |
| MCU max speed | MCU (0x02) | 9 | 2B read | MCU max speed |
| Speed safe lock | MCU (0x02) | 83 | 2B | Speed safety lock |
Battery & power¶
Battery status¶
| Command | Board | Index | Size | Description |
|---|---|---|---|---|
| Battery % | DIS (0x01) | 181 (0xB5) | 2B | Battery percentage |
| Main power | ECU (0x09) | 3 | 2B | Power status (mask 4 = battery online) |
| Current power | DIS (0x01) | 189 | 2B | Current power draw |
| Max power | DIS (0x01) | 73 | 2B | Maximum power |
| Charge power | DIS (0x01) | 145 | 2B | Charge power setting (read/write) |
| Battery type | DIS (0x01) | 146 | 2B | Battery type identifier |
| VRLA voltage | DIS (0x01) | 177 | 2B | 12V lead-acid voltage |
Mileage¶
| Command | Board | Index | Size | Description |
|---|---|---|---|---|
| Total mileage | DIS (0x01) | 183 (0xB7) | 4B | Odometer (0.001 km units, i.e. 1 metre precision) |
| Trip mileage | DIS (0x01) | 185 (0xB9) | 4B | Trip distance (0.001 km units) |
| Remaining range | DIS (0x01) | 37 (0x25) | 2B | Estimated range (0.1 km units) |
| Precise range | DIS (0x01) | 175 (0xAF) | 2B | Precise range (0.1 km units) |
| Trip time | DIS (0x01) | 186 | 2B | Current trip duration |
Per-battery detail¶
Each battery module has its own board ID. For E-series mopeds: BMS1 = 0x07, BMS2 = 0x06, BMS3 = 0x05.
| Register | Index | Size | Description |
|---|---|---|---|
| Voltage | 26 | 2B | Battery voltage |
| Status flags | 2 | 4B | Detailed status |
| Remaining capacity | 38 | 2B | Capacity remaining |
| Manufacture date | 32 | 2B | Production date |
| Energy throughput | 39 | 4B | Total energy (Wh) |
| Capacity throughput | 42 | 4B | Total capacity (Ah) |
| Cycle count | 27 | 2B | Charge cycles |
| Deep discharge count | 122 | 2B | Deep discharges |
| State of charge | 50 | 2B | SoC percentage |
Security & auth¶
Vehicle actions (cmd 0x64 / 100)¶
Vehicle control commands (power on/off, open seat/trunk) use a special command byte and require an $ident authentication token as the payload. The target board differs by device generation:
| Generation | Target board | Cmd byte | Notes |
|---|---|---|---|
| Gen2 (E-series mopeds) | VCU (0x09) | 0x64 (100) | E125S, E150S, E250S, E300SE |
| Gen3 (kick scooters) | BLE (0x04) | 0x03 (WRITE_NR) | Max G2, GT-series, P-series |
Target board matters
Sending vehicle actions to the wrong board (e.g., BLE 0x04 on an E-series moped) silently fails — the device does not respond or act. The E-series requires target VCU (0x09).
Action registers¶
| Action | Index | Hex | Description |
|---|---|---|---|
| Open ACC | 78 | 0x4E | Power on the vehicle (turn on ignition) |
| Close ACC | 70 | 0x46 | Power off the vehicle |
| Open trunk | 115 | 0x73 | Open seat/trunk compartment |
$ident payload¶
The $ident token is a 16-byte authorization secret issued by the Segway cloud during first-time device pairing. It is sent as the data payload of the command frame. The device verifies this token before executing the action.
Frame (Gen2, open ACC):
Target: VCU (0x09)
Cmd: 0x64
Index: 0x4E
Data: 16 bytes of hex-decoded ident
Silent failure on wrong ident
Frames with a mismatched ident still decrypt cleanly and pass MAC verification — the device just ignores them. There is no error response. This is the failure mode for an app that has the correct handshake password but the wrong (or missing) ident: handshake succeeds, reads work, but no vehicle action ever fires.
Wire format. The value is stored on-device as a 32-character lowercase hex string and decoded to 16 binary bytes before being placed on the wire:
Stored (UserDefaults): "00112233445566778899aabbccddeeff" (example)
On-wire payload: 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
Provenance. In the official Segway/Ninebot app (cn.ninebot.device.motor.MotorDevice), the value arrives once via an authenticated cloud API response, is cached in HttpCacheClient under the key "ident", and is written to persistent storage so the app continues to work offline:
| Platform | Storage |
|---|---|
| iOS | Library/Preferences/com.ninebot.segway.plist → UserDefaults key {serialNumber}_ident |
| Android | SharedPreferences (analogous) |
Not to be confused with the "airlock" proximity-unlock feature (iBeacon → auto-unlock while holding the start button near the scooter). The backup keys nb_airlock_ident_{sn} and nb_airlock_beacon_{sn} belong to that feature; they incidentally hold the same 16-byte value in the reference app, but the code paths are independent.
Third-party apps that cannot talk to the Segway cloud must obtain the ident out-of-band — the typical path is to extract it from an iOS backup the user already has. See Credential QR setup for the QR/paste flow used by the open-source Flutter client in this repository.
Other security commands¶
| Command | Board | Index | Cmd | Purpose |
|---|---|---|---|---|
| Unlock | ECU (0x09) | 1 | 99 (0x63) | Unlock vehicle |
| Activate | DIS (0x01) | 0 | 87 (0x57) | Activate vehicle |
| Set NFC pwd | DIS (0x01) | 0 | 102 (0x66) | Set NFC password |
NFC operations (DIS board, cmd 104)¶
| Command | Index | Timeout | Purpose |
|---|---|---|---|
| Enter write mode | 5 | 60s | Enable NFC card writing |
| Delete card | 6 | 10s | Remove NFC card |
| Exit NFC mode | 10 | default | Exit NFC operation mode |
Feature flags¶
Boolean registers control vehicle features. Each register is a bitmask where individual bits enable/disable features.
Toggle bits (rFunBool / rCTLBool2 / rWarn / rBool)¶
Bit → meaning, derived from the app's own UI configs (label = the app's string for that toggle), so it can't drift from the shipped app:
Feature-flag bits and their meanings, derived from the app's UI configs (the label is the app's own string reference for that toggle).
| Register | Mask | UI label |
|---|---|---|
rBool |
0x0040 |
Unlocking Passcode |
rCTLBool2 |
0x0002 |
|
rCTLBool2 |
0x0008 |
Direction indicator sound |
rCTLBool2 |
0x0080 |
Auto-Park |
rCTLBool2 |
0x0400 |
Electronic Brake |
rCTLBool2 |
0x2000 |
Traction Control System (TCS) |
rFunBool |
0x0002 |
Always-on; Assist Mode; Enable with Switch |
rFunBool |
0x0004 |
Auto Key lock; Walk-away Auto-Lock |
rFunBool |
0x0080 |
Extended range with low battery |
rFunBool |
0x0100 |
Parking Detection |
rFunBool |
0x0200 |
Seat detection |
rFunBool |
0x0400 |
Streamline taillight |
rFunBool |
0x0800 |
Direction Indicator Auto-Off |
rFunBool |
0x2000 |
Reverse |
rFunBool |
0x4000 |
Show remaining range |
rWarn |
0x00FF |
Prompt volume |
rWarn |
0xFF00 |
Prompt sound effects; Sound 1; Sound 2; Sound 3 |
Inverted-logic bits
Some bits are stored inverted (e.g. rCTLBool2 0x0008 direction-indicator sound, 0x0400 electronic brake): a set bit means the feature is off. The app's toggle handler negates these.
rFunAppBool (DIS, index 138 / 0x8A)¶
This register gates UI feature visibility in the app:
| Bit | Mask | Features gated |
|---|---|---|
| 4 | 0x0010 | Speed safety lock, electric brake, auto park, handlebar calibrate |
| 10 | 0x0400 | Precise mileage support |
rFunAppBool2 (DIS, index 140 / 0x8C)¶
| Bit | Mask | Feature |
|---|---|---|
| 1 | 0x0002 | TCS support |
| 2 | 0x0004 | Sitting detection (alternate) |
Other boolean registers¶
| Register | Index | Purpose |
|---|---|---|
| rCTLBool | 132 (0x84) | Control flags group 2 |
| rFunAppBool3 | 169 (0xA9) | App feature flags 3 |
| rFunAppBool4 | 156 (0x9C) | App feature flags 4 |
| rBool | 178 (0xB2) | General boolean register |
| rBool2 | 170 (0xAA) | Boolean register 2 |
| rInfoBool2 | 55 (0x37) | Info boolean register |
Lights & sound¶
Sound (DIS board)¶
| Command | Index | Description |
|---|---|---|
| Volume | 117 (0x75) | Mask 0x00FF: volume 0-100% |
| Sound effect | 117 (0x75) | Mask 0xFF00: 0=E1, 256=E2, 512=E3 |
| Alarm volume | 118 | Alarm volume level |
| Alarm sensitivity | 119 | Sensitivity 1-4 (4 = highest) |
| Play sound | — | cmd 17: trigger sound effect |
Headlights (DIS board)¶
| Command | Index | Description |
|---|---|---|
| Brightness | 123 | Headlight brightness level |
| Delay off | 129 | Delay before lights turn off |
| Handle light mode | 141 | Handlebar light mode |
| Handle RGB 1 | 142 | 4-byte RGB color 1 |
| Handle RGB 2 | 157 | 4-byte RGB color 2 |
HEP lights (HEP board, 0x11)¶
| Command | Index | Size | Purpose |
|---|---|---|---|
| Mode (power on) | 0 | 8B | Light mode when powered on |
| Mode (power off) | 4 | 8B | Light mode when powered off |
| Current mode | 8 | 8B | Active light mode |
| Park mode | 12 | 8B | Parking light mode |
| Play animation | 1 | cmd 17 | Trigger light animation |
Vehicle information¶
| Command | Board | Index | Size | Description |
|---|---|---|---|---|
| Serial number | DIS (0x01) | 16 | 14B | ASCII serial number |
| CPU ID | DIS (0x01) | 218 | 12B | CPU identifier |
| Dashboard FW | DIS (0x01) | 26 | 2B | Dashboard firmware version |
| MCU FW | DIS (0x01) | 40 | 2B | MCU firmware version |
| BMS1 FW | DIS (0x01) | 48 | 2B | Battery 1 firmware |
| BMS2 FW | DIS (0x01) | 49 | 2B | Battery 2 firmware |
| BMS3 FW | DIS (0x01) | 50 | 2B | Battery 3 firmware |
| ECU FW | DIS (0x01) | 76 | 2B | ECU firmware version |
| BLE FW | BLE (0x04) | 104 | 2B | BLE module firmware |
| ABS FW | DIS (0x01) | 208 | 2B | ABS firmware version |
| Charger FW | DIS (0x01) | 93 | 2B | Charger firmware |
| DIS part number | DIS (0x01) | 80 | 14B | Dashboard part number |
| ECU part number | ECU (0x09) | 40 | 14B | ECU part number |
| MCU part number | MCU (0x02) | 16 | 14B | MCU part number |
| Timezone | DIS (0x01) | 134 | 2B | Timezone setting (read/write) |
Polling schedule¶
The app polls different registers at different intervals depending on the active screen:
| Period | Register types |
|---|---|
| Once (0) | Static info (serial, firmware versions) |
| 500 ms | Fast-changing: speed, power |
| 1,000 ms | Medium: running time, trip max speed |
| 2,000 ms | Slow: range, battery level, mileage |
| 3,000 ms | Very slow: tire pressure, temperature |