Connect RPI with Bangle and send data

Posted on
Page
of 2
/ 2
Next
  • As a follow on from a previous post( Espruino Hub and BLE) I am trying to connect a raspberrypi to the bangle and send from the pi a short message or two digit code.
    I have researched the espruino ref https://www.espruino.com/Interfacing#bluetooth-le, and in so doing came across a ble wrapper called bleak.
    This seems suited to my purpose but whilst it will connect and appear to send data it the data does not show on the bangle.
    I also do not know what ref I would need for these to entries (UUID_NORDIC_TX = ""
    UUID_NORDIC_RX = ""
    If it helps this is the shell output

    Connecting...
    Connected
    Writing command
    RX> bytearray(b"E.showMessage(\'Hello")
    RX> bytearray(b"\',\'A")
    RX> bytearray(b" Title\')/n")
    Waiting for data
    Done!

    and this is the python code

    e#!/usr/bin/env python3
    
    import asyncio
    import array
    from bleak import discover
    from bleak import BleakClient
    
    address = "D8:AB:E0:B7:F5:AE"
    UUID_NORDIC_TX = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
    UUID_NORDIC_RX = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
    command = b"E.showMessage('Hello','A Title')/n"
    
    def uart_data_received(sender, data):
        print("RX> {0}".format(data))
    
    # You can scan for devices with:
    [#async](https://forum.espruino.com/search/?q=%23async) def run():
    #    devices = await discover()
    #    for d in devices:
    #        print(d)
    
    print("Connecting...")
    async def run(address, loop):
        async with BleakClient(address, loop=loop) as client:
            print("Connected")
            await client.start_notify(UUID_NORDIC_RX, uart_data_received)
            print("Writing command")
            c=command
            while len(c)>0:
              await client.write_gatt_char(UUID_NORDIC_TX, bytearray(c[0:20]), True)
              c = c[20:]
            print("Waiting for data")
            await asyncio.sleep(1.0, loop=loop) # wait for a response
            print("Done!")
    
    
    
    

    Any guidance appreciated.

  • The /n is probably wrong, you are not sending correct end of line, try \n

  • Hi ,Thanks for that.
    It displayed on the watch so, so far so good.
    This is the shell output
    Connecting...
    Connected
    Writing command
    RX> bytearray(b"E.showMessage(\'Hello")
    RX> bytearray(b"\',\'A")
    RX> bytearray(b" Title\')\r\n")
    Waiting for data
    RX> bytearray(b'Uncaught ReferenceEr')
    RX> bytearray(b'ror: "nE" is not def')
    RX> bytearray(b'ined\r\n at line 1 col')
    RX> bytearray(b" 36\r\n...ssage(\'Hello")
    RX> bytearray(b"\',\'A Title\')/nE.show")
    RX> bytearray(b"Message(\'Hello\',\'A T")
    RX> bytearray(b"itle\')...\r\n ")
    RX> bytearray(b' ')
    RX> bytearray(b' ^\r\n>')
    Done!
    It doesnt look to clean. Is that down to how much data it can send?
    Also do you now anything re UUIDNORDIC TX and RX?
    Many Thanks

  • Having got the data to the bangle can any one advise how this could be captured by an app on the bangle to manage the code style etc
    Many thanks

  • Ok, that looks good - the Uncaught ReferenceError: "nE" is not defined would be because you'd previously sent the wrong command (wrong \n) and it was still in the input buffer, so when you later sent the correct command it errored because of the misplaced n.

    If you run it a second time it'd work fine.

    how this could be captured by an app on the bangle to manage the code style etc

    You have three options -

    Create a function, and then send the JS code to call it

    This is by far the easiest and what I'd recommend. So right now you're sending E.showMessage('Hello','A Title').

    Define a function on the Bangle and upload it - let's just call it X:

    function X(some, data) {
      E.showMessage(some, data);
     // or display some other way
    }
    

    For this to work alongside other apps, you want to write this into a storage file called myapp.boot.js.

    Then you can connect and send something like "X('Hello','A Title')\n".

    Have the app 'grab' the Bluetooth Serial port and handle all data that comes in.

    Bluetooth.on('data',function(d) {
      print("Got some data",d);
    });
    Terminal.setConsole();
    

    This isn't ideal though - you have to handle the fact that the data sent will be split up into ~20 byte chunks. On top of that, debugging is a complete pain, because if you're grabbing the serial port that means you're no longer able to connect with the Web IDE.

    Custom service

    You could create a custom service with NRF.setServices and then handle writes using the onWrite handler. It's more secure, but a bit of a pain to set up.

  • This isn't ideal though - you have to handle the fact that the data sent will be split up into ~20 byte chunks

    You could create a custom service with NRF.setServices and then handle writes using the onWrite handler. It's more secure, but a bit of a pain to set up.

    As for custom service, not sure if it helps but I implemented AT command handler as a custom service to emulate DS-D6, HX03W fitness trackers so it can be used with original android app. The code is here and for useful bits start from the bottom. Those trackers implement actually two similar/same services 0x190A,0x190B and both have its own set of read and write characteristics similar to nordic UART. If there is something useful there, it is the handleWrite()+handleCommand() methods that will receive 20 byte arrays, join it to string and call handleATLine on complete lines and then get the response as a string and split it to 20 byte replies and fires the notifications with response. Basically if you would replace handleATLine by eval and catch exceptions there it would evaluate javascript like espruino console.

    Also if you would try it beware of uart:false there that kills the console so keep it as true or have a way to call enableNordicUART() to restore it.

  • Hi Guys,
    Thanks for your replies.
    Fanoush, your approach is a little beyond my experience at this point but thank you.
    Gordan , are (some, data) variables passed to the functon?
    Does myapp.boot.js run in the background or do i need to to write an info file as described in your tutorials.
    Sorry for the basic level of my response.

  • are (some, data) variables passed to the function?

    Yes, you can pass literally anything, you just have to turn it into a string. I'd suggest using Python's built-in functions for creating JSON, so then you can just define whatever Python variable you want, then send the command "X("+json.dumps(my_data)+")\n".

    Does myapp.boot.js run in the background

    Yes, any '.boot.js' file will get automatically loaded along with an app, so you'll be fine without an info file. And if you want to add your code to the app loader all you need is something like this:

    https://github.com/espruino/BangleApps/blob/1d7f2070cffd318b74a42c0392addf48f3dca5fe/apps.json#L2250-L2262

  • Hi Gordon,
    Many thanks for the response.
    I decided to make an app for this to make things a bit more visible.
    However I get an error with json.dumps, which is

    Traceback (most recent call last):
      File "/home/pi/bleak-test.py", line 15, in <module>
        command = b"X("+json.dumps(dict)+")\n"
    TypeError: can't concat str to bytes
    

    I added this snippet to bleak to creates the variable.

    import json
    
    [#create](https://forum.espruino.com/search/?q=%23create) dictionary for the data to be sent
    dict = {'Code': '10'}
    

    And for completeness my app does this

    E.showMessage("Hello Code");
    
    function X(dict) {
      E.showMessage(dict);
    
    }
    

    So do I have to convert the json to bytes?
    Thanks for your patience
    mike

  • yes, or remove b from b"X

  • Thanks I will try that.

  • Fanoush,
    Thank you tried but still get an error

    File "/home/pi/bleak-test.py", line 34, in run
        await client.write_gatt_char(UUID_NORDIC_TX, bytearray(c[0:20]), True)
    TypeError: string argument without an encoding
    
  • https://docs.python.org/3/library/functions.html#func-bytearray says
    'If it is a string, you must also give the encoding'. For a start you can use 'ascii' as encoding.

    Or you can add encode call to c=command line to convert string to bytes.

  • thanks for that
    I have amended the code.

    command = "X('+json.dumps(dict).encode('utf-8')+')/n"
    

    and

    await client.write_gatt_char(UUID_NORDIC_TX,bytearray((c[0:20]),'utf-8') , True)
              c = c[20:]
    

    Not sure if I need both utf-8's, however I am assuming that passing this to my function I will need to decode it.

  • Not sure if it is just forum formatting but the first part came out wrong(?) It looks like you are still a bit confused about which part is python and which is JS, which part is executed where, and also what is the difference between bytes/bytearrays vs strings in python. I think that just the second change would fix that last error.

    BTW, Espruino interpreter does not handle utf-8 so ascii would be better. But maybe in this case it makes no difference anyway as json.dumps will hopefully quote non-ascii characters properly.

  • Thanks fanoush.
    Re my understanding yes you are probably right.
    To clarify I thought this line converted the python dictionary to a string:-

    command = "X('+json.dumps(dict)+')/n"
    
    

    and this line converted the json string to bytes:-

    await client.write_gatt_char(UUID_NORDIC_TX,bytearray((c[0:20]),'utf-8') , True)
              c = c[20:]
    

    I thought this would then be sent and my function would decode the bytes back to a json string:-

    E.showMessage("Hello Code");
    
    function X(dict) {
      msg = json.loads(dict.decode('utf-8'));
      
      E.showMessage(msg);
    
    }
    
    

    I dont now get the previous error, but the bleak library seems a little unreliable and gives a connection failure after a few attempts. However I am getting no response on the watch so I cant say its working yet.
    I will try ascii and see if there's a difference,but I really need a more reliable bluetooth tranfer code. Hopefully the code comes out OK.
    Many thanks for your help.
    mike

  • json.loads and dict.decode is python code so it won't run on the watch, it is not neeed however if you quote the command=... stuff right (as it was before)

  • Hi fanoush, thanks for your patience.
    So to go back I made the following changes but I stiil get no display on the watch. Just to note the function has changed to Y rather than X.
    This is the console output.

    Connecting...
    Connected
    Writing command
    Waiting for data
    RX> bytearray(b'Y({"Code": "10"})/n')
    RX> bytearray(b'Y({"Code": "10"})/n')
    Done!
    

    Putting the command back to what is was

    command = "Y("+json.dumps(dict)+")/n"
    
    

    and my app function is now

    E.showMessage("Hello Code");
    
    function Y(dict) {
      E.showMessage(dict);
    
    }
    
    
  • end of lines again

  • I am sorry I dont understand what you mean?

  • post #2, \n

  • Thanks got you.
    I now get output on the watch. Not what I want but progress.
    Watch shows

    +json.dumps(dict)+
    

    Console shows

    Connecting... Connected Writing command Waiting for data RX> bytearray(b'Y({"Code": "10"})\r\n') RX> bytearray(b'Y({"Code": "10"})\r\n') RX> bytearray(b'Uncaught ReferenceEr') RX> bytearray(b'Uncaught ReferenceEr') RX> bytearray(b'ror: "nY" is not def') RX> bytearray(b'ror: "nY" is not def') RX> bytearray(b'ined\r\n at line 1 col') RX> bytearray(b'ined\r\n at line 1 col') RX> bytearray(b" 25\r\nY(\'+json.dumps(") RX> bytearray(b" 25\r\nY(\'+json.dumps(") RX> bytearray(b'dict)+\')/nY({"Code":') RX> bytearray(b'dict)+\')/nY({"Code":') RX> bytearray(b' "10"})/nY({"Code": ') RX> bytearray(b' "10"})/nY({"Code": ') RX> bytearray(b'"10"}...\r\n ') RX> bytearray(b'"10"}...\r\n ') RX> bytearray(b' ^\r\n>') RX> bytearray(b' ^\r\n>') Done! `
    I will try the acii next.
    Thanks

  • Sorry console didnt post right

    Connecting...
    Connected
    Writing command
    Waiting for data
    RX> bytearray(b'Y({"Code": "10"})\r\n')
    RX> bytearray(b'Y({"Code": "10"})\r\n')
    RX> bytearray(b'Uncaught ReferenceEr')
    RX> bytearray(b'Uncaught ReferenceEr')
    RX> bytearray(b'ror: "nY" is not def')
    RX> bytearray(b'ror: "nY" is not def')
    RX> bytearray(b'ined\r\n at line 1 col')
    RX> bytearray(b'ined\r\n at line 1 col')
    RX> bytearray(b" 25\r\nY(\'+json.dumps(")
    RX> bytearray(b" 25\r\nY(\'+json.dumps(")
    RX> bytearray(b'dict)+\')/nY({"Code":')
    RX> bytearray(b'dict)+\')/nY({"Code":')
    RX> bytearray(b' "10"})/nY({"Code": ')
    RX> bytearray(b' "10"})/nY({"Code": ')
    RX> bytearray(b'"10"}...\r\n          ')
    RX> bytearray(b'"10"}...\r\n          ')
    RX> bytearray(b'              ^\r\n>')
    RX> bytearray(b'              ^\r\n>')
    Done!
    
  • I think what you've got is this line:

    command = "X('+json.dumps(dict)+')/n"
    

    Note that you're using two different types of quotes - so you're actually just sending the string X('+json.dumps(dict)+')/n which is exactly what Espruino is seeing.

    If you did:

    command = "X("+json.dumps(dict)+")/n"
    

    Then you may have more success - that should actually send the data in dict rather than just the text json.dumps(dict).

  • Thanks Gordon,
    I have tried your suggestion but bleak throws an error, which I will show below.
    My single quotes show jason.dump etc as before. This is also below.
    However I am confused, as per conversation with fanoush, I thought my problem is for my function to convert the bytearray back to a string using javascript. ( Icant find a way to do this yet) but any clarity on this matter would help.
    With the following

    command = "Y("+json.dumps(dict)+")\n"
    
    

    gives an error. Note I am not convinced that this is due to the command line as bleak seems a bit unstable.

    File "/home/pi/.local/lib/python3.7/site-packages/bleak/backends/bluezdbus/client.py", line 146, in connect
        raise BleakError(str(e))
    bleak.exc.BleakError: org.bluez.Error.Failed: Software caused connection abort
    

    With this

    command = "Y('+json.dumps(dict)+')\n"
    
    ``Console gives
    
    

    Connecting...
    Connected
    Writing command
    RX> bytearray(b"Y(\'+json.dumps(dict)")
    RX> bytearray(b"Y(\'+json.dumps(dict)")
    RX> bytearray(b"+\')")
    RX> bytearray(b"+\')")
    RX> bytearray(b'\r\n')
    RX> bytearray(b'\r\n')
    Waiting for data
    RX> bytearray(b'=undefined\r\n>')
    RX> bytearray(b'=undefined\r\n>')
    Done!

    The watch displays
    
    

    +json.dumps(dict)+
    ```

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Connect RPI with Bangle and send data

Posted by Avatar for user118216 @user118216

Actions