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

How to write to a Characteristic #20

Closed
mcsarge opened this issue Nov 20, 2014 · 6 comments
Closed

How to write to a Characteristic #20

mcsarge opened this issue Nov 20, 2014 · 6 comments

Comments

@mcsarge
Copy link

mcsarge commented Nov 20, 2014

Not really an Issue, but I have a question:
The device I am trying to communicate with requires that I set the value of a characteristic and then write to the characteristic. The response from this write contains the result of the command. For instance if I write "read temp\r" I get back "66.0". I have looked through your code, but not being an expert in Python, I am having a bit of a time figuring out how I write a string (like "status") to the characteristic and then get back the response. I am pretty sure this is something simple, but I am stuck.

@mcsarge
Copy link
Author

mcsarge commented Dec 2, 2014

So in doing a bit more investigation, it looks like you are always expecting the data sent to the device to be encoded as a HEX string. I need to be able to send data to my device as a string terminated with a .
There is a line in btle.py :
self._writeCmd("%s %X %s\n" % (cmd, handle, binascii.b2a_hex(val).decode('utf-8')))

That appears to convert the data into a HEX string. Should I change this for when I am sending straight character data?

In looking at the code on the C++ side, it does not appear that you unpack that data and turn it back into a string before sending to the device is that the case since device you are interfacing to uses this HEX format?

@IanHarvey
Copy link
Owner

There's a small example of how to write to a characteristic in SensorBase in sensortag.py. It gets a characteristic object (self.ctrl) using service.getCharacteristics(), and then calls write() on that characteristic.

The value you pass to write() will be passed to binascii.b2a_hex() as you found above. For Python 2.x, it needs to be of type str, and for Python 3.x, it needs to be bytes. If you want to write code which works on both, you can do the following:

characteristic.write( "read temp\r".encode('utf-8') )

as encode() will return the right type of result on both 2.x and 3.x

Hope that helps,
Ian

@mcsarge
Copy link
Author

mcsarge commented Dec 3, 2014

Thanks!, and I think it helped, but I am not sure, so I made that change and the device may be receiving the command, but the only way I know it is if I am able to read the data returned in the response from the write command. It looks like in the char_write_req_cb of bluepy-helper.c that user_data never has anything done with it. Is it possible that the command returned some data and it is lost because user_data is never looked at?
Here is the code:

static void char_write_req_cb(guint8 status, const guint8 *pdu, guint16 plen,
                            gpointer user_data)
{
    if (status != 0) {
        resp_error(err_COMM_ERR); // Todo: status
        return;
    }
    if (!dec_write_resp(pdu, plen) && !dec_exec_write_resp(pdu, plen)) {
        resp_error(err_PROTO_ERR);
        return;
    }
    resp_begin(rsp_WRITE);
    resp_end();
}

I am thinking that the user_data needs to be sent back too, I think that is where the response to my "read temp\r" is.

@mcsarge
Copy link
Author

mcsarge commented Dec 3, 2014

OK, I have more information. So, if I use the gatttool, I can send and receive the updates to the characteristics. Here is what happens. I write a characteristic, like read temp\r and it is sent to the device, the device then sends a characteristic update with the response. In the gatttool, this is received and printed by the events_handler. Here is it running in gatttool:

image

How would I get the response that is created in the events_handler using the helper code? It looks like it is sending it somehow, but I do not know how to catch it. Can I register a callback in the Python code somehow or can I make a call that will collect the response that has been written using the resp_begin etc.? in the Here is that in the helper code:

static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
{
    uint8_t *opdu;
        uint8_t evt;
    uint16_t handle, i, olen;
    size_t plen;

        evt = pdu[0];

        if ( evt != ATT_OP_HANDLE_NOTIFY && evt != ATT_OP_HANDLE_IND )
        {
          printf("#Invalid opcode %02X in event handler??\n", evt);
          return;
        }

        assert( len >= 3 );
    handle = att_get_u16(&pdu[1]);

        resp_begin( evt==ATT_OP_HANDLE_NOTIFY ? rsp_NOTIFY : rsp_IND );
        send_uint( tag_HANDLE, handle );
        send_data( pdu+3, len-3 );
        resp_end();

    if (evt == ATT_OP_HANDLE_NOTIFY)
        return;

    opdu = g_attrib_get_buffer(attrib, &plen);
    olen = enc_confirmation(opdu, plen);

    if (olen > 0)
        g_attrib_send(attrib, 0, opdu, olen, NULL, NULL, NULL);
}

@mcsarge
Copy link
Author

mcsarge commented Dec 3, 2014

OK,
I think i solved it. Here is what I did. I added a method in the Peripheral class that allowed me to ask for the "ntfy" response. Here is the code in the Peripheral:
image

I then added a method to the Characteristic class that called that for the particular characteristic.

Down in my main code, I added a loop after I write the characteristic that calls this readNtfy repeatedly until it gets an answer, this seems to work. Here is that code:
image

I will, of course, update the code so that it does not check forever and also to chain the responses together because the device can write back lots of data in 20 byte chunks. Also, it seems that both a ntfy and an ind can be sent from the callback so I will research if I need to update and rename my readNtfy to something more event-ish. I will update my version and if you are interested, you can merge my changes in.

@IanHarvey
Copy link
Owner

I have now (version 0.9.0, just pushed) made some proper arrangements for handling notifications from peripherals. It lets you register an object to be called whenever a notification is sent, and has a method which waits for notifications with a selectable timeout.

The main documentation page describing this is at http://ianharvey.github.io/bluepy-doc/notifications.html
If you're still developing this, I'd be happy to hear whether this is useful.

Thanks
Ian

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

No branches or pull requests

2 participants