Android Question Connect To Bluetooth Dice

bocker77

Active Member
Licensed User
Longtime User
The company that manufactures bluetooth dice were kind enough to send me a browser application to test out the dice to make sure they are working properly. The application consists of an html, css, and two javascript files. Now I am attempting to make sense of what needs to be done to incorporate this into B4X. The javascript code is a main and a class.

The code in the main, main.js, to request a connection
B4X:
// Open the Bluetooth connection dialog for choosing a GoDice to connect
function openConnectionDialog() {
    const newDice = new GoDice();
    newDice.requestDevice();
}

The code in the class, godice.js, to connect to a die
B4X:
/**
     * Open a connection dialog to connect a single GoDice, after successfull connection it will follow by corresponding "DiceConnected" event (response).
     */
    requestDevice() {
        return navigator.bluetooth.requestDevice({
            filters: [{ namePrefix: 'GoDice_' }],
            optionalServices: ['6e400001-b5a3-f393-e0a9-e50e24dcca9e']
        })
            .then(async device => {                
                this.GlobalDeviceId = device.id.toString();
                console.log("GlobalDeviceId: ", this.GlobalDeviceId);                
                this.bluetoothDevice = device;
                console.log("bluetoothDevice: ", this.bluetoothDevice);
                var _self = this
                this.bluetoothDevice.addEventListener('gattserverdisconnected', function() {
                    _self.onDiceDisconnected(_self.GlobalDeviceId, _self)
                })
                await this.connectDeviceAndCacheCharacteristics();                
            });
    }

Putting Chrome into debug mode and adding some logging I see this.

GlobalDeviceId: aOHCIYtwaFac7kbrO7ac2Q== godice.js:275
bluetoothDevice: godice.js:277
BluetoothDevice {id: 'aOHCIYtwaFac7kbrO7ac2Q==', name: 'GoDice_CE03C9_B_v04',
gatt: BluetoothRemoteGATTServer, ongattserverdisconnected: null}
gatt: BluetoothRemoteGATTServer {device: BluetoothDevice, connected: true}
id: "aOHCIYtwaFac7kbrO7ac2Q=="
name: "GoDice_CE03C9_B_v04"
ongattserverdisconnected: null
[[Prototype]]: BluetoothDevice
Dice connected: aOHCIYtwaFac7kbrO7ac2Q== main.js:24

The Bluetooth.requestDevice() method of the Bluetooth interface returns a Promise to a BluetoothDevice object with the specified options. If there is no chooser UI, this method returns the first device matching the criteria.

The BluetoothRemoteGATTServer interface of the Web Bluetooth API represents a GATT Server on a remote device. Does this mean that the die is the GATT server or that the web application needed the interface to use bluetooth functionality, or is something else going on?

Either way does B4X have the capability of doing this request. I would think probably so but I am getting nowhere testing with the various bluetooth examples that exists in this forum.

In the unfiltered B4X log I am seeing this information.

Found: GoDice_CE03C9_B_v04, D9:E9:F4:C9:03:CE, RSSI = -54, (MyMap) {1=[B@f832ea0, 9=[B@2cfca59, 7=[B@bf6761e, 0=[B@9219eff}
stopLeScan()
isLeEnabled(): ON
D9:E9:F4:C9:03:CE
connecting to GoDice_CE03C9_B_v04
connect() - device: D9:E9:F4:C9:03:CE, auto: true
registerApp()
registerApp() - UUID=cfbfad8e-4090-4113-abef-ccff536c2d2c
onClientRegistered() - status=0 clientIf=12
onClientConnectionState() - status=133 clientIf=12 device=D9:E9:F4:C9:03:CE
close()
unregisterApp() - mClientIf=12
Disconnected

I read that the status code of 133 on the OnClientConnection state is that it timed out which makes sense.
 

bocker77

Active Member
Licensed User
Longtime User
After looking into this further I need more information from the makers of the device. Not much information came with the dice. On Windows through the Bluetooth admin I can pair a die but on my phone it will not pair. If I remove it on Windows the html program still works. The dice maker has an Android app with games that uses the dice and they work just fine. I requested more information from them and are waiting for a response before I can do anything in B4X. But I'm thinking "I'll be waiting until hell freezes over".

In the meantime I can revert back to Voice Recognition in my game to get die rolls.
 
Upvote 0

emexes

Expert
Licensed User
Upvote 0

emexes

Expert
Licensed User
a 32-bit processor to communicate a 3-bit value feels like over-engineering šŸ¤£

but if it transmits raw(ish) imu readings too... that could be interesting šŸ¤”
 
Upvote 0

bocker77

Active Member
Licensed User
Longtime User
This is exactly what I am looking for, I hope. I am able to connect to a die using Erel's BleCentral example but I am assuming I need the proper UUIDs sent to the device to allow commands and responses. I tried with what the example had hardcoded but it timed out. With the app that you suggested should point me in the right direction. The bluetooth analyzer apps that I downloaded from Playstore were not very helpful although there was one that returned so many UUIDs that I have no idea what it was telling me.

Anyway many thanks for the heads up on nRF Connect app and your step by step instructions on how to use it. I hope not but I'll probably be needing your help if you don't mind.
 
Upvote 0

bocker77

Active Member
Licensed User
Longtime User
So I executed the nRF Connect app and have attached two screenshots of what it displayed. Good information because I see the UUID of what the html app uses to communicate with the dice. Erel's example that I am using in the initialization builds, I am guessing, generic capabilities of most bluetooth devices. So now I need to plug in the correct UUIDs into the program. After that I guess I need to either glean from the js code or the website you mentioned above to send/receive from the dice. Can you confirm that I am on the right track and possibly let me know what I should plug into the below values?

B4X:
Sub Service_Create
    manager.Initialize("manager")
    ServiceId = UUID("1800")
    ReadChar = UUID("1801")
    WriteChar = UUID("2902")
    messagesToSend.Initialize
End Sub
...
Private Sub UUID(id As String) As String
#if B4A
    Return "0000" & id.ToLowerCase & "-0000-1000-8000-00805f9b34fb"
#else if B4I
    Return id.ToUpperCase
#End If
End Sub

Using "2902" does not create the proper UUID.
built one '00002902-0000-1000-8000-00805f9634fb'
correct one '6e400001-b5a3-f393-e0a9-e50e24dcca9e'

I am not sure if this UUID is a ServiceId, a ReadChar or a WriteChar.
 

Attachments

  • Screenshot_1.png
    Screenshot_1.png
    204.7 KB · Views: 60
  • Screenshot_2.png
    Screenshot_2.png
    215.6 KB · Views: 67
Upvote 0

bocker77

Active Member
Licensed User
Longtime User
Another question is do I have still need to use the Serial library to send/receive messages to the dice or is the BleManager2 enough? I would think that I would need the Serial library for this.
 
Upvote 0

emexes

Expert
Licensed User
So I executed the nRF Connect app and have attached two screenshots of what it displayed.

Uh oh. :oops:

I was expecting that there'd be a BLE GATT characteristic that you could read normally that would return the current die orientation presumably as a value 1..6 or possibly a computer diehard value 0..5

But the mention of the Nordic UART Service makes me think that perhaps the dice are using a serial stream of bytes layered upon the BLE GATT model, and presumably structuring that into packets.

If you're lucky, then the dice will automatically emit UART data every time the orientation changes to a new stable throw value, and you can read the emitted bytes by hooking up for notifications from the TX Characteristic of the Nordic UART Service. Then we'd just need to look at what bytes are received when a die is moved to ("lands on") 1, 2, 3, 4, 5 and 6. Sounds easy - what could possibly go wrong?!?!

Or maybe the UART Service is for firmware updates or similar, and one of the other GATT characteristics is returning the die's current value, in which case I'd also try: for each characteristic, put the die in position 1, read the characteristic, then repeat for position 2, 3, 4, etc and see if the characteristic value changes accordingly. You never know your luck in the big city - perhaps reading the die's throw value could really be just that easy. šŸ™ƒ
 
Upvote 0

f0raster0

Well-Known Member
Licensed User
Longtime User
Screenshot_2.png


Do you read data when click Notify true?
if yes, then using B4X example before read notify should be activated
 
Upvote 0

emexes

Expert
Licensed User
It just occurred to me that expecting you to connect to 5 separate BLE devices is a bit of an ask, and that perhaps the dice orientations are included in (piggybacked onto) the advertising broadcasts.
 
Upvote 0

bocker77

Active Member
Licensed User
Longtime User
For a six sided dice this is what it uses to determine which side is up. I only included the code that looks to be for those dice. Of course there is more code that is in regards to rolling a die. If you wish I could attach the JavaScript class to this thread. Once I am able to at least start communicating with it I plan on creating a b4x class modeled after this code. Most probably only for the six sided dice for now. At the end of this message is the b4x code values that I believe I need to continue testing.

B4X:
    static diceTypes = {
        D6: 0,
        D20: 1,
        D10: 2,
        D10X: 3,
        D4: 4,
        D8: 5,
        D12: 6,
    }
    // The vectors for each die and shell type, used to check which side is facing up
    static d6Vectors = {
        1: [-64, 0, 0],
        2: [0, 0, 64],
        3: [0, 64, 0],
        4: [0, -64, 0],
        5: [0, 0, -64],
        6: [64, 0, 0],
    }
...
// Get Die number from acc raw data according to die type
    getDieValue(data, startByte) {
        
        const xyzArray = this.getXyzFromBytes(data, startByte)
        var value = 0;
        
        // Calculating closest vector according to current die type
        if (this.dieType == GoDice.diceTypes.D6){
            value = this.getClosestVector(GoDice.d6Vectors, xyzArray)
        }
...
    // Gets the xyz coord from sent message
    getXyzFromBytes(data, startByte) {
        const x = data.getInt8(startByte);
        const y = data.getInt8(startByte + 1);
        const z = data.getInt8(startByte + 2);
        return [x, y, z]
    }
...
    
    /**
    * Calculates the closest vector from the table to the given coord
    * @param {dictionary} DieTable - Vector table of current shell
    * @param {dictionary} coord - Vector of die
    *
    **/
    getClosestVector(DieTable, coord) {
        
        const coordX = coord[0]
        const coordY = coord[1]
        const coordZ = coord[2]
                
        var minDistance = Number.MAX_SAFE_INTEGER
        var value = 0
        var vector = []
        var XResult = 0
        var YResult = 0
        var ZResult = 0
        var curDist = 0
        
        // Calculating distance to each value in vector array
        for (let dieValue in DieTable) 
        {    
            vector = DieTable[dieValue]
            
            XResult = coordX - vector[0]
            YResult = coordY - vector[1]
            ZResult = coordZ - vector[2]
            
            // Calculating squared magnitude (since it's only for comparing there's no need for sqrt)
            curDist = ((XResult ** 2) + (YResult ** 2) + (ZResult ** 2))
            
            if (curDist < minDistance) {
                minDistance = curDist
                value = dieValue
            }
            
        }
        return value
    }

changed values in b4x program and test with this to see where I get.

B4X:
Sub Service_Create
    manager.Initialize("manager")
    FrDice.Initialize
    AlDice.Initialize
'    ServiceId = UUID("1800")
'    ReadChar = UUID("1801")
'    WriteChar = UUID("2902")
    ServiceId = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
    RXChar = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
    TXChar = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
    messagesToSend.Initialize
End Sub
 
Upvote 0

bocker77

Active Member
Licensed User
Longtime User
As for the three down arrows, yes I did select it and I didn't see anything change. I will retry it to see if I missed something but I hit it about three times and nothing that I saw changed.
 
Upvote 0

bocker77

Active Member
Licensed User
Longtime User
Retried the three down arrows and the UUID 0x2902 under Descriptors/Client Characteristic Configuration changed the Value from "Notifications and indications disabled" to "Notifications enabled".
 
Upvote 0

bocker77

Active Member
Licensed User
Longtime User
Below is what I am seeing in the Console Log in Chrome. It Paired and Connected. Then I requested to get the battery level.

GlobalDeviceId: Qp8SFodh4q2outXSW2h4LA==
godice.js:277 bluetoothDevice: BluetoothDevice {id: 'Qp8SFodh4q2outXSW2h4LA==', name: 'GoDice_CE03C9_B_v04', gatt: BluetoothRemoteGATTServer, ongattserverdisconnected: null}
godice.js:528 Connecting to GATT Server...
godice.js:549 Starting Notifications...
godice.js:552 onDiceConnected
main.js:24 Dice connected: Qp8SFodh4q2outXSW2h4LA==
godice.js:248 [3]
godice.js:353 messageArray [3]
godice.js:355 byteMessage Uint8Array [3, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array']
godice.js:357 after write response undefined
godice.js:467 data: DataView(4)
godice.js:468 deviceId: Qp8SFodh4q2outXSW2h4LA==
main.js:205 BetteryLevel: Qp8SFodh4q2outXSW2h4LA== 76
main.js:161 Roll Start: Qp8SFodh4q2outXSW2h4LA==
godice.js:291 Reconnecting to: Qp8SFodh4q2outXSW2h4LA==
godice.js:528 Connecting to GATT Server...

It looks like the GATT service returns a unique value ending in two equal signs and uses that to communicate with the die. Now to learn about the service and look on this forum on how to use it.
 
Upvote 0

f0raster0

Well-Known Member
Licensed User
Longtime User
my friend, in my opinion the steps are:

nRF App
1. Connect your device to nRF App to know and take note of the services and characteristics. (you did it already) - check the hardware, play sending commands/reading etc.
2. if needed active notify true in nRF app to test readying and writing (maybe you already did it)

B4X BLE Example:
3. Add your services and characteristics to the B4X example.
4. In the logs (add logs if needed) you will read the value same as in nRF; you can write and read, based in the B4X example.
5. Work on your Own App :)
 
Upvote 0

bocker77

Active Member
Licensed User
Longtime User
I am able to retrieve service IDs and store the values from the die. Looking at the html/js on the PC it looks like there is a request to a GATT service (?) which returns a unique value that is then used to communicate with the die. As in the below case it is "Qp8SFodh4q2outXSW2h4LA==". At least that is what it looks like to me.

GlobalDeviceId: Qp8SFodh4q2outXSW2h4LA==
godice.js:277 bluetoothDevice: BluetoothDevice {id: 'Qp8SFodh4q2outXSW2h4LA==', name: 'GoDice_CE03C9_B_v04', gatt: BluetoothRemoteGATTServer, ongattserverdisconnected: null}
godice.js:528 Connecting to GATT Server...
godice.js:549 Starting Notifications...
godice.js:552 onDiceConnected
main.js:24 Dice connected: Qp8SFodh4q2outXSW2h4LA==
godice.js:248 [3]
godice.js:353 messageArray [3]
godice.js:355 byteMessage Uint8Array [3, buffer: ArrayBuffer(1), byteLength: 1, byteOffset: 0, length: 1, Symbol(Symbol.toStringTag): 'Uint8Array']
godice.js:357 after write response undefined
godice.js:467 data: DataView(4)
godice.js:468 deviceId: Qp8SFodh4q2outXSW2h4LA==
main.js:205 BetteryLevel: Qp8SFodh4q2outXSW2h4LA== 76
godice.js:291 Reconnecting to: Qp8SFodh4q2outXSW2h4LA==
godice.js:528 Connecting to GATT Server...

I sent this code before but it's worth stating it here as well. Looking through the forum I am not seeing if I need to do request to a GATT service or if I need to, how.

B4X:
/**
     * Open a connection dialog to connect a single GoDice, after successfull connection it will follow by corresponding "DiceConnected" event (response).
     */
    requestDevice() {
        return navigator.bluetooth.requestDevice({
            filters: [{ namePrefix: 'GoDice_' }],
            optionalServices: ['6e400001-b5a3-f393-e0a9-e50e24dcca9e']
        })
            .then(async device => {                
                this.GlobalDeviceId = device.id.toString();
                console.log("GlobalDeviceId: ", this.GlobalDeviceId);                
                this.bluetoothDevice = device;
                console.log("bluetoothDevice: ", this.bluetoothDevice);
                var _self = this
                this.bluetoothDevice.addEventListener('gattserverdisconnected', function() {
                    _self.onDiceDisconnected(_self.GlobalDeviceId, _self)
                })
                await this.connectDeviceAndCacheCharacteristics();                
            });
    }

I feel I am making process though but the code to determine which die number is rolled looks to be a bitch. The other stuff like asking for battery usage seems pretty cut and dry.
 
Upvote 0

f0raster0

Well-Known Member
Licensed User
Longtime User
...Looking at the html/js on the PC it looks like there is a request to a GATT service (?) I sent this code before but it's worth stating it here as well. Looking through the forum I am not seeing if I need to do request to a GATT service or if I need to, how.
use the nRF app and B4X example.. it will work.. post your logs. (this forum is about b4x)
I sent this code before but it's worth stating it here as well. Looking through the forum I am not seeing if I need to do request to a GATT service or if I need to, how.
the nRF app will tell it, then using B4X example just do the same.
In the nRF App should be the information about read/write, check if you need or don't need NotifyTrue

for example: if yes, then in B4X use something like: first test using nRF App)
B4X:
manager.SetNotify("6e400001-b5a3-f393-e0a9-e50e24dcca9e", "6e400002-b5a3-f393-e0a9-e50e24dcca9e", True)

for read in B4X use something like this: (first test using nRF App)
B4X:
Sub Manager_DataAvailable (ServiceId As String, Characteristics As Map)
    CallSub3(Main, "DataAvailable", ServiceId, Characteristics)
    Dim bc As ByteConverter
    Dim KeyCode As String
 
    Dim bc As ByteConverter
    For Each key As String In Characteristics.Keys
        Dim b() As Byte = Characteristics.Get(key)
        Log("Key: " & key &", value: " & bc.HexFromBytes(b))
    Next

End Sub

EDIT: Check this: https://www.b4x.com/android/forum/threads/uart-ble-adafruit.99791/
maybe same UUID :) ?
 
Last edited:
Upvote 0

bocker77

Active Member
Licensed User
Longtime User
Thanks for the response f0raster0 and yes I know what forum I am on. I am trying to convert the stuff that the die company sent me to b4x. It runs on a web browser. I have used the nRF app and gleaned information about the die. I have a b4x code modeled after BLEExample getting the information that the nRF app is showing. Now I am trying to issue a command to the die and get a response. I am starting with a simple one which is to get the battery level. Once I figure that out then I should be on my way. Although as also stated getting the proper number that is rolled will not be easy.
 
Upvote 0

f0raster0

Well-Known Member
Licensed User
Longtime User
Thanks for the response f0raster0 and yes I know what forum I am on. I am trying to convert the stuff that the die company sent me to b4x. It runs on a web browser. I have used the nRF app and gleaned information about the die. I have a b4x code modeled after BLEExample getting the information that the nRF app is showing. Now I am trying to issue a command to the die and get a response. I am starting with a simple one which is to get the battery level. Once I figure that out then I should be on my way. Although as also stated getting the proper number that is rolled will not be easy.
if you post the b4X logs after connected I can update a code to read battery level..
same for the others value please focus on nRF app and b4x then I can help a bit more, if you can do (show) reading data using nRF App we can do the same using b4x
 
Upvote 0
Top