omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    Welcome!

    This is the community forum for my apps Pythonista and Editorial.

    For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.


    Using cb library to connect simultaneously multiple TI SensorTag via Bluetooth Low Energy

    Pythonista
    4
    37
    20402
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • ccc
      ccc last edited by ccc

      f-strings are described at https://docs.python.org/3/reference/lexical_analysis.html#formatted-string-literals

      They are new in Python 3.6 so the following are all equivalent:

      $ python3.6
      >>> class P():
      ...     name = 'name'
      ...     uuid = 'uuid'
      ...
      >>> p = P()
      >>> f'{p.name}-{p.uuid}'
      'name-uuid'
      >>> '{}-{}'.format(p.name, p.uuid)
      'name-uuid'
      >>> '%s-%s' % (p.name, p.uuid)
      'name-uuid'
      >>> '-'.join((p.name, p.uuid))
      'name-uuid'
      >>> p.name + '-' + p.uuid
      'name-uuid'
      

      So p.name is not unique and p.uuid is not descriptive but if we put p.name, hyphen, p.uuid together then we have a string that is both descriptive and unique.

      1 Reply Last reply Reply Quote 0
      • ccc
        ccc last edited by

        Of course in Go, it would be:

        package main
        
        import "fmt"
        
        type p_struct struct {
        	name, uuid string
        }
        
        func main() {
        	p := p_struct{"name", "uuid"}
        	fmt.Printf("%v-%v", p.name, p.uuid)
        }
        
        1 Reply Last reply Reply Quote 0
        • ProgrammingGo
          ProgrammingGo last edited by

          Thanks a will try it out :).
          I have a question regarding the example of the SensorTag which is given in the documentation of cb:

             def did_discover_peripheral(self, p):
                  print('+++ Discovered peripheral: %s (%s)' % (p.name, p.uuid))
                  if p.name and 'Sensor Tag' in p.name and not self.peripheral:
                      # Keep a reference to the peripheral, so it doesn't get garbage-collected:
                      self.peripheral = p
                      cb.connect_peripheral(self.peripheral)
          

          This is the function to detect peripherals which are advertising. In the if statement there is something which is a bit unclear. Does it mean that if p.name is available and SensorTag is in P.name is clear, but why not self.peripheral?

          1 Reply Last reply Reply Quote 0
          • omz
            omz last edited by

            @ProgrammingGo

            In the if statement there is something which is a bit unclear. Does it mean that if p.name is available and SensorTag is in P.name is clear, but why not self.peripheral?

            The example connects to just one SensorTag, and that SensorTag is stored in the self.peripheral attribute, so the if not self.peripheral condition means "ignore this if we already found it". As you want to connect to multiple SensorTags, you'd have to handle this differently, of course.

            @ccc's code from earlier should be a good starting point. You might just need to use UUIDs instead of names, if all your SensorTags have the same name (not sure if they do generally).

            1 Reply Last reply Reply Quote 0
            • ProgrammingGo
              ProgrammingGo last edited by

              Hi,
              okay thanks for the explanation. I will try it out. that means the dict {} stores the connected peripherals so that the central does not try to connect to them again. I saw that the dict {} in the code is empty, should this stay like this or can I define the names of the nodes to which I want to have the connection?
              Regarding the name I can change the name of the SensorTag so that I could distinguish them

              1 Reply Last reply Reply Quote 0
              • ccc
                ccc last edited by

                When you start the dict is empty. Each time that did_discover_peripheral() is run, it will add a new entry in the dict if it is not already there.

                1 Reply Last reply Reply Quote 0
                • ccc
                  ccc last edited by

                  @ProgrammingGo Please run the following with multiple SensorTags turned on and post the output here:

                  class MultipleDeviceManager(object):
                      def __init__(self):
                          self.by_name = {}  # dict of peripheral names and peripheral objects
                          self.by_uuid = {}  # dict of peripheral uuids and peripheral objects
                  
                      def did_discover_peripheral(self, p):
                          if p.name not in self.by_name:
                              self.by_name[p.name] = p
                              print(f'Connecting to a new device: {p.name}...')
                          if p.uuid not in self.by_uuid :
                              self.by_uuid[p.uuid] = p
                              print(f'Connecting to a new device: {p.uuid}...')
                              cb.connect_peripheral(p)
                          print(f'{len(self.by_name)} unique names and {len(self.by_uuid)} unique uuids')
                  
                      def connect_to_device(self, device_name):
                          """May raise a KeyError"""
                          cb.connect_peripheral(self.by_name[device_name])
                  
                  ProgrammingGo 1 Reply Last reply Reply Quote 1
                  • ProgrammingGo
                    ProgrammingGo @ccc last edited by ProgrammingGo

                    @ccc I tried it out with two tags ( using the same name for both tags) and I have the following output:

                    Output:

                    Connecting to new device: CC2650 SensorTag....
                    Connecting to new device: <UUID> (too long but was displayed)

                    1 unique names and 1 unique uuids

                    Connecting to new device: <UUID> ---> only uuid was displayed because second SensorTag has same name

                    1 unique names and 2 unique uuids.

                    So logically I could use the uuid to have multiple sensor connected. I see the problem for the callback functions where I try to read out the sensor values or update it. For services it works, but to read out the same characteristics from both tags it is a problem. I would need to have a reference of the peripheral in the callback function did_discover_services. I wanted to modify the function, is there another way as workaround?

                    1 Reply Last reply Reply Quote 0
                    • ccc
                      ccc last edited by ccc

                      This is exactly what I anticipated that you would find... All these devices share a common name but each one has a unique UUID.

                      import cb
                      
                      
                      class MultipleCC2650Manager(object):
                          name = 'CC2650 SensorTag'  # we only care about devices with this name
                      
                          def __init__(self):
                              self.devices = {}  # dict of peripheral uuids and peripheral objects
                      
                          def did_discover_peripheral(self, p):
                              if p.name != self.name:
                                  return  # ignore devices do not have the name 'CC2650 SensorTag'
                              if p.uuid in self.devices:
                                  return  # ignore devices that we have already discovered/registered
                              self.devices[p.uuid] = p
                              print(f'Connecting to a new device: {p.uuid}...')
                              cb.connect_peripheral(p)
                              print(f'{len(self.devices)} unique {self.name} devices')
                      
                          def connect_to_device(self, device_uuid):
                              cb.connect_peripheral(self.devices[device_uuid])
                      
                      ProgrammingGo 1 Reply Last reply Reply Quote 0
                      • JonB
                        JonB last edited by

                        One thing you have to watch out for is that if you are trying to do notify in characteristics, the callbacks dont tell you whch peripheral it came from. That is a major shortcoming in my mind of the cb implementation. You might be better off going to an objc_util based solution

                        ProgrammingGo 1 Reply Last reply Reply Quote 0
                        • ProgrammingGo
                          ProgrammingGo @ccc last edited by ProgrammingGo

                          @ccc Yeah thanks a lot. I understood it very well with your example. Thank you so much. So I need to work with the uuid of the devices. A question regarding the example given : you call cb.connect_peripheral(p) in did_discover_peripheral and then in def connect_to_device again,why?

                          delegate = MyCentralManagerDelegate()
                          print('Scanning for peripherals...')
                          cb.set_central_delegate(delegate)
                          cb.scan_for_peripherals()
                          
                          # Keep the connection alive until the 'Stop' button is pressed:
                          try:
                              while True: pass
                          except KeyboardInterrupt:
                              # Disconnect everything:
                              cb.reset()
                          

                          This part is still needed right?

                          1 Reply Last reply Reply Quote 0
                          • ProgrammingGo
                            ProgrammingGo @JonB last edited by

                            @JonB Ok thank you for your hint

                            1 Reply Last reply Reply Quote 0
                            • ccc
                              ccc last edited by ccc

                              did_discover_peripheral() will always leave you connected to the last device that it discovered. So when you want to chose which device that you want to connect to, you call connect_to_device().

                              ProgrammingGo 1 Reply Last reply Reply Quote 0
                              • ProgrammingGo
                                ProgrammingGo @ccc last edited by ProgrammingGo

                                @ccc Hi sorry for the question I am a bit confused. It is not clear to me how do you mean that :(. Can you explain it may be again? My target is to be connected to 3 SensorTags at the same time to receive data.

                                1 Reply Last reply Reply Quote 0
                                • ccc
                                  ccc last edited by

                                  Let's say that you have 3 CC2650 SensorTags and their UUIDs are 0, 1, 2... Getting data would be:

                                  data = []  # empty list
                                  for device_uuid in (0, 1, 2):
                                      cc2650_manager.connect_to_device(device_uuid)
                                      data.append(get_data())
                                  print(data)
                                  

                                  You can only talk to one device at a time but you can loop thru all the devices and call connect_to_device() and then get the data from that device.

                                  ProgrammingGo 1 Reply Last reply Reply Quote 0
                                  • ccc
                                    ccc last edited by

                                    Do you have GitHub, GitLab, or BitBucket? It would probably be easier to collaborate on a repo.

                                    1 Reply Last reply Reply Quote 0
                                    • ProgrammingGo
                                      ProgrammingGo @ccc last edited by ProgrammingGo

                                      @ccc Ok that means I am using a loop to read node by node. But for me, it is not so clear how to use connect_to_periphera to connect to the next. - How can I call it? For my understanding normally at first, I would call connect_to_peripheral and then did_discover_peripheral.
                                      I gave a look in the example given for SensorTag and calling the callback functions is a bit different ---> it happens inside the cb lib.

                                      No Iam not using GitHub or the other tools.

                                      1 Reply Last reply Reply Quote 0
                                      • JonB
                                        JonB last edited by

                                        You can connect to each device once, as long as you hang into references to the peripheral object and Characteristic.

                                        The trick is then keeping track of the reads, since did_update_value does not include the peripheral, so you cannot tell which is which.

                                        so pseudo code would be:

                                        1. discover devices
                                        2. connect to devices, storing peripheral in a list or dict
                                        3. discover service/characteristic, and store the characteristic
                                        4. once you have a characteristic, i would have a loop that pools:
                                        for p in peripherals:
                                           current_p=p (store as a global)
                                           p.read_characteristic_value(C)
                                           (sleep for a while p, or use threading Event etc to await did_update_value)
                                        

                                        in did_update_value, check current_p to get uuid, then store the read value somewhere, print it out, whatever, and set the Event flag to awake the main loop

                                        ProgrammingGo 3 Replies Last reply Reply Quote 0
                                        • ProgrammingGo
                                          ProgrammingGo @JonB last edited by ProgrammingGo

                                          @JonB Hi thanks for the nice description: I tried to work with the structure of the given example for the sensortag Example and try it out to adapt it. But Iam getting stuck. Iam not able to read out the data from all 3 sensortags. When I try to register for characteristics nothing worked. I tried several things out but without success.

                                          1 Reply Last reply Reply Quote 0
                                          • ProgrammingGo
                                            ProgrammingGo @JonB last edited by ProgrammingGo

                                            @JonB said:

                                            You can connect to each device once, as long as you hang into references to the peripheral object and Characteristic.

                                            The trick is then keeping track of the reads, since did_update_value does not include the peripheral, so you cannot tell which is which.

                                            so pseudo code would be:

                                            1. discover devices
                                            2. connect to devices, storing peripheral in a list or dict
                                            3. discover service/characteristic, and store the characteristic
                                            4. once you have a characteristic, i would have a loop that pools:
                                            for p in peripherals:
                                               current_p=p (store as a global)
                                               p.read_characteristic_value(C)
                                               (sleep for a while p, or use threading Event etc to await did_update_value)
                                            

                                            in did_update_value, check current_p to get uuid, then store the read value somewhere, print it out, whatever, and set the Event flag to awake the main loop

                                            The steps from the pseudo code are understandable but do you mean storing the discovered peripherals in a list and use a for loop to connect one by one and read the data or do you mean connect to all and use a for loop to read out the data of one device per time`?

                                            1 Reply Last reply Reply Quote 0
                                            • First post
                                              Last post
                                            Powered by NodeBB Forums | Contributors