From ca0487e9400b7d92880fe40123fecfce7546d16f Mon Sep 17 00:00:00 2001 From: andrewchch Date: Fri, 16 Nov 2018 19:28:16 +1300 Subject: [PATCH] Initial commit --- .gitignore | 6 + .travis.yml | 67 +++++++++++ .vscode/extensions.json | 7 ++ include/README | 39 ++++++ lib/README | 46 +++++++ platformio.ini | 14 +++ src/data.cpp | 53 +++++++++ src/data.h | 16 +++ src/main.cpp | 257 ++++++++++++++++++++++++++++++++++++++++ test/README | 11 ++ 10 files changed, 516 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 .vscode/extensions.json create mode 100644 include/README create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 src/data.cpp create mode 100644 src/data.h create mode 100644 src/main.cpp create mode 100644 test/README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2de98ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.pio +.pioenvs +.piolibdeps +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7c486f1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,67 @@ +# Continuous Integration (CI) is the practice, in software +# engineering, of merging all developer working copies with a shared mainline +# several times a day < https://docs.platformio.org/page/ci/index.html > +# +# Documentation: +# +# * Travis CI Embedded Builds with PlatformIO +# < https://docs.travis-ci.com/user/integration/platformio/ > +# +# * PlatformIO integration with Travis CI +# < https://docs.platformio.org/page/ci/travis.html > +# +# * User Guide for `platformio ci` command +# < https://docs.platformio.org/page/userguide/cmd_ci.html > +# +# +# Please choose one of the following templates (proposed below) and uncomment +# it (remove "# " before each line) or use own configuration according to the +# Travis CI documentation (see above). +# + + +# +# Template #1: General project. Test it using existing `platformio.ini`. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio run + + +# +# Template #2: The project is intended to be used as a library with examples. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# env: +# - PLATFORMIO_CI_SRC=path/to/test/file.c +# - PLATFORMIO_CI_SRC=examples/file.ino +# - PLATFORMIO_CI_SRC=path/to/test/directory +# +# install: +# - pip install -U platformio +# - platformio update +# +# script: +# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..272828b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ] +} \ No newline at end of file diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..4b30716 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,14 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +board = esp32dev +framework = arduino diff --git a/src/data.cpp b/src/data.cpp new file mode 100644 index 0000000..fb8aeaf --- /dev/null +++ b/src/data.cpp @@ -0,0 +1,53 @@ +#include +#include +#include +#include "data.h" + +inline int min(int a, int b) { return ((a)<(b) ? (a) : (b)); } + +/* + +The data schema is: + +- 2 bytes sequencing header: + - byte 1: stroke number (0-255, cycles back to 0) + - byte 2: reading number (0-255 max, will give 2.5 seconds at 50Hz) +- 18 bytes of data + +totalling the maximum of 20 bytes per GATT packet. + +We'll still break down + +*/ + +void sendData (uint8_t data[], int stroke_index, int size, BLECharacteristic* pCharacteristic) { + int index = 0; + int tx_index = 0; + int packet_index = 0; + uint8_t tx_buffer[BLE_MAX_BYTES]; + + // TODO: need to add current time (millis) and length of packet to buffer + + while (index < size) { + // Set the header bytes in the tx_buffer + packet_index += 1; + tx_buffer[0] = stroke_index; + tx_buffer[1] = packet_index; + tx_index = BLE_HEADER_BYTES; + + // Transfer a chunk of payload data to the tx_buffer + for (int i=index; isetValue(tx_buffer, BLE_MAX_BYTES); + pCharacteristic->indicate(); + + // Increment the readings buffer index + index += BLE_PAYLOAD_BYTES; + + delay(20); // bluetooth stack will go into congestion, if too many packets are sent + } +} diff --git a/src/data.h b/src/data.h new file mode 100644 index 0000000..861d634 --- /dev/null +++ b/src/data.h @@ -0,0 +1,16 @@ + + +#ifndef HEADER_DATA +#define HEADER_DATA + +#define BLE_DATA_LENGTH 2 +#define BLE_HEADER_BYTES 2 +#define BLE_MAX_BYTES 20 +#define BLE_PAYLOAD_BYTES 18 +#define MIN_READINGS 50 +#define MAX_READINGS 100 + +//Prototype for data helper functions found in data.cpp +void sendData (uint8_t data[], int stroke_index, int size, BLECharacteristic* pCharacteristic); + +#endif diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..863e373 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,257 @@ +/* + * From https://github.com/G6EJD/ESP32-ADC-Accuracy-Improvement-function/blob/master/ESP32_ADC_Read_Voltage_Accurate.ino + */ + +#include +#include +#include +#include +#include +#include +#include + +// Local includes +#include "data.h" + +TaskHandle_t Task1, Task2; +SemaphoreHandle_t semaphore; + +double reading = 0; +double VCC = 3.14; + +// Declaration for task functions +void gatherData( void * parameter ); + + +BLEServer* pServer = NULL; +BLECharacteristic* pCharacteristic = NULL; +bool deviceConnected = false; +bool oldDeviceConnected = false; +uint8_t ble_data[BLE_DATA_LENGTH]; +uint8_t readings[MAX_READINGS * BLE_DATA_LENGTH]; +int num_readings = 0; +uint8_t readings_buffer[MAX_READINGS * BLE_DATA_LENGTH]; +int num_buffer_readings = 0; +int num_return_readings = 0; +int stroke_index = 0; +int READING_INTERVAL_MS = 20; // Interval between readings +float READING_THRESHOLD_KGS = 0.1; +float NUM_RETURN_READINGS = 3; // the number of readings below threshold before we trigger a return and start sending BLE stroke data +uint32_t stroke_start; + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + + +class MyServerCallbacks: public BLEServerCallbacks { + void onConnect(BLEServer* pServer) { + deviceConnected = true; + }; + + void onDisconnect(BLEServer* pServer) { + deviceConnected = false; + } +}; + +void setup() { + Serial.begin(115200); + /* + analogReadResolution(12); // Sets the sample bits and read resolution, default is 12-bit (0 - 4095), range is 9 - 12 bits + analogSetWidth(12); // Sets the sample bits and read resolution, default is 12-bit (0 - 4095), range is 9 - 12 bits + // 9-bit gives an ADC range of 0-511 + // 10-bit gives an ADC range of 0-1023 + // 11-bit gives an ADC range of 0-2047 + // 12-bit gives an ADC range of 0-4095 + analogSetCycles(8); // Set number of cycles per sample, default is 8 and provides an optimal result, range is 1 - 255 + analogSetSamples(1); // Set number of samples in the range, default is 1, it has an effect on sensitivity has been multiplied + analogSetClockDiv(1); // Set the divider for the ADC clock, default is 1, range is 1 - 255 + analogSetAttenuation(ADC_11db); // Sets the input attenuation for ALL ADC inputs, default is ADC_11db, range is ADC_0db, ADC_2_5db, ADC_6db, ADC_11db + analogSetPinAttenuation(VP,ADC_11db); // Sets the input attenuation, default is ADC_11db, range is ADC_0db, ADC_2_5db, ADC_6db, ADC_11db + // ADC_0db provides no attenuation so IN/OUT = 1 / 1 an input of 3 volts remains at 3 volts before ADC measurement + // ADC_2_5db provides an attenuation so that IN/OUT = 1 / 1.34 an input of 3 volts is reduced to 2.238 volts before ADC measurement + // ADC_6db provides an attenuation so that IN/OUT = 1 / 2 an input of 3 volts is reduced to 1.500 volts before ADC measurement + // ADC_11db provides an attenuation so that IN/OUT = 1 / 3.6 an input of 3 volts is reduced to 0.833 volts before ADC measurement + adcAttachPin(VP); // Attach a pin to ADC (also clears any other analog mode that could be on), returns TRUE/FALSE result + adcStart(VP); // Starts an ADC conversion on attached pin's bus + adcBusy(VP); // Check if conversion on the pin's ADC bus is currently running, returns TRUE/FALSE result + adcEnd(VP); // Get the result of the conversion (will wait if it have not finished), returns 16-bit integer result + */ + + semaphore = xSemaphoreCreateMutex(); + +// A viewer suggested to use : &codeForTask1, because his ESP crashed + + // Gathering data on core 1 + xTaskCreatePinnedToCore( + &gatherData, /* Function to implement the task */ + "dataGathering", /* Name of the task */ + 4096, /* Stack size in words */ + NULL, /* Task input parameter */ + 0, /* priority */ + &Task1, /* Task handle. */ + 1); /* Core where the task should run */ + + delay(500); // needed to start-up task 1 + + /* + xTaskCreatePinnedToCore( + sendDataViaBLE, + "BLEcomms", + 1000, + NULL, + 1, + &Task2, + 0); + */ + + // Set up the analog read pin + //adcAttachPin(13); + //analogSetClockDiv(255); // 1338mS + + // Create the BLE Device + BLEDevice::init("PowerLogger"); + + // Create the BLE Server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new MyServerCallbacks()); + + // Create the BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create a BLE Characteristic + pCharacteristic = pService->createCharacteristic( + CHARACTERISTIC_UUID, + BLECharacteristic::PROPERTY_NOTIFY || + BLECharacteristic::PROPERTY_INDICATE || + BLECharacteristic::PROPERTY_WRITE || + BLECharacteristic::PROPERTY_READ + ); + + // https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml + // Create a BLE Descriptor for clients to notify us that someone is listening + BLE2902 *ble2902 = new BLE2902(); + ble2902->setNotifications(true); + pCharacteristic->addDescriptor(ble2902); + + // Start the service + pService->start(); + + // Start advertising + pServer->getAdvertising()->start(); + Serial.println("Waiting a client connection to notify..."); +} + +double readVoltage(byte pin){ + double reading = analogRead(pin); // Reference voltage is 3v3 so maximum reading is 3v3 = 4095 in range 0 to 4095 + if(reading < 1 || reading > 4095) return 0; + // return -0.000000000009824 * pow(reading,3) + 0.000000016557283 * pow(reading,2) + 0.000854596860691 * reading + 0.065440348345433; + return -0.000000000000016 * pow(reading,4) + 0.000000000118171 * pow(reading,3)- 0.000000301211691 * pow(reading,2)+ 0.001109019271794 * reading + 0.034143524634089; +} // Added an improved polynomial, use either, comment out as required + +double voltageToOhms (double voltage) { + if (voltage <= 0) return DBL_MAX; + return 330.0 * ((VCC/voltage) - 1.0); +} + +double ohmsToKgs (double ohms) { + if (ohms == DBL_MAX) return 0; + return 52952 * pow(ohms,-1.55); +} + +void doubleToIntArray(double val) { + uint16_t tempValue; + // multiply by 100 to get 2 digits mantissa and convert into uint16_t + tempValue = (uint16_t)(val * 100); + // set LSB of array + ble_data[0] = tempValue; + // set MSB of characteristic + ble_data[1] = tempValue>>8; +} + +/* + * We're going to store all readings until the force drops below a given threshhold, and then send all readings at once. + */ +void gatherData( void * parameter ) +{ + for (;;) { + double voltage = readVoltage(36); + double ohms = voltageToOhms(voltage); + reading = ohmsToKgs(ohms); + + // Append to the readings array + if (reading > READING_THRESHOLD_KGS) { + doubleToIntArray(reading); // populates ble_data + for (int i=0; i= NUM_RETURN_READINGS) { + xSemaphoreTake( semaphore, portMAX_DELAY ); + + // Transfer all available readings to a buffer so we can start sending them and continue to accumulate + // readings for the next batch + int index = 0; + for (int r=0; rstartAdvertising(); // restart advertising + Serial.println("start advertising"); + oldDeviceConnected = deviceConnected; + } + // connecting + if (deviceConnected && !oldDeviceConnected) { + // do stuff here on connecting + oldDeviceConnected = deviceConnected; + } +} + +/* ADC readings v voltage + * y = -0.000000000009824x3 + 0.000000016557283x2 + 0.000854596860691x + 0.065440348345433 + // Polynomial curve match, based on raw data thus: + * 464 0.5 + * 1088 1.0 + * 1707 1.5 + * 2331 2.0 + * 2951 2.5 + * 3775 3.0 + * + */ + +// See: https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-adc.h diff --git a/test/README b/test/README new file mode 100644 index 0000000..df5066e --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html