Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update BLE interface and incorporate some functions into writeToCharacteristic( ) #246

Open
13 of 18 tasks
brettfiedler opened this issue May 7, 2024 · 5 comments
Open
13 of 18 tasks
Assignees

Comments

@brettfiedler
Copy link
Contributor

brettfiedler commented May 7, 2024

Service Selection Changes

We should simplify the information presented to users. Let's focus on hiding the UUIDs and simplifying language:

  • (JG EDIT, from slack discussion) - Change "UUID" Heading to "Bluetooth Service"

image

  • "Select the UUID of the bluetooth service you want to use:" -> "Select the Bluetooth Service you want to use from your microcontroller:"
  • "Select the UUID of the bluetooth characteristic you want to use:" -> "Select the Bluetooth Characteristic you want to use from the selected BLE Service:"

image

Dropdown:

  • Remove the UUIDs from the dropdowns
  • Rename UART characteristic "micro:bit UART TX (read)" to "micro:bit TX (read from micro:bit)"
  • Rename UART characteristic "micro:bit UART RX (write)" to "micro:bit RX (write to micro:bit)"
  • Rename "Random Service 1-5" to "Custom Arduino Service 1-5" and "Random Characteristic" to "Custom Arduino Characteristic"...
  • Move relevant UUID for each service/characteristic to the space under the drop-down after the service/characteristic has been selected (for reference).

Clean up Service/Characteristic selection

A lot of services either only have one relevant characteristic, or we are only supporting one. Let's automatically select it or possibly drop the second dropdown altogether when there is only one?

  • When there is only one possible characteristic UUID, let's just automatically select it. I believe only Button, IO Pins, UART, and LED have more than one characteristic UUID option.

RX (write to the microcontroller, microcontroller is reading)

We can only write to the RX service.

  • Remove the Read/Write tabs for RX.

What you write to the microcontroller does not always have to be a string, and it seems micro:bit handles this gracefully, but it would be nice to abstract some of the text part. I think the best option here is to duplicate the RX characteristic and then repurpose one to make the writeToCharacteristic( ) automatically decode (for both RX and TX).

  • Duplicate "micro:bit RX (write to micro:bit)" and rename the duplicate to "micro:bit RX Text (write string to micro:bit)"

UUID: 6E400003B5A3F393E0A9E50E24DCCA9E

image

Let's also update the writeToCharacteristic( ) function to do add the delimiters to the string and encode the string to UTF-8, which the micro:bit expects. The user will still need to define the string, but it could be a String component, which would make things even simpler.

Example JS from microbit-ratio-game:

// Construct the command string based on LED state
let str = "";
if (readyToLaunch) {
    if (isInGoalRatioDerived) {
        str = "success"; // Command for LED pin 0 Up
    } else {
        str = "failure"; // Command for LED pin 0 Down
    }
}
str = "$" + str + "|"; // Format the command string with start and end markers

// Encode the string to UTF-8
var encoder = new TextEncoder(); // TextEncoder defaults to UTF-8
var encoded = encoder.encode(str);

// Create an ArrayBuffer from the encoded string
var buffer = new ArrayBuffer(encoded.length);
var bufferView = new Uint8Array(buffer);

for (var i = 0; i < encoded.length; i++) {
    bufferView[i] = encoded[i];
}

// Send the command over BLE
writeToCharacteristic(buffer);
console.log(`Command sent: ${str}`);
console.log(buffer);
  • Update new RX Text write function so that it does the encoding and the user provides `writeToCharacteristic( "string", dataDelimiter, stringDelimiter) where defaults are: dataDelimiter = "$" and stringDelimiter = "|".

TX (read from microcontroller, microcontroller is writing)

UUID: 6E400002B5A3F393E0A9E50E24DCCA9E

We can only read from the TX service.

  • Remove the Read/Write tabs for RX.

Same logic as the RX, I think the best option here is to duplicate the TX characteristic and then repurpose one to make the writeToCharacteristic( ) automatically decode into a string, and store the string into deviceValue.

  • Duplicate "micro:bit TX (read from micro:bit)" and rename the duplicate to "micro:bit TX Text (read string from micro:bit)"
    image

Example JS from microbit-ble-demos:

// Assuming `data` is your byte array from UART
const data = deviceValue;

const textDecoder = new TextDecoder('utf-8'); // Default is utf-8, which is typical for UART text data
const text = textDecoder.decode(data);

console.log( 'received text' );
setReceivedUARTString( text );
  • Incorporate textDecoder into new TX Text read function so deviceValue returns a string.

LED Service

LED Matrix

UUID: E95D7B77251D470AA062FA1922DFA9A8

Let's update writeToCharacteristic( ) by incorporating the code we've used from https://github.com/antefact/microBit.js/blob/b512cdc79994a8d120851bd1ed360ec05e0c5bd1/src/microBit.js#L115C1-L141.

I think the goal here should be that someone only has to define an const icon = [ insert matrix here] and then provide it to the characteristic as icon and that should be good to go. The writeToCharacteristic( ) function will convert it to the Array it expects and send it off.

  • How might we incorporate help for the icon form into the documentation above the custom code?

Also is this function to convert a string to a UTF8Array helpful?
https://github.com/antefact/microBit.js/blob/b512cdc79994a8d120851bd1ed360ec05e0c5bd1/src/microBit.js#L346-L375

LED_Matrix_State : uint8[ ]

  • Update LED Matrix writeToCharacteristic( icon(5x5 uint8Array) ) to automatically convert the matrix to a form the microbit will accept.

LED Text

BF EDIT: NEVERMIND. I somehow missed we already had this. Sorry!

Button, Magnetometer, Accelerometer, Temperature

Each can only be read, not written to.

  • Remove Read/Write tabs from the these services.

All of the custom services can retain the read/write tabs, since we don't know how they are being used. I don't think it makes a difference which they choose, but it could be helpful for tracking.

Remove Microbit Event service

The event service needs some more time and can't be used in the current implementation. It needs to send the respective requirements service at connection (on either side, client or microbit), then follows up with the respective event service. Not worth our time unless someone is very invested in it. I think UART should be a fine substitution.

  • Remove the entire microbit event service
@brettfiedler brettfiedler self-assigned this May 7, 2024
@brettfiedler
Copy link
Contributor Author

brettfiedler commented May 7, 2024

{Placeholder for documentation updates to different characteristics, including link to BLE documentation for micro:bit, and showing the expect form of the value read or written}

Micro:bit BLE documentation: https://lancaster-university.github.io/microbit-docs/resources/bluetooth/bluetooth_profile.html

@brettfiedler
Copy link
Contributor Author

@jessegreenberg , I think the first bit is in place. I want to expand a bit on the information we present in each menu, but the first post should reflect some needed changes that are more structural in nature.

jessegreenberg added a commit that referenced this issue May 9, 2024
jessegreenberg added a commit that referenced this issue May 9, 2024
@jessegreenberg
Copy link
Contributor

jessegreenberg commented May 9, 2024

OK, I think these requests are done. However, for the following I took a slightly different approach.

  • Duplicate "micro:bit RX (write to micro:bit)" and rename the duplicate to "micro:bit RX Text (write string to micro:bit)"
  • Update new RX Text write function so that it does the encoding and the user provides `writeToCharacteristic( "string", dataDelimiter, stringDelimiter) where defaults are: dataDelimiter = "$" and stringDelimiter = "|".
  • Incorporate textDecoder into new TX Text read function so deviceValue returns a string.
  • Update LED Matrix writeToCharacteristic( icon(5x5 uint8Array) ) to automatically convert the matrix to a form the microbit will accept.

Instead of overloading writeToCharacteristic, I think it would be better to have separate function per action. I think that is clearer and will work for multiple characteristics. For example, string decoding may apply to a few characteristics and the "Custom" ones.

For the LED characteristic, you can now use

writeMatrixToCharacteristic( [
    [ 1, 1, 1, 1, 1 ],
    [ 1, 0, 0, 0, 1 ],
    [ 1, 0, 0, 0, 1 ],
    [ 1, 0, 0, 0, 1 ],
    [ 1, 1, 1, 1, 1 ]
] );

to draw a square.

Some of these (like the LED example) are characteristic specific, and if you would like we can hide the function documentation when it isn't relevant. Ill do that if you are OK with this direction.

Ready for you to review on the dev branch @brettfiedler !

@jessegreenberg jessegreenberg removed their assignment May 9, 2024
@brettfiedler
Copy link
Contributor Author

brettfiedler commented May 10, 2024

I think that approach is great and certainly more generalizable.

Noting the slack conversation here:

Alright, fixed up a few programs. They are much shorter! I'm a little torn on the LED Text service. It expects a UTF8 string, not a Uint8Array with delimiters, which is what our writeStringToCharacteristic function does. So, taking your LED Text Face example will send it with delimiters (though it does work...) to the LED matrix. By torn, I am wondering if we should leave it as an example to encode and document it, or if we should try to remove those documentation entries for writeStringToCharacteristic and add another for the UTF8string.

Or maybe we could just add a couple more "general purpose" functions to all of the write characteristics?
writeAsUTF8(), writeAsUint8Array() or something?
UTF8s and Uint8[] covers most of the write characteristics in micro:bit. The rest for the most part are reads and are a variety of signed or unsigned (4-32 bit) integers, which I think we can add to the documentation, but do not need to abstract out.

I think adding one more function to the Write characteristics form should cover us well and make the LED Text characteristic easier to use:

  • writeCharacteristicAsUTF8s( string ) : Encode the string as UTF8s without sending any delimiters. (open to other, shorter function names)

Otherwise, I'll check in with AE and company to see if the changes help.

@jessegreenberg
Copy link
Contributor

@brettfiedler what do you think of this?

  • Rename writeStringToCharacteristic -> writeStringToCharacteristicWithDelimeters
  • Add a new writeStringToCharacteristic that behaves as you suggest.

Then the case where delimeters ARE is made clear by the function name. And the more general function gets a simpler name.

This would require changing code in existing BLE examples so want to check with you before doing this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants