package anki import ( "bytes" "encoding/binary" "log" ) // VehicleMsgMaxSize = 20 // VehicleMsgPayloadMaxSize = 18 // VehicleMsgBaseSize = 1 // Identifier for a vehicle message const ( // BLE Connection VehicleMsgC2VDisconnect ID = 0x0d // Ping request / response VehicleMsgC2VPingRequest ID = 0x16 VehicleMsgV2CPingResponse ID = 0x17 // Messages for checking vehicle version info VehicleMsgC2VVersionRequest ID = 0x18 VehicleMsgV2CVersionResponse ID = 0x19 // Baterry level VehicleMsgC2VBatteryLevelRequest ID = 0x1a VehicleMsgV2CBatteryLevelResponse ID = 0x1b // Lights VehicleMsgC2VSetLights ID = 0x1d // Driving Commands VehicleMsgC2VSetSpeed ID = 0x24 VehicleMsgC2VChangeLane ID = 0x25 VehicleMsgC2VCancelLaneChange ID = 0x26 VehicleMsgC2VTurn ID = 0x32 // Vehicle position updates VehicleMsgV2CLocalizationPositionUpdate ID = 0x27 VehicleMsgV2CLocalizationTransitionUpdate ID = 0x29 VehicleMsgV2CLocalizationIntersectionUpdate ID = 0x2a VehicleMsgV2cVehicleDelocalized ID = 0x2b VehicleMsgC2VSetOffsetFromRoadCenter ID = 0x2c VehicleMsgV2COffsetFromRoadCenterUpdate ID = 0x2d // Light Patterns VehicleMsgC2VLightsPattern ID = 0x33 // Vehicle Configuration Prameters VehicleMsgC2VSetConfigParams ID = 0x45 // SDK Mode VehicleMsgC2VSDKMode ID = 0x90 ) type VehicleMsg struct { Size uint8 MsgID ID Payload []uint8 // MaxSize 18 } func SplitMsg(b []byte) (id ID, payload []byte) { if len(b) < 2 { return 0, nil } return ID(b[1]), b[2:] } func Encode(v interface{}) []byte { buf := new(bytes.Buffer) err := binary.Write(buf, binary.LittleEndian, v) if err != nil { log.Println("Encode", err) } b := buf.Bytes()[:] z := make([]byte, len(b)) copy(z, b) // FIXME workaround "cgo argument has Go pointer to Go pointer" return z } func EncodeMsg(id ID, v interface{}) ([]byte, error) { buf := new(bytes.Buffer) sz := binary.Size(v) + 1 if err := buf.WriteByte(byte(sz)); err != nil { return nil, err } if err := buf.WriteByte(byte(id)); err != nil { return nil, err } if err := binary.Write(buf, binary.LittleEndian, v); err != nil { return nil, err } b := make([]byte, buf.Len()) copy(b, buf.Bytes()) return b, nil } func Decode(b []byte, v interface{}) { buf := bytes.NewBuffer(b) err := binary.Read(buf, binary.LittleEndian, v) if err != nil { log.Println("Decode", err) } } func DecodeMsg(b []byte, v interface{}) error { buf := bytes.NewReader(b) if err := binary.Read(buf, binary.LittleEndian, v); err != nil { return err } return nil } type VehicleMsgVersionResponse struct { Size uint8 MsgID ID Version uint32 } type VehicleMsgBatteryLevelResponse struct { Size uint8 MsgID ID BatteryLevel MilliVolt // mV } type VehicleMsgSDKMode struct { Size uint8 MsgID ID On uint8 Flags uint8 } func (v *VehicleMsgSDKMode) Set(on uint8, flags uint8) { v.Size = uint8(binary.Size(v) - 1) v.MsgID = VehicleMsgC2VSDKMode v.On = on v.Flags = flags } type VehicleMsgSetSpeed struct { Size uint8 MsgID ID Speed MMperSec // mm/sec Accel MMperSecSQ // mm/secĀ² SpeedLimit Bool // respect road piece speed limit } func (v *VehicleMsgSetSpeed) Set(speed MMperSec, accel MMperSecSQ) { v.Size = uint8(binary.Size(v) - 1) v.MsgID = VehicleMsgC2VSetSpeed v.Speed = speed v.Accel = accel } const ( VehicleTurnNone = iota VehicleTurnLeft VehicleTurnRight VehicleTurnUTurn VehicleTurnUTurnJump ) const ( VehicleTurnTriggerImmediate = iota // Run immediately VehicleTurnTriggerIntersection // Run at the next intersection ) type VehicleMsgTurn struct { Size uint8 MsgID ID Type uint8 Trigger uint8 } const ( Lane1 float32 = 68.0 Lane2 float32 = 23.0 Center float32 = 0.0 Lane3 float32 = -23.0 Lane4 float32 = -68.0 ) /* Lanes direction ^ -23.0 23.0 | -68.0 0.0 68.0 offset | |____._____.__+__._____.____| 4 3 2 1 track see also: https://github.com/IBM-Bluemix/node-mqtt-for-anki-overdrive */ type VehicleMsgSetOffsetFromRoadCenter struct { Size uint8 MsgID ID Offset OffsetMM // mm } func (v *VehicleMsgSetOffsetFromRoadCenter) Set(offset OffsetMM) { v.Size = uint8(binary.Size(v) - 1) v.MsgID = VehicleMsgC2VSetOffsetFromRoadCenter v.Offset = offset } type VehicleMsgChangeLane struct { Size uint8 MsgID ID HorizontalSpeed MMperSec // mm/sec HorizontalAccel MMperSecSQ // mm/secĀ² Offset OffsetMM // from road center mm HopIntent uint8 Tag uint8 } func (v *VehicleMsgChangeLane) Set(hspeed MMperSec, haccel MMperSecSQ, offset OffsetMM) { v.Size = uint8(binary.Size(v) - 1) v.MsgID = VehicleMsgC2VChangeLane v.HorizontalSpeed = hspeed v.HorizontalAccel = haccel v.Offset = offset } const ( ParseflagsMaskNumBits Flags = 0x0f ParseflagsMaskInvertedColor Flags = 0x80 ParseflagsMaskReverseParsing Flags = 0x40 ParseflagsMaskReverseDriving Flags = 0x20 ) type VehicleLocalizationPositionUpdate struct { Size uint8 MsgID ID LocalizationID uint8 RoadPieceID uint8 Offset OffsetMM // from road center mm Speed MMperSec // mm/sec ParsingFlags Flags // ACK commands received LastRecvLaneChangeCmdID uint8 LastExecLaneChangeCmdID uint8 LastDesiredHorizontalSpeed MMperSec // mm/sec LastDesiredSpeed MMperSec // mm/sec } // VehicleDrivingDirection const ( Forward Direction = iota Reverse ) type VehicleMsgLocalizationTransitionUpdate struct { Size uint8 MsgID ID RoadPieceIDX uint8 RoadPieceIDXPrev uint8 Offset float32 // from road center mm Direction Direction // driving direction // ACK commands received LastRecvLaneChangeCmdID uint8 LastExecLaneChangeCmdID uint8 LastDesiredHorizontalSpeed MMperSec // mm/sec LastDesiredSpeed MMperSec // mm/sec // track grade detection UphillCounter uint8 DownhillCounter uint8 // wheel displacement (cm) since last transition bar LeftWheelDist uint8 // cm RightWheelDist uint8 // cm } const ( IntersectionCodeEntryFirst = iota IntersectionCodeExitFirst IntersectionCodeEntrySecond IntersectionCodeExitSecond ) type VehicleMsgLocalizationIntersectionUpdate struct { Size uint8 MsgID ID RoadPieceIDX uint8 Offset float32 // from road center mm Direction uint8 // driving direction IntersectionCode uint8 IntersectionTurn uint8 IsExiting uint8 } type VehicleMsgOffsetFromRoadCenterUpdate struct { Size uint8 MsgID ID Offset float32 // from road center mm LaneChangeID uint8 } // Lights // The bits in the simple light message corresponding to each type of light const ( LightHeadlights = 1 << iota LightBrakelights LightFrontlights LightEngine ) type VehicleMsgSetLights struct { Size uint8 MsgID ID LightMask uint8 // valid and value bits for lights } func (v *VehicleMsgSetLights) Set(mask uint8) { v.Size = uint8(binary.Size(v) - 1) v.MsgID = VehicleMsgC2VSetLights v.LightMask = mask } const ( VehicleMaxLightIntensity = 14 VehicleMaxLightTime = 11 ) // LED channel definitions - for RGB engine, front, and tail lights const ( LightRed = iota LightTail LightBlue LightGreen LightFrontL LightFrontR ) const ( EffectSteady = iota // Simply set the light intensity to 'start' value EffectFade // Fade intensity from 'start' to 'end' EffectThrob // Fade intensity from 'start' to 'end' and back to 'start' EffectFlash // Tun on LED between time 'start' and time 'end' inclusive EffectRandom // Flash the LED erratically - ignoring start/end ) type VehicleLightConfig struct { Channel uint8 Effect uint8 Start uint8 End uint8 Cycles uint8 // per 10 sec } func (v *VehicleLightConfig) Set(channel, effect uint8, start, end uint8, cycles uint16) { v.Channel = channel v.Effect = effect v.Start = start if v.Start > VehicleMaxLightIntensity { v.Start = VehicleMaxLightIntensity } v.End = end if v.End > VehicleMaxLightIntensity { v.End = VehicleMaxLightIntensity } cpm := cycles if cpm > VehicleMaxLightTime { cpm = VehicleMaxLightTime } v.Cycles = uint8(cpm / 6) // BUG integer division } type VehicleMsgLightsPattern struct { Size uint8 MsgID ID ChannelCount uint8 ChannelConfig [3]VehicleLightConfig } func (v *VehicleMsgLightsPattern) Set(config VehicleLightConfig) { v.Size = uint8(binary.Size(v) - 1) v.MsgID = VehicleMsgC2VLightsPattern if int(v.ChannelCount) < len(v.ChannelConfig) { v.ChannelConfig[v.ChannelCount] = config v.ChannelCount++ } } const ( TrackMaterialPlastic = iota TrackMaterialVinyl ) const ( SupercodeNone = iota SupercodeBoostJump ) type VehicleMsgSetConfigParams struct { Size uint8 MsgID ID SuperCodeParseMask uint8 TrackMaterial uint8 }