From df911dcfb0ae7a509337daa5e8b656352beb778b Mon Sep 17 00:00:00 2001 From: Drew Bednar Date: Thu, 26 Aug 2021 17:35:04 -0400 Subject: [PATCH] working Arni controller --- bluetooth/esp32/arni/README.md | 28 +++ bluetooth/esp32/arni/ble_advertising.py | 93 ++++++++++ .../{ => arni}/ble_arni_odrive_controller.py | 29 +++- bluetooth/esp32/arni/robot.py | 164 ++++++++++++++++++ uart/odrive_uart.py | 90 ++++++++-- 5 files changed, 378 insertions(+), 26 deletions(-) create mode 100644 bluetooth/esp32/arni/README.md create mode 100644 bluetooth/esp32/arni/ble_advertising.py rename bluetooth/esp32/{ => arni}/ble_arni_odrive_controller.py (86%) create mode 100644 bluetooth/esp32/arni/robot.py diff --git a/bluetooth/esp32/arni/README.md b/bluetooth/esp32/arni/README.md new file mode 100644 index 0000000..98c778f --- /dev/null +++ b/bluetooth/esp32/arni/README.md @@ -0,0 +1,28 @@ +# Arni + +This code supports using an ESP32 and the Dabble ios app to control 2 hoverboard motors +driven by an Odrive. + +## Install + +``` +upy put bluetooth/esp32/arni/ble_advertising.py ble_advertising.py +upy put bluetooth/esp32/arni/robot.py robot.py +upy put bluetooth/esp32/ble_arni_odrive_controller.py main.py +``` + +Confirm with `upy ls` + +## Usage + +With the ESP32 and Odrive powered on open the Dabble and connect. This +does not support Joystick yet. This is a work in progress + +### Buttons + +|Key |Action | +|----------|---------------------------------------------------------| +|Select |Toggles Odrive idle / loop control states | +|Start |Performs an Odrive AXIS_STATE_ENCODER_OFFSET_CALIBRATION | +| | | +| | | \ No newline at end of file diff --git a/bluetooth/esp32/arni/ble_advertising.py b/bluetooth/esp32/arni/ble_advertising.py new file mode 100644 index 0000000..24433e7 --- /dev/null +++ b/bluetooth/esp32/arni/ble_advertising.py @@ -0,0 +1,93 @@ +# Helpers for generating BLE advertising payloads. + +from micropython import const +import struct +import bluetooth + +# Advertising payloads are repeated packets of the following form: +# 1 byte data length (N + 1) +# 1 byte type (see constants below) +# N bytes type-specific data + +_ADV_TYPE_FLAGS = const(0x01) +_ADV_TYPE_NAME = const(0x09) +_ADV_TYPE_UUID16_COMPLETE = const(0x3) +_ADV_TYPE_UUID32_COMPLETE = const(0x5) +_ADV_TYPE_UUID128_COMPLETE = const(0x7) +_ADV_TYPE_UUID16_MORE = const(0x2) +_ADV_TYPE_UUID32_MORE = const(0x4) +_ADV_TYPE_UUID128_MORE = const(0x6) +_ADV_TYPE_APPEARANCE = const(0x19) + + +# Generate a payload to be passed to gap_advertise(adv_data=...). +def advertising_payload(limited_disc=False, br_edr=False, name=None, services=None, appearance=0): + payload = bytearray() + + def _append(adv_type, value): + nonlocal payload + payload += struct.pack("BB", len(value) + 1, adv_type) + value + + _append( + _ADV_TYPE_FLAGS, + struct.pack("B", (0x01 if limited_disc else 0x02) + (0x18 if br_edr else 0x04)), + ) + + if name: + _append(_ADV_TYPE_NAME, name) + + if services: + for uuid in services: + b = bytes(uuid) + if len(b) == 2: + _append(_ADV_TYPE_UUID16_COMPLETE, b) + elif len(b) == 4: + _append(_ADV_TYPE_UUID32_COMPLETE, b) + elif len(b) == 16: + _append(_ADV_TYPE_UUID128_COMPLETE, b) + + # See org.bluetooth.characteristic.gap.appearance.xml + if appearance: + _append(_ADV_TYPE_APPEARANCE, struct.pack("