{"id":24432153,"url":"https://github.com/akilmarshall/esp32-ble-demo","last_synced_at":"2025-04-12T13:36:47.106Z","repository":{"id":112966241,"uuid":"301618174","full_name":"akilmarshall/esp32-ble-demo","owner":"akilmarshall","description":"A simple project to learn something about Bluetooth low energy and micropython.","archived":false,"fork":false,"pushed_at":"2020-10-06T05:07:19.000Z","size":11,"stargazers_count":10,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-26T08:11:29.569Z","etag":null,"topics":["ble","bluetooth","bluetooth-low-energy","eps32","esp32-wroom","micropython"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/akilmarshall.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-10-06T04:58:17.000Z","updated_at":"2025-02-28T12:08:02.000Z","dependencies_parsed_at":null,"dependency_job_id":"01c44f06-c281-4ec6-bf5b-b67bee6e2d49","html_url":"https://github.com/akilmarshall/esp32-ble-demo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akilmarshall%2Fesp32-ble-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akilmarshall%2Fesp32-ble-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akilmarshall%2Fesp32-ble-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/akilmarshall%2Fesp32-ble-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/akilmarshall","download_url":"https://codeload.github.com/akilmarshall/esp32-ble-demo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248574039,"owners_count":21126945,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ble","bluetooth","bluetooth-low-energy","eps32","esp32-wroom","micropython"],"created_at":"2025-01-20T15:34:20.885Z","updated_at":"2025-04-12T13:36:47.100Z","avatar_url":"https://github.com/akilmarshall.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# esp32-ble-demo\n\nThis is a demo project to learn about and implement a basic environmental sensor\nwith a controllable led using bluetooth low energy (ble).\nThis project is written in micropython.\n\nThe environment is sampled using a bme280 or bmp280 sensor and served via a GATT server.\nAdditionally an l.e.d. can be blinked when the utf-8 string 'blink' is sent to the GATT server via the string characteristic.\n\n\n\u003c!-- vim-markdown-toc GFM --\u003e\n\n* [parts](#parts)\n* [ble_environment.py](#ble_environmentpy)\n    * [BLEEnvironment class](#bleenvironment-class)\n        * [\\_\\_init\\_\\_](#__init_)\n        * [_irq](#irq)\n        * [set_environment_data](#set_environment_data)\n        * [read_act](#read_act)\n        * [_advertise](#advertise)\n* [bme280_float.py](#bme280_floatpy)\n* [gpio.py](#gpiopy)\n* [ble_advertising.py](#ble_advertisingpy)\n\n\u003c!-- vim-markdown-toc --\u003e\n## parts\n\n- ESP32-WROOM-32 microcontroller\n- bmp280 environmental sensor (temperature, pressure)\n- led\n\n## ble_environment.py\n\nThis file defines the BLEEnvironment class that runs the application\n\n```python\nimport bluetooth\nimport random\nimport struct\nimport time\nfrom ble_advertising import advertising_payload\n\nfrom micropython import const\n\nimport bme280_float as bme280\nfrom machine import Pin, I2C\n\nfrom gpio import blink\n\n```\n\nstandard micropython and project specific libraries.\nble_advertising, bme280_float, and gpio will be explained in their own sections.\n\n```python\n_IRQ_CENTRAL_CONNECT = const(1)\n_IRQ_CENTRAL_DISCONNECT = const(2)\n_IRQ_GATTS_INDICATE_DONE = const(20)\n\n```\n\nDefine event code constants.\n\n```python\n# org.bluetooth.service.environmental_sensing\n_ENV_SENSE_UUID = bluetooth.UUID(0x181A)\n```\n\nUse a predefined uuid for the enviornmental_sensing service.\n\n```python\n\n# org.bluetooth.characteristic.temperature\n# temperature\n_TEMP_CHAR = (\n    bluetooth.UUID(0x2A6E),\n    bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY | bluetooth.FLAG_INDICATE,\n)\n\n# atmospheric pressure\n# org.bluetooth.characteristic.pressure\n_PRESSURE_CHAR = (\n    bluetooth.UUID(0x2A6D),\n    bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY | bluetooth.FLAG_INDICATE,\n)\n\n# percentage humidity\n# org.bluetooth.characteristic.humidity\n_HUMIDITY_CHAR = (\n    bluetooth.UUID(0x2A6F),\n    bluetooth.FLAG_READ | bluetooth.FLAG_NOTIFY | bluetooth.FLAG_INDICATE,\n)\n\n# communication channel\n# org.bluetooth.characteristic.string\n_STRING_CHAR = (\n    bluetooth.UUID(0x2A3D),\n    bluetooth.FLAG_READ | bluetooth.FLAG_WRITE,\n)\n```\n\nAlso using predefine uuids define the characteristics the service will offer.\n\n```python\n\n_ENV_SENSE_SERVICE = (\n    _ENV_SENSE_UUID,\n    (_TEMP_CHAR, _PRESSURE_CHAR, _HUMIDITY_CHAR, _STRING_CHAR),\n)\n\n# org.bluetooth.characteristic.gap.appearance.xml\n_ADV_APPEARANCE_GENERIC_ENVIRONMENTAL_SENSOR = const(5696)\n\n```\n\nUsing the service and characteristics previously defined fully define the service\nto be registered on the GATT server.\n\n### BLEEnvironment class\n\nThe following are methods of BLEEnvironment class.\n\n#### \\_\\_init\\_\\_\n\n```python\ndef __init__(self, ble, name=\"esp32-ble-demo\"):\n    \"\"\"\n    __init__ :: BLEEnvironment -\u003e bluetooth.BLE -\u003e str -\u003e BLEEnvironment\n    \"\"\"\n    self._ble = ble\n    self._ble.active(True)\n    # register the event handler for events in the BLE stack\n    self._ble.irq(self._irq)\n    # unpack the gatt handles returned from service registration\n    ((self._temp_handle, self._pressure_handle, self._humidity_handle, self._string_handle),\n     ) = self._ble.gatts_register_services((_ENV_SENSE_SERVICE,))\n    # a set to contain connections to enable the sending of notifications\n    self._connections = set()\n    # create the payload for advertising the server\n    self._payload = advertising_payload(\n        name=name,\n        services=[_ENV_SENSE_UUID],\n        appearance=_ADV_APPEARANCE_GENERIC_ENVIRONMENTAL_SENSOR\n    )\n    # begin advertising the gatt server\n    self._advertise()\n```\n\n\\_\\_init\\_\\_ constructs an object of the BLEEnvironment class.\nble is a bluetooth.BLE object.\nThis method sets up and begins to advertise the device for bluetooth connections.\n\n#### _irq\n\n```python\ndef _irq(self, event, data):\n    # callback function for events from the BLE stack\n    # Track connections so we can send notifications.\n    if event == _IRQ_CENTRAL_CONNECT:\n        conn_handle, _, _ = data\n        self._connections.add(conn_handle)\n    elif event == _IRQ_CENTRAL_DISCONNECT:\n        conn_handle, _, _ = data\n        self._connections.remove(conn_handle)\n        # Start advertising again to allow a new connection.\n        self._advertise()\n    elif event == _IRQ_GATTS_INDICATE_DONE:\n        conn_handle, value_handle, status = data\n```\n\nDefine a simple event handler for GATT stack events.\nThis method is used by bluethooth.BLE.irq\n\n####  set_environment_data\n\n```python\ndef set_environment_data(self, temp, pressure, humidity, notify=False, indicate=False):\n\n    # write fresh temperature, pressure, and humidity data to the GATT server characteristics\n    self._ble.gatts_write(\n        self._temp_handle, struct.pack(\"\u003ci\", int(temp)))\n    self._ble.gatts_write(self._pressure_handle,\n                          struct.pack(\"\u003ci\", int(pressure)))\n    self._ble.gatts_write(self._humidity_handle,\n                          struct.pack(\"\u003ci\", int(humidity)))\n\n    # write fresh temperature, pressure, and humidity data to the GATT server characteristics\n    if notify or indicate:\n        for conn_handle in self._connections:\n            if notify:\n                # Notify connected centrals.\n                self._ble.gatts_notify(conn_handle, self._temp_handle)\n                self._ble.gatts_notify(conn_handle, self._pressure_handle)\n                self._ble.gatts_notify(conn_handle, self._humidity_handle)\n            if indicate:\n                # Indicate connected centrals.\n                self._ble.gatts_indicate(conn_handle, self._temp_handle)\n                self._ble.gatts_indicate(\n                    conn_handle, self._pressure_handle)\n                self._ble.gatts_indicate(\n                    conn_handle, self._humidity_handle)\n```\nset_environment_data is used to update the environmental data that is being served by the GATT server.\nOptionally connected centrals can be notified or indicated about updates to the characteristics.\n\n#### read_act\n\n```python\ndef read_act(self):\n    \"\"\"\n    Read the string channel in GATT, Act if the message is recognized\n    \"\"\"\n    # value is read from the _string_handle\n    value = self._ble.gatts_read(self._string_handle)\n\n    # the message protocol is implemented\n    # alternatively the message protocol can be implemented as a mapping from (utf-8 -\u003e function.obj)\n    # the functions could be written such that they can be spawned in a new thread so the main loop is not blocked.\n    # currently the action requested by a message blocks until the action completes.\n    if value == bytes('blink', 'utf-8'):\n        p = Pin(23, Pin.OUT)\n        for _ in range(3):\n            blink(p)\n\n    # the channel is cleared\n    self._ble.gatts_write(self._string_handle, struct.pack(\"\u003ci\", 0))\n\n```\nread_act implements the message protocol.\nThe method reads from the string characteristic and calls the appropriate method\nif a recognized message is received.\n\n#### _advertise\n\n```python\ndef _advertise(self, interval_us=500000):\n    self._ble.gap_advertise(interval_us, adv_data=self._payload)\n```\n_advertise uses the precomputed self._payload to advertise the device.\n\n\n\n## bme280_float.py\nThis file implements the BME280 class which allows a bme280 or bmp280 environment\nsensor to sample temperature, pressure, and humidity data.\n\n## gpio.py\nThis file implements common uses of the esp32 GPIO.\nSpecifically a blink functionality.\n\n## ble_advertising.py\nHelper functions for generation ble advertising payloads.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakilmarshall%2Fesp32-ble-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fakilmarshall%2Fesp32-ble-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fakilmarshall%2Fesp32-ble-demo/lists"}