diff --git a/main.cpp b/main.cpp index 402467e..5633b48 100755 --- a/main.cpp +++ b/main.cpp @@ -3,19 +3,20 @@ MicroBit uBit; const int8_t CALIBRATED_POWERS[] = {-49, -37, -33, -28, -25, -20, -15, -10}; uint8_t advertising = 0; uint8_t tx_power_level = 6; -uint8_t AdvData [] = {0xff, 0xff, - 0x55, 0x97, - '0', '0', - '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.', '.'}; +uint8_t AdvData [26] = {0xff, 0xff, + 0x55, 0x98, + 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; uint8_t buttonsState = 0; uint8_t eventCounter = 0; char digit(uint8_t n) -{ + { return '0' + n; -} + } -void startAdvertising() { +void startAdvertising() + { int connectable = 0; int interval = 160; uBit.bleManager.setTransmitPower(tx_power_level); @@ -25,34 +26,34 @@ void startAdvertising() { uBit.bleManager.ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); uBit.bleManager.ble->accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData)); uBit.bleManager.ble->startAdvertising(); - uBit.display.printAsync("ULTIBO", 200); + uBit.display.printAsync("ULTIBO 98", 200); advertising = 1; -} + } void updatePayload () -{ + { uBit.bleManager.ble->clearAdvertisingPayload(); uBit.bleManager.ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); - AdvData [4] = digit (eventCounter / 10); - AdvData [5] = digit (eventCounter % 10); - for (int i = 19; i >= 1; i--) - { - AdvData [i + 6] = AdvData [i + 6 - 1]; - } - AdvData [6] = buttonsState + '0'; + AdvData [4] = eventCounter; + for (int i = 20; i >= 1; i--) + { + AdvData [i + 5] = AdvData [i + 5 - 1]; + } + AdvData [5] = (buttonsState << 6); uBit.bleManager.ble->accumulateAdvertisingPayload(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, AdvData, sizeof(AdvData)); -} + } -void stopAdvertising() { +void stopAdvertising() + { uBit.bleManager.stopAdvertising(); uBit.display.scroll("OFF"); advertising = 0; -} + } char text[] = "?"; void onButton(MicroBitEvent e) -{ + { uint8_t mask; uint8_t prev = buttonsState; if (e.source == MICROBIT_ID_BUTTON_A) @@ -80,10 +81,17 @@ void onButton(MicroBitEvent e) buttonsState &= ~mask; if (buttonsState != prev) - { - eventCounter = (eventCounter + 1) % 100; - switch (buttonsState) { + if (eventCounter == 255) + { + eventCounter = 128; + } + else + { + eventCounter = eventCounter + 1; + } + switch (buttonsState) + { case 0: text [0] = ' '; break; case 1: text [0] = 'A'; @@ -93,10 +101,10 @@ void onButton(MicroBitEvent e) case 3: text [0] = '2'; break; default: text [0] = '?'; - } + } uBit.display.printAsync (text); updatePayload (); - } + } // if (e.value == MICROBIT_BUTTON_EVT_CLICK) // uBit.serial.printf("CLICK"); @@ -111,13 +119,13 @@ void onButton(MicroBitEvent e) // uBit.serial.printf("DOUBLE_CLICK"); // // uBit.serial.printf("\r\n"); -} + } int main() -{ + { uBit.init(); uBit.messageBus.listen(MICROBIT_ID_BUTTON_A, MICROBIT_EVT_ANY, onButton); uBit.messageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_EVT_ANY, onButton); startAdvertising(); release_fiber(); -} + } diff --git a/microbitdemo.lpr b/microbitdemo.lpr index a6175b1..7b68ab6 100644 --- a/microbitdemo.lpr +++ b/microbitdemo.lpr @@ -54,8 +54,15 @@ type TArrayOfByte = Array of Byte; + PMicroBitPeripheral = ^TMicroBitPeripheral; + TMicroBitPeripheral = record + AddressString:String; + ButtonCounter:Integer; + ButtonChordStarted:Boolean; + end; var + MicroBitPeripherals:Array of TMicroBitPeripheral; ScanRxCount:Integer; BluetoothUartDeviceDescription:String; ScanCycleCounter:LongWord; @@ -69,8 +76,6 @@ UART0:PSerialDevice = Nil; KeyboardLoopHandle:TThreadHandle = INVALID_HANDLE_VALUE; ReadByteCounter:Integer; - ButtonCounter:Integer; - ButtonChordStarted:Boolean; function ReadByte:Byte; forward; @@ -255,20 +260,6 @@ procedure HciCommand(OGF:byte; OCF:Word; Params:array of byte); HciCommand((OGF shl 10) or OCF,Params); end; -procedure SetLEScanResponseData(Data:array of byte); -var - Params:array of byte; - Len:byte; - i:integer; -begin - Len:=Min(Length(Data),31); - SetLength(Params,Len + 1); - Params[0]:=Len; - for i:=0 to Len - 1 do - Params[i + 1]:=Data[i]; - HciCommand(OGF_LE_CONTROL,$09,Params); -end; - function EventReadFirstByte:Byte; var c:LongWord; @@ -564,7 +555,7 @@ function KeyboardLoop(Parameter:Pointer):PtrInt; 'Q' : SystemRestart(0); 'R' : begin - RestoreBootFile('bluetooth-dev-bluetoothtest','config.txt'); + RestoreBootFile('microbitdemo','config.txt'); SystemRestart(0); end; end; @@ -599,6 +590,29 @@ function AsWord(Hi,Lo:Integer):Word; Result:=(Hi shl 8) or Lo; end; +function FindOrMakeMicroBitPeripheral(NewAddressString:String):PMicroBitPeripheral; +var + I:Integer; +begin + Result:=Nil; + for I:= 0 to High(MicroBitPeripherals) do + if MicroBitPeripherals[I].AddressString = NewAddressString then + Result:=@MicroBitPeripherals[I]; + if Result = nil then + begin + SetLength(MicroBitPeripherals,Length(MicroBitPeripherals) + 1); + Result:=@MicroBitPeripherals[High(MicroBitPeripherals)]; + with Result^ do + begin + AddressString:=NewAddressString; + ButtonCounter:=0; + ButtonChordStarted:=False; + end; + Log(''); + Log(Format('detected new micro:bit peripheral %s',[NewAddressString])); + end; +end; + procedure ParseEvent; var I:Integer; @@ -613,6 +627,10 @@ procedure ParseEvent; LeEventType:Byte; NewButtonCounter:Integer; ButtonMessage:String; + CounterByte:Byte; + MicroEventIndex:Integer; + MicroEvent:Byte; + MicroBitPeripheral:PMicroBitPeripheral; function GetByte:Byte; begin Result:=Event[GetByteIndex]; @@ -675,8 +693,9 @@ function GetByte:Byte; MfrHi:=GetByte; SignatureLo:=GetByte; SignatureHi:=GetByte; - if (MainType = $ff) and (AsWord(MfrHi,MfrLo) = Word(ManufacturerTesting)) and (SignatureLo = $55) and (SignatureHi = $97) then + if (MainType = $ff) and (AsWord(MfrHi,MfrLo) = Word(ManufacturerTesting)) and (AsWord(SignatureHi,Signaturelo) = $9755) then begin + MicroBitPeripheral:=FindOrMakeMicroBitPeripheral(AddressString); GetByteIndex:=20; NewButtonCounter:=(GetByte - Ord('0'))*10; NewButtonCounter:=NewButtonCounter + (GetByte - Ord('0')); @@ -685,28 +704,77 @@ function GetByte:Byte; begin S:=S + Char(GetByte); end; - if NewButtonCounter <> ButtonCounter then + if NewButtonCounter <> MicroBitPeripheral^.ButtonCounter then begin - ButtonCounter:=NewButtonCounter; - if not ButtonChordStarted then + MicroBitPeripheral^.ButtonCounter:=NewButtonCounter; + if not MicroBitPeripheral^.ButtonChordStarted then begin LoggingOutput(''); - ButtonChordStarted:=True; + MicroBitPeripheral^.ButtonChordStarted:=True; end; case S[1] of '0': begin ButtonMessage:='Released '; - ButtonChordStarted:=False; + MicroBitPeripheral^.ButtonChordStarted:=False; end; '1': ButtonMessage:='A down '; '2': ButtonMessage:='B down '; '3': ButtonMessage:='A and B down'; else ButtonMessage:='????????????'; end; - LoggingOutput(Format('micro:bit addr %s %s - %02.2d events, history: %s',[AddressString,ButtonMessage,ButtonCounter,S])); + LoggingOutput(Format('micro:bit addr %s %s - %02.2d events, history: %s',[AddressString,ButtonMessage,MicroBitPeripheral^.ButtonCounter,S])); end; - end; + end + else if (MainType = $ff) and (AsWord(MfrHi,MfrLo) = Word(ManufacturerTesting)) and (AsWord(SignatureHi,SignatureLo) = $9855) then + begin + MicroBitPeripheral:=FindOrMakeMicroBitPeripheral(AddressString); + GetByteIndex:=20; + CounterByte:=GetByte; + NewbuttonCounter:=CounterByte and $7f; + S:=''; + while GetByteIndex <= High(Event) do + S:=S + Char(Ord('0') + (GetByte shr 6)); + while (MicroBitPeripheral^.ButtonCounter mod 128) <> NewButtonCounter do + begin + Inc(MicroBitPeripheral^.ButtonCounter); + MicroEventIndex:=NewButtonCounter - (MicroBitPeripheral^.ButtonCounter mod 128); + // Log(Format('counter %d new %d index %d %s',[MicroBitPeripheral^.ButtonCounter,NewButtonCounter,MicroEventIndex,S])); + if MicroEventIndex < 0 then + Inc(MicroEventIndex,128); + if MicroEventIndex >= 21 then + begin + Log(Format('unable to reconstruct event %d index %d from %d %s',[MicroBitPeripheral^.ButtonCounter,MicroEventIndex,NewButtonCounter,S])); + if (CounterByte and $80) = 0 then + begin + MicroBitPeripheral^.ButtonCounter:=NewButtonCounter; + Log('the micro:bit seems to have restarted'); + end; + end + else + begin + GetByteIndex:=21 + MicroEventIndex; + if not MicroBitPeripheral^.ButtonChordStarted then + begin + LoggingOutput(''); + MicroBitPeripheral^.ButtonChordStarted:=True; + end; + MicroEvent:=GetByte shr 6; + case MicroEvent of + 0: + begin + ButtonMessage:='Released '; + MicroBitPeripheral^.ButtonChordStarted:=False; + end; + 1: ButtonMessage:='A down '; + 2: ButtonMessage:='B down '; + 3: ButtonMessage:='A and B down'; + else ButtonMessage:='????????????'; + end; + LoggingOutput(Format('micro:bit addr %s event number %03.3d %s - history: %s',[AddressString,MicroBitPeripheral^.ButtonCounter,ButtonMessage,S])); + end; + end; + end; end; begin @@ -734,8 +802,7 @@ function GetByte:Byte; Log('Init complete'); ScanCycleCounter:=0; ReadByteCounter:=0; - ButtonCounter:=0; - ButtonChordStarted:=False; + SetLength(MicroBitPeripherals,0); while True do begin ReadBackLog:=0; diff --git a/release-message.md b/release-message.md index c4a3da7..0570837 100644 --- a/release-message.md +++ b/release-message.md @@ -1,4 +1,4 @@ -# Ultibo prototype-microbit-as-ultibo-peripheral repository +# prototype-microbit-as-ultibo-peripheral # Requires: * a micro:bit computer and a usb cable @@ -20,6 +20,7 @@ * attach the microbit to the computer with the usb cable * the microbit should appear as a usb folder * copy microbit-as-ultibo-peripheral.hex from the sd card to the microbit usb folder + * safely eject the sd card * insert the sd card into the pi * connect the pi to the tv using the hdmi cable * connect the pi to the optional usb keyboard