From aa98c2cecdc53f3062aba0c029fd41f4277e10a4 Mon Sep 17 00:00:00 2001 From: Thijs van Liempd Date: Thu, 30 May 2024 14:41:03 +0200 Subject: [PATCH 1/3] appears to work someone else has already gone through the trouble of porting the ArduinoBLE library to STM32, all i did was copy the NanoIOT headers and added some minor preamble. Also, i chose to 'fix'(?) a compiler warning regarding a zero-sized char array for setting MTU size in (presumably JSON code of) experiment.cpp NOTE: personally, i would rename the NanoIOT.h header and cpp, be the 'general/basic' ArduinoBLE implementation, then one could delete the copied code from the new STM32 header & cpp, and just refer to the (former) NanoIOT header. --- src/experiment.cpp | 2 +- src/phyphoxBLE_NRF52.h | 4 +- src/phyphoxBLE_STM32.cpp | 389 +++++++++++++++++++++++++++++++++++++++ src/phyphoxBLE_STM32.h | 84 +++++++++ src/phyphoxBle.h | 2 + 5 files changed, 479 insertions(+), 2 deletions(-) create mode 100644 src/phyphoxBLE_STM32.cpp create mode 100644 src/phyphoxBLE_STM32.h diff --git a/src/experiment.cpp b/src/experiment.cpp index 90ca7e1..3e2885a 100644 --- a/src/experiment.cpp +++ b/src/experiment.cpp @@ -154,7 +154,7 @@ void PhyphoxBleExperiment::getFirstBytes(char *buffArray, const char *DEVICENAME strcat(buffArray, DEVICENAME); if(MTU!=20){ - char add[0]; + char add[10]; sprintf(add, "\" mtu=\"%i", MTU); strcat(buffArray, add); } diff --git a/src/phyphoxBLE_NRF52.h b/src/phyphoxBLE_NRF52.h index 4167272..e6c8167 100644 --- a/src/phyphoxBLE_NRF52.h +++ b/src/phyphoxBLE_NRF52.h @@ -1,6 +1,8 @@ #ifndef PHYPHOXBLE_NRF52_H #define PHYPHOXBLE_NRF52_H -#define NDEBUG +#if !defined(NDEBUG) && defined(ARDUINO_ARCH_MBED) + #define NDEBUG +#endif #include #include diff --git a/src/phyphoxBLE_STM32.cpp b/src/phyphoxBLE_STM32.cpp new file mode 100644 index 0000000..c8917ec --- /dev/null +++ b/src/phyphoxBLE_STM32.cpp @@ -0,0 +1,389 @@ +#if defined(ARDUINO_ARCH_STM32) + +#include "phyphoxBLE_STM32.h" +#include "Arduino.h" +#include + +#if defined(ARDUINO_STEVAL_MKBOXPRO) + /* STEVAL-MKBOXPRO */ + SPIClass SpiHCI(PA7, PA6, PA5); + HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_LP, PA2, PB11, PD4, 1000000, SPI_MODE3); + #if !defined(FAKE_BLELOCALDEVICE) + BLELocalDevice BLEObj(&HCISpiTransport); + BLELocalDevice& BLE = BLEObj; + #endif +#elif defined(ARDUINO_STEVAL_MKSBOX1V1) + /* STEVAL-MKSBOX1V1 */ + SPIClass SpiHCI(PC3, PD3, PD1); + HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_1S, PD0, PD4, PA8, 1000000, SPI_MODE1); + #if !defined(FAKE_BLELOCALDEVICE) + BLELocalDevice BLEObj(&HCISpiTransport); + BLELocalDevice& BLE = BLEObj; + #endif +#elif defined(ARDUINO_B_L475E_IOT01A) || defined(ARDUINO_B_L4S5I_IOT01A) + /* B-L475E-IOT01A1 or B_L4S5I_IOT01A */ + SPIClass SpiHCI(PC12, PC11, PC10); + HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, PD13, PE6, PA8, 8000000, SPI_MODE0); + #if !defined(FAKE_BLELOCALDEVICE) + BLELocalDevice BLEObj(&HCISpiTransport); + BLELocalDevice& BLE = BLEObj; + #endif +#elif defined(ARDUINO_NUCLEO_WB15CC) || defined(ARDUINO_P_NUCLEO_WB55RG) ||\ + defined(ARDUINO_STM32WB5MM_DK) || defined(ARDUINO_P_NUCLEO_WB55_USB_DONGLE) + HCISharedMemTransportClass HCISharedMemTransport; + #if !defined(FAKE_BLELOCALDEVICE) + BLELocalDevice BLEObj(&HCISharedMemTransport); + BLELocalDevice& BLE = BLEObj; + #endif +#else + /* Shield IDB05A2 with SPI clock on D3 */ + SPIClass SpiHCI(D11, D12, D3); + HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0); + #if !defined(FAKE_BLELOCALDEVICE) + BLELocalDevice BLEObj(&HCISpiTransport); + BLELocalDevice& BLE = BLEObj; + #endif + /* Shield IDB05A2 with SPI clock on D13 */ + // #define SpiHCI SPI + // HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0); + // #if !defined(FAKE_BLELOCALDEVICE) + // BLELocalDevice BLEObj(&HCISpiTransport); + // BLELocalDevice& BLE = BLEObj; + // #endif + /* Shield IDB05A1 with SPI clock on D3 */ + // SPIClass SpiHCI(D11, D12, D3); + // HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0); + // #if !defined(FAKE_BLELOCALDEVICE) + // BLELocalDevice BLEObj(&HCISpiTransport); + // BLELocalDevice& BLE = BLEObj; + // #endif + /* Shield IDB05A1 with SPI clock on D13 */ + // #define SpiHCI SPI + // HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0); + // #if !defined(FAKE_BLELOCALDEVICE) + // BLELocalDevice BLEObj(&HCISpiTransport); + // BLELocalDevice& BLE = BLEObj; + // #endif + /* Shield BNRG2A1 with SPI clock on D3 */ + // SPIClass SpiHCI(D11, D12, D3); + // HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1); + // #if !defined(FAKE_BLELOCALDEVICE) + // BLELocalDevice BLEObj(&HCISpiTransport); + // BLELocalDevice& BLE = BLEObj; + // #endif + /* Shield BNRG2A1 with SPI clock on D13 */ + // #define SpiHCI SPI + // HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1); + // #if !defined(FAKE_BLELOCALDEVICE) + // BLELocalDevice BLEObj(&HCISpiTransport); + // BLELocalDevice& BLE = BLEObj; + // #endif +#endif + +BLEService PhyphoxBLE::phyphoxExperimentService{phyphoxBleExperimentServiceUUID}; // create service +BLECharacteristic PhyphoxBLE::experimentCharacteristic{phyphoxBleExperimentCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; +BLECharacteristic PhyphoxBLE::controlCharacteristic{phyphoxBleExperimentControlCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; +BLECharacteristic PhyphoxBLE::eventCharacteristic{phyphoxBleEventCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; + +BLEService PhyphoxBLE::phyphoxDataService{phyphoxBleDataServiceUUID}; // create service +BLECharacteristic PhyphoxBLE::dataCharacteristic{phyphoxBleDataCharacteristicUUID, BLERead | BLEWrite | BLENotify, 20, false}; +BLECharacteristic PhyphoxBLE::configCharacteristic{phyphoxBleConfigCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; + +uint16_t PhyphoxBLE::minConInterval = 6; //7.5ms +uint16_t PhyphoxBLE::maxConInterval = 24; //30ms +uint16_t PhyphoxBLE::slaveLatency = 0; +uint16_t PhyphoxBLE::timeout = 50; + +uint16_t PhyphoxBLE::MTU = 20; +uint16_t PhyphoxBleExperiment::MTU = 20; + +int64_t PhyphoxBLE::experimentTime = NULL; +int64_t PhyphoxBLE::systemTime = NULL; +uint8_t PhyphoxBLE::eventType = NULL; + +uint8_t* PhyphoxBLE::data = nullptr; //this pointer points to the data the user wants to write in the characteristic +uint8_t* PhyphoxBLE::p_exp = nullptr; //this pointer will point to the byte array which holds an experiment + +size_t PhyphoxBLE::expLen = 0; //try o avoid this maybe use std::array or std::vector +const int maxExperimentSize = 6000; +uint8_t storage[maxExperimentSize]; +uint8_t PhyphoxBLE::eventData[17]={0}; +//uint8_t eventData[17]; +char *PhyphoxBLE::EXPARRAY=(char*)storage; + +void(*PhyphoxBLE::configHandler)() = nullptr; +void(*PhyphoxBLE::experimentEventHandler)() = nullptr; + +void PhyphoxBLE::start(const char* DEVICE_NAME, uint8_t* exp_pointer, size_t len){ + p_exp = exp_pointer; + expLen = len; + start(DEVICE_NAME); +} + +void PhyphoxBLE::start(uint8_t* exp_pointer, size_t len){ + p_exp = exp_pointer; + expLen = len; + start(); +} + +void PhyphoxBLE::start(const char* DEVICE_NAME) +{ + deviceName = DEVICE_NAME; + + controlCharacteristic.setEventHandler(BLEWritten, controlCharacteristicWritten); + eventCharacteristic.setEventHandler(BLEWritten, eventCharacteristicWritten); + configCharacteristic.setEventHandler(BLEWritten, configCharacteristicWritten); + + if(p_exp == nullptr){ + + PhyphoxBleExperiment defaultExperiment; + + //View + PhyphoxBleExperiment::View firstView; + + //Graph + PhyphoxBleExperiment::Graph firstGraph; //Create graph which will plot random numbers over time + firstGraph.setChannel(0,1); + + firstView.addElement(firstGraph); + defaultExperiment.addView(firstView); + + addExperiment(defaultExperiment); + } + + + if(!BLE.begin()) { Serial.println("failed to BLE.begin()!"); } + BLE.setLocalName(DEVICE_NAME); + BLE.setAdvertisedService(phyphoxExperimentService); + //BLE.setAdvertisedService(phyphoxDataService); + + // add the characteristics to the service + phyphoxExperimentService.addCharacteristic(experimentCharacteristic); + phyphoxExperimentService.addCharacteristic(controlCharacteristic); + phyphoxExperimentService.addCharacteristic(eventCharacteristic); + phyphoxDataService.addCharacteristic(configCharacteristic); + phyphoxDataService.addCharacteristic(dataCharacteristic); + + // add the service + BLE.addService(phyphoxExperimentService); + BLE.addService(phyphoxDataService); + + // set connection parameter + BLE.setConnectionInterval(minConInterval, maxConInterval); + + // start advertising + BLE.advertise(); + +} + +void PhyphoxBLE::start() { + PhyphoxBLE::start("phyphox-Arduino"); +} + +void PhyphoxBLE::poll() +{ + BLE.poll(); +} + +void PhyphoxBLE::poll(int timeout) +{ + BLE.poll(timeout); +} + +void PhyphoxBLE::read(uint8_t *arrayPointer, unsigned int arraySize) +{ + configCharacteristic.readValue(arrayPointer, arraySize); +} + +void PhyphoxBLE::read(float& f) +{ + uint8_t readDATA[4]; + configCharacteristic.readValue(readDATA, 4); + memcpy(&f,&readDATA[0],4); +} + +void PhyphoxBLE::read(float& f1, float& f2) +{ + uint8_t readDATA[8]; + configCharacteristic.readValue(readDATA, 8); + memcpy(&f1,readDATA,4); + memcpy(&f2,readDATA+4,4); +} +void PhyphoxBLE::read(float& f1, float& f2, float& f3) +{ + uint8_t readDATA[12]; + configCharacteristic.readValue(readDATA, 12); + memcpy(&f1,readDATA,4); + memcpy(&f2,readDATA+4,4); + memcpy(&f3,readDATA+8,4); +} +void PhyphoxBLE::read(float& f1, float& f2, float& f3, float& f4) +{ + uint8_t readDATA[16]; + configCharacteristic.readValue(readDATA, 16); + memcpy(&f1,readDATA,4); + memcpy(&f2,readDATA+4,4); + memcpy(&f3,readDATA+8,4); + memcpy(&f4,readDATA+12,4); +} +void PhyphoxBLE::read(float& f1, float& f2, float& f3, float& f4, float& f5) +{ + uint8_t readDATA[20]; + configCharacteristic.readValue(readDATA, 20); + memcpy(&f1,readDATA,4); + memcpy(&f2,readDATA+4,4); + memcpy(&f3,readDATA+8,4); + memcpy(&f4,readDATA+12,4); + memcpy(&f5,readDATA+16,4); +} + +void PhyphoxBLE::addExperiment(PhyphoxBleExperiment& exp) +{ + memset(EXPARRAY,0,maxExperimentSize); + + exp.getFirstBytes(EXPARRAY, deviceName); + + + for(uint8_t i=0;i(&value); + dataCharacteristic.writeValue(data,4); +} + + +void PhyphoxBLE::write(float& f1, float& f2) +{ + float array[2] = {f1, f2}; + data = reinterpret_cast(array); + dataCharacteristic.writeValue(data,8); +} + +void PhyphoxBLE::write(float& f1, float& f2, float& f3) +{ + float array[3] = {f1, f2, f3}; + data = reinterpret_cast(array); + dataCharacteristic.writeValue(data,12); +} + +void PhyphoxBLE::write(float& f1, float& f2, float& f3 , float& f4) +{ + float array[4] = {f1, f2, f3, f4}; + data = reinterpret_cast(array); + dataCharacteristic.writeValue(data,16); +} + +void PhyphoxBLE::write(float& f1, float& f2, float& f3 , float& f4, float& f5) +{ + float array[5] = {f1, f2, f3, f4, f5}; + data = reinterpret_cast(array); + dataCharacteristic.writeValue(data,20); +} + +void PhyphoxBLE::controlCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) { + byte value = 0; + characteristic.readValue(value); + if (value & 0x01) { + //sendexperiment + transferExperiment(); + } else { + //experiment transfered + } + +} + +void PhyphoxBLE::transferExperiment(){ + + BLE.stopAdvertise(); + + uint8_t* exp = p_exp; + size_t exp_len = expLen; + + uint8_t header[20] = {0}; //20 byte as standard package size for ble transfer + const char phyphox[] = "phyphox"; + uint32_t table[256]; + phyphoxBleCrc32::generate_table(table); + uint32_t checksum = phyphoxBleCrc32::update(table, 0, exp, exp_len); + size_t arrayLength = exp_len; + uint8_t experimentSizeArray[4] = {0}; + experimentSizeArray[0]= (arrayLength >> 24); + experimentSizeArray[1]= (arrayLength >> 16); + experimentSizeArray[2]= (arrayLength >> 8); + experimentSizeArray[3]= arrayLength; + + uint8_t checksumArray[4] = {0}; + checksumArray[0]= (checksum >> 24) & 0xFF; + checksumArray[1]= (checksum >> 16) & 0xFF; + checksumArray[2]= (checksum >> 8) & 0xFF; + checksumArray[3]= checksum & 0xFF; + + memcpy(&header[0],&phyphox[0],7); + memcpy(&header[0]+7,&experimentSizeArray[0],4); + memcpy(&header[0]+7+4,&checksumArray[0],4); + experimentCharacteristic.writeValue(header,sizeof(header)); + + for(size_t i = 0; i < exp_len/20; ++i){ + memcpy(&header[0],&exp[0]+i*20,20); + experimentCharacteristic.writeValue(header,sizeof(header)); + delay(5); + } + +if(exp_len%20 != 0){ + const size_t rest = exp_len%20; + uint8_t slice[rest]; + memcpy(&slice[0],&exp[0]+exp_len-rest,rest); + experimentCharacteristic.writeValue(slice,sizeof(slice)); + + delay(5); +} + + BLE.advertise(); +} + +void PhyphoxBLE::configCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic){ + if(configHandler!=nullptr){ + (*configHandler)(); + } +} + +void PhyphoxBLE::eventCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic){ + + uint8_t read_buffer[17]; + eventCharacteristic.readValue(read_buffer, 17); + + + memcpy(&eventData[0],read_buffer,17); + int64_t et,st; + memcpy(&et,&eventData[0]+1,8); + memcpy(&st,&eventData[0]+1+8,8); + PhyphoxBLE::eventType = eventData[0]; + PhyphoxBLE::systemTime = swap_int64(st); + PhyphoxBLE::experimentTime = swap_int64(et); + + if(experimentEventHandler!=nullptr){ + (*experimentEventHandler)(); + } +} + +void PhyphoxBLE::printXML(HardwareSerial* printer){ + printer->println(""); + for(int i =0; iprint(CHAR); + } + printer->println(""); +} +#endif diff --git a/src/phyphoxBLE_STM32.h b/src/phyphoxBLE_STM32.h new file mode 100644 index 0000000..94a95e9 --- /dev/null +++ b/src/phyphoxBLE_STM32.h @@ -0,0 +1,84 @@ +#ifndef PHYPHOXBLE_STM32_H +#define PHYPHOXBLE_STM32_H +#if !defined(NDEBUG) && defined(ARDUINO_ARCH_STM32) + #define NDEBUG +#endif + +#include + +#include +#include "phyphoxBleExperiment.h" + +class PhyphoxBLE +{ + private: + + static uint8_t data_package[20]; + + static void controlCharacteristicWritten(BLEDevice, BLECharacteristic); + static void eventCharacteristicWritten(BLEDevice, BLECharacteristic); + static void configCharacteristicWritten(BLEDevice, BLECharacteristic); + + static BLEService phyphoxExperimentService; + static BLECharacteristic experimentCharacteristic; + static BLECharacteristic controlCharacteristic; + static BLECharacteristic eventCharacteristic; + + static BLEService phyphoxDataService; + static BLECharacteristic dataCharacteristic; + static BLECharacteristic configCharacteristic; + + + static uint8_t* data; //this pointer points to the data the user wants to write in the characteristic + static uint8_t* p_exp; //this pointer will point to the byte array which holds an experiment + + static size_t expLen; //try o avoid this maybe use std::array or std::vector + static char *EXPARRAY; + + public: + + static void start(const char* DEVICE_NAME, uint8_t* p, size_t n = 0); + static void start(const char* DEVICE_NAME); + static void start(uint8_t* p, size_t n = 0); + static void start(); + + static void addExperiment(PhyphoxBleExperiment&); + static void transferExperiment(); + static void write(float&); + static void write(float&, float&); + static void write(float&, float&, float&); + static void write(float&, float&, float&, float&); + static void write(float&, float&, float&, float&, float&); + + static void read(uint8_t*, unsigned int); + static void read(float&); + static void read(float&, float&); + static void read(float&, float&, float&); + static void read(float&, float&, float&, float&); + static void read(float&, float&, float&, float&, float&); + + + static void poll(); + static void poll(int timeout); + + static void(*configHandler)(); + static void(*experimentEventHandler)(); + + static uint16_t minConInterval; + static uint16_t maxConInterval; + static uint16_t slaveLatency; + static uint16_t timeout; + static uint16_t MTU; + + static int64_t experimentTime; + static int64_t systemTime; + static uint8_t eventType; + + static uint8_t eventData[17]; + + static void printXML(HardwareSerial*); + +}; + + +#endif diff --git a/src/phyphoxBle.h b/src/phyphoxBle.h index 3578aba..909287a 100644 --- a/src/phyphoxBle.h +++ b/src/phyphoxBle.h @@ -56,6 +56,8 @@ static const char *deviceName = "phyphox-Arduino"; #elif defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_UNOR4_WIFI) #include #include "phyphoxBLE_NanoIOT.h" +#elif defined(ARDUINO_ARCH_STM32) + #include "phyphoxBLE_STM32.h" #else #error "Unsupported board selected!" #endif From bd866f589ee77e6365f6042927ca6fcbb1210df0 Mon Sep 17 00:00:00 2001 From: Thijs van Liempd Date: Thu, 30 May 2024 14:51:05 +0200 Subject: [PATCH 2/3] updated readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index bf79717..58d8ab8 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,14 @@ The purpose of this library is to use the phyphox app (see www.phyphox.org) to p - Arduino Uno R4 Wifi (see note below) - senseBox MCU with NINA-B31 module - ESP 32 +- STM32 (e.g. STM32WB55) Note: The Arduino Nano 33 IoT and the Arduino uno R4 are somewhat unusual. You will need to install the ArduinoBLE library to use it and you will need to call "PhyphoxBLE::poll()" periodically for it to work. Note: When using the NINA-B31 module you must call PhyphoxBLE::poll() periodically (in loop() ) or the library will not work. + The same applies to STM32 + +Note: to use STM32 BLE, you need the STM32duinoBLE library, and (at least for the STM32WBx5) the appropriate BLE stack (see Copro binaries) ## Concept of phyphox @@ -29,6 +33,7 @@ Alternatively, you can download this repository here as a zip file from github a You will may also need to install an BLE library specific to your board: - ArduinoBLE for the Arduino Nano 33 IoT +- STM32duinoBLE for an STM32 ## Usage From 7829c08f6e37d91ac6e2a377e2810caf3d9b4877 Mon Sep 17 00:00:00 2001 From: Thijs van Liempd Date: Thu, 20 Jun 2024 13:52:26 +0200 Subject: [PATCH 3/3] fix compiler warning an int64_t was initialized to NULL instead of 0, the compiler threw a warning --- src/phyphoxBLE_STM32.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/phyphoxBLE_STM32.cpp b/src/phyphoxBLE_STM32.cpp index c8917ec..fb8c955 100644 --- a/src/phyphoxBLE_STM32.cpp +++ b/src/phyphoxBLE_STM32.cpp @@ -97,9 +97,9 @@ uint16_t PhyphoxBLE::timeout = 50; uint16_t PhyphoxBLE::MTU = 20; uint16_t PhyphoxBleExperiment::MTU = 20; -int64_t PhyphoxBLE::experimentTime = NULL; -int64_t PhyphoxBLE::systemTime = NULL; -uint8_t PhyphoxBLE::eventType = NULL; +int64_t PhyphoxBLE::experimentTime = 0; +int64_t PhyphoxBLE::systemTime = 0; +uint8_t PhyphoxBLE::eventType = 0; uint8_t* PhyphoxBLE::data = nullptr; //this pointer points to the data the user wants to write in the characteristic uint8_t* PhyphoxBLE::p_exp = nullptr; //this pointer will point to the byte array which holds an experiment