GotwayProtocol

open class GotwayProtocol(scope: CoroutineScope = CoroutineScope(Dispatchers.IO)) : EUCProtocol(source)

Gotway/Begode reverse-engineered protocol (updated)

This comment summarizes the observed variants:

  • Raw "A" / "B" frames (controller serial stream, header 0x55 0xAA)

  • Short "legacy" packets (e.g., 0x01 / 0x02) re-emitted by some adapters

  • Type 0xA5 packets (compressed commands/status emitted by firmware/adapters)

General observations:

  • Raw A/B frames observed on the serial port typically use header 0x55 0xAA and Big Endian (BE) fields for 16/32 bit integers.

  • Legacy packets (0x01/0x02) and some 0xA5 packets often use Little Endian (LE) encoding and a more compact format.

  • Current/temperature fields can be signed; must be converted correctly.

  • Many firmware/adapters do not add checksum. The stream can be fragmented, delayed, or lose bytes on the BLE side (no flow control).

  • Some packets include BMS cell voltages at the end, encoded as pairs of 2 bytes (LE) or in mV/centivolts depending on the variant.

Example (observed): A: 55 AA 19 F0 00 00 00 00 00 00 01 2C FD CA 00 01 FF F8 00 18 5A 5A 5A 5A B: 55 AA 00 0A 4A 12 48 00 1C 20 00 2A 00 03 00 07 00 08 04 18 5A 5A 5A 5A

Summary format (adjust according to firmware/model):

  • Frame A (header 0x55 0xAA): Bytes 0-1: 0x55 0xAA Bytes 2-3: BE voltage (fixed point, e.g., 1/100) Bytes 4-5: BE speed (fixed point, e.g., 3.6 * value / 100 -> km/h) Bytes 6-9: BE distance (uint32, meters) Bytes 10-11: BE current (signed, fixed point) Bytes 12-13: BE temperature (signed or raw MPU value) Bytes 14-17: unknown / flags Byte 18: frame type (e.g., 0x00) Byte 19: footer (0x18) Bytes 20-..: footer 0x5A 0x5A 0x5A 0x5A (or variants) + optional BMS trailing

  • Frame B (header 0x55 0xAA): Bytes 2-5: BE total distance (uint32) Byte 6: pedals mode / alarms (nibbles) Bytes 7-12: additional unknown fields Byte 13: LED / mode Bytes 14-17: unknown Byte 18: frame type (e.g., 0x04) Footer same

  • Legacy packets (e.g., 0x01 / 0x02): - Often sent by Serial->BLE adapter or alternative firmware. - LE fields, more compact formats; can represent voltage/speed/etc.

  • 0xA5 packets: - Used for commands (LIGHT_ON/OFF, BEEP, POWER_OFF) and sometimes for compressed states. Different structure (header 0xA5 ...).

Parsing recommendations:

  • Dispatch by first byte/header: 0x55 (A/B raw), 0x01/0x02 (legacy), 0xA5 (command/status), or by type byte in the frame if present.

  • For A/B: process integers as BE. For legacy/0xA5: try LE.

  • Handle fragmentation: tolerate variable sizes, ignore too short frames, attempt re-synchronization on 0x55 0xAA or adapter headers.

  • Dynamically extract cell voltages from the queue if present: read pairs of 2 bytes (LE) and convert to V (mV -> V or /100 -> V depending on ranges).

  • Correctly convert signed values (current, motor temperatures).

  • Stay defensive: validate plausible ranges (voltage, current, temperature).

Why were these variants not in the old comment?

  • The original comment described the raw serial stream observed on a specific controller/firmware. Other firmware/adapters (Serial->BLE) re-emit or transform these bytes (different headers, different endianness) — these variants were not necessarily present during the initial reverse engineering.

Inheritors

Constructors

Link copied to clipboard
constructor(scope: CoroutineScope = CoroutineScope(Dispatchers.IO))

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard
open override val dataFlow: Flow<EUCData>
Link copied to clipboard
open override val manufacturer: String

Manufacturer name

Link copied to clipboard
open override val rawFrameFlow: Flow<ByteArray>

Flow that emits every raw BLE characteristic notification received by this protocol, as a defensive copy of the original byte array. Collectors can use this to write raw logs or perform any custom processing on the unmodified BLE data.

Link copied to clipboard

Explicit command support matrix for this protocol. Commands outside this set are considered unsupported by design.

Link copied to clipboard
open override val supportedModels: List<String>

List of supported models

Link copied to clipboard
open val writeFlow: Flow<ByteArray>

Flow of commands or raw bytes that the protocol needs to write to the device automatically.

Functions

Link copied to clipboard
open override fun canHandle(device: EUCDevice): Boolean

Check if this protocol can handle the given device

Link copied to clipboard
open override fun close()
Link copied to clipboard
open override fun createCommand(commandType: CommandType, value: Any): ByteArray

Create a command for the EUC

Link copied to clipboard
open override fun decode(data: ByteArray): EUCData?

Decode raw BLE data into EUCData

Link copied to clipboard
open override fun getBMSData(): List<BMSData>

Returns the current BMS data snapshots for all detected battery packs. Each entry represents one BMS unit (typically 1 or 2 for dual-battery wheels). Data is accumulated from Type 2/3 frames which contain smart BMS cell voltage pages.

Link copied to clipboard

API-level command support check (used by framework and clients).

Link copied to clipboard
open override fun getDataCharacteristicUUID(): UUID

Get the UUID for the data characteristic

Link copied to clipboard
open override fun getPollingPlan(): ProtocolPollingPlan

Optional polling/query plan consumed by BLEManager orchestration.

Link copied to clipboard
open override fun getServiceUUID(): UUID

Get the UUID for the service

Link copied to clipboard

Get the UUID for the write characteristic (if different from data characteristic)

Link copied to clipboard
open override fun isDeviceReady(data: EUCData): Boolean

Check if the device is ready for operation

Link copied to clipboard
open override fun looksLikeMyFrames(chunk: ByteArray): Boolean

Optional fast header check: returns true if chunk appears to contain frames belonging to this protocol, based on magic-byte patterns alone. The default returns false (opt-out). Protocols override this to enable frame-header-based routing without full decoding.

Link copied to clipboard
open override fun matchesQueryResponse(query: ProtocolQuerySpec, data: ByteArray): Boolean

Optional query/response matcher used by BLEManager observability and retry loop.