From a7189d37145ff18d48185012e1592e3a733c723c Mon Sep 17 00:00:00 2001 From: Tanner Collin Date: Wed, 8 May 2024 14:31:38 -0600 Subject: [PATCH] Initial commit --- .gitignore | 114 +++++++++++++++++++++++++++++ btproxy/ble_uart_echo_client.py | 39 ++++++++++ btproxy/ble_uart_echo_test.py | 27 +++++++ btproxy/main.py | 43 +++++++++++ btproxy/print_uart.py | 26 +++++++ firmware/firmware.ino | 125 ++++++++++++++++++++++++++++++++ nrfutil/requirements.txt | 5 ++ 7 files changed, 379 insertions(+) create mode 100644 .gitignore create mode 100644 btproxy/ble_uart_echo_client.py create mode 100644 btproxy/ble_uart_echo_test.py create mode 100644 btproxy/main.py create mode 100644 btproxy/print_uart.py create mode 100644 firmware/firmware.ino create mode 100644 nrfutil/requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8936d40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,114 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Editor +*.swp +*.swo + +# DB +db.sqlite3 + +# Test mount +test/ + +# VS Code +.vscode/ + +output/ diff --git a/btproxy/ble_uart_echo_client.py b/btproxy/ble_uart_echo_client.py new file mode 100644 index 0000000..48a0f23 --- /dev/null +++ b/btproxy/ble_uart_echo_client.py @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +""" +Used with ble_uart_echo_test.py. Transmits "echo" to the UARTService and receives it back. +""" + +import time + +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.services.nordic import UARTService + +ble = BLERadio() +while True: + while ble.connected and any( + UARTService in connection for connection in ble.connections + ): + for connection in ble.connections: + if UARTService not in connection: + continue + print("echo") + uart = connection[UARTService] + uart.write(b"echo") + # Returns b'' if nothing was read. + one_byte = uart.read(4) + if one_byte: + print(one_byte) + print() + time.sleep(1) + + print("disconnected, scanning") + for advertisement in ble.start_scan(ProvideServicesAdvertisement, timeout=1): + if UARTService not in advertisement.services: + continue + ble.connect(advertisement) + print("connected") + break + ble.stop_scan() diff --git a/btproxy/ble_uart_echo_test.py b/btproxy/ble_uart_echo_test.py new file mode 100644 index 0000000..734d226 --- /dev/null +++ b/btproxy/ble_uart_echo_test.py @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2020 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +""" +Can be used with ble_uart_echo_client.py or with the UART page on the +Adafruit Bluefruit Connect app. Receives characters from the UARTService +and transmits them back. +""" + +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.services.nordic import UARTService + +ble = BLERadio() +uart = UARTService() +advertisement = ProvideServicesAdvertisement(uart) + +while True: + ble.start_advertising(advertisement) + while not ble.connected: + pass + while ble.connected: + # Returns b'' if nothing was read. + one_byte = uart.read(1) + if one_byte: + print(one_byte) + #uart.write(one_byte) diff --git a/btproxy/main.py b/btproxy/main.py new file mode 100644 index 0000000..48ce546 --- /dev/null +++ b/btproxy/main.py @@ -0,0 +1,43 @@ +import time + +import bleak +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.services.nordic import UARTService + +ble = BLERadio() + +def scan(): + print("disconnected, scanning") + for advertisement in ble.start_scan(ProvideServicesAdvertisement, timeout=1): + if not advertisement.complete_name.startswith('Feather'): + continue + + ble.connect(advertisement) + print("connected to", advertisement.complete_name) + break + ble.stop_scan() + +def read(connection): + uart = connection[UARTService] + if uart.in_waiting: + res = uart.read(uart.in_waiting) + print(res) + +def loop(): + while True: + while ble.connected: + for connection in ble.connections: + try: + read(connection) + except bleak.exc.BleakError: + print('Disconnected, waiting 3 seconds and trying to reconnect...') + connection.disconnect() + time.sleep(3) + + time.sleep(0.1) + + scan() + +if __name__ == '__main__': + loop() diff --git a/btproxy/print_uart.py b/btproxy/print_uart.py new file mode 100644 index 0000000..6aeeb78 --- /dev/null +++ b/btproxy/print_uart.py @@ -0,0 +1,26 @@ +from adafruit_ble import BLERadio +from adafruit_ble.advertising.standard import ProvideServicesAdvertisement +from adafruit_ble.services.nordic import UARTService + +ble = BLERadio() +while True: + while ble.connected and any( + UARTService in connection for connection in ble.connections + ): + for connection in ble.connections: + if UARTService not in connection: + continue + uart = connection[UARTService] + while ble.connected: + one_byte = uart.readline() + if one_byte: + print(one_byte.decode().strip()) + + print("disconnected, scanning") + for advertisement in ble.start_scan(ProvideServicesAdvertisement, timeout=1): + if UARTService not in advertisement.services: + continue + ble.connect(advertisement) + print("connected") + break + ble.stop_scan() diff --git a/firmware/firmware.ino b/firmware/firmware.ino new file mode 100644 index 0000000..05d1d6a --- /dev/null +++ b/firmware/firmware.ino @@ -0,0 +1,125 @@ +#include +#include +#include +#include "HX711.h" + +//#define calibration_factor -6980.0 // black wired load cell +#define calibration_factor -5760.0 // orange wired load cell + +#define DOUT 30 +#define CLK 27 + +HX711 scale; + +// BLE Service +BLEDfu bledfu; // OTA DFU service +BLEDis bledis; // device information +BLEUart bleuart; // uart over ble +BLEBas blebas; // battery + +void setup() +{ + Serial.begin(115200); + + + scale.begin(DOUT, CLK); + digitalWrite(CLK, LOW); // hangs without this + scale.set_scale(calibration_factor); //This value is obtained by using the SparkFun_HX711_Calibration sketch + scale.tare(); //Assuming there is no weight on the scale at start up, reset the scale to 0 + + Serial.println(""); + Serial.println(""); + Serial.println("Load Cell Demo"); + Serial.println("--------------\n"); + + Bluefruit.autoConnLed(true); + Bluefruit.configPrphBandwidth(BANDWIDTH_MAX); + + Bluefruit.begin(); + Bluefruit.setTxPower(4); + Bluefruit.Periph.setConnectCallback(connect_callback); + Bluefruit.Periph.setDisconnectCallback(disconnect_callback); + + bledfu.begin(); + + bledis.setManufacturer("Tanner"); + bledis.setModel("Load Cell"); + bledis.begin(); + + bleuart.begin(); + + blebas.begin(); + blebas.write(100); + + startAdv(); + + Serial.println("Please use Adafruit's Bluefruit LE app to connect in UART mode"); +} + +void startAdv(void) +{ + // Advertising packet + Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE); + Bluefruit.Advertising.addTxPower(); + + // Include bleuart 128-bit uuid + Bluefruit.Advertising.addService(bleuart); + + // Secondary Scan Response packet (optional) + // Since there is no room for 'Name' in Advertising packet + Bluefruit.ScanResponse.addName(); + + /* Start Advertising + * - Enable auto advertising if disconnected + * - Interval: fast mode = 20 ms, slow mode = 152.5 ms + * - Timeout for fast mode is 30 seconds + * - Start(timeout) with timeout = 0 will advertise forever (until connected) + * + * For recommended advertising interval + * https://developer.apple.com/library/content/qa/qa1931/_index.html + */ + Bluefruit.Advertising.restartOnDisconnect(true); + Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms + Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode + Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds +} + +void loop() +{ + char buf[100] = ""; + + int reading = (int) scale.get_units(); + String message = String(reading) + "\n"; + message.toCharArray(buf, message.length()+1); + bleuart.write(buf, message.length()+1); + + delay(50); +} + +// callback invoked when central connects +void connect_callback(uint16_t conn_handle) +{ + // Get the reference to current connection + BLEConnection* connection = Bluefruit.Connection(conn_handle); + + char central_name[32] = { 0 }; + connection->getPeerName(central_name, sizeof(central_name)); + + Serial.print("Connected to "); + Serial.println(central_name); +} + +/** + * Callback invoked when a connection is dropped + * @param conn_handle connection where this event happens + * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h + */ +void disconnect_callback(uint16_t conn_handle, uint8_t reason) +{ + (void) conn_handle; + (void) reason; + + Serial.println(); + Serial.print("Disconnected, reason = 0x"); + Serial.println(reason, HEX); +} diff --git a/nrfutil/requirements.txt b/nrfutil/requirements.txt new file mode 100644 index 0000000..6378979 --- /dev/null +++ b/nrfutil/requirements.txt @@ -0,0 +1,5 @@ +adafruit-nrfutil==0.5.3.post16 +click==8.1.7 +ecdsa==0.19.0 +pyserial==3.5 +six==1.16.0