Advertisement
TonyGo

PiCow Bluetooth communication - integers

Jun 19th, 2023
1,719
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 13.32 KB | Source Code | 0 0
  1. # Save as BLE_server.py
  2. # Modified to send random integers much faster
  3. # Tony Goodhew 19th June 2023
  4. # This is quick and dirty. It uses the Environmental Sensing and Temperature UUIDs
  5. import bluetooth
  6. import random
  7. import struct
  8. import time
  9. import machine
  10. import ubinascii
  11. from ble_advertising import advertising_payload
  12. from micropython import const
  13. from machine import Pin
  14. import random ############################# EXTRA LINE ###########
  15. _IRQ_CENTRAL_CONNECT = const(1)
  16. _IRQ_CENTRAL_DISCONNECT = const(2)
  17. _IRQ_GATTS_INDICATE_DONE = const(20)
  18.  
  19. _FLAG_READ = const(0x0002)
  20. _FLAG_NOTIFY = const(0x0010)
  21. _FLAG_INDICATE = const(0x0020)
  22.  
  23. # org.bluetooth.service.environmental_sensing
  24. _ENV_SENSE_UUID = bluetooth.UUID(0x181A)
  25.  
  26. # org.bluetooth.characteristic.temperature
  27. _TEMP_CHAR = (
  28.     bluetooth.UUID(0x2A6E),
  29.     _FLAG_READ | _FLAG_NOTIFY | _FLAG_INDICATE,
  30. )
  31. _ENV_SENSE_SERVICE = (
  32.     _ENV_SENSE_UUID,
  33.     (_TEMP_CHAR,),
  34. )
  35.  
  36. # org.bluetooth.characteristic.gap.appearance.xml
  37. _ADV_APPEARANCE_GENERIC_THERMOMETER = const(768)
  38.  
  39. class BLETemperature:
  40.     def __init__(self, ble, name=""):
  41.         self._sensor_temp = machine.ADC(4)
  42.         self._ble = ble
  43.         self._ble.active(True)
  44.         self._ble.irq(self._irq)
  45.         ((self._handle,),) = self._ble.gatts_register_services((_ENV_SENSE_SERVICE,))
  46.         self._connections = set()
  47.         if len(name) == 0:
  48.             name = 'Pico %s' % ubinascii.hexlify(self._ble.config('mac')[1],':').decode().upper()
  49.         print('Sensor name %s' % name)
  50.         self._payload = advertising_payload(
  51.             name=name, services=[_ENV_SENSE_UUID]
  52.         )
  53.         self._advertise()
  54.  
  55.     def _irq(self, event, data):
  56.         # Track connections so we can send notifications.
  57.         if event == _IRQ_CENTRAL_CONNECT:
  58.             conn_handle, _, _ = data
  59.             self._connections.add(conn_handle)
  60.         elif event == _IRQ_CENTRAL_DISCONNECT:
  61.             conn_handle, _, _ = data
  62.             self._connections.remove(conn_handle)
  63.             # Start advertising again to allow a new connection.
  64.             self._advertise()
  65.         elif event == _IRQ_GATTS_INDICATE_DONE:
  66.             conn_handle, value_handle, status = data
  67.        
  68.     def update_temperature(self, notify=False, indicate=False):
  69.         # Write the local value, ready for a central to read.
  70.         data = random.randint(0,9999)   # Generate a random number
  71. #        print("write temp %.2f degc" % temp_deg_c);
  72.         print(data,"Current value being sent");
  73.         self._ble.gatts_write(self._handle, struct.pack("<h", data)) ###
  74.         if notify or indicate:
  75.             for conn_handle in self._connections:
  76.                 if notify:
  77.                     # Notify connected centrals.
  78.                     self._ble.gatts_notify(conn_handle, self._handle)
  79.                 if indicate:
  80.                     # Indicate connected centrals.
  81.                     self._ble.gatts_indicate(conn_handle, self._handle)
  82.  
  83.     def _advertise(self, interval_us=5000):  # was 500000
  84.         self._ble.gap_advertise(interval_us, adv_data=self._payload)
  85. '''
  86.    def _get_temp(self):   #################### Modified here ##########
  87.        x = random.randint(0,9999)
  88.        
  89.        return float(x)
  90. '''
  91.  
  92. def demo():
  93.     ble = bluetooth.BLE()
  94.     temp = BLETemperature(ble)
  95.     counter = 0
  96.     led = Pin('LED', Pin.OUT)
  97.     while True:
  98.         if counter % 2 == 0:
  99.             temp.update_temperature(notify=True, indicate=False)
  100.         led.toggle()
  101.         time.sleep_ms(100)
  102.         counter += 1
  103.  
  104. if __name__ == "__main__":
  105.     demo()
  106.  
  107.  
  108. ############ SECOND PROGRAM BELOW ############
  109.    
  110. # Save as BLE_reader.py
  111. # Finds and connects to a BLE temperature sensor but reads integers
  112. # Modified to receive random integers much faster
  113. # Tony Goodhew 19th June 2023
  114. import bluetooth
  115. import random
  116. import struct
  117. import time
  118. import micropython
  119. from ble_advertising import decode_services, decode_name
  120. from micropython import const
  121. from machine import Pin
  122.  
  123. _IRQ_CENTRAL_CONNECT = const(1)
  124. _IRQ_CENTRAL_DISCONNECT = const(2)
  125. _IRQ_GATTS_WRITE = const(3)
  126. _IRQ_GATTS_READ_REQUEST = const(4)
  127. _IRQ_SCAN_RESULT = const(5)
  128. _IRQ_SCAN_DONE = const(6)
  129. _IRQ_PERIPHERAL_CONNECT = const(7)
  130. _IRQ_PERIPHERAL_DISCONNECT = const(8)
  131. _IRQ_GATTC_SERVICE_RESULT = const(9)
  132. _IRQ_GATTC_SERVICE_DONE = const(10)
  133. _IRQ_GATTC_CHARACTERISTIC_RESULT = const(11)
  134. _IRQ_GATTC_CHARACTERISTIC_DONE = const(12)
  135. _IRQ_GATTC_DESCRIPTOR_RESULT = const(13)
  136. _IRQ_GATTC_DESCRIPTOR_DONE = const(14)
  137. _IRQ_GATTC_READ_RESULT = const(15)
  138. _IRQ_GATTC_READ_DONE = const(16)
  139. _IRQ_GATTC_WRITE_DONE = const(17)
  140. _IRQ_GATTC_NOTIFY = const(18)
  141. _IRQ_GATTC_INDICATE = const(19)
  142.  
  143. _ADV_IND = const(0x00)
  144. _ADV_DIRECT_IND = const(0x01)
  145. _ADV_SCAN_IND = const(0x02)
  146. _ADV_NONCONN_IND = const(0x03)
  147.  
  148. # org.bluetooth.service.environmental_sensing
  149. _ENV_SENSE_UUID = bluetooth.UUID(0x181A)
  150. # org.bluetooth.characteristic.temperature
  151. _TEMP_UUID = bluetooth.UUID(0x2A6E)
  152. _TEMP_CHAR = (
  153.     _TEMP_UUID,
  154.     bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY,
  155. )
  156. _ENV_SENSE_SERVICE = (
  157.     _ENV_SENSE_UUID,
  158.     (_TEMP_CHAR,),
  159. )
  160.  
  161. class BLETemperatureCentral:
  162.     def __init__(self, ble):
  163.         self._ble = ble
  164.         self._ble.active(True)
  165.         self._ble.irq(self._irq)
  166.         self._reset()
  167.         self._led = Pin('LED', Pin.OUT)
  168.  
  169.     def _reset(self):
  170.         # Cached name and address from a successful scan.
  171.         self._name = None
  172.         self._addr_type = None
  173.         self._addr = None
  174.  
  175.         # Cached value (if we have one)
  176.         self._value = None
  177.  
  178.         # Callbacks for completion of various operations.
  179.         # These reset back to None after being invoked.
  180.         self._scan_callback = None
  181.         self._conn_callback = None
  182.         self._read_callback = None
  183.          
  184.         # Persistent callback for when new data is notified from the device.
  185.         self._notify_callback = None
  186.  
  187.         # Connected device.
  188.         self._conn_handle = None
  189.         self._start_handle = None
  190.         self._end_handle = None
  191.         self._value_handle = None
  192.  
  193.     def _irq(self, event, data):
  194.         if event == _IRQ_SCAN_RESULT:
  195.             addr_type, addr, adv_type, rssi, adv_data = data
  196.             if adv_type in (_ADV_IND, _ADV_DIRECT_IND):
  197.                 type_list = decode_services(adv_data)
  198.                 if _ENV_SENSE_UUID in type_list:
  199.                     # Found a potential device, remember it and stop scanning.
  200.                     self._addr_type = addr_type
  201.                     self._addr = bytes(addr) # Note: addr buffer is owned by caller so need to copy it.
  202.                     self._name = decode_name(adv_data) or "?"
  203.                     self._ble.gap_scan(None)
  204.  
  205.         elif event == _IRQ_SCAN_DONE:
  206.             if self._scan_callback:
  207.                 if self._addr:
  208.                     # Found a device during the scan (and the scan was explicitly stopped).
  209.                     self._scan_callback(self._addr_type, self._addr, self._name)
  210.                     self._scan_callback = None
  211.                 else:
  212.                     # Scan timed out.
  213.                     self._scan_callback(None, None, None)
  214.  
  215.         elif event == _IRQ_PERIPHERAL_CONNECT:
  216.             # Connect successful.
  217.             conn_handle, addr_type, addr = data
  218.             if addr_type == self._addr_type and addr == self._addr:
  219.                 self._conn_handle = conn_handle
  220.                 self._ble.gattc_discover_services(self._conn_handle)
  221.  
  222.         elif event == _IRQ_PERIPHERAL_DISCONNECT:
  223.             # Disconnect (either initiated by us or the remote end).
  224.             conn_handle, _, _ = data
  225.             if conn_handle == self._conn_handle:
  226.                 # If it was initiated by us, it'll already be reset.
  227.                 self._reset()
  228.  
  229.         elif event == _IRQ_GATTC_SERVICE_RESULT:
  230.             # Connected device returned a service.
  231.             conn_handle, start_handle, end_handle, uuid = data
  232.             if conn_handle == self._conn_handle and uuid == _ENV_SENSE_UUID:
  233.                 self._start_handle, self._end_handle = start_handle, end_handle
  234.          
  235.         elif event == _IRQ_GATTC_SERVICE_DONE:
  236.             # Service query complete.
  237.             if self._start_handle and self._end_handle:
  238.                 self._ble.gattc_discover_characteristics(
  239.                     self._conn_handle, self._start_handle, self._end_handle)
  240.        
  241.             else:
  242.                 print("Failed to find environmental sensing service.")
  243.  
  244.         elif event == _IRQ_GATTC_CHARACTERISTIC_RESULT:
  245.             # Connected device returned a characteristic.
  246.             conn_handle, def_handle, value_handle, properties, uuid = data
  247.             if conn_handle == self._conn_handle and uuid == _TEMP_UUID:
  248.                 self._value_handle = value_handle
  249.  
  250.         elif event == _IRQ_GATTC_CHARACTERISTIC_DONE:
  251.             # Characteristic query complete.
  252.             if self._value_handle:
  253.                 # We've finished connecting and discovering device, fire the connect callback.
  254.                 if self._conn_callback:
  255.                     self._conn_callback()
  256.             else:
  257.                 print("Failed to find temperature characteristic.")
  258.  
  259.         elif event == _IRQ_GATTC_READ_RESULT:
  260.             # A read completed successfully.
  261.             conn_handle, value_handle, char_data = data
  262.             if conn_handle == self._conn_handle and value_handle == self._value_handle:
  263.                 self._update_value(char_data)
  264.                 if self._read_callback:
  265.                     self._read_callback(self._value)
  266.                     self._read_callback = None
  267.  
  268.         elif event == _IRQ_GATTC_READ_DONE:
  269.             # Read completed (no-op).
  270.             conn_handle, value_handle, status = data
  271.          
  272.         elif event == _IRQ_GATTC_NOTIFY:
  273.             # The ble_temperature.py demo periodically notifies its value.
  274.             conn_handle, value_handle, notify_data = data
  275.             if conn_handle == self._conn_handle and value_handle == self._value_handle:
  276.                 self._update_value(notify_data)
  277.                 if self._notify_callback:
  278.                     self._notify_callback(self._value)
  279.  
  280.         # Returns true if we've successfully connected and discovered characteristics.
  281.     def is_connected(self):
  282.         return self._conn_handle is not None and self._value_handle is not None
  283.  
  284.     # Find a device advertising the environmental sensor service.
  285.     def scan(self, callback=None):
  286.         self._addr_type = None
  287.         self._addr = None
  288.         self._scan_callback = callback
  289.         self._ble.gap_scan(2000, 30000, 30000)
  290.  
  291.     # Connect to the specified device (otherwise use cached address from a scan).
  292.     def connect(self, addr_type=None, addr=None, callback=None):
  293.         self._addr_type = addr_type or self._addr_type
  294.         self._addr = addr or self._addr
  295.         self._conn_callback = callback
  296.         if self._addr_type is None or self._addr is None:
  297.             return False
  298.         self._ble.gap_connect(self._addr_type, self._addr)
  299.         return True
  300.  
  301.     # Disconnect from current device.
  302.     def disconnect(self):
  303.         if not self._conn_handle:
  304.             return
  305.         self._ble.gap_disconnect(self._conn_handle)
  306.         self._reset()
  307.  
  308.     # Issues an (asynchronous) read, will invoke callback with data.
  309.     def read(self, callback):
  310.         if not self.is_connected():
  311.             return
  312.         self._read_callback = callback
  313.         try:
  314.             self._ble.gattc_read(self._conn_handle, self._value_handle)
  315.         except OSError as error:
  316.             print(error)
  317.  
  318.     # Sets a callback to be invoked when the device notifies us.
  319.     def on_notify(self, callback):
  320.         self._notify_callback = callback
  321.  
  322.     def _update_value(self, data):
  323.     # Data is sint16 in degrees Celsius with a resolution of 0.01 degrees Celsius.
  324.         try:
  325.             self._value = struct.unpack("<h", data)[0]   ####### Modified
  326.         except OSError as error:
  327.             print(error)
  328.  
  329.     def value(self):
  330.         return self._value
  331.  
  332. def sleep_ms_flash_led(self, flash_count, delay_ms):
  333.     self._led.off()
  334.     while(delay_ms > 0):
  335.         for i in range(flash_count):
  336.             self._led.on()
  337.             time.sleep_ms(100)
  338.             self._led.off()
  339.             time.sleep_ms(100)
  340.             delay_ms -= 200
  341.         time.sleep_ms(10) # was 1000
  342.         delay_ms -= 1000
  343.      
  344. def print_temp(result):
  345.     print(result, "Received")  ### Modified
  346.  
  347. def demo(ble, central):
  348.     not_found = False
  349.  
  350.     def on_scan(addr_type, addr, name):
  351.         if addr_type is not None:
  352.             print("Found sensor: %s" % name)
  353.             central.connect()
  354.         else:
  355.             nonlocal not_found
  356.             not_found = True
  357.             print("No sensor found.")
  358.  
  359.     central.scan(callback=on_scan)
  360.  
  361.     # Wait for connection...
  362.     while not central.is_connected():
  363.         time.sleep_ms(100)
  364.         if not_found:
  365.             return
  366.  
  367.     print("Connected")
  368.  
  369.     # Explicitly issue reads
  370.     while central.is_connected():
  371.         central.read(callback=print_temp)
  372.         sleep_ms_flash_led(central, 1, 2) ## was 2000
  373.  
  374.     print("Disconnected")
  375.  
  376. if __name__ == "__main__":
  377.     ble = bluetooth.BLE()
  378.     central = BLETemperatureCentral(ble)
  379.     while(True):
  380.         demo(ble, central)
  381.         sleep_ms_flash_led(central, 1, 10000)
  382.  
  383.  
  384.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement