From 1550c7d15d940b088af7e06d06666494972f481f Mon Sep 17 00:00:00 2001 From: Linar Yusupov Date: Thu, 29 Feb 2024 09:47:09 +0300 Subject: [PATCH] Academy: build of ArduinoBLE driver --- .github/workflows/main.yml | 5 + .../source/SoftRF/src/driver/Bluetooth.h | 4 +- .../source/SoftRF/src/driver/WiFi.cpp | 6 +- .../source/SoftRF/src/platform/RA4M1.cpp | 7 +- .../source/SoftRF/src/platform/RA4M1.h | 2 + .../src/platform/bluetooth/ArduinoBLE.cpp | 315 ++++++++++++++++++ .../src/platform/bluetooth/ArduinoBLE.h | 44 +++ 7 files changed, 378 insertions(+), 5 deletions(-) create mode 100644 software/firmware/source/SoftRF/src/platform/bluetooth/ArduinoBLE.cpp create mode 100644 software/firmware/source/SoftRF/src/platform/bluetooth/ArduinoBLE.h diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cedfb289a..93ddd2d91 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -47,6 +47,7 @@ jobs: env: ARDUINO_IDE_VERSION: 1.8.13 ARDUINO_CLI_VERSION: 0.34.2 + ARDUINO_BLE_VERSION: 1.3.6 ENERGIA_IDE_VERSION: 1.8.10E23 BOARD: ${{ matrix.board }} @@ -321,6 +322,10 @@ jobs: arduino --pref "boardsmanager.additional.urls=https://downloads.arduino.cc/packages/package_index.json" --save-prefs ; arduino --install-boards arduino:renesas_uno:1.0.5 ; arduino --board $BOARD --save-prefs ; + wget https://github.com/arduino-libraries/ArduinoBLE/archive/refs/tags/${ARDUINO_BLE_VERSION}.tar.gz ; + tar xzf ${ARDUINO_BLE_VERSION}.tar.gz ; + rm ${ARDUINO_BLE_VERSION}.tar.gz ; + mv ArduinoBLE-${ARDUINO_BLE_VERSION} $HOME/Arduino/libraries/ ; cd $GITHUB_WORKSPACE ; fi if [[ "$BOARD" =~ "rp2040:rp2040:" ]]; then diff --git a/software/firmware/source/SoftRF/src/driver/Bluetooth.h b/software/firmware/source/SoftRF/src/driver/Bluetooth.h index f3c132afb..312d5d49f 100644 --- a/software/firmware/source/SoftRF/src/driver/Bluetooth.h +++ b/software/firmware/source/SoftRF/src/driver/Bluetooth.h @@ -37,6 +37,8 @@ enum #include "../platform/bluetooth/Bluefruit.h" #elif defined(ARDUINO_ARCH_RP2040) && defined(ARDUINO_RASPBERRY_PI_PICO_W) #include "../platform/bluetooth/BTstack.h" -#endif /* ESP32 or ARDUINO_ARCH_NRF52 or ARDUINO_ARCH_RP2040 */ +#elif defined(ARDUINO_ARCH_RENESAS) +#include "../platform/bluetooth/ArduinoBLE.h" +#endif /* ESP32 or NRF52 or RP2040 or RENESAS */ #endif /* BLUETOOTHHELPER_H */ diff --git a/software/firmware/source/SoftRF/src/driver/WiFi.cpp b/software/firmware/source/SoftRF/src/driver/WiFi.cpp index 7b46fba62..b828e1e85 100644 --- a/software/firmware/source/SoftRF/src/driver/WiFi.cpp +++ b/software/firmware/source/SoftRF/src/driver/WiFi.cpp @@ -133,13 +133,13 @@ void WiFi_setup() // ... Try to connect to WiFi station. WiFi.begin(station_ssid.c_str(), station_psk.c_str()); - // ... Pritn new SSID + // ... Print new SSID Serial.print(F("new SSID: ")); Serial.println(WiFi.SSID()); Serial.println(F("Wait for WiFi connection.")); - // ... Give Wi-Fi 10-20 seconds to connect to station. + // ... Give Wi-Fi 10-20 seconds to connect to an AP. unsigned long startTime = millis(); while (WiFi.status() != WL_CONNECTED && millis() - startTime < WIFI_STA_TIMEOUT) @@ -236,7 +236,7 @@ void WiFi_setup() Serial.println(F("Wait for WiFi connection.")); - // ... Give Wi-Fi 10-20 seconds to connect to station. + // ... Give Wi-Fi 10-20 seconds to connect to an AP. unsigned long startTime = millis(); while (WiFi.status() != WL_CONNECTED && millis() - startTime < WIFI_STA_TIMEOUT) diff --git a/software/firmware/source/SoftRF/src/platform/RA4M1.cpp b/software/firmware/source/SoftRF/src/platform/RA4M1.cpp index 869363a66..de5aecabb 100644 --- a/software/firmware/source/SoftRF/src/platform/RA4M1.cpp +++ b/software/firmware/source/SoftRF/src/platform/RA4M1.cpp @@ -29,6 +29,7 @@ #include "../driver/Baro.h" #include "../driver/Sound.h" #include "../driver/Battery.h" +#include "../driver/Bluetooth.h" #include "../protocol/data/NMEA.h" #include "../protocol/data/GDL90.h" #include "../protocol/data/D1090.h" @@ -874,7 +875,11 @@ const SoC_ops_t RA4M1_ops = { RA4M1_SPI_begin, RA4M1_swSer_begin, RA4M1_swSer_enableRx, - NULL, /* RA4M1 has no built-in Bluetooth */ +#if !defined(EXCLUDE_BLUETOOTH) + &RA4M1_Bluetooth_ops, +#else + NULL, +#endif /* EXCLUDE_BLUETOOTH */ &RA4M1_USBSerial_ops, NULL, RA4M1_Display_setup, diff --git a/software/firmware/source/SoftRF/src/platform/RA4M1.h b/software/firmware/source/SoftRF/src/platform/RA4M1.h index 952d17f6b..5228c1d3c 100644 --- a/software/firmware/source/SoftRF/src/platform/RA4M1.h +++ b/software/firmware/source/SoftRF/src/platform/RA4M1.h @@ -130,6 +130,7 @@ struct rst_info { #if defined(ARDUINO_UNOR4_MINIMA) #define EXCLUDE_WIFI +#define EXCLUDE_BLUETOOTH #elif defined(ARDUINO_UNOR4_WIFI) #define USE_ARDUINO_WIFI #define EXCLUDE_OTA @@ -137,6 +138,7 @@ struct rst_info { #define USE_WIFI_CUSTOM true #include #define Serial_setDebugOutput(x) ({}) +//#define EXCLUDE_BLUETOOTH #endif #define EXCLUDE_CC13XX diff --git a/software/firmware/source/SoftRF/src/platform/bluetooth/ArduinoBLE.cpp b/software/firmware/source/SoftRF/src/platform/bluetooth/ArduinoBLE.cpp new file mode 100644 index 000000000..37cc3e1c9 --- /dev/null +++ b/software/firmware/source/SoftRF/src/platform/bluetooth/ArduinoBLE.cpp @@ -0,0 +1,315 @@ +/* + * ArduinoBLE.cpp + * Copyright (C) 2024 Linar Yusupov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#if defined(ARDUINO_ARCH_RENESAS) + +#include "../../system/SoC.h" + +#if !defined(EXCLUDE_BLUETOOTH) + +#include + +#include + +#include "../../driver/EEPROM.h" +#include "../../driver/Bluetooth.h" +#include "../../driver/WiFi.h" +#include "../../driver/Battery.h" + +// #include // TODO +#define ARDUINO_RA4M1_RELEASE "1.1.0" + +bool deviceConnected = false; +bool oldDeviceConnected = false; + +RingBufferN BLE_FIFO_TX = RingBufferN(); +RingBufferN BLE_FIFO_RX = RingBufferN(); + +String BT_name = HOSTNAME; + +static unsigned long BLE_Notify_TimeMarker = 0; +static unsigned long BLE_Advertising_TimeMarker = 0; + +BLEService UARTService(UART_SERVICE_UUID16); +BLEService BATService (UUID16_SVC_BATTERY); + +BLECharacteristic UARTCharacteristic(UART_CHARACTERISTIC_UUID16, + BLERead | BLEWriteWithoutResponse | BLENotify, + BLE_MAX_WRITE_CHUNK_SIZE); +BLEDescriptor UARTDescriptor("2901", "HMSoft"); + +BLEUnsignedCharCharacteristic BATCharacteristic(UUID16_CHR_BATTERY_LEVEL, + BLERead | BLENotify); + +BLEService DevInfoService(UUID16_SVC_DEVICE_INFORMATION); +BLEStringCharacteristic ModelCharacteristic(UUID16_CHR_MODEL_NUMBER_STRING, + BLERead, BLE_MAX_WRITE_CHUNK_SIZE); +BLEStringCharacteristic SerialCharacteristic(UUID16_CHR_SERIAL_NUMBER_STRING, + BLERead, BLE_MAX_WRITE_CHUNK_SIZE); +BLEStringCharacteristic FirmwareCharacteristic(UUID16_CHR_FIRMWARE_REVISION_STRING, + BLERead, BLE_MAX_WRITE_CHUNK_SIZE); +BLEStringCharacteristic HardwareCharacteristic(UUID16_CHR_HARDWARE_REVISION_STRING, + BLERead, BLE_MAX_WRITE_CHUNK_SIZE); +BLEStringCharacteristic SoftwareCharacteristic(UUID16_CHR_SOFTWARE_REVISION_STRING, + BLERead, BLE_MAX_WRITE_CHUNK_SIZE); +BLEStringCharacteristic ManufacturerCharacteristic(UUID16_CHR_MANUFACTURER_NAME_STRING, + BLERead, BLE_MAX_WRITE_CHUNK_SIZE); + +void onConnect(BLEDevice central) { +// Serial.print("Connected event, central: "); +// Serial.println(central.address()); + + deviceConnected = true; +}; + +void onDisconnect(BLEDevice central) { +// Serial.print("Disconnected event, central: "); +// Serial.println(central.address()); + + deviceConnected = false; + BLE_Advertising_TimeMarker = millis(); +} + +void UARTCharacteristicWritten(BLEDevice central, + BLECharacteristic characteristic) { + size_t valueLength = characteristic.valueLength(); + + if (valueLength > 0) { + char buf[BLE_MAX_WRITE_CHUNK_SIZE]; + size_t len = sizeof(buf); + + characteristic.readValue(buf, len); + + if (valueLength > len) { valueLength = len; } + + size_t size = BLE_FIFO_RX.availableForStore(); + size = (size < valueLength) ? size : valueLength; + for (size_t i = 0; i < size; i++) { + BLE_FIFO_RX.store_char(buf[i]); + } + } +} + +static void RA4M1_Bluetooth_setup() +{ + BT_name += "-"; + BT_name += String(SoC->getChipId() & 0x00FFFFFFU, HEX); + + switch (settings->bluetooth) + { + case BLUETOOTH_LE_HM10_SERIAL: + { + BLE.begin(); + + char LocalName[BLE_MAX_WRITE_CHUNK_SIZE]; + snprintf(LocalName, sizeof(LocalName), "%s-LE", BT_name.c_str()); + BLE.setLocalName(LocalName); + BLE.setDeviceName(LocalName); + + BLE.setAdvertisedService(UARTService); + UARTCharacteristic.setEventHandler(BLEWritten, UARTCharacteristicWritten); + UARTCharacteristic.addDescriptor(UARTDescriptor); + UARTService.addCharacteristic(UARTCharacteristic); + BLE.addService(UARTService); + +// BLE.setAdvertisedService(BATService); + BATService.addCharacteristic(BATCharacteristic); + BLE.addService(BATService); + + const char *Model = hw_info.model == SOFTRF_MODEL_STANDALONE ? "Standalone Edition" : + hw_info.model == SOFTRF_MODEL_PRIME_MK2 ? "Prime Mark II" : + hw_info.model == SOFTRF_MODEL_PRIME_MK3 ? "Prime Mark III" : + hw_info.model == SOFTRF_MODEL_HAM ? "Ham Edition" : + hw_info.model == SOFTRF_MODEL_MIDI ? "Midi Edition" : + hw_info.model == SOFTRF_MODEL_ACADEMY ? "Academy Edition" : + "Unknown"; + char SerialNum[9]; + snprintf(SerialNum, sizeof(SerialNum), "%08X", SoC->getChipId()); + + const char *Firmware = "Arduino RA4M1 " ARDUINO_RA4M1_RELEASE; + + char Hardware[9]; + snprintf(Hardware, sizeof(Hardware), "%08X", hw_info.revision); + + const char *Manufacturer = SOFTRF_IDENT; + const char *Software = SOFTRF_FIRMWARE_VERSION; + + ModelCharacteristic. writeValue((char *) Model); + SerialCharacteristic. writeValue((char *) SerialNum); + FirmwareCharacteristic. writeValue((char *) Firmware); + HardwareCharacteristic. writeValue((char *) Hardware); + SoftwareCharacteristic. writeValue((char *) Software); + ManufacturerCharacteristic.writeValue((char *) Manufacturer); + + DevInfoService.addCharacteristic(ModelCharacteristic); + DevInfoService.addCharacteristic(SerialCharacteristic); + DevInfoService.addCharacteristic(FirmwareCharacteristic); + DevInfoService.addCharacteristic(HardwareCharacteristic); + DevInfoService.addCharacteristic(SoftwareCharacteristic); + DevInfoService.addCharacteristic(ManufacturerCharacteristic); + BLE.addService(DevInfoService); + + BLE.setEventHandler(BLEConnected, onConnect); + BLE.setEventHandler(BLEDisconnected, onDisconnect); + + BLE.advertise(); + + BLE_Advertising_TimeMarker = millis(); + } + break; + case BLUETOOTH_A2DP_SOURCE: + break; + case BLUETOOTH_NONE: + default: + break; + } +} + +static void RA4M1_Bluetooth_loop() +{ + switch (settings->bluetooth) + { + case BLUETOOTH_LE_HM10_SERIAL: + { + BLE.poll(); + + if (deviceConnected && (millis() - BLE_Notify_TimeMarker > 10)) { /* < 18000 baud */ + uint8_t chunk[BLE_MAX_WRITE_CHUNK_SIZE]; + + size_t size = BLE_FIFO_TX.available(); + size = size < BLE_MAX_WRITE_CHUNK_SIZE ? size : BLE_MAX_WRITE_CHUNK_SIZE; + + if (size > 0) { + for (int i=0; i < size; i++) { + chunk[i] = BLE_FIFO_TX.read_char(); + } + UARTCharacteristic.writeValue(chunk, size); + } + + BLE_Notify_TimeMarker = millis(); + } + // disconnecting + if (!deviceConnected && oldDeviceConnected && (millis() - BLE_Advertising_TimeMarker > 500) ) { + // give the bluetooth stack the chance to get things ready + + // BLE.advertise(); // restart advertising + + oldDeviceConnected = deviceConnected; + BLE_Advertising_TimeMarker = millis(); + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } + if (deviceConnected && isTimeToBattery()) { + uint8_t battery_level = Battery_charge(); + + BATCharacteristic.writeValue(battery_level); + } + } + break; + case BLUETOOTH_NONE: + case BLUETOOTH_SPP: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } +} + +static void RA4M1_Bluetooth_fini() +{ + /* TBD */ + + BLE.end(); +} + +static int RA4M1_Bluetooth_available() +{ + int rval = 0; + + switch (settings->bluetooth) + { + case BLUETOOTH_LE_HM10_SERIAL: + rval = BLE_FIFO_RX.available(); + break; + case BLUETOOTH_NONE: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } + + return rval; +} + +static int RA4M1_Bluetooth_read() +{ + int rval = -1; + + switch (settings->bluetooth) + { + case BLUETOOTH_LE_HM10_SERIAL: + rval = BLE_FIFO_RX.read_char(); + break; + case BLUETOOTH_NONE: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } + + return rval; +} + +static size_t RA4M1_Bluetooth_write(const uint8_t *buffer, size_t size) +{ + size_t rval = size; + + switch (settings->bluetooth) + { + case BLUETOOTH_LE_HM10_SERIAL: + { + size_t avail = BLE_FIFO_TX.availableForStore(); + if (size > avail) { + rval = avail; + } + for (size_t i = 0; i < rval; i++) { + BLE_FIFO_TX.store_char(buffer[i]); + } + } + break; + case BLUETOOTH_NONE: + case BLUETOOTH_A2DP_SOURCE: + default: + break; + } + + return rval; +} + +IODev_ops_t RA4M1_Bluetooth_ops = { + "RA4M1 Bluetooth", + RA4M1_Bluetooth_setup, + RA4M1_Bluetooth_loop, + RA4M1_Bluetooth_fini, + RA4M1_Bluetooth_available, + RA4M1_Bluetooth_read, + RA4M1_Bluetooth_write +}; + +#endif /* EXCLUDE_BLUETOOTH */ +#endif /* ARDUINO_ARCH_RENESAS */ diff --git a/software/firmware/source/SoftRF/src/platform/bluetooth/ArduinoBLE.h b/software/firmware/source/SoftRF/src/platform/bluetooth/ArduinoBLE.h new file mode 100644 index 000000000..861db542f --- /dev/null +++ b/software/firmware/source/SoftRF/src/platform/bluetooth/ArduinoBLE.h @@ -0,0 +1,44 @@ +/* + * ArduinoBLE.h + * Copyright (C) 2018-2024 Linar Yusupov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ARDUINOBLE_H +#define ARDUINOBLE_H + +#define UART_SERVICE_UUID16 "FFE0" +#define UART_CHARACTERISTIC_UUID16 "FFE1" + +#define UUID16_SVC_BATTERY "180F" +#define UUID16_CHR_BATTERY_LEVEL "2A19" + +#define UUID16_SVC_DEVICE_INFORMATION "180A" +#define UUID16_CHR_MODEL_NUMBER_STRING "2A24" +#define UUID16_CHR_SERIAL_NUMBER_STRING "2A25" +#define UUID16_CHR_FIRMWARE_REVISION_STRING "2A26" +#define UUID16_CHR_HARDWARE_REVISION_STRING "2A27" +#define UUID16_CHR_SOFTWARE_REVISION_STRING "2A28" +#define UUID16_CHR_MANUFACTURER_NAME_STRING "2A29" + +/* (FLAA x MAX_TRACKING_OBJECTS + GNGGA + GNRMC + FLAU) x 80 symbols */ +#define BLE_FIFO_TX_SIZE 256 /* TBD */ +#define BLE_FIFO_RX_SIZE 128 /* TBD */ + +#define BLE_MAX_WRITE_CHUNK_SIZE 20 + +extern IODev_ops_t RA4M1_Bluetooth_ops; + +#endif /* ARDUINOBLE_H */