├── .gitignore ├── src ├── copyToMem.h ├── copyToMem.cpp ├── error.cpp ├── element.cpp ├── defines.h ├── exportdata.cpp ├── boards │ ├── NINAB31serial.h │ ├── phyphoxBLE_NINAB31.h │ ├── phyphoxBLE_NanoIOT.h │ ├── phyphoxBLE_STM32.h │ ├── phyphoxBLE_ESP32.h │ ├── phyphoxBLE_NRF52.h │ ├── NINAB31serial.cpp │ ├── phyphoxBLE_NINAB31.cpp │ ├── phyphoxBLE_NanoIOT.cpp │ ├── phyphoxBLE_STM32.cpp │ ├── phyphoxBLE_NRF52.cpp │ └── phyphoxBLE_ESP32.cpp ├── exportSet.cpp ├── view.cpp ├── infoField.cpp ├── separator.cpp ├── edit.cpp ├── value.cpp ├── subgraph.cpp ├── sensor.cpp ├── errorhandler.cpp ├── phyphoxBle.h ├── graph.cpp ├── phyphoxBleExperiment.h └── experiment.cpp ├── examples ├── randomNumbers │ └── randomNumbers.ino ├── getSystemAndEventTime │ └── getSystemAndEventTime.ino ├── connectionParameter │ └── connectionParameter.ino ├── readoutADC │ └── readoutADC.ino ├── getDataFromSmartphone │ └── getDataFromSmartphone.ino ├── getSensorDataFromSmartphone │ └── getSensorDataFromSmartphone.ino ├── rangefinder │ └── rangefinder.ino ├── multigraph │ └── multigraph.ino ├── CreateExperiment │ └── CreateExperiment.ino └── CO2kit │ └── CO2kit.ino ├── library.properties ├── COPYING.LESSER └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* -------------------------------------------------------------------------------- /src/copyToMem.h: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | 3 | void copyToMem(char **target, const char *data); -------------------------------------------------------------------------------- /src/copyToMem.cpp: -------------------------------------------------------------------------------- 1 | #include "copyToMem.h" 2 | 3 | void copyToMem(char **target, const char *data) { 4 | // if (*target != NULL) { 5 | // free(*target); // Causes crashes 6 | // } 7 | *target = (char*) malloc(sizeof(char) * (strlen(data)+1)); 8 | strcpy(*target, data); 9 | } -------------------------------------------------------------------------------- /src/error.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | void PhyphoxBleExperiment::Error::getBytes(char *buffArray) { 5 | strcat(buffArray,"\t\t\n"); 8 | strcat(buffArray,"\t\t\n"); 9 | } 10 | -------------------------------------------------------------------------------- /src/element.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | void PhyphoxBleExperiment::Element::setLabel(const char *l){ 5 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(l, 41, "setLabel") : ERROR; 6 | copyToMem(&LABEL, (std::string(l)).c_str()); 7 | } 8 | 9 | // void PhyphoxBleExperiment::Element::setLabel(const char *l){ 10 | // copyToMem(&LABEL, (" label=\"" + std::string(l) + "\"").c_str()); 11 | // } 12 | -------------------------------------------------------------------------------- /src/defines.h: -------------------------------------------------------------------------------- 1 | // Experiment constants 2 | #define phyphoxBleNViews 15 3 | #define phyphoxBleNSensors 5 4 | #define phyphoxBleNElements 20 5 | #define phyphoxBleNExportSets 10 6 | #define phyphoxBleNChannel 10 7 | 8 | // Graph style options 9 | #define STYLE_LINES "lines" 10 | #define STYLE_DOTS "dots" 11 | #define STYLE_VBARS "vbars" 12 | #define STYLE_HBARS "hbars" 13 | #define STYLE_MAP "map" 14 | 15 | // Graph layout options 16 | #define LAYOUT_AUTO "auto" 17 | #define LAYOUT_EXTEND "extend" 18 | #define LAYOUT_FIXED "fixed" -------------------------------------------------------------------------------- /examples/randomNumbers/randomNumbers.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void setup() { 4 | PhyphoxBLE::start("phyphox device"); //Start the BLE server 5 | } 6 | 7 | void loop() { 8 | float randomNumber = random(0,100); //Generate random number in the range 0 to 100 9 | PhyphoxBLE::write(randomNumber); //Send value to phyphox 10 | delay(50); //Shortly pause before repeating 11 | PhyphoxBLE::poll(); //IMPORTANT: In contrast to other devices, poll() needs to be called periodically on the 33 IoT 12 | } 13 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=phyphox BLE 2 | version=1.2.6 3 | author=RWTH Aachen University 4 | maintainer=Dominik Dorsel 5 | sentence=Use the app phyphox to visualize your sensor data on your phone or tablet! 6 | paragraph=The purpose of this library is to use the open source phyphox app (see https://phyphox.org) to plot sensor data on your phone. phyphox is much more than only 'plotting your data'. You can also perform data analysis with it or access your phones sensors to use in your Arduino project. 7 | category=Other 8 | url=https://phyphox.org/arduino 9 | architectures=mbed,esp32,samd,mbed_nano,renesas_uno 10 | includes=phyphoxBle.h 11 | -------------------------------------------------------------------------------- /src/exportdata.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | void PhyphoxBleExperiment::ExportData::setDatachannel(int d){ 5 | char tmp[20]; 6 | sprintf(tmp, "CH%d", d); 7 | copyToMem(&BUFFER, tmp); 8 | } 9 | 10 | void PhyphoxBleExperiment::ExportData::setXMLAttribute(const char *xml){ 11 | copyToMem(&XMLAttribute, (" " + std::string(xml)).c_str()); 12 | } 13 | 14 | void PhyphoxBleExperiment::ExportData::setLabel(const char *l) 15 | { 16 | copyToMem(&LABEL, (std::string(l)).c_str()); 17 | } 18 | 19 | void PhyphoxBleExperiment::ExportData::getBytes(char *buffArray) 20 | { 21 | strcat(buffArray,"\t\t"); 25 | if (!BUFFER) {strcat(buffArray,"CH1");} else {strcat(buffArray,BUFFER);} 26 | strcat(buffArray, "\n"); 27 | 28 | } -------------------------------------------------------------------------------- /src/boards/NINAB31serial.h: -------------------------------------------------------------------------------- 1 | #ifndef NINAB31SERIAL_H 2 | #define NINAB31SERIAL_H 3 | 4 | #include 5 | 6 | 7 | class NINAB31Serial 8 | { 9 | 10 | private: 11 | 12 | static String m_input; 13 | static bool checkUnsolicited(); 14 | static bool connected; 15 | 16 | public: 17 | static bool configModule(); 18 | static bool begin(); 19 | static bool setLocalName(String name); 20 | static bool writeValue(int characteristic, String value); 21 | static bool writeValue(int characteristic, uint8_t* value, int len); 22 | static bool setConnectionInterval(int minInterval, int maxInterval); 23 | 24 | static int parseResponse(String msg, uint32_t timeout); 25 | static bool checkResponse(String msg, uint32_t timeout); 26 | static bool poll(); 27 | static String checkCharWritten(int handle); 28 | static void flushInput(); 29 | static bool advertise(); 30 | static bool stopAdvertise(); 31 | 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /src/exportSet.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | void PhyphoxBleExperiment::ExportSet::addElement(Element& e) 5 | { 6 | for(int i=0; i\n"); 30 | 31 | //loop over elements 32 | for(int i=0; igetBytes(buffArray); 35 | } 36 | } 37 | strcat(buffArray,"\t\n"); 38 | 39 | 40 | 41 | } 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/getSystemAndEventTime/getSystemAndEventTime.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void setup() { 4 | Serial.begin(115200); 5 | PhyphoxBLE::start(); 6 | PhyphoxBLE::experimentEventHandler = &newExperimentEvent; // declare which function should be called after receiving an experiment event 7 | PhyphoxBLE::printXML(&Serial); 8 | } 9 | 10 | void loop() { 11 | float randomNumber = random(0,100); //Generate random number in the range 0 to 100 12 | PhyphoxBLE::write(randomNumber); //Send value to phyphox 13 | delay(100); 14 | PhyphoxBLE::poll(); //Only required for the Arduino Nano 33 IoT, but it does no harm for other boards. 15 | 16 | } 17 | 18 | //declare function which is called after phyphox wrote to the event characteristic 19 | void newExperimentEvent(){ 20 | Serial.println("New experiment event received:"); 21 | Serial.print("Event type: "); 22 | Serial.print(PhyphoxBLE::eventType); 23 | Serial.println(" (0 = Paused, 1 = Started, 2 = Clear, 255 = SYNC)"); 24 | Serial.print("Experiment time [ms]: "); 25 | Serial.println(PhyphoxBLE::experimentTime); 26 | Serial.print("Unix system time [ms]: "); 27 | Serial.println(PhyphoxBLE::systemTime); 28 | } 29 | -------------------------------------------------------------------------------- /src/view.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | void PhyphoxBleExperiment::View::addElement(Element& e) 5 | { 6 | for(int i=0; i\n"); 29 | } 30 | 31 | if(ELEMENTS[elem]!=nullptr){ 32 | if(ELEMENTS[elem]->ERROR.MESSAGE == NULL) { 33 | ELEMENTS[elem]->getBytes(buffArray); 34 | } 35 | } 36 | 37 | if(elem == phyphoxBleNElements-1) { 38 | strcat(buffArray,"\t\n"); 39 | } 40 | 41 | 42 | 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/infoField.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | void PhyphoxBleExperiment::InfoField::setInfo(const char *i) 5 | { 6 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(i, 191, "setInfo") : ERROR; 7 | copyToMem(&INFO, (std::string(i)).c_str()); 8 | } 9 | 10 | void PhyphoxBleExperiment::InfoField::setColor(const char *c) 11 | { 12 | ERROR = ERROR.MESSAGE == NULL ? err_checkHex(c, "setColor") : ERROR; 13 | copyToMem(&COLOR, (" color=\"" + std::string(c) + "\"").c_str()); 14 | } 15 | 16 | void PhyphoxBleExperiment::InfoField::setXMLAttribute(const char *xml){ 17 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(xml, 98, "setXMLAttribute") : ERROR; 18 | copyToMem(&XMLAttribute, (" " + std::string(xml)).c_str()); 19 | } 20 | 21 | void PhyphoxBleExperiment::InfoField::getBytes(char *buffArray) 22 | { 23 | 24 | strcat(buffArray,"\t\t\n"); 35 | strcat(buffArray,"\t\t\n"); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/separator.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | void PhyphoxBleExperiment::Separator::setHeight(float h) 5 | { 6 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper((int) h, 10, "setHeight") : ERROR; 7 | char tmp[20]; 8 | sprintf(tmp, " height=\"%.2f\"", h); 9 | copyToMem(&HEIGHT, tmp); 10 | } 11 | 12 | void PhyphoxBleExperiment::Separator::setColor(const char *c) 13 | { 14 | ERROR = ERROR.MESSAGE == NULL ? err_checkHex(c, "setColor") : ERROR; 15 | copyToMem(&COLOR, (std::string(c)).c_str()); 16 | } 17 | 18 | void PhyphoxBleExperiment::Separator::setXMLAttribute(const char * xml) { 19 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(xml, 98, "setXMLAttribute") : ERROR; 20 | copyToMem(&XMLAttribute, (" " + std::string(xml)).c_str()); 21 | } 22 | 23 | void PhyphoxBleExperiment::Separator::getBytes(char *buffArray) 24 | { 25 | strcat(buffArray,"\t\t\n"); 34 | strcat(buffArray, "\t\t\n"); 35 | } 36 | -------------------------------------------------------------------------------- /examples/connectionParameter/connectionParameter.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void setup() { 4 | /* 5 | Minimum interval between two connection events allowed for a connection. 6 | It shall be less than or equal to maxConnectionInterval. This value, in units of 1.25ms, is included in the range [0x0006 : 0x0C80]. 7 | */ 8 | PhyphoxBLE::minConInterval = 6; //6 = 7.5ms 9 | 10 | /*Maximum interval between two connection events allowed for a connection. 11 | It shall be greater than or equal to minConnectionInterval. This value is in unit of 1.25ms and is in the range [0x0006 : 0x0C80]. 12 | */ 13 | PhyphoxBLE::maxConInterval = 24; //10 = 12.5ms 14 | 15 | /* 16 | Number of connection events the slave can drop if it has nothing to communicate to the master. 17 | This value shall be in the range [0x0000 : 0x01F3]. 18 | */ 19 | PhyphoxBLE::slaveLatency = 0; // 20 | /* 21 | Link supervision timeout for the connection. 22 | Time after which the connection is considered lost if the device didn't receive a packet from its peer. 23 | This value is in the range [0x000A : 0x0C80] and is in unit of 10 ms. 24 | */ 25 | PhyphoxBLE::timeout = 50; //50 = 500ms 26 | 27 | 28 | PhyphoxBLE::start(); //Start the BLE server 29 | } 30 | 31 | void loop() { 32 | float randomNumber = random(0,100); //Generate random number in the range 0 to 100 33 | PhyphoxBLE::write(randomNumber); //Send value to phyphox 34 | delay(20); //Shortly pause before repeating 35 | } -------------------------------------------------------------------------------- /examples/readoutADC/readoutADC.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int ADC_GPIO = 25; // ESP32 4 | //int ADC_GPIO = A0; // Arduino Nano 33 BLE 5 | 6 | void setup() { 7 | 8 | 9 | // put your setup code here, to run once: 10 | Serial.begin(115200); 11 | PhyphoxBLE::start("Voltmeter"); 12 | 13 | PhyphoxBleExperiment Voltmeter; 14 | 15 | Voltmeter.setTitle("Voltmeter"); 16 | Voltmeter.setCategory("Arduino Experiments"); 17 | Voltmeter.setDescription("This experiment will plot the measured voltage over time."); 18 | 19 | //View 20 | PhyphoxBleExperiment::View firstView; 21 | firstView.setLabel("Rawdata"); //Create a "view" 22 | 23 | //Graph 24 | PhyphoxBleExperiment::Graph firstGraph; //Create graph which will plot random numbers over time 25 | firstGraph.setLabel("Voltmeter"); 26 | firstGraph.setUnitX("s"); 27 | firstGraph.setUnitY("V"); 28 | firstGraph.setLabelX("time"); 29 | firstGraph.setLabelY("Voltage"); 30 | 31 | /* Assign Channels, so which data is plotted on x or y axis 32 | first parameter represents x-axis, second y-axis 33 | Channel 0 means a timestamp is created after the BLE package arrives in phyphox 34 | Channel 1 to N corresponding to the N-parameter which is written in server.write() 35 | */ 36 | 37 | firstGraph.setChannel(0, 1); 38 | 39 | firstView.addElement(firstGraph); //attach graph to view 40 | Voltmeter.addView(firstView); //Attach view to experiment 41 | PhyphoxBLE::addExperiment(Voltmeter); //Attach experiment to server 42 | 43 | 44 | 45 | } 46 | 47 | void loop() { 48 | // put your main code here, to run repeatedly: 49 | 50 | float voltage = 3.3 * analogRead(ADC_GPIO)/4095; 51 | delay(1); 52 | 53 | PhyphoxBLE::write(voltage); 54 | 55 | Serial.print("Voltage = "); 56 | Serial.println(voltage); 57 | 58 | delay(20); 59 | } -------------------------------------------------------------------------------- /examples/getDataFromSmartphone/getDataFromSmartphone.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* In this example we can change the blink interval of our mikrocontroller via phyphox 4 | */ 5 | 6 | void receivedData(); 7 | 8 | long lastTimestamp = 0; 9 | float blinkInterval = 100; 10 | bool led = true; 11 | 12 | void setup() 13 | { 14 | Serial.begin(115200); 15 | PhyphoxBLE::start(); 16 | PhyphoxBLE::configHandler=&receivedData; 17 | pinMode(LED_BUILTIN, OUTPUT); 18 | 19 | //Experiment 20 | PhyphoxBleExperiment getDataFromSmartphone; 21 | getDataFromSmartphone.setTitle("Set Blink Interval"); 22 | getDataFromSmartphone.setCategory("Arduino Experiments"); 23 | getDataFromSmartphone.setDescription("User can set Blink Interval of Mikrocontroller LED"); 24 | 25 | //View 26 | PhyphoxBleExperiment::View firstView; 27 | firstView.setLabel("FirstView"); //Create a "view" 28 | 29 | //Edit 30 | PhyphoxBleExperiment::Edit Interval; 31 | Interval.setLabel("Interval"); 32 | Interval.setUnit("ms"); 33 | Interval.setSigned(false); 34 | Interval.setDecimal(false); 35 | Interval.setChannel(1); 36 | 37 | firstView.addElement(Interval); 38 | getDataFromSmartphone.addView(firstView); //attach view to experiment 39 | PhyphoxBLE::addExperiment(getDataFromSmartphone); //attach experiment to server 40 | } 41 | 42 | 43 | void loop() 44 | { 45 | PhyphoxBLE::poll(); //Only required for the Arduino Nano 33 IoT, but it does no harm for other boards. 46 | 47 | if(millis()-lastTimestamp>blinkInterval){ 48 | lastTimestamp = millis(); 49 | led=!led; 50 | digitalWrite(LED_BUILTIN, led); 51 | } 52 | } 53 | 54 | void receivedData(){ 55 | float receivedInterval; 56 | PhyphoxBLE::read(receivedInterval); 57 | if(receivedInterval>0){ 58 | blinkInterval =receivedInterval; 59 | } 60 | Serial.println(blinkInterval); 61 | } -------------------------------------------------------------------------------- /examples/getSensorDataFromSmartphone/getSensorDataFromSmartphone.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void setup() { 4 | Serial.begin(115200); 5 | 6 | PhyphoxBLE::start(); 7 | PhyphoxBLE::configHandler=&receivedData; 8 | 9 | PhyphoxBleExperiment getDataFromSmartphonesensor; 10 | getDataFromSmartphonesensor.setTitle("Get Accelerometer Data"); 11 | getDataFromSmartphonesensor.setCategory("Arduino Experiments"); 12 | getDataFromSmartphonesensor.setDescription("Send smartphone accelerometer data to an arduino/esp32"); 13 | 14 | PhyphoxBleExperiment::View firstView; 15 | firstView.setLabel("FirstView"); //Create a "view" 16 | 17 | PhyphoxBleExperiment::InfoField infoText; 18 | infoText.setInfo("Acc data is sent to smartphone"); 19 | firstView.addElement(infoText); 20 | 21 | PhyphoxBleExperiment::Sensor smartphoneAcc; // add new sensor 22 | 23 | // Set type of sensor: 24 | // SENSOR_ACCELEROMETER 25 | // SENSOR_ACCELEROMETER_WITHOUT_G 26 | // SENSOR_GYROSCOPE 27 | // SENSOR_MAGNETOMETER 28 | // SENSOR_PRESSURE 29 | smartphoneAcc.setType(SENSOR_ACCELEROMETER); 30 | smartphoneAcc.setAverage(true); 31 | smartphoneAcc.setRate(80); 32 | 33 | // map sensor channel to incoming data channels 34 | smartphoneAcc.mapChannel("x",1); 35 | smartphoneAcc.mapChannel("y",2); 36 | smartphoneAcc.mapChannel("z",3); 37 | 38 | getDataFromSmartphonesensor.addView(firstView); 39 | getDataFromSmartphonesensor.addSensor(smartphoneAcc); 40 | 41 | PhyphoxBLE::addExperiment(getDataFromSmartphonesensor); 42 | PhyphoxBLE::printXML(&Serial); //print the generated xml file into the serial monitor 43 | } 44 | 45 | void loop() { 46 | delay(100); 47 | PhyphoxBLE::poll(); //Only required for the Arduino Nano 33 IoT, but it does no harm for other boards. 48 | } 49 | 50 | void receivedData(){ 51 | Serial.println("data:"); 52 | float x,y,z; 53 | PhyphoxBLE::read(x,y,z); 54 | Serial.print("x: "); 55 | Serial.print(x); 56 | 57 | Serial.print(" y: "); 58 | Serial.print(y); 59 | 60 | Serial.print(" z: "); 61 | Serial.println(z); 62 | } -------------------------------------------------------------------------------- /src/edit.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | void PhyphoxBleExperiment::Edit::setUnit(const char *u) 5 | { 6 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(u, 12, "setUnit") : ERROR; 7 | copyToMem(&UNIT, (std::string(u)).c_str()); 8 | } 9 | 10 | void PhyphoxBleExperiment::Edit::setSigned(bool s) 11 | { 12 | if(s) copyToMem(&SIGNED, "true"); 13 | else copyToMem(&SIGNED, "false"); 14 | } 15 | 16 | void PhyphoxBleExperiment::Edit::setDecimal(bool d) 17 | { 18 | if(d) copyToMem(&DECIMAL, "true"); 19 | else copyToMem(&DECIMAL, "false"); 20 | } 21 | 22 | void PhyphoxBleExperiment::Edit::setXMLAttribute(const char *xml){ 23 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(xml, 98, "setXMLAttribute") : ERROR; 24 | copyToMem(&XMLAttribute, (" " + std::string(xml)).c_str()); 25 | } 26 | 27 | void PhyphoxBleExperiment::Edit::setChannel(int b){ 28 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(b, 5, "setChannel") : ERROR; 29 | char tmp[20]; 30 | sprintf(tmp, "CB%i", b); 31 | copyToMem(&BUFFER, tmp); 32 | } 33 | 34 | void PhyphoxBleExperiment::Edit::getBytes(char *buffArray) 35 | { 36 | strcat(buffArray,"\t\t\n"); 63 | strcat(buffArray,"\t\t"); 64 | if (!BUFFER) {strcat(buffArray,"CH5");} else {strcat(buffArray,BUFFER);} 65 | strcat(buffArray,"\n"); 66 | strcat(buffArray, "\t\t\n"); 67 | } 68 | -------------------------------------------------------------------------------- /src/value.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | void PhyphoxBleExperiment::Value::setColor(const char *c) 5 | { 6 | ERROR = ERROR.MESSAGE == NULL ? err_checkHex(c, "setColor") : ERROR; 7 | copyToMem(&COLOR, (std::string(c)).c_str()); 8 | } 9 | 10 | void PhyphoxBleExperiment::Value::setPrecision(int p) 11 | { 12 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(p, 999, "setPrecision") : ERROR; 13 | char tmp[20]; 14 | sprintf(tmp,"%i", p); 15 | copyToMem(&PRECISION, tmp); 16 | } 17 | 18 | void PhyphoxBleExperiment::Value::setUnit(const char* u) 19 | { 20 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(u, 12, "setUnit") : ERROR; 21 | copyToMem(&UNIT, (std::string(u)).c_str()); 22 | } 23 | 24 | void PhyphoxBleExperiment::Value::setChannel(int c) 25 | { 26 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(c, numberOfChannels, "setChannel") : ERROR; 27 | char tmp[20]; 28 | sprintf(tmp, "CH%i", c); 29 | copyToMem(&INPUTVALUE, tmp); 30 | } 31 | 32 | void PhyphoxBleExperiment::Value::setXMLAttribute(const char *xml){ 33 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(xml, 98, "setXMLAttribute") : ERROR; 34 | copyToMem(&XMLAttribute, (" " + std::string(xml)).c_str()); 35 | } 36 | 37 | void PhyphoxBleExperiment::Value::getBytes(char *buffArray) 38 | { 39 | 40 | strcat(buffArray,"\t\t\n"); 66 | 67 | strcat(buffArray, "\t\t\t"); 68 | if (!INPUTVALUE) {strcat(buffArray,"CH3");} else {strcat(buffArray,INPUTVALUE);} 69 | strcat(buffArray, "\n\t\t\n"); 70 | } 71 | -------------------------------------------------------------------------------- /examples/rangefinder/rangefinder.ino: -------------------------------------------------------------------------------- 1 | #include //Phyphox BLE library 2 | 3 | #include //Required for the VL53L0X rangefinder 4 | #include //Required for the VL53L0X rangefinder 5 | VL53L0X sensor; //Instance of the rangefinder sensor 6 | 7 | void setup() { 8 | PhyphoxBLE::start("Rangefinder"); 9 | 10 | //Experiment 11 | PhyphoxBleExperiment experiment; 12 | 13 | experiment.setTitle("Rangefinder"); 14 | experiment.setCategory("Arduino Experiments"); 15 | experiment.setDescription("Plot the distance from a time-of-flight sensor over time."); 16 | 17 | //View 18 | PhyphoxBleExperiment::View view; 19 | 20 | //Graph 21 | PhyphoxBleExperiment::Graph graph; 22 | graph.setLabel("Distance over time"); 23 | graph.setUnitX("s"); 24 | graph.setUnitY("mm"); 25 | graph.setLabelX("time"); 26 | graph.setLabelY("distance"); 27 | 28 | //In contrast to other examples, we will not generate the timestamp on the phone. 29 | //For experiments with a high data rate, we can achieve a better temporal 30 | //accuracy if we generate the timestamp on the Arduino and send it in pairs with the 31 | //measured values. 32 | graph.setChannel(1,2); 33 | 34 | view.addElement(graph); //Attach graph to view 35 | experiment.addView(view); //Attach view to experiment 36 | PhyphoxBLE::addExperiment(experiment); //Attach experiment to server 37 | 38 | //Start the rangefinder 39 | Wire.begin(); 40 | while (!sensor.init()) 41 | delay(100); 42 | sensor.setTimeout(500); 43 | sensor.startContinuous(); 44 | } 45 | 46 | 47 | void loop() { 48 | float t = 0.001 * (float)millis(); //Time in seconds 49 | float distance = sensor.readRangeContinuousMillimeters(); //Distance in millimeters 50 | 51 | if (distance == 8190) //This is an error state if no distance could be determined. Zero looks better in this case. 52 | distance = 0; 53 | 54 | PhyphoxBLE::write(t, distance); //Send data to phyphox 55 | 56 | PhyphoxBLE::poll(); //Only required for the Arduino Nano 33 IoT, but it does no harm for other boards. 57 | } 58 | -------------------------------------------------------------------------------- /src/subgraph.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | void PhyphoxBleExperiment::Graph::Subgraph::getBytes(char *buffArray) 5 | { 6 | //strcat(buffArray, "\n"); 7 | strcat(buffArray, "\n\t\t\t"); 24 | if (!INPUTX) {strcat(buffArray,"CH0");} else {strcat(buffArray,INPUTX);} 25 | strcat(buffArray, "\n\t\t\t"); 26 | if (!INPUTY) {strcat(buffArray,"CH1");} else {strcat(buffArray,INPUTY);} 27 | strcat(buffArray, ""); 28 | } 29 | 30 | void PhyphoxBleExperiment::Graph::Subgraph::setColor(const char *c){ 31 | ERROR = ERROR.MESSAGE == NULL ? err_checkHex(c, "setColor") : ERROR; 32 | copyToMem(&COLOR, (std::string(c)).c_str()); 33 | } 34 | 35 | void PhyphoxBleExperiment::Graph::Subgraph::setLinewidth(float w){ 36 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(w, 10, "setLinewidth") : ERROR; 37 | char tmp[10]; 38 | sprintf(tmp, "%.2f", w); 39 | copyToMem(&WIDTH, tmp); 40 | } 41 | /** 42 | * @param s STYLE_DOTS 43 | */ 44 | void PhyphoxBleExperiment::Graph::Subgraph::setStyle(const char *s){ 45 | Error styleError = err_checkStyle(s, "setStyle"); 46 | ERROR = ERROR.MESSAGE == NULL ? styleError : ERROR; 47 | if(styleError.MESSAGE == NULL){ 48 | copyToMem(&STYLE, ("" + std::string(s)).c_str()); 49 | } 50 | } 51 | 52 | void PhyphoxBleExperiment::Graph::Subgraph::setChannel(int x, int y) 53 | { 54 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(x, numberOfChannels, "setChannel") : ERROR; 55 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(y, numberOfChannels, "setChannel") : ERROR; 56 | 57 | char tmpX[20]; 58 | sprintf(tmpX, "CH%i", x); 59 | copyToMem(&INPUTX, tmpX); 60 | char tmpY[20]; 61 | sprintf(tmpY, "CH%i", y); 62 | copyToMem(&INPUTY, tmpY); 63 | } -------------------------------------------------------------------------------- /src/boards/phyphoxBLE_NINAB31.h: -------------------------------------------------------------------------------- 1 | #ifndef PHYPHOXBLE_NINAB31_H 2 | #define PHYPHOXBLE_NINAB31_H 3 | 4 | #include 5 | #include "phyphoxBleExperiment.h" 6 | #include "NINAB31serial.h" 7 | 8 | class PhyphoxBLE 9 | { 10 | private: 11 | 12 | static uint8_t data_package[20]; 13 | 14 | static NINAB31Serial port; 15 | 16 | static void controlCharacteristicWritten(); 17 | static void configCharacteristicWritten(); 18 | 19 | static int h_phyphoxExperimentService; 20 | static int h_experimentCharacteristic; 21 | static int h_controlCharacteristic; 22 | 23 | static int h_phyphoxDataService; 24 | static int h_dataCharacteristic; 25 | static int h_configCharacteristic; 26 | static bool exploaded; 27 | 28 | static uint8_t* data; //this pointer points to the data the user wants to write in the characteristic 29 | static uint8_t* p_exp; //this pointer will point to the byte array which holds an experiment 30 | 31 | static size_t expLen; //try o avoid this maybe use std::array or std::vector 32 | static uint8_t EXPARRAY[4000];// block some storage 33 | 34 | static uint8_t controlCharValue[21]; 35 | static uint8_t configCharValue[21]; 36 | 37 | public: 38 | 39 | static void start(const char* DEVICE_NAME, uint8_t* p, size_t n = 0); 40 | static void start(const char* DEVICE_NAME); 41 | static void start(uint8_t* p, size_t n = 0); 42 | static void start(); 43 | 44 | static void addExperiment(PhyphoxBleExperiment&); 45 | static void transferExperiment(); 46 | static void write(float&); 47 | static void write(float&, float&); 48 | static void write(float&, float&, float&); 49 | static void write(float&, float&, float&, float&); 50 | static void write(float&, float&, float&, float&, float&); 51 | 52 | static void read(uint8_t*, unsigned int); 53 | static void read(float&); 54 | 55 | 56 | 57 | static void poll(); 58 | static void poll(int timeout); 59 | 60 | static void(*configHandler)(); 61 | static uint16_t minConInterval; 62 | static uint16_t maxConInterval; 63 | static uint16_t slaveLatency; 64 | static uint16_t timeout; 65 | static uint16_t MTU; 66 | 67 | }; 68 | 69 | 70 | #endif 71 | -------------------------------------------------------------------------------- /src/boards/phyphoxBLE_NanoIOT.h: -------------------------------------------------------------------------------- 1 | #ifndef PHYPHOXBLE_NANOIOT_H 2 | #define PHYPHOXBLE_NANOIOT_H 3 | 4 | #include 5 | #include "phyphoxBleExperiment.h" 6 | 7 | class PhyphoxBLE 8 | { 9 | private: 10 | 11 | static uint8_t data_package[20]; 12 | 13 | static void controlCharacteristicWritten(BLEDevice, BLECharacteristic); 14 | static void eventCharacteristicWritten(BLEDevice, BLECharacteristic); 15 | static void configCharacteristicWritten(BLEDevice, BLECharacteristic); 16 | 17 | static BLEService phyphoxExperimentService; 18 | static BLECharacteristic experimentCharacteristic; 19 | static BLECharacteristic controlCharacteristic; 20 | static BLECharacteristic eventCharacteristic; 21 | 22 | static BLEService phyphoxDataService; 23 | static BLECharacteristic dataCharacteristic; 24 | static BLECharacteristic configCharacteristic; 25 | 26 | 27 | static uint8_t* data; //this pointer points to the data the user wants to write in the characteristic 28 | static uint8_t* p_exp; //this pointer will point to the byte array which holds an experiment 29 | 30 | static size_t expLen; //try o avoid this maybe use std::array or std::vector 31 | static char *EXPARRAY; 32 | 33 | public: 34 | 35 | static void start(const char* DEVICE_NAME, uint8_t* p, size_t n = 0); 36 | static void start(const char* DEVICE_NAME); 37 | static void start(uint8_t* p, size_t n = 0); 38 | static void start(); 39 | 40 | static void addExperiment(PhyphoxBleExperiment&); 41 | static void transferExperiment(); 42 | static void write(float&); 43 | static void write(float&, float&); 44 | static void write(float&, float&, float&); 45 | static void write(float&, float&, float&, float&); 46 | static void write(float&, float&, float&, float&, float&); 47 | 48 | static void read(uint8_t*, unsigned int); 49 | static void read(float&); 50 | static void read(float&, float&); 51 | static void read(float&, float&, float&); 52 | static void read(float&, float&, float&, float&); 53 | static void read(float&, float&, float&, float&, float&); 54 | 55 | 56 | static void poll(); 57 | static void poll(int timeout); 58 | 59 | static void(*configHandler)(); 60 | static void(*experimentEventHandler)(); 61 | 62 | static uint16_t minConInterval; 63 | static uint16_t maxConInterval; 64 | static uint16_t slaveLatency; 65 | static uint16_t timeout; 66 | static uint16_t MTU; 67 | 68 | static int64_t experimentTime; 69 | static int64_t systemTime; 70 | static uint8_t eventType; 71 | 72 | static uint8_t eventData[17]; 73 | 74 | static void printXML(HardwareSerial*); 75 | 76 | }; 77 | 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /src/sensor.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | #include 4 | 5 | void PhyphoxBleExperiment::Sensor::setType(const char *t) 6 | { 7 | ERROR = ERROR.MESSAGE == NULL ? err_checkSensor(t, "setType") : ERROR; 8 | copyToMem(&TYPE, (std::string(t)).c_str()); 9 | } 10 | 11 | void PhyphoxBleExperiment::Sensor::mapChannel(const char *comp, int ch) 12 | { 13 | for(int i=0;i<5;i++){ 14 | if (COMPONENT[i]==NULL) 15 | { 16 | ERROR = ERROR.MESSAGE == NULL ? err_checkComponent(comp, "routeData") : ERROR; 17 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(ch,5, "routeData") : ERROR; 18 | copyToMem(&COMPONENT[i], (std::string(comp)).c_str()); 19 | std::ostringstream s; 20 | s << ch; 21 | std::string temp = s.str(); 22 | copyToMem(&CHANNEL[i], ("CB"+temp).c_str()); 23 | break; 24 | } 25 | } 26 | } 27 | void PhyphoxBleExperiment::Sensor::setAverage(bool b) 28 | { 29 | if(b){ 30 | copyToMem(&AVERAGE, "true"); 31 | }else{ 32 | copyToMem(&AVERAGE, "false"); 33 | } 34 | } 35 | 36 | void PhyphoxBleExperiment::Sensor::setRate(int r){ 37 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(r,100, "setRate") : ERROR; 38 | std::ostringstream s; 39 | s << r; 40 | std::string temp = s.str(); 41 | copyToMem(&RATE, (" rate=\""+temp+"\"").c_str()); 42 | } 43 | 44 | void PhyphoxBleExperiment::Sensor::setXMLAttribute(const char *xml){ 45 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(xml, 98, "setXMLAttribute") : ERROR; 46 | copyToMem(&XMLAttribute, (" " + std::string(xml)).c_str()); 47 | } 48 | 49 | void PhyphoxBleExperiment::Sensor::getBytes(char *buffArray) 50 | { 51 | if(ERROR.MESSAGE == NULL){ 52 | strcat(buffArray,"\t\n"); 65 | //route components 66 | for (int i = 0; i < 5; i++){ 67 | if(CHANNEL[i]!=NULL && COMPONENT[i]!=NULL){ 68 | strcat(buffArray,"\t\t"); 73 | strcat(buffArray,CHANNEL[i]); 74 | strcat(buffArray,"\n"); 75 | } 76 | } 77 | strcat(buffArray,"\t\n"); 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /src/errorhandler.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | 4 | 5 | PhyphoxBleExperiment::Error PhyphoxBleExperiment::Errorhandler::err_checkLength(const char *input, int maxLength, const char *origin) { 6 | Error ret; 7 | if(strlen(input) > maxLength) { 8 | copyToMem(&ret.MESSAGE, ("ERR_01, in " + std::string(origin) + "(). \n").c_str()); 9 | } 10 | return ret; 11 | } 12 | 13 | PhyphoxBleExperiment::Error PhyphoxBleExperiment::Errorhandler::err_checkUpper(int input, int upperValue, const char *origin) { 14 | Error ret; 15 | if(input > upperValue) { 16 | copyToMem(&ret.MESSAGE, ("ERR_02, in " + std::string(origin) + "(). \n").c_str()); 17 | } 18 | return ret; 19 | } 20 | 21 | PhyphoxBleExperiment::Error PhyphoxBleExperiment::Errorhandler::err_checkHex(const char* input, const char *origin){ 22 | Error ret; 23 | if(strlen(input) != 6) { 24 | copyToMem(&ret.MESSAGE, ("ERR_03, in " + std::string(origin) + "(). \n").c_str()); 25 | }; 26 | for(int i=0; i<=5; i++) { 27 | if(!((input[i] <='f' && input[i] >='a') || (input[i] <='F' && input[i] >='A') || (input[i] <='9' && input[i] >='0'))) { 28 | copyToMem(&ret.MESSAGE, ("ERR_03, in " + std::string(origin) + "(). \n").c_str()); 29 | } 30 | } 31 | return ret; 32 | } 33 | 34 | PhyphoxBleExperiment::Error PhyphoxBleExperiment::Errorhandler::err_checkStyle(const char *input, const char *origin) { 35 | Error ret; 36 | if(strcmp(input,"lines")!=0 && strcmp(input,"dots")!=0 && strcmp(input,"vbars")!=0 && strcmp(input,"hbars")!=0 && strcmp(input,"map")!=0) { 37 | copyToMem(&ret.MESSAGE, ("ERR_04, in " + std::string(origin) + "(). \n").c_str()); 38 | } 39 | return ret; 40 | } 41 | 42 | PhyphoxBleExperiment::Error PhyphoxBleExperiment::Errorhandler::err_checkLayout(const char *input, const char *origin) { 43 | Error ret; 44 | copyToMem(&ret.MESSAGE, ("ERR_05, in " + std::string(origin) + "(). \n").c_str()); 45 | return ret; 46 | } 47 | 48 | PhyphoxBleExperiment::Error PhyphoxBleExperiment::Errorhandler::err_checkSensor(const char *input, const char *origin) { 49 | Error ret; 50 | if(strcmp(input,"accelerometer")!=0 && strcmp(input,"linear_acceleration")!=0 && strcmp(input,"gyroscope")!=0 && strcmp(input,"light")!=0 && strcmp(input,"magnetic_field")!=0 && strcmp(input,"pressure")!=0 && strcmp(input,"temperature")!=0) { 51 | copyToMem(&ret.MESSAGE, ("ERR_04, in " + std::string(origin) + "(). \n").c_str()); 52 | } 53 | return ret; 54 | } 55 | 56 | PhyphoxBleExperiment::Error PhyphoxBleExperiment::Errorhandler::err_checkComponent(const char *input, const char *origin) { 57 | Error ret; 58 | if(strcmp(input,"x")!=0 && strcmp(input,"y")!=0 && strcmp(input,"z")!=0 && strcmp(input,"abs")!=0 && strcmp(input,"accuracy")!=0 && strcmp(input,"t")!=0) { 59 | copyToMem(&ret.MESSAGE, ("ERR_04, in " + std::string(origin) + "(). \n").c_str()); 60 | } 61 | return ret; 62 | } -------------------------------------------------------------------------------- /src/boards/phyphoxBLE_STM32.h: -------------------------------------------------------------------------------- 1 | #ifndef PHYPHOXBLE_STM32_H 2 | #define PHYPHOXBLE_STM32_H 3 | #if !defined(NDEBUG) && defined(ARDUINO_ARCH_STM32) 4 | #define NDEBUG 5 | #endif 6 | 7 | #include 8 | 9 | #include 10 | #include "phyphoxBleExperiment.h" 11 | 12 | class PhyphoxBLE 13 | { 14 | private: 15 | 16 | static uint8_t data_package[20]; 17 | 18 | static void controlCharacteristicWritten(BLEDevice, BLECharacteristic); 19 | static void eventCharacteristicWritten(BLEDevice, BLECharacteristic); 20 | static void configCharacteristicWritten(BLEDevice, BLECharacteristic); 21 | 22 | static BLEService phyphoxExperimentService; 23 | static BLECharacteristic experimentCharacteristic; 24 | static BLECharacteristic controlCharacteristic; 25 | static BLECharacteristic eventCharacteristic; 26 | 27 | static BLEService phyphoxDataService; 28 | static BLECharacteristic dataCharacteristic; 29 | static BLECharacteristic configCharacteristic; 30 | 31 | 32 | static uint8_t* data; //this pointer points to the data the user wants to write in the characteristic 33 | static uint8_t* p_exp; //this pointer will point to the byte array which holds an experiment 34 | 35 | static size_t expLen; //try o avoid this maybe use std::array or std::vector 36 | static char *EXPARRAY; 37 | 38 | public: 39 | 40 | static void start(const char* DEVICE_NAME, uint8_t* p, size_t n = 0); 41 | static void start(const char* DEVICE_NAME); 42 | static void start(uint8_t* p, size_t n = 0); 43 | static void start(); 44 | 45 | static void addExperiment(PhyphoxBleExperiment&); 46 | static void transferExperiment(); 47 | static void write(float&); 48 | static void write(float&, float&); 49 | static void write(float&, float&, float&); 50 | static void write(float&, float&, float&, float&); 51 | static void write(float&, float&, float&, float&, float&); 52 | 53 | static void read(uint8_t*, unsigned int); 54 | static void read(float&); 55 | static void read(float&, float&); 56 | static void read(float&, float&, float&); 57 | static void read(float&, float&, float&, float&); 58 | static void read(float&, float&, float&, float&, float&); 59 | 60 | 61 | static void poll(); 62 | static void poll(int timeout); 63 | 64 | static void(*configHandler)(); 65 | static void(*experimentEventHandler)(); 66 | 67 | static uint16_t minConInterval; 68 | static uint16_t maxConInterval; 69 | static uint16_t slaveLatency; 70 | static uint16_t timeout; 71 | static uint16_t MTU; 72 | 73 | static int64_t experimentTime; 74 | static int64_t systemTime; 75 | static uint8_t eventType; 76 | 77 | static uint8_t eventData[17]; 78 | 79 | static void printXML(HardwareSerial*); 80 | 81 | }; 82 | 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /examples/multigraph/multigraph.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define PI 3.1415926535897932384626433832795 4 | float periodTime = 2.0;//in s 5 | 6 | void setup() { 7 | Serial.begin(115200); 8 | PhyphoxBLE::start(); 9 | 10 | PhyphoxBleExperiment MultiGraph; 11 | 12 | MultiGraph.setTitle("Multi Graph Example"); 13 | MultiGraph.setCategory("Arduino Experiments"); 14 | MultiGraph.setDescription("ArduinoBLE Example"); 15 | 16 | PhyphoxBleExperiment::View firstView; 17 | firstView.setLabel("FirstView"); //Create a "view" 18 | 19 | //Multiple graphs in one plot can be realised by two different methods. 20 | // OPTION 1 is do create a graph as usual and add additional datastreams the following way 21 | PhyphoxBleExperiment::Graph myFirstGraph; 22 | myFirstGraph.setLabel("Trigonometric functions"); 23 | myFirstGraph.setLabelX("time"); 24 | myFirstGraph.setUnitX("s"); 25 | myFirstGraph.setLabelY("sin(t), cos(t)"); 26 | 27 | myFirstGraph.setChannel(1,2); 28 | myFirstGraph.setStyle(STYLE_DOTS);//"lines" are used if you dont set a style 29 | myFirstGraph.setColor("ffffff"); 30 | myFirstGraph.setLinewidth(2);//if you dont select a linewidth, a width of 1 is used by default 31 | 32 | PhyphoxBleExperiment::Graph::Subgraph additionalData; 33 | additionalData.setChannel(1,3); 34 | additionalData.setStyle(STYLE_LINES); 35 | additionalData.setColor("ff00ff"); 36 | additionalData.setLinewidth(1); 37 | 38 | myFirstGraph.addSubgraph(additionalData); 39 | 40 | //OPTION 2: you can also skip editing the graph object and just add datastreams 41 | PhyphoxBleExperiment::Graph mySecondGraph; 42 | mySecondGraph.setLabel("Trigonometric functions"); 43 | mySecondGraph.setLabelX("time"); 44 | mySecondGraph.setUnitX("s"); 45 | mySecondGraph.setLabelY("sin(t), cos(t)"); 46 | 47 | PhyphoxBleExperiment::Graph::Subgraph firstData; 48 | firstData.setChannel(1,2); 49 | firstData.setColor("ffffff"); 50 | firstData.setStyle(STYLE_DOTS); 51 | firstData.setLinewidth(2); 52 | 53 | mySecondGraph.addSubgraph(firstData); 54 | 55 | PhyphoxBleExperiment::Graph::Subgraph secondData; 56 | secondData.setChannel(1,3); 57 | secondData.setColor("ff00ff"); 58 | secondData.setLinewidth(1);//if you dont select a linewidth, a width of 1 is used by default 59 | secondData.setStyle(STYLE_LINES); //"lines" are used if you dont set a style 60 | 61 | 62 | mySecondGraph.addSubgraph(secondData); 63 | 64 | firstView.addElement(myFirstGraph); 65 | firstView.addElement(mySecondGraph); 66 | 67 | MultiGraph.addView(firstView); 68 | 69 | PhyphoxBLE::addExperiment(MultiGraph); 70 | 71 | PhyphoxBLE::printXML(&Serial); 72 | } 73 | 74 | void loop() { 75 | 76 | float currentTime = millis()/1000.0; 77 | float sinus = generateSin(currentTime); 78 | float cosinus = generateSin(currentTime+0.25*periodTime); 79 | PhyphoxBLE::write(currentTime,sinus,cosinus); 80 | delay(100); 81 | PhyphoxBLE::poll(); //Only required for the Arduino Nano 33 IoT, but it does no harm for other boards. 82 | } 83 | 84 | float generateSin(float x){ 85 | return 1.0 * sin(x*2.0*PI/periodTime); 86 | } -------------------------------------------------------------------------------- /src/phyphoxBle.h: -------------------------------------------------------------------------------- 1 | #ifndef PHYPHOXBLE 2 | #define PHYPHOXBLE 3 | 4 | #include "Arduino.h" 5 | 6 | static const char *phyphoxBleExperimentServiceUUID = "cddf0001-30f7-4671-8b43-5e40ba53514a"; 7 | static const char *phyphoxBleExperimentCharacteristicUUID = "cddf0002-30f7-4671-8b43-5e40ba53514a"; 8 | static const char *phyphoxBleExperimentControlCharacteristicUUID = "cddf0003-30f7-4671-8b43-5e40ba53514a"; 9 | static const char *phyphoxBleEventCharacteristicUUID = "cddf0004-30f7-4671-8b43-5e40ba53514a"; 10 | 11 | static const char *phyphoxBleDataServiceUUID = "cddf1001-30f7-4671-8b43-5e40ba53514a"; 12 | static const char *phyphoxBleDataCharacteristicUUID = "cddf1002-30f7-4671-8b43-5e40ba53514a"; 13 | static const char *phyphoxBleConfigCharacteristicUUID = "cddf1003-30f7-4671-8b43-5e40ba53514a"; 14 | 15 | static const char *deviceName = "phyphox-Arduino"; 16 | 17 | #define SENSOR_ACCELEROMETER "accelerometer" 18 | #define SENSOR_ACCELEROMETER_WITHOUT_G "linear_acceleration" 19 | #define SENSOR_GYROSCOPE "gyroscope" 20 | #define SENSOR_MAGNETOMETER "magnetometer" 21 | #define SENSOR_PRESSURE "pressure" 22 | #define SENSOR_TEMPERATURE "temperature" 23 | #define SENSOR_LIGHT "light" 24 | #define SENSOR_HUMIDITY "humidity" 25 | #define SENSOR_PROXIMITY "proximity" 26 | 27 | #define STYLE_DOTS "dots" 28 | #define STYLE_LINES "lines" 29 | #define STYLE_VBARS "vbars" 30 | #define STYLE_HBARS "hbars" 31 | #define STYLE_MAP "map" 32 | 33 | #define COLOR_RED "fe005d" 34 | #define COLOR_BLUE "39a2ff" 35 | #define COLOR_GREEN "2bfb4c" 36 | #define COLOR_ORANGE "ff7e22" 37 | #define COLOR_WHITE "ffffff" 38 | #define COLOR_YELLOW "edf668" 39 | #define COLOR_MAGENTA "eb46f4" 40 | 41 | #define LAYOUT_AUTO "auto" 42 | #define LAYOUT_EXTEND "extend" 43 | #define LAYOUT_FIXED "fixed" 44 | 45 | #ifndef CONFIGSIZE 46 | #define CONFIGSIZE 20 47 | #endif 48 | #if defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SENSEBOX_MCU_ESP32S2) 49 | #include "boards/phyphoxBLE_NINAB31.h" 50 | #elif defined(ARDUINO_ARCH_MBED) 51 | #include "boards/phyphoxBLE_NRF52.h" 52 | #elif defined(ESP32) && !defined(ARDUINO_SENSEBOX_MCU_ESP32S2) 53 | #include "boards/phyphoxBLE_ESP32.h" 54 | #elif defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_SAMD_MKRWIFI1010) 55 | #include 56 | #include "boards/phyphoxBLE_NanoIOT.h" 57 | #elif defined(ARDUINO_ARCH_STM32) 58 | #include "boards/phyphoxBLE_STM32.h" 59 | #else 60 | #error "Unsupported board selected!" 61 | #endif 62 | 63 | #include "Arduino.h" 64 | 65 | struct phyphoxBleCrc32 66 | { 67 | static void generate_table(uint32_t(&table)[256]) 68 | { 69 | uint32_t polynomial = 0xEDB88320; 70 | for (uint32_t i = 0; i < 256; i++) 71 | { 72 | uint32_t c = i; 73 | for (size_t j = 0; j < 8; j++) 74 | { 75 | if (c & 1) { 76 | c = polynomial ^ (c >> 1); 77 | } 78 | else { 79 | c >>= 1; 80 | } 81 | } 82 | table[i] = c; 83 | } 84 | } 85 | 86 | static uint32_t update(uint32_t (&table)[256], uint32_t initial, const uint8_t* buf, size_t len) 87 | { 88 | uint32_t c = initial ^ 0xFFFFFFFF; 89 | const uint8_t* u = static_cast(buf); 90 | for (size_t i = 0; i < len; ++i) 91 | { 92 | c = table[(c ^ u[i]) & 0xFF] ^ (c >> 8); 93 | } 94 | return c ^ 0xFFFFFFFF; 95 | } 96 | }; 97 | 98 | static int64_t swap_int64( int64_t val ){ 99 | val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); 100 | val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); 101 | return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); 102 | } 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /src/boards/phyphoxBLE_ESP32.h: -------------------------------------------------------------------------------- 1 | #ifdef ESP32 2 | #ifndef PHYPHOXBLE_ESP32_H 3 | #define PHYPHOXBLE_ESP32_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "phyphoxBleExperiment.h" 12 | 13 | using std::copy; 14 | 15 | 16 | class PhyphoxBLE 17 | { 18 | private: 19 | 20 | static uint8_t data_package[20]; 21 | 22 | static BLEServer *myServer; 23 | static BLEService *phyphoxDataService; 24 | static BLEService *phyphoxExperimentService; 25 | static BLEDescriptor *myExperimentDescriptor; 26 | static BLEDescriptor *myDataDescriptor; 27 | static BLEDescriptor *myEventDescriptor; 28 | static BLEDescriptor *myConfigDescriptor; 29 | static BLECharacteristic *dataCharacteristic; 30 | static BLECharacteristic *experimentCharacteristic; 31 | static BLECharacteristic *eventCharacteristic; 32 | static BLECharacteristic *configCharacteristic; 33 | static BLEAdvertising *myAdvertising; 34 | 35 | // void when_connected(); 36 | // virtual void onDisconnectionComplete(); 37 | 38 | static uint8_t* data; //this pointer points to the data the user wants to write in the characteristic 39 | static uint8_t* p_exp; //this pointer will point to the byte array which holds an experiment 40 | static TaskHandle_t TaskTransfer; 41 | 42 | static char *EXPARRAY; 43 | 44 | static size_t expLen; //try o avoid this maybe use std::array or std::vector 45 | 46 | public: 47 | 48 | static void disconnected(); 49 | static void start(const char* DEVICE_NAME, uint8_t* p, size_t n = 0); 50 | static void start(const char* DEVICE_NAME); 51 | static void start(uint8_t* p, size_t n = 0); 52 | static void start(); 53 | 54 | static void when_subscription_received(); 55 | static void addExperiment(PhyphoxBleExperiment&); 56 | 57 | static void poll(); 58 | static void poll(int timeout); 59 | 60 | static void write(float&); 61 | static void write(float&, float&); 62 | static void write(float&, float&, float&); 63 | static void write(float&, float&, float&, float&); 64 | static void write(float&, float&, float&, float&, float&); 65 | static void write(uint8_t *, unsigned int ); 66 | static void write(float *,unsigned int); 67 | 68 | 69 | static void read(uint8_t*, unsigned int); 70 | static void read(float&); 71 | static void read(float&, float&); 72 | static void read(float&, float&, float&); 73 | static void read(float&, float&, float&, float&); 74 | static void read(float&, float&, float&, float&, float&); 75 | static void configHandlerDebug(); 76 | static void (*configHandler)(); 77 | 78 | static void eventCharacteristicHandler(); 79 | static void (*experimentEventHandler)(); 80 | 81 | static void printXML(HardwareSerial*); 82 | 83 | static void setMTU(uint16_t); 84 | static uint16_t MTU; 85 | 86 | static void startTask(); 87 | static void staticStartTask(void*); 88 | 89 | static HardwareSerial* printer; //for debug purpose 90 | static void begin(HardwareSerial*); //for debug purpose 91 | 92 | static uint16_t minConInterval; 93 | static uint16_t maxConInterval; 94 | static uint16_t slaveLatency; 95 | static uint16_t timeout; 96 | static uint16_t currentConnections; 97 | static bool isSubscribed; 98 | 99 | static uint8_t eventData[17]; 100 | static int64_t experimentTime; 101 | static int64_t systemTime; 102 | static uint8_t eventType; 103 | }; 104 | 105 | 106 | #endif 107 | #endif 108 | -------------------------------------------------------------------------------- /src/boards/phyphoxBLE_NRF52.h: -------------------------------------------------------------------------------- 1 | #ifndef PHYPHOXBLE_NRF52_H 2 | #define PHYPHOXBLE_NRF52_H 3 | #if !defined(NDEBUG) && defined(ARDUINO_ARCH_MBED) 4 | #define NDEBUG 5 | #endif 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "phyphoxBleExperiment.h" 20 | 21 | #ifndef NDEBUG 22 | using arduino::HardwareSerial; 23 | #endif 24 | using events::EventQueue; 25 | using rtos::Thread; 26 | using mbed::callback; 27 | using std::copy; 28 | 29 | #ifndef DATASIZE 30 | #define DATASIZE 20 31 | #endif 32 | 33 | class PhyphoxBleEventHandler : public ble::Gap::EventHandler { 34 | public: 35 | 36 | virtual void onDisconnectionComplete(const ble::DisconnectionCompleteEvent&); 37 | virtual void onConnectionComplete(const ble::ConnectionCompleteEvent&); 38 | PhyphoxBleEventHandler(BLE& ble): 39 | ble(ble) { 40 | } 41 | 42 | private: 43 | BLE& ble; 44 | }; 45 | 46 | class PhyphoxBLE 47 | { 48 | 49 | private: 50 | 51 | static PhyphoxBleEventHandler eventHandler; 52 | 53 | 54 | static const UUID phyphoxExperimentServiceUUID; 55 | static const UUID phyphoxDataServiceUUID; 56 | 57 | static const UUID experimentCharacteristicUUID; 58 | static const UUID dataCharacteristicUUID; 59 | static const UUID configCharacteristicUUID; 60 | static const UUID eventCharacteristicUUID; 61 | 62 | static char name[50]; 63 | 64 | static uint8_t data_package[20]; 65 | static uint8_t eventData[17]; 66 | static uint8_t config_package[CONFIGSIZE]; 67 | 68 | /*BLE stuff*/ 69 | static BLE& ble; 70 | static ReadWriteArrayGattCharacteristic dataCharacteristic; //Note: Use { } instead of () google most vexing parse 71 | static uint8_t readValue[DATASIZE]; 72 | static ReadWriteArrayGattCharacteristic eventCharacteristic; 73 | static ReadWriteArrayGattCharacteristic configCharacteristic; 74 | static ReadOnlyArrayGattCharacteristic experimentCharacteristic; 75 | 76 | static Thread bleEventThread; 77 | static Thread transferExpThread; 78 | static EventQueue queue; 79 | /*end BLE stuff*/ 80 | static EventQueue transferQueue; 81 | 82 | 83 | //helper function to initialize BLE server and for connection poperties 84 | static void bleInitComplete(BLE::InitializationCompleteCallbackContext*); 85 | static void when_subscription_received(GattAttribute::Handle_t); 86 | static void configReceived(const GattWriteCallbackParams *params); 87 | static void eventReceived(const GattWriteCallbackParams *params); 88 | 89 | //helper functon that runs in the thread ble_server 90 | //static void waitForEvent(); 91 | static void transferExp(); 92 | static GattCharacteristic* phyphoxCharacteristics[]; 93 | static GattService phyphoxService; 94 | 95 | static GattCharacteristic* phyphoxDataCharacteristics[]; 96 | static GattService phyphoxDataService; 97 | 98 | static void schedule_ble_events(BLE::OnEventsToProcessCallbackContext *context); 99 | 100 | #ifndef NDEBUG 101 | static inline HardwareSerial* printer; //for debug purpose 102 | #endif 103 | static uint8_t* data; //this pointer points to the data the user wants to write in the characteristic 104 | static uint8_t* config; 105 | static uint8_t* event; 106 | static uint8_t* p_exp; //this pointer will point to the byte array which holds an experiment 107 | 108 | 109 | public: 110 | 111 | static char EXPARRAY[4096];// block some storage 112 | static size_t expLen; //try o avoid this maybe use std::array or std::vector 113 | 114 | static inline uint16_t minConInterval = 6; //7.5ms 115 | static inline uint16_t maxConInterval = 24; //30ms 116 | static inline uint16_t slaveLatency = 0; 117 | static inline uint16_t timeout = 50; 118 | static inline uint16_t MTU = 20; 119 | static inline uint16_t currentConnections = 0; 120 | 121 | static void (*configHandler)(); 122 | static void (*experimentEventHandler)(); 123 | 124 | static void printXML(HardwareSerial*); 125 | 126 | static void poll(); 127 | static void poll(int timeout); 128 | 129 | static void start(const char* DEVICE_NAME, uint8_t* p, size_t n = 0); 130 | static void start(const char* DEVICE_NAME); 131 | static void start(uint8_t* p, size_t n = 0); 132 | static void start(); 133 | 134 | static void write(uint8_t*, unsigned int); 135 | static void write(float&); 136 | static void write(float&, float&, float&, float&, float&); 137 | static void write(float&, float&, float&, float&); 138 | static void write(float&, float&, float&); 139 | static void write(float&, float&); 140 | static void read(uint8_t*, unsigned int); 141 | static void read(float&); 142 | static void read(float&, float&); 143 | static void read(float&, float&, float&); 144 | static void read(float&, float&, float&, float&); 145 | static void read(float&, float&, float&, float&, float&); 146 | 147 | static void addExperiment(PhyphoxBleExperiment&); 148 | 149 | static int64_t experimentTime; 150 | static int64_t systemTime; 151 | static uint8_t eventType; 152 | 153 | #ifndef NDEBUG 154 | static void begin(HardwareSerial*); //for debug purpose 155 | static void output(const char*); //for debug purpose 156 | static void output(const uint32_t); 157 | #endif 158 | }; 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /examples/CreateExperiment/CreateExperiment.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void setup() 4 | { 5 | Serial.begin(115200); 6 | PhyphoxBLE::start("create experiment"); 7 | 8 | //Experiment 9 | PhyphoxBleExperiment plotRandomValues; //generate experiment on Arduino which plot random values 10 | 11 | 12 | plotRandomValues.setTitle("Create Experiment"); 13 | plotRandomValues.setCategory("Arduino Experiments"); 14 | plotRandomValues.setDescription("Random numbers are generated on Arduino and visualized with phyphox afterwards"); 15 | 16 | //View 17 | PhyphoxBleExperiment::View firstView; 18 | firstView.setLabel("FirstView"); //Create a "view" 19 | PhyphoxBleExperiment::View secondView; 20 | secondView.setLabel("SecondView"); //Create a "view" 21 | 22 | //Graph 23 | PhyphoxBleExperiment::Graph firstGraph; //Create graph which will plot random numbers over time 24 | firstGraph.setMinY(0, LAYOUT_FIXED); 25 | firstGraph.setMaxY(100, LAYOUT_FIXED); 26 | firstGraph.setLabel("Random number over time"); 27 | firstGraph.setUnitX("s"); 28 | firstGraph.setUnitY(""); 29 | firstGraph.setLabelX("time"); 30 | firstGraph.setLabelY("random number"); 31 | firstGraph.setXPrecision(1); //The amount of digits shown after the decimal point 32 | firstGraph.setYPrecision(1); 33 | 34 | /* Assign Channels, so which data is plotted on x or y axis 35 | first parameter represents x-axis, second y-axis 36 | Channel 0 means a timestamp is created after the BLE package arrives in phyphox 37 | Channel 1 to N corresponding to the N-parameter which is written in server.write() 38 | */ 39 | 40 | firstGraph.setChannel(0, 1); 41 | 42 | //Second Graph 43 | 44 | PhyphoxBleExperiment::Graph secondGraph; //Create graph which will plot random numbers over time 45 | secondGraph.setLabel("Random number squared over random number"); 46 | secondGraph.setUnitX(""); 47 | secondGraph.setUnitY(""); 48 | secondGraph.setLabelX("random number"); 49 | secondGraph.setLabelY("squared"); 50 | secondGraph.setStyle(STYLE_DOTS); 51 | secondGraph.setColor("2E728E"); //Sets Color of line 52 | 53 | /* Assign Channels, so which data is plotted on x or y axis 54 | first parameter represents x-axis, second y-axis 55 | Channel 0 means a timestamp is created after the BLE package arrives in phyphox 56 | Channel 1 to N corresponding to the N-parameter which is written in server.write() 57 | */ 58 | 59 | secondGraph.setChannel(1, 2); 60 | 61 | 62 | //Info 63 | PhyphoxBleExperiment::InfoField myInfo; //Creates an info-box. 64 | myInfo.setInfo("This is an Info field!"); 65 | myInfo.setColor("890128"); //Sets font color. Uses a 6 digit hexadecimal value in "quotation marks". 66 | myInfo.setXMLAttribute("size=\"1.2\""); 67 | 68 | //Separator 69 | PhyphoxBleExperiment::Separator mySeparator; //Creates a line to separate elements. 70 | mySeparator.setHeight(0.3); //Sets height of the separator. 71 | mySeparator.setColor("404040"); //Sets color of the separator. Uses a 6 digit hexadecimal value in "quotation marks". 72 | 73 | //Value 74 | PhyphoxBleExperiment::Value myValue; //Creates a value-box. 75 | myValue.setLabel("Number"); //Sets the label 76 | myValue.setPrecision(2); //The amount of digits shown after the decimal point. 77 | myValue.setUnit("unit"); //The physical unit associated with the displayed value. 78 | myValue.setColor("FFFFFF"); //Sets font color. Uses a 6 digit hexadecimal value in "quotation marks". 79 | myValue.setChannel(0); 80 | myValue.setXMLAttribute("size=\"2\""); 81 | 82 | //Export 83 | PhyphoxBleExperiment::ExportSet mySet; //Provides exporting the data to excel etc. 84 | mySet.setLabel("mySet"); 85 | PhyphoxBleExperiment::ExportData myData1; 86 | myData1.setLabel("myData1"); 87 | myData1.setDatachannel(1); 88 | 89 | PhyphoxBleExperiment::ExportData myData2; 90 | myData2.setLabel("myData2"); 91 | myData2.setDatachannel(2); 92 | 93 | //attach to experiment 94 | 95 | firstView.addElement(firstGraph); //attach graph to view 96 | firstView.addElement(secondGraph); //attach second graph to view 97 | secondView.addElement(myInfo); //attach info to view 98 | secondView.addElement(mySeparator); //attach separator to view 99 | secondView.addElement(myValue); //attach value to view 100 | 101 | plotRandomValues.addView(firstView); //attach view to experiment 102 | plotRandomValues.addView(secondView); 103 | mySet.addElement(myData1); //attach data to exportSet 104 | mySet.addElement(myData2); //attach data to exportSet 105 | plotRandomValues.addExportSet(mySet); //attach exportSet to experiment 106 | PhyphoxBLE::addExperiment(plotRandomValues); //attach experiment to server 107 | } 108 | 109 | 110 | void loop() 111 | { 112 | float randomValue = random(0, 100); //create random number between 0 - 100 113 | float randomValue2 = randomValue * randomValue; 114 | 115 | /* The random number is written into Channel 1 116 | Up to 5 Channels can written at the same time with server.write(randomDistance, valueChannel2, valueChannel3.. ) 117 | */ 118 | PhyphoxBLE::write(randomValue, randomValue2); 119 | delay(50); 120 | 121 | PhyphoxBLE::poll(); //Only required for the Arduino Nano 33 IoT, but it does no harm for other boards. 122 | } -------------------------------------------------------------------------------- /src/boards/NINAB31serial.cpp: -------------------------------------------------------------------------------- 1 | #if defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SENSEBOX_MCU_ESP32S2) 2 | #include "NINAB31serial.h" 3 | 4 | #if defined(ARDUINO_SAMD_MKR1000) 5 | #define SerialBLE Serial3 6 | #elif defined(ARDUINO_SENSEBOX_MCU_ESP32S2) 7 | #define SerialBLE Serial1 8 | #endif 9 | 10 | 11 | String NINAB31Serial::m_input=""; 12 | bool NINAB31Serial::connected=false; 13 | 14 | bool NINAB31Serial::configModule(){ 15 | SerialBLE.print("AT+UMRS=115200,2,8,1,1\r"); //115200, no flow control, 8 data bits, 1 stop bit, no parity 16 | SerialBLE.print("AT&W0\r"); //write configuration to nonvolatile memory 17 | SerialBLE.print("AT+CPWROFF\r"); //restart module into new configuration 18 | SerialBLE.flush(); 19 | delay(2000); //wait for module to come up 20 | /*digitalWrite(22,HIGH); //hardware power cycle - avoid if possible 21 | delay(500); 22 | digitalWrite(22,LOW); 23 | delay(1000); 24 | */ 25 | return checkResponse("ATE0",500); //send command (echo off) and check if module is responding correctly 26 | } 27 | 28 | bool NINAB31Serial::begin(){ 29 | SerialBLE.begin(115200); 30 | delay(500); 31 | checkResponse("AT",500); //throwaway command in case buffer gets reinitialized because SerialBLE.begin was already called 32 | for(int i=0;i<3;i++){ //try to send a command, in case it fails configure device and restart it 33 | if(checkResponse("ATE0",500)){ 34 | return true; 35 | }else{ 36 | if(configModule()){ 37 | return true; 38 | } 39 | } 40 | } 41 | return false; 42 | } 43 | 44 | bool NINAB31Serial::setLocalName(String name){ 45 | if(name.length()>29){ 46 | return false; 47 | } 48 | return checkResponse(String("AT+UBTLN=\"")+name+"\"", 1000); 49 | } 50 | 51 | bool NINAB31Serial::advertise(){ 52 | return checkResponse(String("AT+UBTDM=3"), 1000); 53 | } 54 | 55 | bool NINAB31Serial::stopAdvertise(){ 56 | return checkResponse(String("AT+UBTDM=1"), 1000); 57 | } 58 | 59 | 60 | bool NINAB31Serial::setConnectionInterval(int minInterval, int maxInterval){ 61 | if(minInterval<32 || minInterval >16384 || maxInterval<32 || maxInterval >16384 || maxInterval40){ 72 | return false; 73 | } 74 | return checkResponse(String("AT+UBTGSN=0,")+characteristic+","+value, 1000); 75 | } 76 | 77 | bool NINAB31Serial::writeValue(int characteristic, uint8_t* value, int len){ 78 | if(!connected){ 79 | return false; 80 | } 81 | if(len>20){ 82 | return false; 83 | } 84 | auto msg=String("AT+UBTGSN=0,")+characteristic+","; 85 | for(int i=0;i>4,HEX); 87 | msg+=String((value[i]&0xf),HEX); 88 | } 89 | return checkResponse(msg, 1000); 90 | } 91 | 92 | 93 | 94 | int NINAB31Serial::parseResponse(String cmd, uint32_t timeout){ 95 | while(SerialBLE.available()){ 96 | (SerialBLE.read()); 97 | } 98 | SerialBLE.print(cmd); 99 | SerialBLE.write('\r'); 100 | SerialBLE.flush(); 101 | String input=""; 102 | auto starttime=millis(); 103 | while(millis()-starttime seccommapos){ 182 | int rhandle=m_input.substring(commapos+1,seccommapos).toInt(); 183 | if(rhandle==handle){ 184 | return m_input.substring(seccommapos+1,thirdcommapos); 185 | } 186 | } 187 | 188 | }else{ 189 | //Serial.println(m_input); 190 | } 191 | return ""; 192 | 193 | } 194 | 195 | void NINAB31Serial::flushInput(){ 196 | m_input=""; 197 | } 198 | 199 | #endif 200 | 201 | 202 | -------------------------------------------------------------------------------- /COPYING.LESSER: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /src/graph.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | #include 4 | 5 | void PhyphoxBleExperiment::Graph::setUnitX(const char *ux){ 6 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(ux, 5, "setUnitX") : ERROR; 7 | copyToMem(&UNITX, (std::string(ux)).c_str()); 8 | } 9 | 10 | void PhyphoxBleExperiment::Graph::setUnitY(const char *uy){ 11 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(uy, 5, "setUnitY") : ERROR; 12 | copyToMem(&UNITY, (std::string(uy)).c_str()); 13 | } 14 | 15 | void PhyphoxBleExperiment::Graph::setLabelX(const char *lx){ 16 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(lx, 20, "setLabelX") : ERROR; 17 | copyToMem(&LABELX, (std::string(lx)).c_str()); 18 | } 19 | 20 | void PhyphoxBleExperiment::Graph::setLabelY(const char *ly){ 21 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(ly, 20, "setLabelY") : ERROR; 22 | copyToMem(&LABELY, (std::string(ly)).c_str()); 23 | } 24 | 25 | 26 | void PhyphoxBleExperiment::Graph::setColor(const char *c){ 27 | FIRSTSUBGRAPH.setColor(c); 28 | } 29 | 30 | void PhyphoxBleExperiment::Graph::setLinewidth(float w){ 31 | FIRSTSUBGRAPH.setLinewidth(w); 32 | } 33 | 34 | void PhyphoxBleExperiment::Graph::setXPrecision(int px){ 35 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(px, 9999, "setXPrecision") : ERROR; 36 | char tmp[20]; 37 | sprintf(tmp, "%i", px); 38 | copyToMem(&XPRECISION, tmp); 39 | } 40 | 41 | void PhyphoxBleExperiment::Graph::setYPrecision(int py){ 42 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(py, 9999, "setYPrecision") : ERROR; 43 | char tmp[20]; 44 | sprintf(tmp, "%i", py); 45 | copyToMem(&YPRECISION, tmp); 46 | } 47 | 48 | void PhyphoxBleExperiment::Graph::setTimeOnX(bool b){ 49 | char tmp[10]; 50 | if(b){ 51 | sprintf(tmp, "true"); 52 | }else{ 53 | sprintf(tmp, "false"); 54 | } 55 | copyToMem(&TIMEONX, tmp); 56 | } 57 | 58 | void PhyphoxBleExperiment::Graph::setTimeOnY(bool b){ 59 | char tmp[10]; 60 | if(b){ 61 | sprintf(tmp, "true"); 62 | }else{ 63 | sprintf(tmp, "false"); 64 | } 65 | copyToMem(&TIMEONY, tmp); 66 | } 67 | 68 | void PhyphoxBleExperiment::Graph::setSystemTime(bool b){ 69 | char tmp[10]; 70 | if(b){ 71 | sprintf(tmp, "true"); 72 | }else{ 73 | sprintf(tmp, "false"); 74 | } 75 | copyToMem(&SYSTEMTIME, tmp); 76 | } 77 | 78 | void PhyphoxBleExperiment::Graph::setChannel(int x, int y) 79 | { 80 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(x, numberOfChannels, "setChannel") : ERROR; 81 | ERROR = ERROR.MESSAGE == NULL ? err_checkUpper(y, numberOfChannels, "setChannel") : ERROR; 82 | 83 | char tmpX[20]; 84 | sprintf(tmpX, "CH%i", x); 85 | copyToMem(&FIRSTSUBGRAPH.INPUTX, tmpX); 86 | char tmpY[20]; 87 | sprintf(tmpY, "CH%i", y); 88 | copyToMem(&FIRSTSUBGRAPH.INPUTY, tmpY); 89 | SUBGRAPHS[0]=&FIRSTSUBGRAPH; 90 | } 91 | void PhyphoxBleExperiment::Graph::addSubgraph(Subgraph& sg){ 92 | for (int i = 0; i < phyphoxBleNChannel; i++){ 93 | if(SUBGRAPHS[i]==nullptr){ 94 | SUBGRAPHS[i] = &sg; 95 | if(ERROR.MESSAGE==NULL){ 96 | ERROR = sg.ERROR; 97 | } 98 | break; 99 | } 100 | } 101 | } 102 | 103 | void PhyphoxBleExperiment::Graph::setStyle(const char *s){ 104 | FIRSTSUBGRAPH.setStyle(s); 105 | } 106 | 107 | void PhyphoxBleExperiment::Graph::setMinX(float value, const char * layout) { 108 | /** 109 | * Sets the min x value for the co-system. 110 | * 111 | * @param value The min x value. 112 | * @param layout Choose between auto, extend and fixed. 113 | */ 114 | std::string layoutString; 115 | layoutString.assign(layout); 116 | std::ostringstream valueStringStream; 117 | valueStringStream << value; 118 | if(strcmp(layout, LAYOUT_AUTO) == 0 || strcmp(layout, LAYOUT_EXTEND) == 0 || strcmp(layout, LAYOUT_FIXED) == 0) { 119 | copyToMem(&MINX, (" scaleMinX=\"" + layoutString + "\"" + " minX=\"" + valueStringStream.str() + "\"").c_str()); 120 | } else { 121 | ERROR = ERROR.MESSAGE == NULL ? err_checkLayout(layout, "setMinX") : ERROR; 122 | } 123 | } 124 | 125 | void PhyphoxBleExperiment::Graph::setMaxX(float value, const char * layout) { 126 | /** 127 | * Sets the min x value for the co-system. 128 | * 129 | * @param value The max x value. 130 | * @param layout Choose between auto, extend and fixed. 131 | */ 132 | std::string layoutString; 133 | layoutString.assign(layout); 134 | std::ostringstream valueStringStream; 135 | valueStringStream << value; 136 | if(strcmp(layout, "auto") == 0 || strcmp(layout, "extend") == 0 || strcmp(layout, "fixed") == 0) { 137 | copyToMem(&MAXX, (" scaleMaxX=\"" + layoutString + "\"" + " maxX=\"" + valueStringStream.str() + "\"").c_str()); 138 | } else { 139 | ERROR = ERROR.MESSAGE == NULL ? err_checkLayout(layout, "setMaxX") : ERROR; 140 | } 141 | } 142 | 143 | void PhyphoxBleExperiment::Graph::setMinY(float value, const char * layout) { 144 | /** 145 | * Sets the min x value for the co-system. 146 | * 147 | * @param value The min y value. 148 | * @param layout Choose between auto, extend and fixed. 149 | */ 150 | std::string layoutString; 151 | layoutString.assign(layout); 152 | std::ostringstream valueStringStream; 153 | valueStringStream << value; 154 | if(strcmp(layout, "auto") == 0 || strcmp(layout, "extend") == 0 || strcmp(layout, "fixed") == 0) { 155 | copyToMem(&MINY, (" scaleMinY=\"" + layoutString + "\"" + " minY=\"" + valueStringStream.str() + "\"").c_str()); 156 | } else { 157 | ERROR = ERROR.MESSAGE == NULL ? err_checkLayout(layout, "setMinY") : ERROR; 158 | } 159 | } 160 | void PhyphoxBleExperiment::Graph::setMaxY(float value, const char * layout) { 161 | /** 162 | * Sets the min x value for the co-system. 163 | * 164 | * @param value The max y value. 165 | * @param layout Choose between auto, extend and fixed. 166 | */ 167 | std::string layoutString; 168 | layoutString.assign(layout); 169 | std::ostringstream valueStringStream; 170 | valueStringStream << value; 171 | if(strcmp(layout, "auto") == 0 || strcmp(layout, "extend") == 0 || strcmp(layout, "fixed") == 0) { 172 | copyToMem(&MAXY, (" scaleMaxY=\"" + layoutString + "\"" + " maxY=\"" + valueStringStream.str() + "\"").c_str()); 173 | } else { 174 | ERROR = ERROR.MESSAGE == NULL ? err_checkLayout(layout, "setMaxY") : ERROR; 175 | } 176 | } 177 | 178 | void PhyphoxBleExperiment::Graph::setXMLAttribute(const char *xml){ 179 | ERROR = ERROR.MESSAGE == NULL ? err_checkLength(xml, 98, "setXMLAttribute") : ERROR; 180 | copyToMem(&XMLAttribute, (" " + std::string(xml)).c_str()); 181 | } 182 | 183 | void PhyphoxBleExperiment::Graph::getBytes(char *buffArray) 184 | { 185 | strcat(buffArray,"\t\t"); 251 | 252 | for(int i=0;igetBytes(buffArray); 255 | } 256 | } 257 | 258 | strcat(buffArray, "\n\t\t\n"); 259 | 260 | } -------------------------------------------------------------------------------- /src/phyphoxBleExperiment.h: -------------------------------------------------------------------------------- 1 | #ifndef PHYPHOX_BLE_EXPERIMENT 2 | #define PHYPHOX_BLE_EXPERIMENT 3 | 4 | #include 5 | #include "copyToMem.h" 6 | #include "defines.h" 7 | #include 8 | 9 | class PhyphoxBleExperiment { 10 | 11 | public: 12 | 13 | PhyphoxBleExperiment() = default; 14 | PhyphoxBleExperiment(const PhyphoxBleExperiment&) = delete; 15 | PhyphoxBleExperiment &operator=(const PhyphoxBleExperiment &) = delete; 16 | ~PhyphoxBleExperiment() = default; 17 | 18 | static uint16_t MTU; 19 | 20 | class Error { 21 | public: 22 | char* MESSAGE = NULL; 23 | void getBytes(char *); 24 | }; 25 | 26 | 27 | class Errorhandler { 28 | public: 29 | virtual Error err_checkLength(const char *, int, const char *); 30 | virtual Error err_checkUpper(int, int, const char *); 31 | virtual Error err_checkHex(const char *, const char *); 32 | virtual Error err_checkStyle(const char *, const char *); 33 | virtual Error err_checkLayout(const char *, const char *); 34 | virtual Error err_checkComponent(const char *, const char *); 35 | virtual Error err_checkSensor(const char *, const char *); 36 | }; 37 | 38 | class Element : public Errorhandler { 39 | public: 40 | Element() = default; 41 | Element(const Element &) = delete; 42 | Element &operator=(const Element &) = delete; 43 | ~Element() = default; 44 | 45 | int typeID = 0; 46 | 47 | char* LABEL = NULL; 48 | 49 | Error ERROR; 50 | 51 | virtual void setLabel(const char *); 52 | virtual void getBytes(char *)=0; 53 | 54 | private: 55 | }; 56 | 57 | 58 | 59 | class Graph : public Element 60 | { 61 | public: 62 | 63 | class Subgraph : public Errorhandler 64 | { 65 | public: 66 | Subgraph() = default; 67 | Subgraph(const Subgraph &) = delete; 68 | Subgraph &operator=(const Subgraph &) = delete; 69 | ~Subgraph() = default; 70 | 71 | char* INPUTX = NULL; 72 | char* INPUTY = NULL; 73 | char* COLOR = NULL; 74 | char* WIDTH = NULL; 75 | char* STYLE = NULL; 76 | bool isActive = false; 77 | 78 | void setColor(const char *); 79 | void setStyle(const char *); 80 | void setLinewidth(float w); 81 | void setChannel(int, int); 82 | void getBytes(char *); 83 | 84 | Error ERROR; 85 | private: 86 | }; 87 | 88 | Graph() = default; 89 | Graph(const Graph &) = delete; 90 | Graph &operator=(const Graph &) = delete; 91 | ~Graph() = default; 92 | 93 | char* UNITX = NULL; 94 | char* UNITY = NULL; 95 | char* LABELX = NULL; 96 | char* LABELY = NULL; 97 | char* XPRECISION = NULL; 98 | char* YPRECISION = NULL; 99 | char* MINX = NULL; 100 | char* MAXX = NULL; 101 | char* MINY = NULL; 102 | char* MAXY = NULL; 103 | 104 | char* TIMEONX = NULL; 105 | char* TIMEONY = NULL; 106 | 107 | char* SYSTEMTIME = NULL; 108 | 109 | char* INPUTX = NULL; 110 | char* INPUTY = NULL; 111 | 112 | Subgraph *SUBGRAPHS[phyphoxBleNChannel]={nullptr}; 113 | Subgraph FIRSTSUBGRAPH; 114 | 115 | char* XMLAttribute = NULL; 116 | 117 | void setUnitX(const char *); 118 | void setUnitY(const char *); 119 | void setLabelX(const char *); 120 | void setLabelY(const char *); 121 | void setXPrecision(int); 122 | void setYPrecision(int); 123 | void setTimeOnX(bool); 124 | void setTimeOnY(bool); 125 | void setSystemTime(bool); 126 | 127 | void setChannel(int, int); 128 | void addSubgraph(Subgraph &); 129 | void addChannel(int, int, const char*); 130 | void setStyle(const char *); 131 | void setColor(const char *); 132 | void setLinewidth(float w); 133 | void setMinX(float, const char *); 134 | void setMaxX(float, const char *); 135 | void setMinY(float, const char *); 136 | void setMaxY(float, const char *); 137 | void setXMLAttribute(const char *); 138 | 139 | void phyphoxTimestamp(); 140 | void getBytes(char *); 141 | 142 | private: 143 | }; 144 | 145 | class View 146 | { 147 | public: 148 | View() = default; 149 | View(const View &) = delete; 150 | View &operator=(const View &) = delete; 151 | ~View() = default; 152 | 153 | void setLabel(const char *); 154 | void getBytes(char *, uint8_t); 155 | void addElement(Element &); 156 | void setXMLAttribute(const char *); 157 | 158 | char* LABEL = NULL; 159 | char* XMLAttribute = NULL; 160 | 161 | Element *ELEMENTS[phyphoxBleNElements] = {nullptr}; 162 | 163 | private: 164 | }; 165 | 166 | class ExportData : public Element 167 | { 168 | public: 169 | ExportData(){}; 170 | ExportData(const ExportData &) = delete; 171 | ExportData &operator=(const ExportData &) = delete; 172 | ~ExportData() = default; 173 | 174 | char* BUFFER = NULL; 175 | char* LABEL = NULL; 176 | char* XMLAttribute = NULL; 177 | void setDatachannel(int); 178 | void setXMLAttribute(const char *); 179 | void setLabel(const char *); 180 | void getBytes(char *); 181 | 182 | private: 183 | }; 184 | 185 | class ExportSet 186 | { 187 | public: 188 | ExportSet(){}; 189 | ExportSet(const ExportSet &) = delete; 190 | ExportSet &operator=(const ExportSet &) = delete; 191 | ~ExportSet() = default; 192 | 193 | void setLabel(const char *); 194 | void getBytes(char *); 195 | void addElement(Element &); 196 | void setXMLAttribute(const char *); 197 | 198 | char* LABEL = NULL; 199 | char* XMLAttribute = NULL; 200 | Element *ELEMENTS[phyphoxBleNExportSets] = {nullptr}; 201 | 202 | private: 203 | }; 204 | 205 | class Sensor : public Errorhandler 206 | { 207 | public: 208 | Sensor(){}; 209 | Sensor(const Sensor &) = delete; 210 | Sensor &operator=(const Sensor &) = delete; 211 | ~Sensor() = default; 212 | 213 | void setType(const char *); 214 | void setComponent(const char *); 215 | void setAverage(bool); 216 | void setRate(int); 217 | void mapChannel(const char *, int); 218 | void setXMLAttribute(const char *); 219 | void getBytes(char *); 220 | 221 | char* TYPE = NULL; 222 | char* CHANNEL[5] = {NULL}; 223 | char* COMPONENT[5] = {NULL}; 224 | char* RATE = NULL; 225 | char* AVERAGE = NULL; 226 | char* XMLAttribute = NULL; 227 | 228 | Error ERROR; 229 | 230 | private: 231 | }; 232 | 233 | class InfoField : public Element 234 | { 235 | public: 236 | InfoField(){}; 237 | InfoField(const InfoField &) = delete; 238 | InfoField &operator=(const InfoField &) = delete; 239 | ~InfoField() = default; 240 | 241 | void setInfo(const char *); 242 | void setColor(const char *); 243 | void setXMLAttribute(const char *); 244 | void getBytes(char *); 245 | 246 | char* INFO = NULL; 247 | char* COLOR = NULL; 248 | char* XMLAttribute = NULL; 249 | 250 | private: 251 | }; 252 | 253 | class Separator : public Element 254 | { 255 | public: 256 | Separator(){}; 257 | Separator(const Separator &) = delete; 258 | Separator &operator=(const Separator &) = delete; 259 | ~Separator() = default; 260 | 261 | void setHeight(float); 262 | void setColor(const char *); 263 | void setXMLAttribute(const char *); 264 | void getBytes(char *); 265 | 266 | char* COLOR = NULL; 267 | char* HEIGHT = NULL; 268 | char* XMLAttribute = NULL; 269 | 270 | private: 271 | }; 272 | 273 | class Value : public Element 274 | { 275 | public: 276 | Value(){}; 277 | Value(const Value &) = delete; 278 | Value &operator=(const Value &) = delete; 279 | ~Value() = default; 280 | 281 | void setPrecision(int); 282 | void setUnit(const char *); 283 | void setColor(const char *); 284 | void getBytes(char *); 285 | void setChannel(int); 286 | void setXMLAttribute(const char *); 287 | 288 | char* PRECISION = NULL; 289 | char* UNIT = NULL; 290 | char* COLOR = NULL; 291 | char* INPUTVALUE = NULL; 292 | char* XMLAttribute = NULL; 293 | 294 | private: 295 | }; 296 | 297 | class Edit : public Element 298 | { 299 | public: 300 | Edit(){}; 301 | Edit(const Edit &) = delete; 302 | Edit &operator=(const Edit &) = delete; 303 | ~Edit() = default; 304 | 305 | void setUnit(const char *); 306 | void setSigned(bool); 307 | void setDecimal(bool); 308 | void setXMLAttribute(const char *); 309 | void setChannel(int); 310 | void getBytes(char *); 311 | 312 | char* UNIT = NULL; 313 | char* SIGNED = NULL; 314 | char* DECIMAL = NULL; 315 | char* XMLAttribute = NULL; 316 | char* BUFFER = NULL; 317 | 318 | private: 319 | }; 320 | 321 | void setTitle(const char *); 322 | void setCategory(const char *); 323 | void setDescription(const char *); 324 | void setColor(const char *); 325 | void setRepeating(const int); 326 | // void setConfig(const char *); 327 | void setSubscribeOnStart(bool); 328 | 329 | void getBytes(char *); 330 | void getFirstBytes(char *, const char *); 331 | void getViewBytes(char *, uint8_t, uint8_t); 332 | void getLastBytes(char *); 333 | void addView(View &); 334 | void addSensor(Sensor &); 335 | void addExportSet(ExportSet &); 336 | 337 | char* TITLE = NULL; 338 | char* CATEGORY = NULL; 339 | char* DESCRIPTION = NULL; 340 | char* COLOR = NULL; 341 | // char* CONFIG = NULL; 342 | char* SUBSCRIBEONSTART = NULL; 343 | 344 | View *VIEWS[phyphoxBleNViews] = {nullptr}; 345 | Sensor *SENSORS[phyphoxBleNSensors] = {nullptr}; 346 | ExportSet *EXPORTSETS[phyphoxBleNExportSets] = {nullptr}; 347 | 348 | static int numberOfChannels; 349 | int repeating = 0; 350 | 351 | 352 | }; 353 | 354 | #endif 355 | -------------------------------------------------------------------------------- /src/boards/phyphoxBLE_NINAB31.cpp: -------------------------------------------------------------------------------- 1 | #if defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SENSEBOX_MCU_ESP32S2) 2 | 3 | #include "phyphoxBle.h" 4 | #include "NINAB31serial.h" 5 | #include "Arduino.h" 6 | #include 7 | /* 8 | BLEService PhyphoxBLE::phyphoxExperimentService{phyphoxBleExperimentServiceUUID}; // create service 9 | BLECharacteristic PhyphoxBLE::experimentCharacteristic{phyphoxBleExperimentCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 10 | BLECharacteristic PhyphoxBLE::controlCharacteristic{phyphoxBleExperimentControlCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 11 | 12 | BLEService PhyphoxBLE::phyphoxDataService{phyphoxBleDataServiceUUID}; // create service 13 | BLECharacteristic PhyphoxBLE::dataCharacteristic{phyphoxBleDataCharacteristicUUID, BLERead | BLEWrite | BLENotify, 20, false}; 14 | BLECharacteristic PhyphoxBLE::configCharacteristic{phyphoxBleConfigCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 15 | */ 16 | uint16_t PhyphoxBLE::minConInterval = 12; //7.5ms 17 | uint16_t PhyphoxBLE::maxConInterval = 48; //30ms 18 | uint16_t PhyphoxBLE::slaveLatency = 0; 19 | uint16_t PhyphoxBLE::timeout = 50; 20 | 21 | uint16_t PhyphoxBLE::MTU = 20; 22 | uint16_t PhyphoxBleExperiment::MTU = 20; 23 | 24 | int PhyphoxBLE::h_phyphoxExperimentService=0; 25 | int PhyphoxBLE::h_experimentCharacteristic=0; 26 | int PhyphoxBLE::h_controlCharacteristic=0; 27 | 28 | int PhyphoxBLE::h_phyphoxDataService=0; 29 | int PhyphoxBLE::h_dataCharacteristic=0; 30 | int PhyphoxBLE::h_configCharacteristic=0; 31 | 32 | bool PhyphoxBLE::exploaded=false; 33 | 34 | uint8_t* PhyphoxBLE::data = nullptr; //this pointer points to the data the user wants to write in the characteristic 35 | uint8_t* PhyphoxBLE::p_exp = nullptr; //this pointer will point to the byte array which holds an experiment 36 | 37 | size_t PhyphoxBLE::expLen = 0; //try o avoid this maybe use std::array or std::vector 38 | uint8_t PhyphoxBLE::EXPARRAY[4000] = {0};// block some storage 39 | 40 | uint8_t PhyphoxBLE::controlCharValue[21]={0}; 41 | uint8_t PhyphoxBLE::configCharValue[21]={0}; 42 | 43 | 44 | 45 | void(*PhyphoxBLE::configHandler)() = nullptr; 46 | 47 | void PhyphoxBLE::start(const char* DEVICE_NAME, uint8_t* exp_pointer, size_t len){ 48 | p_exp = exp_pointer; 49 | expLen = len; 50 | start(DEVICE_NAME); 51 | } 52 | 53 | void PhyphoxBLE::start(uint8_t* exp_pointer, size_t len){ 54 | p_exp = exp_pointer; 55 | expLen = len; 56 | start(); 57 | } 58 | 59 | void PhyphoxBLE::start(const char* DEVICE_NAME) 60 | { 61 | port.begin(); 62 | port.stopAdvertise(); 63 | 64 | port.checkResponse("AT+UBTAD=020A0605121800280011074A5153BA405E438B7146F7300100DFCD",1000); 65 | h_phyphoxExperimentService=port.parseResponse("AT+UBTGSER=CDDF000130F746718B435E40BA53514A",1000); 66 | h_controlCharacteristic=port.parseResponse("AT+UBTGCHA=CDDF000330F746718B435E40BA53514A,1a,1,1",1000); 67 | h_experimentCharacteristic=port.parseResponse("AT+UBTGCHA=CDDF000230F746718B435E40BA53514A,1a,1,1",1000); 68 | 69 | h_phyphoxDataService=port.parseResponse("AT+UBTGSER=CDDF100130F746718B435E40BA53514A",1000); 70 | h_dataCharacteristic=port.parseResponse("AT+UBTGCHA=CDDF100230F746718B435E40BA53514A,1a,1,1",1000); 71 | h_configCharacteristic=port.parseResponse("AT+UBTGCHA=CDDF100330F746718B435E40BA53514A,1a,1,1",1000); 72 | 73 | 74 | 75 | if(p_exp == nullptr){ 76 | 77 | PhyphoxBleExperiment defaultExperiment; 78 | 79 | //View 80 | PhyphoxBleExperiment::View firstView; 81 | 82 | //Graph 83 | PhyphoxBleExperiment::Graph firstGraph; //Create graph which will plot random numbers over time 84 | firstGraph.setChannel(0,1); 85 | 86 | firstView.addElement(firstGraph); 87 | defaultExperiment.addView(firstView); 88 | 89 | addExperiment(defaultExperiment); 90 | } 91 | 92 | 93 | 94 | 95 | // set connection parameter 96 | port.setConnectionInterval(minConInterval, maxConInterval); 97 | port.checkResponse("AT+UBTAD=020A0605121800280011074A5153BA405E438B7146F7300100DFCD",1000); 98 | port.advertise(); 99 | port.setLocalName(DEVICE_NAME); 100 | 101 | } 102 | 103 | void PhyphoxBLE::start() { 104 | PhyphoxBLE::start("phyphox-senseBox"); 105 | } 106 | 107 | 108 | void PhyphoxBLE::poll(int timeout) 109 | { 110 | auto starttime=millis(); 111 | while(millis()-starttime(&value); 163 | port.writeValue(h_dataCharacteristic,data,4); 164 | } 165 | 166 | 167 | void PhyphoxBLE::write(float& f1, float& f2) 168 | { 169 | if(!exploaded){ 170 | return; 171 | } 172 | float array[2] = {f1, f2}; 173 | data = reinterpret_cast(array); 174 | port.writeValue(h_dataCharacteristic,data,8); 175 | } 176 | 177 | void PhyphoxBLE::write(float& f1, float& f2, float& f3) 178 | { 179 | if(!exploaded){ 180 | return; 181 | } 182 | float array[3] = {f1, f2, f3}; 183 | data = reinterpret_cast(array); 184 | port.writeValue(h_dataCharacteristic,data,12); 185 | } 186 | 187 | void PhyphoxBLE::write(float& f1, float& f2, float& f3 , float& f4) 188 | { 189 | if(!exploaded){ 190 | return; 191 | } 192 | float array[4] = {f1, f2, f3, f4}; 193 | data = reinterpret_cast(array); 194 | port.writeValue(h_dataCharacteristic,data,16); 195 | } 196 | 197 | void PhyphoxBLE::write(float& f1, float& f2, float& f3 , float& f4, float& f5) 198 | { 199 | if(!exploaded){ 200 | return; 201 | } 202 | float array[5] = {f1, f2, f3, f4, f5}; 203 | data = reinterpret_cast(array); 204 | port.writeValue(h_dataCharacteristic,data,20); 205 | } 206 | 207 | 208 | bool parseValue(uint8_t* target, String s){ 209 | String hextable="0123456789ABCDEF"; 210 | if(s.length() && s.length()<41){ 211 | s.toUpperCase(); 212 | for(int i=0;i> 24); 268 | experimentSizeArray[1]= (arrayLength >> 16); 269 | experimentSizeArray[2]= (arrayLength >> 8); 270 | experimentSizeArray[3]= arrayLength; 271 | 272 | uint8_t checksumArray[4] = {0}; 273 | checksumArray[0]= (checksum >> 24) & 0xFF; 274 | checksumArray[1]= (checksum >> 16) & 0xFF; 275 | checksumArray[2]= (checksum >> 8) & 0xFF; 276 | checksumArray[3]= checksum & 0xFF; 277 | 278 | memcpy(&header[0],&phyphox[0],7); 279 | memcpy(&header[0]+7,&experimentSizeArray[0],4); 280 | memcpy(&header[0]+7+4,&checksumArray[0],4); 281 | port.writeValue(h_experimentCharacteristic,header,sizeof(header)); 282 | 283 | for(size_t i = 0; i < exp_len/20; ++i){ 284 | memcpy(&header[0],&exp[0]+i*20,20); 285 | port.writeValue(h_experimentCharacteristic,header,sizeof(header)); 286 | delay(5); 287 | } 288 | 289 | if(exp_len%20 != 0){ 290 | const size_t rest = exp_len%20; 291 | uint8_t slice[rest]; 292 | memcpy(&slice[0],&exp[0]+exp_len-rest,rest); 293 | port.writeValue(h_experimentCharacteristic,slice,sizeof(slice)); 294 | } 295 | exploaded=true; 296 | 297 | port.advertise(); 298 | } 299 | 300 | void PhyphoxBLE::configCharacteristicWritten(){ 301 | if(configHandler!=nullptr){ 302 | (*configHandler)(); 303 | } 304 | } 305 | #endif 306 | -------------------------------------------------------------------------------- /src/boards/phyphoxBLE_NanoIOT.cpp: -------------------------------------------------------------------------------- 1 | #if defined(ARDUINO_SAMD_NANO_33_IOT) || defined(ARDUINO_UNOR4_WIFI) || defined(ARDUINO_SAMD_MKRWIFI1010) 2 | 3 | #include "phyphoxBLE_NanoIOT.h" 4 | #include "Arduino.h" 5 | #include 6 | 7 | BLEService PhyphoxBLE::phyphoxExperimentService{phyphoxBleExperimentServiceUUID}; // create service 8 | BLECharacteristic PhyphoxBLE::experimentCharacteristic{phyphoxBleExperimentCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 9 | BLECharacteristic PhyphoxBLE::controlCharacteristic{phyphoxBleExperimentControlCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 10 | BLECharacteristic PhyphoxBLE::eventCharacteristic{phyphoxBleEventCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 11 | 12 | BLEService PhyphoxBLE::phyphoxDataService{phyphoxBleDataServiceUUID}; // create service 13 | BLECharacteristic PhyphoxBLE::dataCharacteristic{phyphoxBleDataCharacteristicUUID, BLERead | BLEWrite | BLENotify, 20, false}; 14 | BLECharacteristic PhyphoxBLE::configCharacteristic{phyphoxBleConfigCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 15 | 16 | uint16_t PhyphoxBLE::minConInterval = 6; //7.5ms 17 | uint16_t PhyphoxBLE::maxConInterval = 24; //30ms 18 | uint16_t PhyphoxBLE::slaveLatency = 0; 19 | uint16_t PhyphoxBLE::timeout = 50; 20 | 21 | uint16_t PhyphoxBLE::MTU = 20; 22 | uint16_t PhyphoxBleExperiment::MTU = 20; 23 | 24 | int64_t PhyphoxBLE::experimentTime = NULL; 25 | int64_t PhyphoxBLE::systemTime = NULL; 26 | uint8_t PhyphoxBLE::eventType = NULL; 27 | 28 | uint8_t* PhyphoxBLE::data = nullptr; //this pointer points to the data the user wants to write in the characteristic 29 | uint8_t* PhyphoxBLE::p_exp = nullptr; //this pointer will point to the byte array which holds an experiment 30 | 31 | size_t PhyphoxBLE::expLen = 0; //try o avoid this maybe use std::array or std::vector 32 | const int maxExperimentSize = 6000; 33 | uint8_t storage[maxExperimentSize]; 34 | uint8_t PhyphoxBLE::eventData[17]={0}; 35 | //uint8_t eventData[17]; 36 | char *PhyphoxBLE::EXPARRAY=(char*)storage; 37 | 38 | void(*PhyphoxBLE::configHandler)() = nullptr; 39 | void(*PhyphoxBLE::experimentEventHandler)() = nullptr; 40 | 41 | void PhyphoxBLE::start(const char* DEVICE_NAME, uint8_t* exp_pointer, size_t len){ 42 | p_exp = exp_pointer; 43 | expLen = len; 44 | start(DEVICE_NAME); 45 | } 46 | 47 | void PhyphoxBLE::start(uint8_t* exp_pointer, size_t len){ 48 | p_exp = exp_pointer; 49 | expLen = len; 50 | start(); 51 | } 52 | 53 | void PhyphoxBLE::start(const char* DEVICE_NAME) 54 | { 55 | deviceName = DEVICE_NAME; 56 | 57 | controlCharacteristic.setEventHandler(BLEWritten, controlCharacteristicWritten); 58 | eventCharacteristic.setEventHandler(BLEWritten, eventCharacteristicWritten); 59 | configCharacteristic.setEventHandler(BLEWritten, configCharacteristicWritten); 60 | 61 | if(p_exp == nullptr){ 62 | 63 | PhyphoxBleExperiment defaultExperiment; 64 | 65 | //View 66 | PhyphoxBleExperiment::View firstView; 67 | 68 | //Graph 69 | PhyphoxBleExperiment::Graph firstGraph; //Create graph which will plot random numbers over time 70 | firstGraph.setChannel(0,1); 71 | 72 | firstView.addElement(firstGraph); 73 | defaultExperiment.addView(firstView); 74 | 75 | addExperiment(defaultExperiment); 76 | } 77 | 78 | 79 | BLE.begin(); 80 | BLE.setLocalName(DEVICE_NAME); 81 | BLE.setAdvertisedService(phyphoxExperimentService); 82 | //BLE.setAdvertisedService(phyphoxDataService); 83 | 84 | // add the characteristics to the service 85 | phyphoxExperimentService.addCharacteristic(experimentCharacteristic); 86 | phyphoxExperimentService.addCharacteristic(controlCharacteristic); 87 | phyphoxExperimentService.addCharacteristic(eventCharacteristic); 88 | phyphoxDataService.addCharacteristic(configCharacteristic); 89 | phyphoxDataService.addCharacteristic(dataCharacteristic); 90 | 91 | // add the service 92 | BLE.addService(phyphoxExperimentService); 93 | BLE.addService(phyphoxDataService); 94 | 95 | // set connection parameter 96 | BLE.setConnectionInterval(minConInterval, maxConInterval); 97 | 98 | // start advertising 99 | BLE.advertise(); 100 | 101 | } 102 | 103 | void PhyphoxBLE::start() { 104 | PhyphoxBLE::start("phyphox-Arduino"); 105 | } 106 | 107 | void PhyphoxBLE::poll() 108 | { 109 | BLE.poll(); 110 | } 111 | 112 | void PhyphoxBLE::poll(int timeout) 113 | { 114 | BLE.poll(timeout); 115 | } 116 | 117 | void PhyphoxBLE::read(uint8_t *arrayPointer, unsigned int arraySize) 118 | { 119 | configCharacteristic.readValue(arrayPointer, arraySize); 120 | } 121 | 122 | void PhyphoxBLE::read(float& f) 123 | { 124 | uint8_t readDATA[4]; 125 | configCharacteristic.readValue(readDATA, 4); 126 | memcpy(&f,&readDATA[0],4); 127 | } 128 | 129 | void PhyphoxBLE::read(float& f1, float& f2) 130 | { 131 | uint8_t readDATA[8]; 132 | configCharacteristic.readValue(readDATA, 8); 133 | memcpy(&f1,readDATA,4); 134 | memcpy(&f2,readDATA+4,4); 135 | } 136 | void PhyphoxBLE::read(float& f1, float& f2, float& f3) 137 | { 138 | uint8_t readDATA[12]; 139 | configCharacteristic.readValue(readDATA, 12); 140 | memcpy(&f1,readDATA,4); 141 | memcpy(&f2,readDATA+4,4); 142 | memcpy(&f3,readDATA+8,4); 143 | } 144 | void PhyphoxBLE::read(float& f1, float& f2, float& f3, float& f4) 145 | { 146 | uint8_t readDATA[16]; 147 | configCharacteristic.readValue(readDATA, 16); 148 | memcpy(&f1,readDATA,4); 149 | memcpy(&f2,readDATA+4,4); 150 | memcpy(&f3,readDATA+8,4); 151 | memcpy(&f4,readDATA+12,4); 152 | } 153 | void PhyphoxBLE::read(float& f1, float& f2, float& f3, float& f4, float& f5) 154 | { 155 | uint8_t readDATA[20]; 156 | configCharacteristic.readValue(readDATA, 20); 157 | memcpy(&f1,readDATA,4); 158 | memcpy(&f2,readDATA+4,4); 159 | memcpy(&f3,readDATA+8,4); 160 | memcpy(&f4,readDATA+12,4); 161 | memcpy(&f5,readDATA+16,4); 162 | } 163 | 164 | void PhyphoxBLE::addExperiment(PhyphoxBleExperiment& exp) 165 | { 166 | memset(EXPARRAY,0,maxExperimentSize); 167 | 168 | exp.getFirstBytes(EXPARRAY, deviceName); 169 | 170 | 171 | for(uint8_t i=0;i(&value); 188 | dataCharacteristic.writeValue(data,4); 189 | } 190 | 191 | 192 | void PhyphoxBLE::write(float& f1, float& f2) 193 | { 194 | float array[2] = {f1, f2}; 195 | data = reinterpret_cast(array); 196 | dataCharacteristic.writeValue(data,8); 197 | } 198 | 199 | void PhyphoxBLE::write(float& f1, float& f2, float& f3) 200 | { 201 | float array[3] = {f1, f2, f3}; 202 | data = reinterpret_cast(array); 203 | dataCharacteristic.writeValue(data,12); 204 | } 205 | 206 | void PhyphoxBLE::write(float& f1, float& f2, float& f3 , float& f4) 207 | { 208 | float array[4] = {f1, f2, f3, f4}; 209 | data = reinterpret_cast(array); 210 | dataCharacteristic.writeValue(data,16); 211 | } 212 | 213 | void PhyphoxBLE::write(float& f1, float& f2, float& f3 , float& f4, float& f5) 214 | { 215 | float array[5] = {f1, f2, f3, f4, f5}; 216 | data = reinterpret_cast(array); 217 | dataCharacteristic.writeValue(data,20); 218 | } 219 | 220 | void PhyphoxBLE::controlCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) { 221 | byte value = 0; 222 | characteristic.readValue(value); 223 | if (value & 0x01) { 224 | //sendexperiment 225 | transferExperiment(); 226 | } else { 227 | //experiment transfered 228 | } 229 | 230 | } 231 | 232 | void PhyphoxBLE::transferExperiment(){ 233 | 234 | BLE.stopAdvertise(); 235 | 236 | uint8_t* exp = p_exp; 237 | size_t exp_len = expLen; 238 | 239 | uint8_t header[20] = {0}; //20 byte as standard package size for ble transfer 240 | const char phyphox[] = "phyphox"; 241 | uint32_t table[256]; 242 | phyphoxBleCrc32::generate_table(table); 243 | uint32_t checksum = phyphoxBleCrc32::update(table, 0, exp, exp_len); 244 | size_t arrayLength = exp_len; 245 | uint8_t experimentSizeArray[4] = {0}; 246 | experimentSizeArray[0]= (arrayLength >> 24); 247 | experimentSizeArray[1]= (arrayLength >> 16); 248 | experimentSizeArray[2]= (arrayLength >> 8); 249 | experimentSizeArray[3]= arrayLength; 250 | 251 | uint8_t checksumArray[4] = {0}; 252 | checksumArray[0]= (checksum >> 24) & 0xFF; 253 | checksumArray[1]= (checksum >> 16) & 0xFF; 254 | checksumArray[2]= (checksum >> 8) & 0xFF; 255 | checksumArray[3]= checksum & 0xFF; 256 | 257 | memcpy(&header[0],&phyphox[0],7); 258 | memcpy(&header[0]+7,&experimentSizeArray[0],4); 259 | memcpy(&header[0]+7+4,&checksumArray[0],4); 260 | experimentCharacteristic.writeValue(header,sizeof(header)); 261 | 262 | for(size_t i = 0; i < exp_len/20; ++i){ 263 | memcpy(&header[0],&exp[0]+i*20,20); 264 | experimentCharacteristic.writeValue(header,sizeof(header)); 265 | delay(5); 266 | } 267 | 268 | if(exp_len%20 != 0){ 269 | const size_t rest = exp_len%20; 270 | uint8_t slice[rest]; 271 | memcpy(&slice[0],&exp[0]+exp_len-rest,rest); 272 | experimentCharacteristic.writeValue(slice,sizeof(slice)); 273 | 274 | delay(5); 275 | } 276 | 277 | BLE.advertise(); 278 | } 279 | 280 | void PhyphoxBLE::configCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic){ 281 | if(configHandler!=nullptr){ 282 | (*configHandler)(); 283 | } 284 | } 285 | 286 | void PhyphoxBLE::eventCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic){ 287 | 288 | uint8_t read_buffer[17]; 289 | eventCharacteristic.readValue(read_buffer, 17); 290 | 291 | 292 | memcpy(&eventData[0],read_buffer,17); 293 | int64_t et,st; 294 | memcpy(&et,&eventData[0]+1,8); 295 | memcpy(&st,&eventData[0]+1+8,8); 296 | PhyphoxBLE::eventType = eventData[0]; 297 | PhyphoxBLE::systemTime = swap_int64(st); 298 | PhyphoxBLE::experimentTime = swap_int64(et); 299 | 300 | if(experimentEventHandler!=nullptr){ 301 | (*experimentEventHandler)(); 302 | } 303 | } 304 | 305 | void PhyphoxBLE::printXML(HardwareSerial* printer){ 306 | printer->println(""); 307 | for(int i =0; iprint(CHAR); 310 | } 311 | printer->println(""); 312 | } 313 | #endif 314 | -------------------------------------------------------------------------------- /src/experiment.cpp: -------------------------------------------------------------------------------- 1 | #include "phyphoxBleExperiment.h" 2 | #include "copyToMem.h" 3 | #include "phyphoxBle.h" 4 | 5 | int PhyphoxBleExperiment::numberOfChannels = 5; 6 | 7 | 8 | void PhyphoxBleExperiment::addView(View& v) 9 | { 10 | for(int i=0; isetLabel("SENSOR RAW DATA"); 29 | VIEWS[phyphoxBleNViews-1] = RawDataSmartoneSensor; 30 | InfoField* SensorType = new InfoField(); 31 | SensorType->INFO = s.TYPE; 32 | for (int j = 0; j < phyphoxBleNElements; j++){ 33 | if(VIEWS[phyphoxBleNViews-1]->ELEMENTS[j]==nullptr){ 34 | VIEWS[phyphoxBleNViews-1]->ELEMENTS[j] = SensorType; 35 | break; 36 | } 37 | } 38 | 39 | } 40 | 41 | for(int mappedCH = 0; mappedCH<5; mappedCH++){ 42 | if( s.CHANNEL[mappedCH]!=nullptr){ 43 | for (int j = 0; j < phyphoxBleNElements; j++){ 44 | if(VIEWS[phyphoxBleNViews-1]->ELEMENTS[j]==nullptr){ 45 | PhyphoxBleExperiment::Value* newValueField = new Value(); 46 | newValueField->INPUTVALUE = s.CHANNEL[mappedCH]; 47 | newValueField->LABEL = s.COMPONENT[mappedCH]; 48 | VIEWS[phyphoxBleNViews-1]->ELEMENTS[j]=newValueField; 49 | break; 50 | } 51 | } 52 | } 53 | } 54 | 55 | break; 56 | } 57 | } 58 | } 59 | 60 | void PhyphoxBleExperiment::setTitle(const char *t){ 61 | copyToMem(&TITLE, t); 62 | } 63 | 64 | void PhyphoxBleExperiment::setCategory(const char *c){ 65 | copyToMem(&CATEGORY, c); 66 | } 67 | 68 | void PhyphoxBleExperiment::setDescription(const char *d){ 69 | copyToMem(&DESCRIPTION, d); 70 | } 71 | 72 | void PhyphoxBleExperiment::setColor(const char *c){ 73 | copyToMem(&COLOR, (std::string(c)).c_str()); 74 | } 75 | 76 | void PhyphoxBleExperiment::setRepeating(const int r){ 77 | PhyphoxBleExperiment::repeating = r; 78 | } 79 | void PhyphoxBleExperiment::setSubscribeOnStart(bool b) { 80 | if(b) copyToMem(&SUBSCRIBEONSTART, "true"); 81 | else copyToMem(&SUBSCRIBEONSTART, "false"); 82 | } 83 | 84 | // void PhyphoxBleExperiment::setConfig(const char *c){ 85 | // copyToMem(&CONFIG, c); 86 | // } 87 | 88 | void PhyphoxBleExperiment::addExportSet(ExportSet& e) 89 | { 90 | for(int i=0; i\n"); 105 | //build title 106 | strcat(buffArray,""); 107 | if (!TITLE) {strcat(buffArray,"Arduino-Experiment");} else {strcat(buffArray,TITLE);} 108 | strcat(buffArray,"\n"); 109 | 110 | //build category 111 | strcat(buffArray, ""); 112 | if (!CATEGORY) {strcat(buffArray,"Arduino Experiments");} else {strcat(buffArray,CATEGORY);} 113 | strcat(buffArray, "\n"); 114 | 115 | //build color 116 | /* 117 | if (!COLOR){ 118 | strcat(buffArray, ""); 119 | strcat(buffArray,COLOR); 120 | strcat(buffArray, "\n"); 121 | } 122 | */ 123 | 124 | //build description 125 | strcat(buffArray, ""); 126 | if (!DESCRIPTION) { 127 | strcat(buffArray,"An experiment created with the phyphox BLE library for Arduino-compatible micro controllers."); 128 | }else { 129 | strcat(buffArray,DESCRIPTION); 130 | } 131 | strcat(buffArray, "\n"); 132 | 133 | //build container 134 | strcat(buffArray, "\n"); 135 | strcat(buffArray, "\tCH0\n"); 136 | strcat(buffArray, "\tCB1\n"); 137 | strcat(buffArray, "\tCB2\n"); 138 | strcat(buffArray, "\tCB3\n"); 139 | strcat(buffArray, "\tCB4\n"); 140 | strcat(buffArray, "\tCB5\n"); 141 | 142 | for(int i=0; iCH"); 144 | char add[20]; 145 | sprintf(add, "%i", i+1); 146 | strcat(buffArray, add); 147 | strcat(buffArray, "\n"); 148 | } 149 | strcat(buffArray, "\n"); 150 | 151 | //build input 152 | strcat(buffArray, "\n"); 153 | strcat(buffArray, "\t\n\t\t"); 164 | 165 | 166 | 167 | 168 | //build config 169 | //strcat(buffArray,"\t\t"); 170 | // if (!CONFIG) {strcat(buffArray,"000000");} else {strcat(buffArray,CONFIG);} 171 | //strcat(buffArray,"\n\t\t"); 172 | 173 | 174 | if(repeating <= 0){ 175 | for(int i=1; i<=numberOfChannels;i++){ 176 | strcat(buffArray, "\t\tCH%i", k,i); 180 | strcat(buffArray, add); 181 | strcat(buffArray,"\n"); 182 | } 183 | }else{ 184 | for(int i=1; i<=numberOfChannels;i++){ 185 | strcat(buffArray, "CH%i", k,repeating,i); 189 | strcat(buffArray, add); 190 | strcat(buffArray,"\n\t\t"); 191 | } 192 | } 193 | 194 | strcat(buffArray,"CH0"); 195 | 196 | strcat(buffArray, "\n\t\n"); 197 | //build sensor input 198 | 199 | for (int i = 0; i < phyphoxBleNSensors; i++) 200 | { 201 | if(SENSORS[i] != nullptr){ 202 | //strcat(buffArray, "\n\t\tCB1"); 203 | SENSORS[i]->getBytes(buffArray); 204 | } 205 | } 206 | strcat(buffArray, "\n"); 207 | 208 | 209 | //build output 210 | strcat(buffArray, "\n"); 211 | strcat(buffArray, "\t\n\t\t"); 214 | 215 | // for(int i=1; i<=1;i++){ 216 | // strcat(buffArray, "CB%i", k,i); 220 | // strcat(buffArray, add); 221 | // strcat(buffArray,"\n\t\t"); 222 | // } 223 | strcat(buffArray, "\t\tCB1\n"); 224 | strcat(buffArray, "\t\tCB2\n"); 225 | strcat(buffArray, "\t\tCB3\n"); 226 | strcat(buffArray, "\t\tCB4\n"); 227 | strcat(buffArray, "\t\tCB5\n"); 228 | 229 | strcat(buffArray, "\t\n"); 230 | strcat(buffArray, "\n"); 231 | 232 | //build analysis 233 | strcat(buffArray, "\n"); 234 | 235 | //build views 236 | strcat(buffArray, "\n"); 237 | 238 | //errorhandling 239 | for(int i=0;iELEMENTS[j]!=nullptr){ 243 | if(VIEWS[i]->ELEMENTS[j]->ERROR.MESSAGE != NULL) { 244 | if(errors == 0) { 245 | strcat(buffArray, "\t \n"); 246 | } 247 | VIEWS[i]->ELEMENTS[j]->ERROR.getBytes(buffArray); 248 | errors++; 249 | } 250 | } 251 | } 252 | } 253 | } 254 | 255 | for(int i=0; iERROR.MESSAGE !=NULL){ 257 | if(errors == 0){ 258 | strcat(buffArray, "\t \n"); 259 | } 260 | SENSORS[i]->ERROR.getBytes(buffArray); 261 | errors++; 262 | } 263 | } 264 | 265 | if(errors>0) { 266 | strcat(buffArray,"\t\t\n"); 267 | //strcat(buffArray,"\" color=\"ff0000\">\n"); 268 | strcat(buffArray,"\t\t\n"); 269 | strcat(buffArray,"\t\t\n"); 270 | //strcat(buffArray,"\" color=\"ff0000\">\n"); 271 | strcat(buffArray,"\t\t\n"); 272 | strcat(buffArray,"\t\n"); 273 | } 274 | } 275 | 276 | void PhyphoxBleExperiment::getViewBytes(char *buffArray, uint8_t view, uint8_t element){ 277 | 278 | if(VIEWS[view]!=nullptr && viewgetBytes(buffArray,element); 280 | } 281 | 282 | } 283 | 284 | void PhyphoxBleExperiment::getLastBytes(char *buffArray){ 285 | bool noExports = true; 286 | 287 | strcat(buffArray,"\n"); 288 | 289 | //build export 290 | strcat(buffArray, "\n"); 291 | for(int i=0;igetBytes(buffArray); 294 | noExports = false; 295 | } 296 | } 297 | if(noExports) { 298 | strcat(buffArray,"\t\n"); 299 | 300 | for(int i=0;iCH%i", i,i); 304 | strcat(buffArray, add); 305 | strcat(buffArray,"\n"); 306 | } 307 | strcat(buffArray,"\t\n"); 308 | } 309 | strcat(buffArray, "\n"); 310 | 311 | //close 312 | strcat(buffArray, ""); 313 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # phyphox Arduino BLE 2 | Use phyphox to plot sensor data from your Microcontroller. 3 | 4 | The purpose of this library is to use the phyphox app (see www.phyphox.org) to plot sensor data on your phone with the open source app phyphox. In the other direction you can also use this library to access sensor data from your phone to use in your [Arduino](https://www.arduino.cc/) or ESP32 project. 5 | 6 | ## Supported boards 7 | - Arduino Nano 33 Ble 8 | - Arduino Nano Sense 9 | - Arduino Nano 33 IoT (see note below) 10 | - Arduino Uno R4 Wifi (see note below) 11 | - Arduino MKR WiFi 1010 12 | - senseBox MCU with NINA-B31 module 13 | - ESP 32 14 | - STM32 (e.g. STM32WB55) 15 | 16 | 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. 17 | 18 | Note: When using the NINA-B31 module you must call PhyphoxBLE::poll() periodically (in loop() ) or the library will not work. 19 | The same applies to STM32 20 | 21 | Note: to use STM32 BLE, you need the STM32duinoBLE library, and (at least for the STM32WBx5) the appropriate BLE stack (see Copro binaries) 22 | 23 | ## Concept of phyphox 24 | 25 | Phyphox is an open source app that has been developed at the RWTH Aachen University. It is available on Android and iOS and primarily aims at making the phone's sensors available for physics experiments. However, the app is based on a very flexible file format that defines data sources, visualizations (i.e. values and graphs) and data analysis (from simple formulas to Fourier transforms). This file format also allows to define Bluetooth Low Energy devices to exchange data from and to. 26 | 27 | This library generates an experiment configuration in this file format and allows phyphox to conenct to your microcontroller. It directly transfers the configuration (including graph configurations, axis labels, etc.) to phyphox and provides function to submit data to be plotted in phyphox or receive sensor data. 28 | 29 | ## Installation 30 | 31 | You should be able to find this library in the library search of your Arduino IDE. There you should usually find the latest release from here, which has been tagged with a version number. 32 | 33 | Alternatively, you can download this repository here as a zip file from github and install it with via the Arduino IDE's menu using the "Add ZIP library" entry. We recommend using the Arduino library manager, but directly installing a version from here might be relevant to try a specific branch or new feature. 34 | 35 | You will may also need to install an BLE library specific to your board: 36 | - ArduinoBLE for the Arduino Nano 33 IoT 37 | - STM32duinoBLE for an STM32 38 | 39 | ## Usage 40 | 41 | The easiest way to learn how to use this library is by looking at the examples in the `examples` folder. In most cases, you can simply connect to your Arduino running this library by scanning for Bluetooth devices via the "+"-button on the main screen of phyphox. 42 | 43 | ### randomNumbers.ino 44 | 45 | This is our minimal example. It submits random numbers to phyphox. All you need to do to submit a value to phyphox is including this library, starting the server (i.e. in `setup()`) and writing your data to the server. 46 | 47 | ```arduino 48 | #include 49 | 50 | void setup() { 51 | PhyphoxBLE::start(); //Start the BLE server 52 | } 53 | 54 | void loop() { 55 | //uncomment next line if using senseBox MCU or Arduino Nano 33 IoT 56 | //PhyphoxBLE::poll(); 57 | float randomNumber = random(0,100); //Generate random number in the range 0 to 100 58 | PhyphoxBLE::write(randomNumber); //Send value to phyphox 59 | delay(50); //Shortly pause before repeating 60 | } 61 | ``` 62 | 63 | ### CreateExperiment.ino 64 | 65 | This example shows how you can set a title, category and description as well as how to define graphs and setting axis labels and units. You can define one or multiple views (shown as tabs in phyphox), each of which can hold one or more graphs. 66 | 67 | For each graph you need to call `setChannel(x, y)` with x and y being an index of your data set. This index corresponds to the order of the values that you transfer in a call to `server.write` while the index `0` is special and corresponds to the timestamp at which phyphox receives the value. At the moment `server.write` supports up to five values. 68 | 69 | For example, let's assume you have the float values `foo` and `bar`. You can then call server.write(foo, bar) to send a set with both values. If you call `setChannel(0,1)`, your graph would plot `foo` on the y axis over time on the x axis. If you call `setChannel(2,1)`, your graph would plot `foo` on the y axis and `bar` on the x axis. 70 | 71 | Here are some useful methods to create your own experiment: 72 | 73 | | Target | Method | Explanation | 74 | | ---------- | ------------------------ | ----------------------------------------------------------------- | 75 | | Experiment | setTitle(char*) | Sets a title for the experiment | 76 | | Experiment | setCategory(char*) | Sets a category for the experiment | 77 | | Experiment | setDescription(char*) | Sets a description for the experiment | 78 | | Experiment | addView(View&) | Adds a view to the corresponding experiment | 79 | | Experiment | addExportSet(ExportSet&) | Adds an exportSet to the corresponding experiment | 80 | | Experiment | addSensor(Sensor&) | Data from smarpthone sensor can will be received (see getSensorDataFromSmartphone example)| 81 | | View | addElement(Element&) | Adds an element to the corresponding view | 82 | | View | setLabel(char*) | Sets a label for the view | 83 | | Graph | setLabel(char*) | Sets a label for the graph | 84 | | Graph | setUnitX(char*) | Sets the unit for x (similar with y) | 85 | | Graph | setLabelX(char*) | Sets a label for x (similar with y) | 86 | | Graph | setXPrecision(int) | Sets the amount of digits after the decimal point (similar with y)| 87 | | Graph | setChannel(int, int) | As explained above (1-5) | 88 | | Graph | addSubgraph(Subgraph) | Adds an additional subgraph (see example "multigraph") | 89 | | Graph | setStyle(char*) | Sets the style (use defines: STYLE_LINES, STYLE_DOTS). | 90 | | Graph | setColor(char*) | Sets the line color of the graph (use a 6 digit hexadecimal code) | 91 | | Graph | setMinX(int, const char *)| Sets the min x value of the co-system and a layout (auto, extend and fixed) | 92 | | Graph | setMaxX(int, const char *)| Sets the max x value of the co-system and a layout (auto, extend and fixed) | 93 | | Graph | setMinY(int, const char *)| Sets the min y value of the co-system and a layout (auto, extend and fixed) | 94 | | Graph | setMaxY(int, const char *)| Sets the max y value of the co-system and a layout (auto, extend and fixed) | 95 | | Graph | setLineWidth(float) | Sets the line width | 96 | | Separator | setHeight(float) | Creates a line to separate parts of the experiment | 97 | | Separator | setColor(char*) | Sets the color of the line (use a 6 digit hexadecimal code) | 98 | | Info | setInfo(char*) | Sets the infotext | 99 | | Info | setColor(char*) | Sets the font color (use a 6 digit hexadecimal code) | 100 | | Value | setLabel(char*) | Sets a label for the displayed value | 101 | | Value | setPrecision(int) | Sets the amount of digits after the decimal point | 102 | | Value | setUnit(char*) | Sets a unit for the displayed value | 103 | | Value | setColor(char*) | Sets the font color (use a 6 digit hexadecimal code) | 104 | | Value | setChannel(int) | As explained above, just with one parameter (1-5) | 105 | | Edit | setLabel(char*) | Sets label for the editfield | 106 | | Edit | setUnit(char*) | Sets unit | 107 | | Edit | setSigned(bool) | true = signed values allowed | 108 | | Edit | setDecimal(bool) | true = decimal values allowed | 109 | | Edit | setChannel(int) | As explained above, just with one parameter (1-5) | 110 | | ExportSet | setLabel(char*) | Sets a label for the exportSet (Used to export to Excel, etc.) | 111 | | ExportData | setLabel(char*) | Sets a label for the exportData | 112 | | ExportData | setDatachannel(int) | Defines which channel should be exported for this dataset (1-5) | 113 | | Everything | setXMLAttribute(char*) | Custom property e.g. setXMLAttribute("lineWidth=\"3\"") | 114 | 115 | 116 | #### Style and Layout oprtions for setStyle and setMax/setMin 117 | STYLE_LINES, STYLE_DOTS, STYLE_VBARS, STYLE_HBARS, STYLE_MAP 118 | 119 | LAYOUT_AUTO, LAYOUT_EXTEND, LAYOUT_FIXED 120 | 121 | #### Error messages 122 | 123 | If for some reason the app shows you an error in form of "ERROR FOUND: ERR_X", with different values for X, this could be the reason: 124 | * ERR_01: The input was too long. 125 | * ERR_02: The value exceeds the upper limit. 126 | * ERR_03: The input was not a 6-digit hexadecimal code. 127 | * ERR_04: The input does not match with a valid value. 128 | * ERR_05: The layout must be auto, extend or fixed. 129 | 130 | If you realize that the microcontroller is continiously rebooting, you maybe added too many elements. 131 | 132 | ### getDataFromSmartphone.ino 133 | 134 | The phyphox file format is much more powerful than what is offered by this library's interface. In the example `getDataFromSmartphone.ino` you can see how a phyphox-file can be used to set up a sensor on the phone and retrieve its data on the Arduino. In contrast to other examples, the phyphox-file is not generated by the library but instead loaded in phyphox with a QR code. You can also convert your manually created phyphox file into a header file and provide it to this library so it is submitted via Bluetooth. 135 | 136 | As the phyphox file format is extremely complex and powerful, please refer to the [phyphox wiki](https://phyphox.org/wiki/index.php/Phyphox_file_format) to learn about it and feel free to contact us if you are stuck or think that a specific aspect of the file format should be easily accessible through our Arduino library. 137 | 138 | ### Further documentation 139 | 140 | For now, this library is rather lightweight. Feel free to browse the `.h` files to learn about the functions that are already available. 141 | 142 | ## Missing features 143 | 144 | In the future we would like to see... 145 | - Some memory optimization 146 | - Compression for the transfer of the phyphox experiment generated on the Arduino 147 | - Option to request a larger mtu size 148 | - Addition graph settings 149 | - Proper documentation 150 | 151 | If you can help with this, we are happy to receive a pull request. You can contact us via contact@phyphox.org if you plan on a large addition to this library and want to make sure that it does not collide with anything we are already working on. 152 | 153 | ## Credits 154 | 155 | This library has been developed by the phyphox team at the RWTH Aachen University. In particular, the foundations and basic concept was created by Alexander Krampe as part of his Master thesis. The library has been further improved and is now maintained by Dominik Dorsel, our PhD student who also supervised Alexander's thesis. The library was also further optimized and extended with new features by Marcel Hagedorn and Edward Leier. 156 | 157 | ## Licence 158 | 159 | This library is released under the GNU Lesser General Public Licence v3.0 (or newer). 160 | 161 | ## Contact 162 | 163 | Contact us any time at contact@phyphox.org and learn more about phyphox on https://phyphox.org. 164 | 165 | -------------------------------------------------------------------------------- /src/boards/phyphoxBLE_STM32.cpp: -------------------------------------------------------------------------------- 1 | #if defined(ARDUINO_ARCH_STM32) 2 | 3 | #include "phyphoxBLE_STM32.h" 4 | #include "Arduino.h" 5 | #include 6 | 7 | #if defined(ARDUINO_STEVAL_MKBOXPRO) 8 | /* STEVAL-MKBOXPRO */ 9 | SPIClass SpiHCI(PA7, PA6, PA5); 10 | HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_LP, PA2, PB11, PD4, 1000000, SPI_MODE3); 11 | #if !defined(FAKE_BLELOCALDEVICE) 12 | BLELocalDevice BLEObj(&HCISpiTransport); 13 | BLELocalDevice& BLE = BLEObj; 14 | #endif 15 | #elif defined(ARDUINO_STEVAL_MKSBOX1V1) 16 | /* STEVAL-MKSBOX1V1 */ 17 | SPIClass SpiHCI(PC3, PD3, PD1); 18 | HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_1S, PD0, PD4, PA8, 1000000, SPI_MODE1); 19 | #if !defined(FAKE_BLELOCALDEVICE) 20 | BLELocalDevice BLEObj(&HCISpiTransport); 21 | BLELocalDevice& BLE = BLEObj; 22 | #endif 23 | #elif defined(ARDUINO_B_L475E_IOT01A) || defined(ARDUINO_B_L4S5I_IOT01A) 24 | /* B-L475E-IOT01A1 or B_L4S5I_IOT01A */ 25 | SPIClass SpiHCI(PC12, PC11, PC10); 26 | HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, PD13, PE6, PA8, 8000000, SPI_MODE0); 27 | #if !defined(FAKE_BLELOCALDEVICE) 28 | BLELocalDevice BLEObj(&HCISpiTransport); 29 | BLELocalDevice& BLE = BLEObj; 30 | #endif 31 | #elif defined(ARDUINO_NUCLEO_WB15CC) || defined(ARDUINO_P_NUCLEO_WB55RG) ||\ 32 | defined(ARDUINO_STM32WB5MM_DK) || defined(ARDUINO_P_NUCLEO_WB55_USB_DONGLE) 33 | HCISharedMemTransportClass HCISharedMemTransport; 34 | #if !defined(FAKE_BLELOCALDEVICE) 35 | BLELocalDevice BLEObj(&HCISharedMemTransport); 36 | BLELocalDevice& BLE = BLEObj; 37 | #endif 38 | #else 39 | /* Shield IDB05A2 with SPI clock on D3 */ 40 | SPIClass SpiHCI(D11, D12, D3); 41 | HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0); 42 | #if !defined(FAKE_BLELOCALDEVICE) 43 | BLELocalDevice BLEObj(&HCISpiTransport); 44 | BLELocalDevice& BLE = BLEObj; 45 | #endif 46 | /* Shield IDB05A2 with SPI clock on D13 */ 47 | // #define SpiHCI SPI 48 | // HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M0, A1, A0, D7, 8000000, SPI_MODE0); 49 | // #if !defined(FAKE_BLELOCALDEVICE) 50 | // BLELocalDevice BLEObj(&HCISpiTransport); 51 | // BLELocalDevice& BLE = BLEObj; 52 | // #endif 53 | /* Shield IDB05A1 with SPI clock on D3 */ 54 | // SPIClass SpiHCI(D11, D12, D3); 55 | // HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0); 56 | // #if !defined(FAKE_BLELOCALDEVICE) 57 | // BLELocalDevice BLEObj(&HCISpiTransport); 58 | // BLELocalDevice& BLE = BLEObj; 59 | // #endif 60 | /* Shield IDB05A1 with SPI clock on D13 */ 61 | // #define SpiHCI SPI 62 | // HCISpiTransportClass HCISpiTransport(SpiHCI, SPBTLE_RF, A1, A0, D7, 8000000, SPI_MODE0); 63 | // #if !defined(FAKE_BLELOCALDEVICE) 64 | // BLELocalDevice BLEObj(&HCISpiTransport); 65 | // BLELocalDevice& BLE = BLEObj; 66 | // #endif 67 | /* Shield BNRG2A1 with SPI clock on D3 */ 68 | // SPIClass SpiHCI(D11, D12, D3); 69 | // HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1); 70 | // #if !defined(FAKE_BLELOCALDEVICE) 71 | // BLELocalDevice BLEObj(&HCISpiTransport); 72 | // BLELocalDevice& BLE = BLEObj; 73 | // #endif 74 | /* Shield BNRG2A1 with SPI clock on D13 */ 75 | // #define SpiHCI SPI 76 | // HCISpiTransportClass HCISpiTransport(SpiHCI, BLUENRG_M2SP, A1, A0, D7, 1000000, SPI_MODE1); 77 | // #if !defined(FAKE_BLELOCALDEVICE) 78 | // BLELocalDevice BLEObj(&HCISpiTransport); 79 | // BLELocalDevice& BLE = BLEObj; 80 | // #endif 81 | #endif 82 | 83 | BLEService PhyphoxBLE::phyphoxExperimentService{phyphoxBleExperimentServiceUUID}; // create service 84 | BLECharacteristic PhyphoxBLE::experimentCharacteristic{phyphoxBleExperimentCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 85 | BLECharacteristic PhyphoxBLE::controlCharacteristic{phyphoxBleExperimentControlCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 86 | BLECharacteristic PhyphoxBLE::eventCharacteristic{phyphoxBleEventCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 87 | 88 | BLEService PhyphoxBLE::phyphoxDataService{phyphoxBleDataServiceUUID}; // create service 89 | BLECharacteristic PhyphoxBLE::dataCharacteristic{phyphoxBleDataCharacteristicUUID, BLERead | BLEWrite | BLENotify, 20, false}; 90 | BLECharacteristic PhyphoxBLE::configCharacteristic{phyphoxBleConfigCharacteristicUUID, BLERead | BLEWrite| BLENotify, 20, false}; 91 | 92 | uint16_t PhyphoxBLE::minConInterval = 6; //7.5ms 93 | uint16_t PhyphoxBLE::maxConInterval = 24; //30ms 94 | uint16_t PhyphoxBLE::slaveLatency = 0; 95 | uint16_t PhyphoxBLE::timeout = 50; 96 | 97 | uint16_t PhyphoxBLE::MTU = 20; 98 | uint16_t PhyphoxBleExperiment::MTU = 20; 99 | 100 | int64_t PhyphoxBLE::experimentTime = 0; 101 | int64_t PhyphoxBLE::systemTime = 0; 102 | uint8_t PhyphoxBLE::eventType = 0; 103 | 104 | uint8_t* PhyphoxBLE::data = nullptr; //this pointer points to the data the user wants to write in the characteristic 105 | uint8_t* PhyphoxBLE::p_exp = nullptr; //this pointer will point to the byte array which holds an experiment 106 | 107 | size_t PhyphoxBLE::expLen = 0; //try o avoid this maybe use std::array or std::vector 108 | const int maxExperimentSize = 6000; 109 | uint8_t storage[maxExperimentSize]; 110 | uint8_t PhyphoxBLE::eventData[17]={0}; 111 | //uint8_t eventData[17]; 112 | char *PhyphoxBLE::EXPARRAY=(char*)storage; 113 | 114 | void(*PhyphoxBLE::configHandler)() = nullptr; 115 | void(*PhyphoxBLE::experimentEventHandler)() = nullptr; 116 | 117 | void PhyphoxBLE::start(const char* DEVICE_NAME, uint8_t* exp_pointer, size_t len){ 118 | p_exp = exp_pointer; 119 | expLen = len; 120 | start(DEVICE_NAME); 121 | } 122 | 123 | void PhyphoxBLE::start(uint8_t* exp_pointer, size_t len){ 124 | p_exp = exp_pointer; 125 | expLen = len; 126 | start(); 127 | } 128 | 129 | void PhyphoxBLE::start(const char* DEVICE_NAME) 130 | { 131 | deviceName = DEVICE_NAME; 132 | 133 | controlCharacteristic.setEventHandler(BLEWritten, controlCharacteristicWritten); 134 | eventCharacteristic.setEventHandler(BLEWritten, eventCharacteristicWritten); 135 | configCharacteristic.setEventHandler(BLEWritten, configCharacteristicWritten); 136 | 137 | if(p_exp == nullptr){ 138 | 139 | PhyphoxBleExperiment defaultExperiment; 140 | 141 | //View 142 | PhyphoxBleExperiment::View firstView; 143 | 144 | //Graph 145 | PhyphoxBleExperiment::Graph firstGraph; //Create graph which will plot random numbers over time 146 | firstGraph.setChannel(0,1); 147 | 148 | firstView.addElement(firstGraph); 149 | defaultExperiment.addView(firstView); 150 | 151 | addExperiment(defaultExperiment); 152 | } 153 | 154 | 155 | if(!BLE.begin()) { Serial.println("failed to BLE.begin()!"); } 156 | BLE.setLocalName(DEVICE_NAME); 157 | BLE.setAdvertisedService(phyphoxExperimentService); 158 | //BLE.setAdvertisedService(phyphoxDataService); 159 | 160 | // add the characteristics to the service 161 | phyphoxExperimentService.addCharacteristic(experimentCharacteristic); 162 | phyphoxExperimentService.addCharacteristic(controlCharacteristic); 163 | phyphoxExperimentService.addCharacteristic(eventCharacteristic); 164 | phyphoxDataService.addCharacteristic(configCharacteristic); 165 | phyphoxDataService.addCharacteristic(dataCharacteristic); 166 | 167 | // add the service 168 | BLE.addService(phyphoxExperimentService); 169 | BLE.addService(phyphoxDataService); 170 | 171 | // set connection parameter 172 | BLE.setConnectionInterval(minConInterval, maxConInterval); 173 | 174 | // start advertising 175 | BLE.advertise(); 176 | 177 | } 178 | 179 | void PhyphoxBLE::start() { 180 | PhyphoxBLE::start("phyphox-Arduino"); 181 | } 182 | 183 | void PhyphoxBLE::poll() 184 | { 185 | BLE.poll(); 186 | } 187 | 188 | void PhyphoxBLE::poll(int timeout) 189 | { 190 | BLE.poll(timeout); 191 | } 192 | 193 | void PhyphoxBLE::read(uint8_t *arrayPointer, unsigned int arraySize) 194 | { 195 | configCharacteristic.readValue(arrayPointer, arraySize); 196 | } 197 | 198 | void PhyphoxBLE::read(float& f) 199 | { 200 | uint8_t readDATA[4]; 201 | configCharacteristic.readValue(readDATA, 4); 202 | memcpy(&f,&readDATA[0],4); 203 | } 204 | 205 | void PhyphoxBLE::read(float& f1, float& f2) 206 | { 207 | uint8_t readDATA[8]; 208 | configCharacteristic.readValue(readDATA, 8); 209 | memcpy(&f1,readDATA,4); 210 | memcpy(&f2,readDATA+4,4); 211 | } 212 | void PhyphoxBLE::read(float& f1, float& f2, float& f3) 213 | { 214 | uint8_t readDATA[12]; 215 | configCharacteristic.readValue(readDATA, 12); 216 | memcpy(&f1,readDATA,4); 217 | memcpy(&f2,readDATA+4,4); 218 | memcpy(&f3,readDATA+8,4); 219 | } 220 | void PhyphoxBLE::read(float& f1, float& f2, float& f3, float& f4) 221 | { 222 | uint8_t readDATA[16]; 223 | configCharacteristic.readValue(readDATA, 16); 224 | memcpy(&f1,readDATA,4); 225 | memcpy(&f2,readDATA+4,4); 226 | memcpy(&f3,readDATA+8,4); 227 | memcpy(&f4,readDATA+12,4); 228 | } 229 | void PhyphoxBLE::read(float& f1, float& f2, float& f3, float& f4, float& f5) 230 | { 231 | uint8_t readDATA[20]; 232 | configCharacteristic.readValue(readDATA, 20); 233 | memcpy(&f1,readDATA,4); 234 | memcpy(&f2,readDATA+4,4); 235 | memcpy(&f3,readDATA+8,4); 236 | memcpy(&f4,readDATA+12,4); 237 | memcpy(&f5,readDATA+16,4); 238 | } 239 | 240 | void PhyphoxBLE::addExperiment(PhyphoxBleExperiment& exp) 241 | { 242 | memset(EXPARRAY,0,maxExperimentSize); 243 | 244 | exp.getFirstBytes(EXPARRAY, deviceName); 245 | 246 | 247 | for(uint8_t i=0;i(&value); 264 | dataCharacteristic.writeValue(data,4); 265 | } 266 | 267 | 268 | void PhyphoxBLE::write(float& f1, float& f2) 269 | { 270 | float array[2] = {f1, f2}; 271 | data = reinterpret_cast(array); 272 | dataCharacteristic.writeValue(data,8); 273 | } 274 | 275 | void PhyphoxBLE::write(float& f1, float& f2, float& f3) 276 | { 277 | float array[3] = {f1, f2, f3}; 278 | data = reinterpret_cast(array); 279 | dataCharacteristic.writeValue(data,12); 280 | } 281 | 282 | void PhyphoxBLE::write(float& f1, float& f2, float& f3 , float& f4) 283 | { 284 | float array[4] = {f1, f2, f3, f4}; 285 | data = reinterpret_cast(array); 286 | dataCharacteristic.writeValue(data,16); 287 | } 288 | 289 | void PhyphoxBLE::write(float& f1, float& f2, float& f3 , float& f4, float& f5) 290 | { 291 | float array[5] = {f1, f2, f3, f4, f5}; 292 | data = reinterpret_cast(array); 293 | dataCharacteristic.writeValue(data,20); 294 | } 295 | 296 | void PhyphoxBLE::controlCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) { 297 | byte value = 0; 298 | characteristic.readValue(value); 299 | if (value & 0x01) { 300 | //sendexperiment 301 | transferExperiment(); 302 | } else { 303 | //experiment transfered 304 | } 305 | 306 | } 307 | 308 | void PhyphoxBLE::transferExperiment(){ 309 | 310 | BLE.stopAdvertise(); 311 | 312 | uint8_t* exp = p_exp; 313 | size_t exp_len = expLen; 314 | 315 | uint8_t header[20] = {0}; //20 byte as standard package size for ble transfer 316 | const char phyphox[] = "phyphox"; 317 | uint32_t table[256]; 318 | phyphoxBleCrc32::generate_table(table); 319 | uint32_t checksum = phyphoxBleCrc32::update(table, 0, exp, exp_len); 320 | size_t arrayLength = exp_len; 321 | uint8_t experimentSizeArray[4] = {0}; 322 | experimentSizeArray[0]= (arrayLength >> 24); 323 | experimentSizeArray[1]= (arrayLength >> 16); 324 | experimentSizeArray[2]= (arrayLength >> 8); 325 | experimentSizeArray[3]= arrayLength; 326 | 327 | uint8_t checksumArray[4] = {0}; 328 | checksumArray[0]= (checksum >> 24) & 0xFF; 329 | checksumArray[1]= (checksum >> 16) & 0xFF; 330 | checksumArray[2]= (checksum >> 8) & 0xFF; 331 | checksumArray[3]= checksum & 0xFF; 332 | 333 | memcpy(&header[0],&phyphox[0],7); 334 | memcpy(&header[0]+7,&experimentSizeArray[0],4); 335 | memcpy(&header[0]+7+4,&checksumArray[0],4); 336 | experimentCharacteristic.writeValue(header,sizeof(header)); 337 | 338 | for(size_t i = 0; i < exp_len/20; ++i){ 339 | memcpy(&header[0],&exp[0]+i*20,20); 340 | experimentCharacteristic.writeValue(header,sizeof(header)); 341 | delay(5); 342 | } 343 | 344 | if(exp_len%20 != 0){ 345 | const size_t rest = exp_len%20; 346 | uint8_t slice[rest]; 347 | memcpy(&slice[0],&exp[0]+exp_len-rest,rest); 348 | experimentCharacteristic.writeValue(slice,sizeof(slice)); 349 | 350 | delay(5); 351 | } 352 | 353 | BLE.advertise(); 354 | } 355 | 356 | void PhyphoxBLE::configCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic){ 357 | if(configHandler!=nullptr){ 358 | (*configHandler)(); 359 | } 360 | } 361 | 362 | void PhyphoxBLE::eventCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic){ 363 | 364 | uint8_t read_buffer[17]; 365 | eventCharacteristic.readValue(read_buffer, 17); 366 | 367 | 368 | memcpy(&eventData[0],read_buffer,17); 369 | int64_t et,st; 370 | memcpy(&et,&eventData[0]+1,8); 371 | memcpy(&st,&eventData[0]+1+8,8); 372 | PhyphoxBLE::eventType = eventData[0]; 373 | PhyphoxBLE::systemTime = swap_int64(st); 374 | PhyphoxBLE::experimentTime = swap_int64(et); 375 | 376 | if(experimentEventHandler!=nullptr){ 377 | (*experimentEventHandler)(); 378 | } 379 | } 380 | 381 | void PhyphoxBLE::printXML(HardwareSerial* printer){ 382 | printer->println(""); 383 | for(int i =0; iprint(CHAR); 386 | } 387 | printer->println(""); 388 | } 389 | #endif 390 | -------------------------------------------------------------------------------- /src/boards/phyphoxBLE_NRF52.cpp: -------------------------------------------------------------------------------- 1 | #if defined(ARDUINO_ARCH_MBED) 2 | #include "phyphoxBLE_NRF52.h" 3 | 4 | const UUID PhyphoxBLE::phyphoxExperimentServiceUUID = UUID(phyphoxBleExperimentServiceUUID); 5 | const UUID PhyphoxBLE::experimentCharacteristicUUID = UUID(phyphoxBleExperimentCharacteristicUUID); 6 | 7 | const UUID PhyphoxBLE::phyphoxDataServiceUUID = UUID(phyphoxBleDataServiceUUID); 8 | const UUID PhyphoxBLE::dataCharacteristicUUID = UUID(phyphoxBleDataCharacteristicUUID); 9 | const UUID PhyphoxBLE::configCharacteristicUUID = UUID(phyphoxBleConfigCharacteristicUUID); 10 | const UUID PhyphoxBLE::eventCharacteristicUUID = UUID(phyphoxBleEventCharacteristicUUID); 11 | 12 | uint16_t PhyphoxBleExperiment::MTU = 20; 13 | 14 | char PhyphoxBLE::name[50] = ""; 15 | 16 | Thread PhyphoxBLE::bleEventThread; 17 | Thread PhyphoxBLE::transferExpThread; 18 | 19 | uint8_t PhyphoxBLE::data_package[20] = {0}; 20 | uint8_t PhyphoxBLE::config_package[CONFIGSIZE] = {0}; 21 | uint8_t PhyphoxBLE::eventData[17] = {0}; 22 | int64_t PhyphoxBLE::experimentTime = NULL; 23 | int64_t PhyphoxBLE::systemTime = NULL; 24 | uint8_t PhyphoxBLE::eventType = NULL; 25 | 26 | /*BLE stuff*/ 27 | BLE& bleInstance = BLE::Instance(BLE::DEFAULT_INSTANCE); 28 | BLE& PhyphoxBLE::ble = bleInstance; 29 | 30 | uint8_t PhyphoxBLE::readValue[DATASIZE] = {0}; 31 | 32 | ReadWriteArrayGattCharacteristic PhyphoxBLE::dataCharacteristic{PhyphoxBLE::dataCharacteristicUUID, PhyphoxBLE::data_package, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY}; //Note: Use { } instead of () google most vexing parse 33 | ReadWriteArrayGattCharacteristic PhyphoxBLE::configCharacteristic{PhyphoxBLE::configCharacteristicUUID, PhyphoxBLE::config_package, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY}; 34 | ReadWriteArrayGattCharacteristic PhyphoxBLE::eventCharacteristic{PhyphoxBLE::eventCharacteristicUUID, PhyphoxBLE::eventData, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY}; 35 | 36 | //Experiment Transfer 37 | ReadOnlyArrayGattCharacteristic PhyphoxBLE::experimentCharacteristic{PhyphoxBLE::experimentCharacteristicUUID, PhyphoxBLE::readValue, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY}; 38 | 39 | EventQueue PhyphoxBLE::queue{32 * EVENTS_EVENT_SIZE}; 40 | /*end BLE stuff*/ 41 | EventQueue PhyphoxBLE::transferQueue{32 * EVENTS_EVENT_SIZE}; 42 | 43 | PhyphoxBleEventHandler PhyphoxBLE::eventHandler(bleInstance); 44 | 45 | GattCharacteristic* PhyphoxBLE::phyphoxCharacteristics[2] = {&PhyphoxBLE::experimentCharacteristic,&PhyphoxBLE::eventCharacteristic}; 46 | GattService PhyphoxBLE::phyphoxService{PhyphoxBLE::phyphoxExperimentServiceUUID, PhyphoxBLE::phyphoxCharacteristics, sizeof(PhyphoxBLE::phyphoxCharacteristics) / sizeof(GattCharacteristic *)}; 47 | 48 | GattCharacteristic* PhyphoxBLE::phyphoxDataCharacteristics[2] = {&PhyphoxBLE::dataCharacteristic, &PhyphoxBLE::configCharacteristic}; 49 | GattService PhyphoxBLE::phyphoxDataService{PhyphoxBLE::phyphoxDataServiceUUID, PhyphoxBLE::phyphoxDataCharacteristics, sizeof(PhyphoxBLE::phyphoxDataCharacteristics) / sizeof(GattCharacteristic *)}; 50 | 51 | uint8_t* PhyphoxBLE::data = nullptr; //this pointer points to the data the user wants to write in the characteristic 52 | uint8_t* PhyphoxBLE::config =nullptr; 53 | uint8_t* PhyphoxBLE::event =nullptr; 54 | uint8_t* PhyphoxBLE::p_exp = nullptr; //this pointer will point to the byte array which holds an experiment 55 | 56 | char PhyphoxBLE::EXPARRAY[4096] = {0};// block some storage 57 | size_t PhyphoxBLE::expLen = 0; //try o avoid this maybe use std::array or std::vector 58 | 59 | void (*PhyphoxBLE::configHandler)() = nullptr; 60 | void (*PhyphoxBLE::experimentEventHandler)() = nullptr; 61 | 62 | void PhyphoxBleEventHandler::onDisconnectionComplete(const ble::DisconnectionCompleteEvent &event) 63 | { 64 | //#ifndef NDEBUG 65 | //if(printer) 66 | // printer -> println("Disconnection"); 67 | //#endif 68 | ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE); 69 | PhyphoxBLE::currentConnections= PhyphoxBLE::currentConnections -1; 70 | } 71 | 72 | void PhyphoxBleEventHandler::onConnectionComplete(const ble::ConnectionCompleteEvent &event) 73 | { 74 | //#ifndef NDEBUG 75 | //if(printer) 76 | // printer -> println("Connection with device"); 77 | //#endif 78 | 79 | PhyphoxBLE::currentConnections+=1; 80 | 81 | 82 | ble.gap().updateConnectionParameters(event.getConnectionHandle(), 83 | ble::conn_interval_t(PhyphoxBLE::minConInterval), 84 | ble::conn_interval_t(PhyphoxBLE::maxConInterval), 85 | ble::slave_latency_t (PhyphoxBLE::slaveLatency), 86 | ble::supervision_timeout_t(PhyphoxBLE::timeout)); 87 | 88 | } 89 | 90 | #ifndef NDEBUG 91 | void PhyphoxBLE::begin(HardwareSerial* hwPrint) 92 | { 93 | printer = hwPrint; 94 | if(printer) 95 | printer->begin(9600); 96 | } 97 | 98 | void PhyphoxBLE::output(const char* s) 99 | { 100 | if(printer) 101 | printer->println(s); 102 | } 103 | 104 | void PhyphoxBLE::output(const uint32_t num) 105 | { 106 | if(printer) 107 | printer -> println(num); 108 | } 109 | 110 | #endif 111 | 112 | void PhyphoxBLE::printXML(HardwareSerial* printer){ 113 | printer->println(""); 114 | for(int i =0; iprint(CHAR); 117 | } 118 | printer->println(""); 119 | } 120 | 121 | 122 | 123 | void PhyphoxBLE::when_subscription_received(GattAttribute::Handle_t handle) 124 | { 125 | #ifndef NDEBUG 126 | output("Received a subscription"); 127 | #endif 128 | bool sub_dChar = false; 129 | ble.gattServer().areUpdatesEnabled(experimentCharacteristic, &sub_dChar); 130 | 131 | if(sub_dChar) 132 | { 133 | transferQueue.call(transferExp); 134 | sub_dChar = false; 135 | } 136 | //after experiment has been transfered start advertising again 137 | ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE); 138 | 139 | } 140 | void PhyphoxBLE::configReceived(const GattWriteCallbackParams *params) 141 | { 142 | if(params->handle == configCharacteristic.getValueHandle()){ 143 | if(configHandler != nullptr){ 144 | transferQueue.call( configHandler ); 145 | } 146 | }else if (params->handle == eventCharacteristic.getValueHandle()) 147 | { 148 | uint16_t eventSize = 17; 149 | ble.gattServer().read(eventCharacteristic.getValueHandle(), eventData, &eventSize); 150 | int64_t et,st; 151 | memcpy(&et,&eventData[0]+1,8); 152 | memcpy(&st,&eventData[0]+1+8,8); 153 | PhyphoxBLE::eventType = eventData[0]; 154 | PhyphoxBLE::systemTime = swap_int64(st); 155 | PhyphoxBLE::experimentTime = swap_int64(et); 156 | if(experimentEventHandler != nullptr){ 157 | transferQueue.call( experimentEventHandler ); 158 | } 159 | 160 | } 161 | 162 | } 163 | 164 | void PhyphoxBLE::transferExp() 165 | { 166 | BLE &ble = PhyphoxBLE::ble; 167 | uint8_t* exp = PhyphoxBLE::p_exp; 168 | size_t exp_len = PhyphoxBLE::expLen; 169 | 170 | #ifndef NDEBUG 171 | PhyphoxBLE::output("In transfer exp"); 172 | #endif 173 | 174 | uint8_t header[20] = {0}; //20 byte as standard package size for ble transfer 175 | const char phyphox[] = "phyphox-Arduino"; 176 | uint32_t table[256]; 177 | phyphoxBleCrc32::generate_table(table); 178 | uint32_t checksum = phyphoxBleCrc32::update(table, 0, exp, exp_len); 179 | #ifndef NDEBUG 180 | PhyphoxBLE::output("checksum: "); 181 | PhyphoxBLE::output(checksum); 182 | #endif 183 | size_t arrayLength = exp_len; 184 | uint8_t experimentSizeArray[4] = {0}; 185 | experimentSizeArray[0]= (arrayLength >> 24); 186 | experimentSizeArray[1]= (arrayLength >> 16); 187 | experimentSizeArray[2]= (arrayLength >> 8); 188 | experimentSizeArray[3]= arrayLength; 189 | 190 | uint8_t checksumArray[4] = {0}; 191 | checksumArray[0]= (checksum >> 24) & 0xFF; 192 | checksumArray[1]= (checksum >> 16) & 0xFF; 193 | checksumArray[2]= (checksum >> 8) & 0xFF; 194 | checksumArray[3]= checksum & 0xFF; 195 | 196 | copy(phyphox, phyphox+7, header); 197 | copy(experimentSizeArray, experimentSizeArray+ 4, header + 7); 198 | copy(checksumArray, checksumArray + 4, header +11); 199 | 200 | ble.gattServer().write(PhyphoxBLE::experimentCharacteristic.getValueHandle(), header, sizeof(header)); 201 | wait_us(30000); 202 | 203 | for(size_t i = 0; i < exp_len/20; ++i) 204 | { 205 | copy(exp+i*20, exp+i*20+20, header); 206 | ble.gattServer().write(PhyphoxBLE::experimentCharacteristic.getValueHandle(), header, sizeof(header)); 207 | wait_us(30000); 208 | } 209 | 210 | if(exp_len%20 != 0) 211 | { 212 | const size_t rest = exp_len%20; 213 | uint8_t slice[rest]; 214 | copy(exp + exp_len - rest, exp + exp_len, slice); 215 | ble.gattServer().write(PhyphoxBLE::experimentCharacteristic.getValueHandle(), slice, sizeof(slice)); 216 | wait_us(30000); 217 | } 218 | } 219 | 220 | void PhyphoxBLE::bleInitComplete(BLE::InitializationCompleteCallbackContext* params) 221 | { 222 | 223 | ble.gattServer().onUpdatesEnabled(PhyphoxBLE::when_subscription_received); 224 | ble.gattServer().onDataWritten(PhyphoxBLE::configReceived); 225 | 226 | ble.gap().setEventHandler(&PhyphoxBLE::eventHandler); 227 | 228 | uint8_t _adv_buffer[ble::LEGACY_ADVERTISING_MAX_SIZE]; 229 | ble::AdvertisingDataBuilder adv_data_builder(_adv_buffer); 230 | //adv_data_builder.setConnectionIntervalPreference(ble::conn_interval_t(minConInterval) ,ble::conn_interval_t(maxConInterval) ); 231 | ble::AdvertisingParameters adv_parameters(ble::advertising_type_t::CONNECTABLE_UNDIRECTED, ble::adv_interval_t(ble::millisecond_t(100))); 232 | adv_data_builder.setFlags(); 233 | adv_data_builder.setLocalServiceList(mbed::make_Span(&phyphoxExperimentServiceUUID, 1)); 234 | 235 | #ifndef NDEBUG 236 | if(error == BLE_ERROR_BUFFER_OVERFLOW){ 237 | output("BLE_ERROR_BUFFER_OVERFLOW"); 238 | }else if(error == BLE_ERROR_NONE){ 239 | output("BLE_ERROR_NONE"); 240 | }else if(error == BLE_ERROR_INVALID_PARAM){ 241 | output("BLE_ERROR_INVALID_PARAM"); 242 | } 243 | #endif 244 | 245 | ble.gap().setAdvertisingParameters(ble::LEGACY_ADVERTISING_HANDLE, adv_parameters); 246 | ble.gap().setAdvertisingPayload(ble::LEGACY_ADVERTISING_HANDLE,adv_data_builder.getAdvertisingData()); 247 | adv_data_builder.clear(); 248 | adv_data_builder.setName(name); 249 | ble.gap().setAdvertisingScanResponse(ble::LEGACY_ADVERTISING_HANDLE,adv_data_builder.getAdvertisingData()); 250 | ble.gattServer().addService(phyphoxService); 251 | ble.gattServer().addService(phyphoxDataService); 252 | 253 | ble.gap().startAdvertising(ble::LEGACY_ADVERTISING_HANDLE); 254 | #ifndef NDEBUG 255 | output("in init"); 256 | #endif 257 | } 258 | 259 | void PhyphoxBLE::start(const char* DEVICE_NAME, uint8_t* exp_pointer, size_t len) 260 | { 261 | /** 262 | * \brief start the server and begin advertising 263 | * This methods starts the server by. By specifying 264 | * exp_pointer and len you can send your own phyphox 265 | * experiment to your smartphone. Otherwise a default 266 | * experiment will be sent. 267 | * \param exp_pointer a pointer to an experiment 268 | * \param len the len of the experiment 269 | */ 270 | #ifndef NDEBUG 271 | output("In start"); 272 | #endif 273 | 274 | strncpy(name, DEVICE_NAME, 49); 275 | name[50] = '\0'; 276 | 277 | if(exp_pointer != nullptr){ 278 | p_exp = exp_pointer; 279 | expLen = len; 280 | }else if(p_exp==nullptr){ 281 | 282 | PhyphoxBleExperiment defaultExperiment; 283 | 284 | //View 285 | PhyphoxBleExperiment::View firstView; 286 | 287 | //Graph 288 | PhyphoxBleExperiment::Graph firstGraph; //Create graph which will plot random numbers over time 289 | firstGraph.setChannel(0,1); 290 | 291 | firstView.addElement(firstGraph); 292 | defaultExperiment.addView(firstView); 293 | 294 | addExperiment(defaultExperiment); 295 | 296 | } 297 | 298 | bleEventThread.start(callback(&queue, &EventQueue::dispatch_forever)); 299 | transferExpThread.start(callback(&transferQueue, &EventQueue::dispatch_forever)); 300 | ble.onEventsToProcess(PhyphoxBLE::schedule_ble_events); 301 | ble.init(PhyphoxBLE::bleInitComplete); 302 | } 303 | 304 | void PhyphoxBLE::start(uint8_t* exp_pointer, size_t len) { 305 | start("phyphox-Arduino", exp_pointer, len); 306 | } 307 | 308 | void PhyphoxBLE::start(const char* DEVICE_NAME) { 309 | start(DEVICE_NAME, nullptr, 0); 310 | deviceName = DEVICE_NAME; 311 | } 312 | 313 | void PhyphoxBLE::start() { 314 | start("phyphox-Arduino", nullptr, 0); 315 | } 316 | 317 | void PhyphoxBLE::poll() { 318 | } 319 | 320 | void PhyphoxBLE::poll(int timeout) { 321 | } 322 | 323 | void PhyphoxBLE::write(float& value) 324 | { 325 | /** 326 | * \brief Write a single float into characteristic 327 | * The float is parsed to uint8_t* 328 | * because the gattServer write method 329 | * expects a pointer to uint8_t 330 | * \param f1 represent a float most likeley sensor data 331 | */ 332 | data = reinterpret_cast(&value); 333 | ble.gattServer().write(dataCharacteristic.getValueHandle(), data, 4); 334 | } 335 | 336 | 337 | ///todo: public getter for gatt server 338 | 339 | 340 | void PhyphoxBLE::write(float& f1, float& f2) 341 | { 342 | /** 343 | * \brief Write 2 floats into characteristic 344 | * The floats are parsed to uint8_t* 345 | * because the gattServer write method e 346 | * expects a pointer to uint8_t 347 | * \param f1 and f2 represent two floats most likeley sensor data 348 | */ 349 | 350 | float array[2] = {f1, f2}; 351 | data = reinterpret_cast(array); 352 | ble.gattServer().write(dataCharacteristic.getValueHandle(), data, 8); 353 | } 354 | 355 | void PhyphoxBLE::write(float& f1, float& f2, float& f3) 356 | { 357 | /** 358 | * \brief Write 3 floats into characteristic 359 | * The floats are parsed to uint8_t* 360 | * because the gattServer write method 361 | * expects a pointer to uint8_t 362 | * \param param f1 and f2 and f3 represents three floats most likeley sensor data 363 | */ 364 | float array[3] = {f1, f2, f3}; 365 | data = reinterpret_cast(array); 366 | ble.gattServer().write(dataCharacteristic.getValueHandle(), data, 12); 367 | } 368 | 369 | void PhyphoxBLE::write(float& f1, float& f2, float& f3, float& f4) 370 | { 371 | /** 372 | * \brief Write 4 floats into characteristic 373 | * The floats are parsed to uint8_t* 374 | * because the gattServer write method 375 | * expects a pointer to uint8_t 376 | * \param param f1 and f2 and f3 and f4 represents four floats most likeley sensor data 377 | */ 378 | float array[4] = {f1, f2, f3, f4}; 379 | data = reinterpret_cast(array); 380 | ble.gattServer().write(dataCharacteristic.getValueHandle(), data, 16); 381 | } 382 | void PhyphoxBLE::write(float& f1, float& f2, float& f3, float& f4, float& f5) 383 | { 384 | /** 385 | * \brief Write 5 floats into characteristic 386 | * The floats are parsed to uint8_t* 387 | * because the gattServer write method 388 | * expects a pointer to uint8_t 389 | * \param param f1 and f2 and f3 and f4 and f5 represents five floats most likeley sensor data 390 | */ 391 | float array[5] = {f1, f2, f3, f4, f5}; 392 | data = reinterpret_cast(array); 393 | ble.gattServer().write(dataCharacteristic.getValueHandle(), data, 20); 394 | } 395 | void PhyphoxBLE::write(uint8_t *arrayPointer, unsigned int arraySize) 396 | { 397 | ble.gattServer().write(dataCharacteristic.getValueHandle(), arrayPointer, arraySize); 398 | } 399 | 400 | 401 | void PhyphoxBLE::read(float& f1) 402 | { 403 | uint16_t configSize = 4; 404 | uint8_t myConfig[4]; 405 | ble.gattServer().read(configCharacteristic.getValueHandle(), myConfig, &configSize); 406 | memcpy(&f1,&myConfig[0], 4); 407 | } 408 | void PhyphoxBLE::read(float& f1, float& f2) 409 | { 410 | uint16_t configSize = 8; 411 | uint8_t myConfig[8]; 412 | ble.gattServer().read(configCharacteristic.getValueHandle(), myConfig, &configSize); 413 | memcpy(&f1,&myConfig[0],4); 414 | memcpy(&f2,&myConfig[0]+4,4); 415 | } 416 | void PhyphoxBLE::read(float& f1, float& f2, float& f3) 417 | { 418 | uint16_t configSize = 12; 419 | uint8_t myConfig[12]; 420 | ble.gattServer().read(configCharacteristic.getValueHandle(), myConfig, &configSize); 421 | memcpy(&f1,&myConfig[0],4); 422 | memcpy(&f2,&myConfig[0]+4,4); 423 | memcpy(&f3,&myConfig[0]+8,4); 424 | } 425 | void PhyphoxBLE::read(float& f1, float& f2, float& f3, float& f4) 426 | { 427 | uint16_t configSize = 16; 428 | uint8_t myConfig[16]; 429 | ble.gattServer().read(configCharacteristic.getValueHandle(), myConfig, &configSize); 430 | memcpy(&f1,&myConfig[0],4); 431 | memcpy(&f2,&myConfig[0]+4,4); 432 | memcpy(&f3,&myConfig[0]+8,4); 433 | memcpy(&f4,&myConfig[0]+12,4); 434 | } 435 | void PhyphoxBLE::read(float& f1, float& f2, float& f3, float& f4, float& f5) 436 | { 437 | uint16_t configSize = 20; 438 | uint8_t myConfig[20]; 439 | ble.gattServer().read(configCharacteristic.getValueHandle(), myConfig, &configSize); 440 | memcpy(&f1,&myConfig[0],4); 441 | memcpy(&f2,&myConfig[0]+4,4); 442 | memcpy(&f3,&myConfig[0]+8,4); 443 | memcpy(&f4,&myConfig[0]+12,4); 444 | memcpy(&f5,&myConfig[0]+16,4); 445 | } 446 | 447 | void PhyphoxBLE::read(uint8_t *arrayPointer, unsigned int arraySize) 448 | { 449 | uint16_t myArraySize = arraySize; 450 | ble.gattServer().read(configCharacteristic.getValueHandle(), arrayPointer, &myArraySize); 451 | } 452 | 453 | 454 | void PhyphoxBLE::addExperiment(PhyphoxBleExperiment& exp) 455 | { 456 | for (int i = 0; i < 4096; i++){ 457 | PhyphoxBLE::EXPARRAY[i]=0; 458 | } 459 | exp.getFirstBytes(EXPARRAY, deviceName); 460 | for(uint8_t i=0;i(&context->ble, &BLE::processEvents)); 473 | } 474 | 475 | #endif 476 | 477 | 478 | -------------------------------------------------------------------------------- /src/boards/phyphoxBLE_ESP32.cpp: -------------------------------------------------------------------------------- 1 | #if defined(ESP32) && !defined(ARDUINO_SENSEBOX_MCU_ESP32S2) 2 | #include "phyphoxBLE_ESP32.h" 3 | #include "Arduino.h" 4 | #include 5 | #include "esp_system.h" 6 | //#define DEBUG 7 | //init statics 8 | uint8_t PhyphoxBLE::data_package[20] = {0}; 9 | void (*PhyphoxBLE::configHandler)() = nullptr; 10 | void (*PhyphoxBLE::experimentEventHandler)() = nullptr; 11 | uint8_t storage[16000]; 12 | //uint8_t *storage = (uint8_t*) malloc(8000 * sizeof(char)); 13 | char *PhyphoxBLE::EXPARRAY=(char*)storage; 14 | uint8_t* PhyphoxBLE::p_exp = nullptr; 15 | size_t PhyphoxBLE::expLen = 0; 16 | HardwareSerial* PhyphoxBLE::printer =nullptr; 17 | 18 | BLEServer *PhyphoxBLE::myServer; 19 | BLEService *PhyphoxBLE::phyphoxExperimentService; 20 | BLEService *PhyphoxBLE::phyphoxDataService; 21 | BLEDescriptor *PhyphoxBLE::myExperimentDescriptor; 22 | BLEDescriptor *PhyphoxBLE::myDataDescriptor; 23 | BLEDescriptor *PhyphoxBLE::myEventDescriptor; 24 | BLEDescriptor *PhyphoxBLE::myConfigDescriptor; 25 | BLECharacteristic *PhyphoxBLE::dataCharacteristic; 26 | BLECharacteristic *PhyphoxBLE::eventCharacteristic; 27 | BLECharacteristic *PhyphoxBLE::experimentCharacteristic; 28 | BLECharacteristic *PhyphoxBLE::configCharacteristic; 29 | BLEAdvertising *PhyphoxBLE::myAdvertising; 30 | TaskHandle_t PhyphoxBLE::TaskTransfer; 31 | uint8_t* PhyphoxBLE::data; 32 | 33 | uint16_t PhyphoxBLE::minConInterval = 6; //7.5ms 34 | uint16_t PhyphoxBLE::maxConInterval = 24; //30ms 35 | uint16_t PhyphoxBLE::slaveLatency = 0; 36 | uint16_t PhyphoxBLE::timeout = 50; 37 | uint16_t PhyphoxBLE::currentConnections=0; 38 | bool PhyphoxBLE::isSubscribed=false; 39 | 40 | uint8_t PhyphoxBLE::eventData[17]={0}; 41 | int64_t PhyphoxBLE::experimentTime = NULL; 42 | int64_t PhyphoxBLE::systemTime = NULL; 43 | uint8_t PhyphoxBLE::eventType = NULL; 44 | uint16_t PhyphoxBLE::MTU = 20; 45 | uint16_t PhyphoxBleExperiment::MTU = 20; 46 | 47 | #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) 48 | class MyExpCallback: public BLECharacteristicCallbacks { 49 | private: 50 | HardwareSerial* printer; 51 | 52 | public: 53 | MyExpCallback(HardwareSerial* hwPrint) { 54 | printer = hwPrint; 55 | } 56 | private: 57 | 58 | void onSubscribe(BLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue) override { 59 | #ifdef DEBUG 60 | if(printer){ 61 | printer->println("onSubscribe (exp)"); 62 | } 63 | #endif 64 | 65 | if (subValue) { 66 | // start when_subscription_received() on cpu 1 67 | PhyphoxBLE::startTask(); 68 | } 69 | }; 70 | }; 71 | #else 72 | class MyExpDescCallback: public BLEDescriptorCallbacks { 73 | private: 74 | HardwareSerial* printer; 75 | 76 | public: 77 | MyExpDescCallback(HardwareSerial* hwPrint) { 78 | printer = hwPrint; 79 | } 80 | private: 81 | 82 | void onWrite(BLEDescriptor* pDescriptor) override { 83 | #ifdef DEBUG 84 | if(printer){ 85 | printer->println("descriptor write (exp)"); 86 | } 87 | #endif 88 | uint8_t* rxValue = pDescriptor->getValue(); 89 | 90 | if(pDescriptor->getLength() > 0){ 91 | if (rxValue[0] == 1) { 92 | // start when_subscription_received() on cpu 1 93 | PhyphoxBLE::startTask(); 94 | } 95 | } 96 | }; 97 | }; 98 | #endif 99 | 100 | #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) 101 | class MyDataCallback: public BLECharacteristicCallbacks { 102 | private: 103 | HardwareSerial* printer; 104 | 105 | public: 106 | MyDataCallback(HardwareSerial* hwPrint) { 107 | printer = hwPrint; 108 | } 109 | 110 | private: 111 | 112 | void onSubscribe(BLECharacteristic *pCharacteristic, ble_gap_conn_desc *desc, uint16_t subValue) override { 113 | #ifdef DEBUG 114 | if(printer){ 115 | printer->println("onSubscribe (data)"); 116 | } 117 | #endif 118 | 119 | if(subValue > 0){ 120 | PhyphoxBLE::isSubscribed=true; 121 | } 122 | }; 123 | }; 124 | #else 125 | class MyDataDescCallback: public BLEDescriptorCallbacks { 126 | private: 127 | HardwareSerial* printer; 128 | 129 | public: 130 | MyDataDescCallback(HardwareSerial* hwPrint) { 131 | printer = hwPrint; 132 | } 133 | 134 | private: 135 | 136 | void onWrite(BLEDescriptor* pDescriptor) override { 137 | #ifdef DEBUG 138 | if(printer){ 139 | printer->println("descriptor write (data)"); 140 | } 141 | #endif 142 | uint8_t* rxValue = pDescriptor->getValue(); 143 | 144 | if(pDescriptor->getLength() > 0){ 145 | PhyphoxBLE::isSubscribed=true; 146 | } 147 | }; 148 | }; 149 | #endif 150 | 151 | class MyEventCallback: public BLECharacteristicCallbacks { 152 | private: 153 | HardwareSerial* printer; 154 | 155 | public: 156 | MyEventCallback(HardwareSerial* hwPrint) { 157 | printer = hwPrint; 158 | } 159 | private: 160 | 161 | void onWrite(BLECharacteristic *pCharacteristic) override { 162 | #ifdef DEBUG 163 | if(printer){ 164 | printer->println("characteristic write (event)"); 165 | } 166 | #endif 167 | PhyphoxBLE::eventCharacteristicHandler(); 168 | }; 169 | }; 170 | 171 | class MyCharCallback: public BLECharacteristicCallbacks { 172 | private: 173 | HardwareSerial* printer; 174 | 175 | public: 176 | MyCharCallback(HardwareSerial* hwPrint) { 177 | printer = hwPrint; 178 | } 179 | private: 180 | PhyphoxBLE* myServerPointer; 181 | void onWrite(BLECharacteristic *pCharacteristic) override { 182 | #ifdef DEBUG 183 | if(printer){ 184 | printer->println("characteristic write (config)"); 185 | } 186 | #endif 187 | PhyphoxBLE::configHandlerDebug(); 188 | } 189 | }; 190 | 191 | class MyServerCallbacks: public BLEServerCallbacks { 192 | 193 | private: 194 | HardwareSerial* printer; 195 | 196 | public: 197 | MyServerCallbacks(HardwareSerial* hwPrint) { 198 | printer = hwPrint; 199 | } 200 | 201 | #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) 202 | void onConnect(BLEServer *pServer, ble_gap_conn_desc *desc) override { 203 | #ifdef DEBUG 204 | if(printer){ 205 | printer->println("onConnect (NimBLE)"); 206 | } 207 | #endif 208 | pServer->updateConnParams(desc->conn_handle, PhyphoxBLE::minConInterval, PhyphoxBLE::maxConInterval, PhyphoxBLE::slaveLatency, PhyphoxBLE::timeout); 209 | PhyphoxBLE::currentConnections+=1; 210 | }; 211 | #else 212 | void onConnect(BLEServer* pServer, esp_ble_gatts_cb_param_t *param) override { 213 | #ifdef DEBUG 214 | if(printer){ 215 | printer->println("onConnect (legacy)"); 216 | } 217 | #endif 218 | pServer->updateConnParams(param->connect.remote_bda,PhyphoxBLE::minConInterval,PhyphoxBLE::maxConInterval,PhyphoxBLE::slaveLatency,PhyphoxBLE::timeout); 219 | PhyphoxBLE::currentConnections+=1; 220 | }; 221 | #endif 222 | 223 | void onDisconnect(BLEServer* pServer) override { 224 | PhyphoxBLE::disconnected(); 225 | PhyphoxBLE::currentConnections-=1; 226 | } 227 | 228 | 229 | }; 230 | 231 | void PhyphoxBLE::configHandlerDebug(){ 232 | if(configHandler!=nullptr){ 233 | (*configHandler)(); 234 | } 235 | } 236 | 237 | void PhyphoxBLE::eventCharacteristicHandler(){ 238 | uint8_t* data = eventCharacteristic->getData(); 239 | memcpy(&eventData[0],data,17); 240 | int64_t et,st; 241 | memcpy(&et,data+1,8); 242 | memcpy(&st,data+1+8,8); 243 | PhyphoxBLE::eventType = eventData[0]; 244 | PhyphoxBLE::systemTime = swap_int64(st); 245 | PhyphoxBLE::experimentTime = swap_int64(et); 246 | if(experimentEventHandler!=nullptr){ 247 | (*experimentEventHandler)(); 248 | } 249 | } 250 | 251 | void PhyphoxBLE::setMTU(uint16_t mtuSize) { 252 | BLEDevice::setMTU(mtuSize+3); //user mtu size + 3 for overhead 253 | 254 | PhyphoxBLE::MTU = mtuSize; 255 | PhyphoxBleExperiment::MTU = mtuSize; 256 | } 257 | 258 | void PhyphoxBLE::start(const char* DEVICE_NAME, uint8_t* exp_pointer, size_t len){ 259 | p_exp = exp_pointer; 260 | expLen = len; 261 | start(DEVICE_NAME); 262 | } 263 | 264 | void PhyphoxBLE::start(uint8_t* exp_pointer, size_t len){ 265 | p_exp = exp_pointer; 266 | expLen = len; 267 | start(); 268 | } 269 | 270 | void PhyphoxBLE::start(const char * DEVICE_NAME) 271 | { 272 | deviceName = DEVICE_NAME; 273 | #ifdef DEBUG 274 | if(printer){ 275 | printer->println("starting server"); 276 | } 277 | #endif 278 | if(p_exp == nullptr){ 279 | PhyphoxBleExperiment defaultExperiment; 280 | 281 | //View 282 | PhyphoxBleExperiment::View firstView; 283 | 284 | //Graph 285 | PhyphoxBleExperiment::Graph firstGraph; //Create graph which will plot random numbers over time 286 | firstGraph.setChannel(0,1); 287 | 288 | //Value 289 | PhyphoxBleExperiment::Value valueField; 290 | valueField.setChannel(1); 291 | 292 | firstView.addElement(firstGraph); 293 | firstView.addElement(valueField); 294 | defaultExperiment.addView(firstView); 295 | 296 | addExperiment(defaultExperiment); 297 | } 298 | 299 | BLEDevice::init(DEVICE_NAME); 300 | myServer = BLEDevice::createServer(); 301 | myServer->setCallbacks(new MyServerCallbacks(printer)); 302 | phyphoxExperimentService = myServer->createService(phyphoxBleExperimentServiceUUID); 303 | 304 | experimentCharacteristic = phyphoxExperimentService->createCharacteristic( 305 | phyphoxBleExperimentCharacteristicUUID, 306 | BLECharacteristic::PROPERTY_READ | 307 | BLECharacteristic::PROPERTY_WRITE | 308 | BLECharacteristic::PROPERTY_NOTIFY 309 | ); 310 | eventCharacteristic = phyphoxExperimentService->createCharacteristic( 311 | phyphoxBleEventCharacteristicUUID, 312 | BLECharacteristic::PROPERTY_WRITE 313 | ); 314 | 315 | phyphoxDataService = myServer->createService(phyphoxBleDataServiceUUID); 316 | 317 | dataCharacteristic = phyphoxDataService->createCharacteristic( 318 | phyphoxBleDataCharacteristicUUID, 319 | BLECharacteristic::PROPERTY_READ | 320 | BLECharacteristic::PROPERTY_WRITE | 321 | BLECharacteristic::PROPERTY_NOTIFY 322 | 323 | ); 324 | 325 | configCharacteristic = phyphoxDataService->createCharacteristic( 326 | phyphoxBleConfigCharacteristicUUID, 327 | BLECharacteristic::PROPERTY_READ | 328 | BLECharacteristic::PROPERTY_WRITE | 329 | BLECharacteristic::PROPERTY_NOTIFY 330 | ); 331 | 332 | myExperimentDescriptor = new BLE2902(); 333 | myDataDescriptor = new BLE2902(); 334 | myEventDescriptor = new BLE2902(); 335 | myConfigDescriptor = new BLE2902(); 336 | 337 | #if defined(CONFIG_BT_NIMBLE_ROLE_PERIPHERAL) 338 | experimentCharacteristic->setCallbacks(new MyExpCallback(printer)); 339 | dataCharacteristic->setCallbacks(new MyDataCallback(printer)); 340 | #else 341 | myExperimentDescriptor->setCallbacks(new MyExpDescCallback(printer)); 342 | myDataDescriptor->setCallbacks(new MyDataDescCallback(printer)); 343 | #endif 344 | eventCharacteristic->setCallbacks(new MyEventCallback(printer)); 345 | configCharacteristic->setCallbacks(new MyCharCallback(printer)); 346 | 347 | dataCharacteristic->addDescriptor(myDataDescriptor); 348 | experimentCharacteristic->addDescriptor(myExperimentDescriptor); 349 | eventCharacteristic->addDescriptor(myEventDescriptor); 350 | configCharacteristic->addDescriptor(myConfigDescriptor); 351 | 352 | 353 | phyphoxExperimentService->start(); 354 | phyphoxDataService->start(); 355 | myAdvertising = BLEDevice::getAdvertising(); 356 | myAdvertising->addServiceUUID(phyphoxExperimentService->getUUID()); 357 | BLEDevice::startAdvertising(); 358 | 359 | } 360 | 361 | void PhyphoxBLE::start() { 362 | PhyphoxBLE::start("phyphox-Arduino"); 363 | } 364 | 365 | void PhyphoxBLE::poll() { 366 | } 367 | 368 | void PhyphoxBLE::poll(int timeout) { 369 | } 370 | 371 | //thank you stackoverflow =) 372 | void PhyphoxBLE::staticStartTask(void* _this){ 373 | PhyphoxBLE::when_subscription_received(); 374 | delay(1); 375 | } 376 | 377 | void PhyphoxBLE::startTask() 378 | { 379 | xTaskCreatePinnedToCore(staticStartTask, "TaskTransfer",10000, NULL,1, &TaskTransfer, tskNO_AFFINITY); 380 | delay(1); 381 | } 382 | 383 | void PhyphoxBLE::write(float& value) 384 | { 385 | /** 386 | * \brief Write a single float into characteristic 387 | * The float is parsed to uint8_t* 388 | * because the gattServer write method 389 | * expects a pointer to uint8_t 390 | * \param f1 represent a float most likeley sensor data 391 | */ 392 | data = reinterpret_cast(&value); 393 | dataCharacteristic->setValue(data,4); 394 | dataCharacteristic->notify(); 395 | } 396 | 397 | void PhyphoxBLE::write(float& f1, float& f2) 398 | { 399 | float array[2] = {f1, f2}; 400 | data = reinterpret_cast(array); 401 | dataCharacteristic->setValue(data,8); 402 | dataCharacteristic->notify(); 403 | } 404 | void PhyphoxBLE::write(float& f1, float& f2, float& f3) 405 | { 406 | float array[3] = {f1, f2, f3}; 407 | data = reinterpret_cast(array); 408 | dataCharacteristic->setValue(data,12); 409 | dataCharacteristic->notify(); 410 | } 411 | void PhyphoxBLE::write(float& f1, float& f2, float& f3, float& f4) 412 | { 413 | float array[4] = {f1, f2, f3, f4}; 414 | data = reinterpret_cast(array); 415 | dataCharacteristic->setValue(data,16); 416 | dataCharacteristic->notify(); 417 | } 418 | void PhyphoxBLE::write(float& f1, float& f2, float& f3, float& f4, float& f5) 419 | { 420 | float array[5] = {f1, f2, f3, f4, f5}; 421 | data = reinterpret_cast(array); 422 | dataCharacteristic->setValue(data,20); 423 | dataCharacteristic->notify(); 424 | } 425 | void PhyphoxBLE::write(uint8_t *arrayPointer, unsigned int arraySize) 426 | { 427 | dataCharacteristic->setValue(arrayPointer,arraySize); 428 | dataCharacteristic->notify(); 429 | } 430 | void PhyphoxBLE::write(float *arrayPointer, unsigned int arrayLength) 431 | { 432 | uint8_t dataBuffer[arrayLength*4]; 433 | memcpy(&dataBuffer[0], &arrayPointer[0],arrayLength*4); 434 | dataCharacteristic->setValue(&dataBuffer[0],arrayLength*4); 435 | dataCharacteristic->notify(); 436 | } 437 | 438 | 439 | void PhyphoxBLE::read(uint8_t *arrayPointer, unsigned int arraySize) 440 | { 441 | uint8_t* data = configCharacteristic->getData(); 442 | memcpy(arrayPointer,data,arraySize); 443 | } 444 | 445 | void PhyphoxBLE::read(float& f) 446 | { 447 | uint8_t* data = configCharacteristic->getData(); 448 | memcpy(&f,data,4); 449 | } 450 | void PhyphoxBLE::read(float& f1, float& f2) 451 | { 452 | uint8_t* data = configCharacteristic->getData(); 453 | memcpy(&f1,data,4); 454 | memcpy(&f2,data+4,4); 455 | } 456 | void PhyphoxBLE::read(float& f1, float& f2, float& f3) 457 | { 458 | uint8_t* data = configCharacteristic->getData(); 459 | memcpy(&f1,data,4); 460 | memcpy(&f2,data+4,4); 461 | memcpy(&f3,data+8,4); 462 | } 463 | void PhyphoxBLE::read(float& f1, float& f2, float& f3, float& f4) 464 | { 465 | uint8_t* data = configCharacteristic->getData(); 466 | memcpy(&f1,data,4); 467 | memcpy(&f2,data+4,4); 468 | memcpy(&f3,data+8,4); 469 | memcpy(&f4,data+12,4); 470 | } 471 | void PhyphoxBLE::read(float& f1, float& f2, float& f3, float& f4, float& f5) 472 | { 473 | uint8_t* data = configCharacteristic->getData(); 474 | memcpy(&f1,data,4); 475 | memcpy(&f2,data+4,4); 476 | memcpy(&f3,data+8,4); 477 | memcpy(&f4,data+12,4); 478 | memcpy(&f5,data+16,4); 479 | } 480 | 481 | 482 | void PhyphoxBLE::when_subscription_received() 483 | { 484 | 485 | myAdvertising->stop(); 486 | 487 | uint8_t* exp = p_exp; 488 | size_t exp_len = expLen; 489 | 490 | uint8_t header[20] = {0}; //20 byte as standard package size for ble transfer 491 | const char phyphox[] = "phyphox"; 492 | uint32_t table[256]; 493 | phyphoxBleCrc32::generate_table(table); 494 | uint32_t checksum = phyphoxBleCrc32::update(table, 0, exp, exp_len); 495 | size_t arrayLength = exp_len; 496 | uint8_t experimentSizeArray[4] = {0}; 497 | experimentSizeArray[0]= (arrayLength >> 24); 498 | experimentSizeArray[1]= (arrayLength >> 16); 499 | experimentSizeArray[2]= (arrayLength >> 8); 500 | experimentSizeArray[3]= arrayLength; 501 | 502 | uint8_t checksumArray[4] = {0}; 503 | checksumArray[0]= (checksum >> 24) & 0xFF; 504 | checksumArray[1]= (checksum >> 16) & 0xFF; 505 | checksumArray[2]= (checksum >> 8) & 0xFF; 506 | checksumArray[3]= checksum & 0xFF; 507 | 508 | copy(phyphox, phyphox+7, header); 509 | copy(experimentSizeArray, experimentSizeArray+ 4, header + 7); 510 | copy(checksumArray, checksumArray + 4, header +11); 511 | experimentCharacteristic->setValue(header,sizeof(header)); 512 | experimentCharacteristic->notify(); 513 | 514 | for(size_t i = 0; i < exp_len/20; ++i){ 515 | copy(exp+i*20, exp+i*20+20, header); 516 | experimentCharacteristic->setValue(header,sizeof(header)); 517 | experimentCharacteristic->notify(); 518 | delay(10);// mh does not work anymore with 1ms delay?! 519 | } 520 | 521 | if(exp_len%20 != 0){ 522 | const size_t rest = exp_len%20; 523 | uint8_t slice[rest]; 524 | copy(exp + exp_len - rest, exp + exp_len, slice); 525 | experimentCharacteristic->setValue(slice,sizeof(slice)); 526 | experimentCharacteristic->notify(); 527 | delay(1); 528 | } 529 | 530 | 531 | myAdvertising->start(); 532 | 533 | 534 | vTaskDelete( NULL ); 535 | 536 | } 537 | void PhyphoxBLE::addExperiment(PhyphoxBleExperiment& exp) 538 | { 539 | for (int i = 0; i < 16000; i++) 540 | { 541 | storage[i]=0; 542 | } 543 | 544 | exp.getFirstBytes(EXPARRAY, deviceName); 545 | for(uint8_t i=0;i println("device disconnected"); 559 | } 560 | #endif 561 | myAdvertising->start(); 562 | } 563 | 564 | 565 | void PhyphoxBLE::begin(HardwareSerial* hwPrint) 566 | { 567 | #ifdef DEBUG 568 | printer = hwPrint; 569 | if(printer) 570 | printer->begin(115200); 571 | #endif 572 | } 573 | 574 | void PhyphoxBLE::printXML(HardwareSerial* printer){ 575 | printer->println(""); 576 | for(int i =0; iprint(CHAR); 579 | } 580 | printer->println(""); 581 | } 582 | 583 | #endif 584 | -------------------------------------------------------------------------------- /examples/CO2kit/CO2kit.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Dies ist eine aktualisierte Version des Arduino Codes für das phyphox CO2 Monitor Kit 3 | 4 | Um Ihren CO2-Monitor zu aktualisieren, löschen Sie in der phyphox App die Experimente "CO2-Sensor" und "CO₂-Sensor Maintenance" 5 | und folgen Sie dem Software-Teil der Aufbauanleitung. 6 | https://phyphox.org/wp-content/uploads/2021/03/CO2-Monitor-Rev_1_1.pdf 7 | 8 | 9 | This is an updated version of the Arduino Code for the phyphox CO2 Monitor Kit 10 | 11 | To update your CO2 Monitor delete in the phyphox app the experiements "CO2-Sensor" and "CO₂-Sensor Maintenance" 12 | and follow the software part of the assembly instructions 13 | https://phyphox.org/wp-content/uploads/2021/03/CO2-Monitor-Rev_1_1.pdf 14 | */ 15 | 16 | 17 | #include 18 | #include "SparkFun_SCD30_Arduino_Library.h" 19 | #include 20 | #include 21 | 22 | //LED Update Thresholds 23 | //Can be set as desired 24 | float topThreshold = 1000; 25 | float bottomThreshold = 600; 26 | 27 | 28 | bool FORMATFLASH = true; 29 | 30 | SCD30 airSensor; 31 | File fsUploadFile; // a File variable to temporarily store the received file 32 | 33 | int maxDatasets = 7200; //store data every 12s over 24h -> maxDatasets = 24*60*60/12 = 7200 34 | int transferedDatasets = 11; //mtu size = 176 -> 4*4*11 = 176 35 | 36 | const int measuredDataLength = 4; 37 | float measuredData[measuredDataLength]; //co2,temperature,humidity,seconds since uptime, number of dataset 38 | 39 | float averageMeasuredData[measuredDataLength]; 40 | int averageOver = 6; //6*2s rate = store data every 12s 41 | int averageCounter = 0; 42 | int oldDataTransmissionOffset = -1; 43 | int lineNumber = 0; 44 | 45 | void receivedConfig(); 46 | long timeStampBlue = -3; //used to timestamp the moment of connection 47 | int currentConnections = 0; 48 | //Pinout 49 | int pinGreen = 26; 50 | int pinRed = 33; 51 | int pinBlue = 25; 52 | bool RED = 1; 53 | bool GREEN = 0; 54 | bool BLUE = 1; 55 | int readyPin = 5; 56 | 57 | 58 | uint8_t versionID = 3; 59 | uint8_t currentOffset; 60 | uint8_t statusCO2calibration = 0; 61 | /* 62 | The following byte array contains the complete phyphox experiment. 63 | The phyphox experiment (xml-file) where the byte array is generated from can be found in the github repository https://github.com/Dorsel89/phyphox-hardware (CO2-Monitor Kit folder). 64 | */ 65 | uint8_t CO2Monitor[2877] = {80, 75, 3, 4, 20, 0, 0, 0, 8, 0, 129, 91, 97, 84, 18, 124, 19, 223, 161, 10, 0, 0, 113, 58, 0, 0, 11, 0, 0, 0, 99, 111, 50, 46, 112, 104, 121, 112, 104, 111, 120, 213, 91, 221, 146, 219, 182, 21, 190, 222, 206, 244, 29, 80, 206, 120, 98, 79, 35, 81, 210, 202, 187, 30, 143, 86, 157, 141, 29, 39, 158, 38, 217, 142, 215, 205, 79, 61, 30, 15, 68, 130, 75, 100, 33, 128, 5, 193, 213, 202, 147, 43, 191, 72, 111, 114, 223, 62, 64, 175, 226, 55, 201, 147, 244, 0, 32, 41, 146, 162, 36, 80, 145, 215, 169, 60, 182, 68, 242, 156, 131, 239, 252, 226, 0, 132, 39, 73, 188, 76, 98, 113, 139, 110, 136, 76, 169, 224, 103, 222, 176, 63, 28, 120, 211, 63, 254, 1, 193, 103, 162, 168, 98, 100, 250, 228, 226, 215, 119, 239, 122, 95, 11, 78, 149, 144, 19, 223, 222, 204, 41, 2, 172, 200, 149, 144, 203, 156, 232, 146, 240, 84, 211, 148, 183, 11, 50, 193, 132, 156, 142, 142, 207, 207, 199, 167, 19, 223, 94, 229, 143, 66, 146, 6, 146, 38, 10, 70, 159, 190, 140, 105, 138, 200, 109, 66, 36, 157, 19, 174, 208, 13, 77, 51, 204, 232, 91, 146, 34, 21, 19, 148, 26, 233, 40, 196, 10, 35, 17, 153, 91, 5, 126, 51, 124, 78, 208, 71, 207, 128, 106, 46, 36, 65, 148, 71, 66, 206, 177, 22, 254, 169, 150, 70, 149, 225, 202, 169, 169, 34, 90, 78, 46, 163, 47, 228, 85, 31, 189, 140, 137, 36, 134, 8, 39, 9, 163, 129, 225, 69, 32, 5, 100, 133, 2, 190, 48, 213, 178, 141, 45, 40, 191, 66, 22, 48, 195, 148, 147, 176, 143, 126, 16, 25, 10, 48, 71, 152, 165, 2, 69, 192, 97, 97, 84, 84, 162, 33, 193, 41, 90, 80, 21, 3, 158, 144, 8, 163, 153, 36, 253, 137, 95, 53, 68, 110, 27, 26, 216, 177, 65, 131, 51, 47, 85, 122, 64, 207, 90, 122, 226, 211, 160, 164, 179, 190, 146, 152, 167, 204, 192, 77, 55, 220, 71, 76, 4, 152, 145, 51, 143, 112, 175, 66, 210, 116, 53, 154, 183, 185, 186, 248, 56, 186, 188, 248, 124, 8, 255, 182, 217, 202, 140, 229, 87, 148, 221, 101, 131, 144, 108, 179, 65, 239, 235, 15, 101, 131, 167, 148, 164, 160, 239, 231, 43, 43, 164, 138, 48, 166, 80, 72, 9, 178, 130, 192, 0, 132, 35, 96, 106, 168, 127, 89, 216, 7, 66, 252, 59, 2, 209, 11, 161, 245, 124, 21, 225, 132, 155, 144, 131, 175, 75, 16, 133, 179, 168, 100, 211, 129, 126, 35, 120, 25, 233, 232, 169, 144, 10, 130, 80, 134, 102, 212, 115, 190, 32, 60, 204, 32, 154, 223, 102, 18, 189, 192, 217, 156, 101, 145, 122, 255, 223, 25, 145, 11, 28, 196, 250, 1, 145, 215, 236, 253, 207, 82, 245, 209, 121, 246, 254, 95, 68, 134, 100, 94, 29, 44, 52, 242, 114, 72, 21, 213, 158, 135, 4, 40, 230, 144, 118, 223, 154, 96, 223, 232, 58, 99, 38, 27, 225, 8, 18, 235, 138, 114, 204, 206, 188, 23, 224, 163, 207, 8, 35, 89, 16, 43, 64, 145, 166, 10, 48, 92, 147, 137, 111, 41, 29, 248, 225, 14, 185, 129, 44, 103, 161, 9, 43, 111, 250, 5, 73, 19, 66, 3, 200, 57, 176, 201, 83, 99, 104, 172, 77, 24, 18, 201, 157, 229, 190, 36, 115, 80, 17, 171, 76, 2, 192, 213, 133, 51, 255, 151, 217, 156, 134, 84, 45, 189, 233, 87, 96, 232, 200, 232, 71, 175, 174, 193, 126, 206, 34, 212, 111, 132, 16, 31, 0, 2, 248, 216, 155, 254, 163, 11, 203, 83, 227, 3, 99, 118, 103, 158, 111, 41, 89, 120, 211, 115, 158, 130, 215, 220, 71, 122, 146, 73, 105, 147, 11, 204, 146, 62, 6, 1, 215, 42, 131, 60, 35, 18, 93, 218, 91, 200, 89, 22, 23, 101, 0, 33, 73, 2, 66, 111, 72, 232, 77, 191, 17, 65, 140, 192, 96, 188, 136, 35, 112, 66, 132, 249, 85, 7, 213, 214, 164, 62, 134, 18, 199, 72, 160, 16, 134, 236, 98, 144, 77, 104, 70, 152, 88, 180, 134, 109, 57, 220, 99, 180, 120, 255, 115, 204, 8, 202, 184, 185, 79, 57, 84, 5, 224, 189, 193, 140, 65, 25, 72, 157, 225, 92, 198, 98, 97, 241, 68, 82, 204, 97, 232, 72, 228, 115, 33, 88, 81, 170, 162, 38, 87, 10, 184, 158, 23, 193, 58, 115, 75, 135, 185, 13, 135, 8, 234, 148, 198, 14, 57, 165, 27, 139, 43, 131, 146, 112, 139, 251, 177, 51, 156, 33, 138, 69, 38, 189, 233, 16, 60, 150, 65, 169, 113, 102, 28, 25, 198, 212, 155, 142, 114, 78, 119, 143, 140, 11, 214, 113, 103, 214, 81, 201, 59, 234, 206, 92, 134, 171, 113, 63, 212, 198, 74, 196, 66, 201, 77, 23, 56, 102, 238, 150, 19, 156, 45, 17, 131, 128, 202, 139, 222, 55, 153, 180, 151, 221, 50, 239, 5, 209, 115, 38, 176, 173, 138, 69, 121, 107, 239, 170, 33, 9, 235, 107, 129, 125, 35, 172, 111, 5, 245, 221, 13, 133, 229, 12, 102, 178, 144, 138, 91, 170, 167, 240, 191, 138, 152, 233, 112, 19, 81, 100, 239, 57, 75, 130, 153, 80, 137, 116, 201, 33, 164, 211, 90, 91, 226, 77, 255, 86, 121, 84, 157, 207, 90, 100, 183, 245, 28, 19, 191, 165, 25, 155, 48, 202, 175, 17, 195, 51, 98, 198, 46, 91, 78, 232, 134, 71, 30, 138, 233, 85, 204, 224, 47, 244, 121, 17, 102, 41, 40, 22, 43, 149, 164, 143, 125, 127, 177, 88, 244, 27, 228, 254, 196, 215, 194, 218, 5, 111, 82, 106, 199, 16, 75, 145, 169, 108, 70, 250, 129, 152, 251, 11, 172, 130, 248, 47, 55, 103, 151, 217, 143, 163, 243, 31, 111, 191, 188, 24, 95, 215, 70, 204, 199, 213, 177, 213, 11, 4, 87, 186, 249, 149, 181, 166, 179, 188, 11, 77, 246, 91, 104, 182, 96, 73, 17, 136, 55, 148, 79, 252, 242, 201, 14, 114, 61, 191, 117, 98, 128, 144, 234, 54, 0, 152, 101, 141, 97, 11, 203, 208, 168, 192, 197, 194, 113, 136, 97, 174, 67, 39, 14, 173, 196, 26, 195, 78, 187, 234, 113, 58, 90, 182, 27, 139, 134, 213, 141, 195, 88, 119, 157, 101, 151, 42, 85, 234, 237, 180, 157, 21, 232, 134, 189, 78, 189, 155, 62, 164, 81, 212, 109, 132, 64, 64, 197, 15, 84, 71, 87, 135, 52, 133, 197, 230, 178, 155, 157, 186, 115, 105, 135, 119, 102, 50, 62, 111, 229, 218, 26, 244, 176, 176, 166, 170, 20, 240, 150, 72, 225, 158, 47, 73, 162, 121, 186, 210, 175, 143, 225, 8, 144, 95, 176, 176, 206, 120, 116, 180, 149, 161, 232, 235, 116, 223, 187, 215, 136, 50, 95, 193, 124, 150, 41, 37, 92, 171, 219, 67, 111, 58, 91, 42, 114, 46, 101, 155, 251, 38, 126, 123, 225, 158, 80, 158, 100, 170, 42, 119, 198, 50, 162, 132, 80, 49, 162, 225, 153, 247, 228, 2, 230, 41, 142, 231, 36, 255, 9, 93, 95, 72, 116, 115, 172, 104, 148, 239, 147, 120, 8, 22, 33, 86, 131, 52, 155, 233, 117, 222, 140, 92, 240, 75, 221, 60, 22, 147, 14, 154, 171, 12, 8, 78, 79, 214, 86, 223, 48, 1, 193, 248, 40, 136, 177, 60, 243, 130, 48, 140, 134, 131, 193, 168, 119, 60, 136, 78, 123, 227, 147, 211, 97, 239, 209, 108, 124, 220, 123, 72, 198, 131, 25, 126, 120, 252, 112, 56, 198, 30, 2, 29, 202, 141, 171, 136, 9, 172, 142, 71, 95, 81, 5, 235, 245, 207, 121, 72, 161, 27, 133, 142, 53, 74, 9, 140, 125, 12, 200, 72, 66, 176, 210, 45, 0, 12, 127, 82, 206, 71, 118, 212, 187, 196, 114, 186, 142, 197, 36, 233, 199, 65, 51, 28, 174, 195, 209, 217, 95, 67, 115, 116, 116, 244, 97, 65, 60, 92, 7, 81, 206, 207, 85, 20, 213, 190, 171, 140, 206, 50, 174, 171, 1, 60, 41, 217, 182, 132, 115, 211, 208, 148, 175, 169, 120, 236, 174, 98, 153, 115, 181, 244, 171, 130, 218, 4, 188, 134, 117, 130, 161, 61, 93, 234, 238, 45, 101, 132, 36, 80, 6, 250, 144, 109, 130, 255, 61, 37, 242, 185, 22, 86, 164, 82, 189, 138, 252, 169, 215, 123, 227, 242, 233, 245, 166, 117, 174, 159, 86, 6, 184, 36, 10, 105, 187, 35, 93, 33, 171, 150, 249, 105, 141, 235, 151, 127, 31, 234, 143, 17, 93, 23, 142, 104, 132, 138, 66, 173, 119, 88, 7, 159, 162, 5, 172, 66, 48, 172, 59, 160, 220, 160, 37, 192, 12, 9, 172, 48, 231, 122, 215, 213, 130, 21, 220, 172, 77, 111, 17, 190, 165, 41, 172, 42, 136, 250, 36, 69, 89, 106, 151, 175, 90, 22, 44, 97, 231, 137, 94, 194, 98, 190, 68, 156, 228, 11, 221, 68, 80, 88, 117, 97, 189, 81, 171, 155, 94, 164, 55, 136, 133, 225, 177, 62, 41, 214, 188, 90, 130, 222, 247, 13, 51, 70, 250, 168, 9, 217, 60, 109, 207, 218, 114, 138, 90, 143, 71, 191, 96, 171, 220, 3, 197, 25, 73, 211, 51, 79, 201, 12, 138, 37, 249, 103, 134, 89, 126, 177, 33, 88, 25, 193, 178, 140, 136, 194, 104, 235, 97, 87, 97, 81, 203, 4, 106, 244, 13, 102, 90, 232, 96, 43, 105, 93, 122, 161, 74, 43, 131, 213, 175, 149, 195, 34, 106, 49, 0, 141, 254, 143, 180, 47, 11, 146, 187, 250, 157, 148, 63, 64, 2, 151, 251, 157, 51, 211, 46, 124, 164, 4, 198, 73, 66, 120, 232, 96, 253, 225, 161, 28, 245, 17, 73, 115, 191, 99, 8, 90, 248, 89, 171, 252, 149, 137, 107, 226, 151, 70, 169, 135, 123, 215, 16, 111, 246, 131, 135, 212, 20, 154, 16, 181, 244, 144, 191, 69, 69, 73, 210, 140, 41, 175, 0, 101, 97, 183, 169, 188, 57, 204, 75, 67, 28, 202, 226, 77, 147, 236, 52, 251, 1, 18, 237, 220, 136, 93, 77, 35, 191, 191, 68, 171, 135, 77, 32, 58, 86, 174, 98, 171, 99, 167, 49, 221, 49, 148, 77, 174, 59, 138, 213, 6, 202, 1, 113, 20, 221, 173, 59, 12, 205, 113, 48, 20, 54, 103, 246, 112, 200, 33, 135, 223, 211, 23, 135, 132, 176, 151, 27, 14, 9, 96, 207, 249, 252, 195, 23, 152, 75, 33, 109, 75, 26, 18, 6, 93, 46, 10, 51, 123, 16, 129, 164, 232, 142, 11, 76, 42, 164, 115, 107, 212, 161, 147, 10, 68, 7, 98, 27, 119, 206, 228, 38, 72, 28, 60, 90, 137, 129, 70, 100, 239, 100, 10, 68, 103, 22, 77, 223, 153, 105, 181, 235, 218, 50, 173, 230, 174, 217, 103, 98, 29, 109, 53, 80, 101, 59, 179, 101, 216, 182, 112, 215, 196, 68, 191, 189, 162, 16, 163, 174, 225, 242, 198, 106, 214, 45, 247, 54, 194, 106, 66, 168, 60, 147, 250, 237, 163, 125, 153, 234, 138, 77, 75, 115, 110, 173, 76, 35, 2, 11, 209, 93, 237, 236, 218, 252, 218, 49, 176, 43, 33, 212, 37, 25, 186, 178, 184, 249, 102, 155, 55, 170, 100, 129, 216, 65, 176, 59, 39, 26, 101, 127, 19, 152, 214, 176, 168, 251, 254, 112, 21, 26, 62, 95, 16, 174, 79, 92, 216, 157, 1, 179, 233, 96, 110, 223, 117, 133, 214, 135, 127, 50, 134, 81, 254, 125, 230, 221, 127, 53, 124, 243, 186, 247, 106, 244, 250, 207, 175, 142, 95, 63, 112, 94, 54, 111, 240, 246, 222, 171, 236, 205, 219, 0, 187, 162, 170, 124, 47, 210, 226, 207, 92, 203, 195, 249, 242, 153, 137, 13, 115, 140, 193, 190, 121, 39, 161, 117, 168, 137, 156, 59, 246, 101, 61, 90, 127, 167, 179, 104, 27, 117, 205, 109, 174, 92, 171, 186, 105, 223, 170, 108, 139, 139, 234, 139, 175, 157, 229, 196, 145, 180, 246, 138, 107, 103, 121, 217, 66, 218, 94, 102, 38, 126, 177, 141, 155, 147, 78, 110, 40, 89, 20, 23, 229, 141, 226, 189, 189, 61, 24, 213, 24, 255, 74, 226, 36, 46, 40, 26, 7, 31, 16, 163, 156, 124, 71, 67, 21, 159, 121, 35, 189, 15, 205, 4, 152, 86, 159, 176, 29, 159, 122, 150, 231, 251, 252, 136, 150, 189, 250, 65, 111, 121, 255, 250, 238, 157, 135, 50, 78, 21, 60, 75, 73, 96, 127, 195, 147, 36, 153, 123, 38, 240, 47, 248, 247, 197, 238, 91, 186, 76, 193, 156, 47, 233, 156, 180, 239, 79, 160, 149, 99, 117, 249, 59, 243, 110, 155, 175, 0, 205, 195, 93, 92, 203, 250, 123, 205, 214, 48, 240, 141, 37, 154, 119, 205, 252, 187, 201, 60, 229, 171, 180, 4, 194, 146, 218, 13, 122, 176, 83, 26, 80, 221, 37, 68, 52, 40, 227, 48, 51, 239, 218, 140, 5, 54, 98, 45, 215, 195, 237, 240, 12, 146, 230, 221, 148, 36, 24, 102, 8, 40, 45, 49, 177, 199, 46, 134, 45, 155, 43, 53, 31, 87, 207, 21, 22, 46, 141, 162, 32, 28, 226, 77, 46, 173, 30, 3, 108, 115, 236, 47, 255, 121, 242, 17, 29, 171, 209, 253, 118, 215, 214, 172, 210, 213, 175, 218, 0, 27, 145, 86, 54, 24, 62, 172, 99, 215, 79, 82, 149, 25, 59, 56, 61, 126, 118, 190, 201, 189, 171, 195, 82, 109, 206, 189, 247, 17, 93, 91, 43, 159, 251, 122, 182, 197, 44, 93, 253, 123, 111, 179, 138, 171, 125, 27, 119, 231, 54, 104, 242, 125, 244, 18, 238, 218, 113, 226, 58, 125, 199, 141, 110, 67, 158, 207, 39, 27, 119, 49, 93, 228, 247, 30, 157, 140, 7, 131, 221, 131, 216, 153, 182, 178, 127, 113, 116, 180, 55, 220, 234, 193, 134, 246, 233, 211, 183, 214, 107, 145, 82, 157, 60, 245, 44, 88, 33, 105, 251, 85, 155, 41, 237, 113, 96, 215, 148, 212, 26, 214, 66, 110, 237, 92, 112, 213, 12, 13, 181, 236, 189, 156, 96, 142, 19, 4, 237, 138, 62, 149, 129, 230, 248, 214, 156, 206, 104, 59, 24, 60, 241, 129, 114, 141, 105, 152, 51, 13, 189, 169, 235, 161, 223, 138, 160, 85, 172, 234, 11, 183, 250, 179, 162, 202, 43, 141, 48, 109, 138, 87, 50, 13, 250, 219, 217, 26, 194, 27, 116, 250, 127, 214, 20, 54, 61, 192, 185, 97, 132, 25, 189, 2, 59, 73, 61, 166, 231, 79, 183, 230, 97, 243, 120, 171, 91, 146, 116, 207, 143, 214, 104, 174, 157, 66, 64, 235, 224, 138, 83, 203, 142, 153, 123, 124, 50, 56, 16, 176, 173, 168, 202, 35, 209, 142, 176, 78, 71, 119, 2, 107, 220, 17, 214, 112, 60, 190, 27, 115, 117, 5, 182, 95, 1, 118, 1, 182, 37, 33, 55, 23, 183, 202, 41, 242, 90, 125, 179, 227, 239, 172, 107, 245, 4, 107, 175, 104, 38, 114, 115, 22, 251, 123, 106, 67, 127, 3, 249, 233, 104, 69, 110, 126, 79, 243, 152, 220, 64, 111, 92, 93, 48, 216, 139, 233, 184, 202, 113, 100, 50, 17, 254, 49, 102, 90, 49, 62, 58, 169, 48, 218, 139, 233, 104, 92, 229, 172, 213, 212, 82, 204, 134, 73, 201, 94, 148, 171, 56, 40, 98, 118, 231, 181, 226, 32, 149, 31, 139, 107, 171, 70, 230, 148, 116, 254, 92, 247, 101, 232, 62, 160, 124, 208, 236, 186, 52, 209, 22, 198, 39, 23, 35, 116, 31, 86, 40, 15, 234, 107, 165, 29, 92, 149, 190, 25, 221, 135, 62, 248, 65, 179, 33, 223, 193, 191, 214, 157, 161, 251, 247, 30, 52, 58, 191, 134, 136, 137, 15, 214, 40, 13, 87, 218, 106, 226, 231, 39, 216, 167, 255, 3, 80, 75, 1, 2, 31, 0, 20, 0, 0, 0, 8, 0, 129, 91, 97, 84, 18, 124, 19, 223, 161, 10, 0, 0, 113, 58, 0, 0, 11, 0, 36, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 99, 111, 50, 46, 112, 104, 121, 112, 104, 111, 120, 10, 0, 32, 0, 0, 0, 0, 0, 1, 0, 24, 0, 218, 42, 214, 7, 87, 45, 216, 1, 151, 133, 123, 7, 88, 45, 216, 1, 3, 16, 118, 7, 88, 45, 216, 1, 80, 75, 5, 6, 0, 0, 0, 0, 1, 0, 1, 0, 93, 0, 0, 0, 202, 10, 0, 0, 0, 0}; 66 | 67 | void setup() { 68 | 69 | //Start the SPIFFS and list all contents 70 | Serial.println("init spiffs"); 71 | initStorage(); 72 | delay(50); 73 | 74 | //Start the BLE server 75 | PhyphoxBLE::start("CO2 Monitor", &CO2Monitor[0], sizeof(CO2Monitor)); 76 | PhyphoxBLE::configHandler = &receivedConfig; 77 | PhyphoxBLE::setMTU(176); 78 | 79 | Serial.begin(115200); 80 | 81 | pinMode(readyPin, INPUT); 82 | //initialize rgb-led 83 | pinMode(pinGreen, OUTPUT); 84 | pinMode(pinRed, OUTPUT); 85 | pinMode(pinBlue, OUTPUT); 86 | digitalWrite(pinGreen, GREEN); 87 | digitalWrite(pinRed, RED); 88 | digitalWrite(pinBlue, BLUE); 89 | 90 | //initialize the air Sensor 91 | Wire.begin(); 92 | if (airSensor.begin(Wire, false) == false) 93 | { 94 | Serial.println("Air sensor not detected. Please check wiring. Freezing..."); 95 | while (1); 96 | } 97 | 98 | delay(200); 99 | 100 | Serial.print("Temperature offset: "); 101 | Serial.println(airSensor.getTemperatureOffset()); 102 | currentOffset = airSensor.getTemperatureOffset() * 10; 103 | } 104 | 105 | void loop() { 106 | if (digitalRead(readyPin)) 107 | { 108 | //read data 109 | measuredData[0] = airSensor.getCO2(); 110 | measuredData[1] = airSensor.getTemperature(); 111 | measuredData[2] = airSensor.getHumidity(); 112 | measuredData[3] = millis() / 1000; 113 | 114 | //print data 115 | echoDataset("Measured", measuredData); 116 | 117 | uint8_t Data[20] = {0}; 118 | Data[0] = currentOffset; 119 | Data[1] = statusCO2calibration; 120 | Data[2] = versionID; 121 | memcpy(&Data[3], &measuredData[0], 16); 122 | 123 | //Send data to phyphox 124 | PhyphoxBLE::write(&Data[0], 20); 125 | 126 | //calculate average Data over [averageOver] periods 127 | if (averageCounter == averageOver) { 128 | storeMeasuredData(averageMeasuredData); 129 | averageMeasuredData[0] = 0; 130 | averageMeasuredData[1] = 0; 131 | averageMeasuredData[2] = 0; 132 | averageMeasuredData[3] = 0; 133 | averageCounter = 0; 134 | } 135 | if (averageCounter < averageOver) { 136 | averageMeasuredData[0] += measuredData[0] / averageOver; 137 | averageMeasuredData[1] += measuredData[1] / averageOver; 138 | averageMeasuredData[2] += measuredData[2] / averageOver; 139 | averageMeasuredData[3] += measuredData[3] / averageOver; 140 | averageCounter += 1; 141 | } 142 | 143 | updateLED(measuredData[0]); 144 | delay(10); 145 | } 146 | 147 | //Data transfer loop 148 | if (oldDataTransmissionOffset >= 0) { 149 | if (transferOldData(oldDataTransmissionOffset)) 150 | oldDataTransmissionOffset++; 151 | else { 152 | oldDataTransmissionOffset = -1; 153 | Serial.println("Transfer of old date completed."); 154 | } 155 | delay(10); 156 | } 157 | } 158 | 159 | //Print one dataset 160 | void echoDataset(String note, float * data) { 161 | Serial.print(note); 162 | Serial.print(" => CO2: "); 163 | Serial.print(data[0]); 164 | Serial.print(", Temperature: "); 165 | Serial.print(data[1]); 166 | Serial.print(", Humidity: "); 167 | Serial.print(data[2]); 168 | Serial.print(", Timestamp: "); 169 | Serial.println(data[3]); 170 | } 171 | 172 | // Start the SPIFFS and list all contents 173 | void initStorage() { 174 | if (!SPIFFS.begin(true)) { 175 | Serial.println("An Error has occurred while mounting SPIFFS"); 176 | return; 177 | } 178 | 179 | if (FORMATFLASH) { 180 | bool formatted = SPIFFS.format(); 181 | if ( formatted ) { 182 | Serial.println("SPIFFS formatted successfully"); 183 | } else { 184 | Serial.println("Error formatting"); 185 | } 186 | } 187 | 188 | int totalBytes = SPIFFS.totalBytes(); 189 | int usedBytes = SPIFFS.usedBytes(); 190 | Serial.print("total Bytes "); 191 | Serial.println(totalBytes); 192 | Serial.print("total Bytes "); 193 | Serial.println(usedBytes); 194 | 195 | File file = SPIFFS.open("/set.txt", FILE_WRITE); 196 | file.close(); 197 | delay(1); 198 | } 199 | 200 | //Interpretation of received data 201 | void receivedConfig() { 202 | /* 203 | phyphox experiment may send an Array with 5 Bytes 204 | byte information 205 | 0 bool transfer Data 206 | 1 calibrate: 1=CO2 2=temperature 207 | 2 bool setColorBlue 208 | */ 209 | //read data 210 | uint8_t readArray[6] = {0}; 211 | PhyphoxBLE::read(&readArray[0], 6); 212 | 213 | //DATA TRANSFER 214 | if (readArray[0] == 1) { 215 | Serial.println("Resending of old data requested."); 216 | oldDataTransmissionOffset = 0; 217 | } 218 | 219 | //CO2 CALIBRATION 220 | if (readArray[1] == 1) { 221 | Serial.print("Calibration with fresh air "); 222 | airSensor.setAutoSelfCalibration(false); 223 | Serial.println(airSensor.setForcedRecalibrationFactor(400)); 224 | statusCO2calibration = 1; 225 | } 226 | 227 | //TEMPERATURE CALIBRATION 228 | if (readArray[1] == 2) { 229 | Serial.print("Current Offset: "); 230 | Serial.print(airSensor.getTemperatureOffset()); 231 | Serial.println(" °C"); 232 | delay(50); 233 | currentOffset = readArray[5] / 10.0; 234 | if (currentOffset >= 0) { 235 | airSensor.setTemperatureOffset(currentOffset); 236 | } 237 | Serial.print("New Offset: "); 238 | Serial.print(currentOffset); 239 | Serial.println(" °C"); 240 | delay(50); 241 | Serial.print("Restarting ESP32.."); 242 | delay(1000); 243 | ESP.restart(); 244 | } 245 | 246 | } 247 | 248 | //Transfer [transferedDatasets] datasets at position [offset] 249 | bool transferOldData(int offset) { 250 | int i_buffer = transferedDatasets; 251 | char buffer[4 * measuredDataLength * transferedDatasets]; 252 | 253 | File file = SPIFFS.open("/set.txt", "r"); 254 | 255 | //Check if enough datasets are available 256 | for (int i = 0; i < transferedDatasets; i++) { 257 | file.seek(offset * 4 * measuredDataLength * transferedDatasets + i * 4 * measuredDataLength, SeekSet); 258 | if (!file.available() && i == 0) { 259 | //No datasets available 260 | file.close(); 261 | return false; 262 | } 263 | else if (!file.available()) { 264 | //Update number of datsets for transfer 265 | i_buffer = i; 266 | break; 267 | } 268 | } 269 | 270 | //Read and send the available amount of datasets 271 | file.seek(offset * 4 * measuredDataLength * transferedDatasets, SeekSet); 272 | int l = file.readBytes(buffer, 4 * measuredDataLength * i_buffer); 273 | file.close(); 274 | uint8_t bufferArray[4 * measuredDataLength * i_buffer + 3]; 275 | memcpy(&bufferArray[3], &buffer[0], 4 * measuredDataLength * i_buffer); 276 | PhyphoxBLE::write(bufferArray, 4 * measuredDataLength * i_buffer); //Send value to phyphox 277 | 278 | //All datasets are read -> return false 279 | if (i_buffer != transferedDatasets) { 280 | return false; 281 | } else { 282 | return true; 283 | } 284 | } 285 | 286 | //store one dataset in the ESP32 file system 287 | void storeMeasuredData(float dataArray[4]) { 288 | byte byteArray[4 * measuredDataLength]; 289 | memcpy(&byteArray[0], &dataArray[0], 4 * measuredDataLength); 290 | File file = SPIFFS.open("/set.txt", "r+"); 291 | file.seek(lineNumber * 4 * measuredDataLength, SeekSet); 292 | file.write(byteArray, 4 * measuredDataLength); 293 | file.close(); 294 | 295 | //circular data storage 296 | if (lineNumber < (maxDatasets - 1)) { 297 | lineNumber += 1; 298 | } else { 299 | lineNumber = 0; 300 | } 301 | } 302 | 303 | 304 | //Update LED according to thresholds and current CO2 Value or changed connection count 305 | void updateLED(float co2value) { 306 | //update LED to blue if a device (dis)connects 307 | if (PhyphoxBLE::currentConnections != currentConnections) { 308 | BLUE = false; 309 | digitalWrite(pinBlue, BLUE); 310 | timeStampBlue = millis()/1000; 311 | currentConnections = PhyphoxBLE::currentConnections; 312 | Serial.print("Connection Count: "); 313 | Serial.print(currentConnections); 314 | Serial.print(" @ "); 315 | Serial.println(timeStampBlue); 316 | } 317 | if(millis()/1000 > timeStampBlue+0.3){ 318 | BLUE = true; 319 | digitalWrite(pinBlue, BLUE); 320 | } 321 | 322 | if (co2value > topThreshold && RED == true) { 323 | Serial.println("Update to red"); 324 | GREEN = true; 325 | RED = false; 326 | BLUE = true; 327 | digitalWrite(pinGreen, GREEN); 328 | digitalWrite(pinRed, RED); 329 | digitalWrite(pinBlue, BLUE); 330 | } 331 | if (co2value < bottomThreshold && GREEN == true) { 332 | Serial.println("Update to green"); 333 | GREEN = false; 334 | RED = true; 335 | BLUE = true; 336 | digitalWrite(pinGreen, GREEN); 337 | digitalWrite(pinRed, RED); 338 | digitalWrite(pinBlue, BLUE); 339 | 340 | } 341 | 342 | } 343 | --------------------------------------------------------------------------------