2020 2016-06-20 Clemens Gruber | Modularization of upload URL
2121 2016-09-17 Clemens Gruber | Modularization of sensors and debugging
2222 2017-01-09 Clemens Gruber | Add support for HX711 and ESP8266
23+ 2017-01-10 Andreas Motl | Improve sensor data collection
2324
2425
2526 GNU GPL v3 License
5152 A7 battery voltage
5253*/
5354
55+
56+ // Standard C++ (STL) for Arduino for dynamic data structures like vector and map.
57+ // Required for AVR only, STL already seems to be included in Espressif SDK.
58+ #ifndef ARDUINO_ARCH_ESP8266
59+ #include < ArduinoSTL.h>
60+ #endif
61+
62+
63+ // TerkinData - flexible data collection for sensor readings
64+ // to decouple measurement from telemetry domain.
65+ #include < TerkinData.h>
66+ using namespace TerkinData ;
67+ using namespace TerkinUtil ;
68+ using std::string;
69+
70+
5471// -------------------------+------
5572// variables you can modify | START
5673// -------------------------+------
@@ -207,7 +224,44 @@ unsigned long updateInterval = 60UL * 60UL; // s*m*h*d // seems it take 11 sec
207224// do not use spaces before or after an comma
208225// const char datasetHeader[] = "Date/Time,Weight,Outside Temp,Outside Humid,Inside Temp,Inside Humid,H1 Temp,H2 Temp,H3 Temp,H4 Temp,H5 Temp,Voltage";
209226// const char datasetHeader[] = "Date/Time,Weight,Outside Temp,Outside Humid,Inside Temp,Voltage";
210- const char datasetHeader[] = " Datum/Zeit,Gewicht,Aussen-Temperatur,Aussen-Feuchtigkeit,Brut-Temperatur,Spannung" ;
227+ // const char datasetHeader[] = "Datum/Zeit,Gewicht,Aussen-Temperatur,Aussen-Feuchtigkeit,Brut-Temperatur,Spannung";
228+
229+
230+ // ---------------------------------------
231+ // ** dynamic dataset (v2): infrastructure
232+ // ---------------------------------------
233+
234+ // Moved to "TerkinData" library.
235+
236+
237+ // ------------------------------
238+ // ** dynamic dataset (v2): setup
239+ // ------------------------------
240+
241+ void DataManager::setup () {
242+
243+ // List of field names
244+ this ->field_names = new DataHeader ({" time" , " weight" , " temp-outside" , " humidity-outside" , " temp-brood" , " voltage" });
245+
246+ // List of human readable field names for backward compatibility
247+ this ->field_labels = new DataHeader ({" Datum/Zeit" , " Gewicht" , " Aussen-Temperatur" , " Aussen-Feuchtigkeit" , " Brut-Temperatur" , " Spannung" });
248+
249+ // Optionally prefix header line with string
250+ // this->csv_header_prefix = "";
251+
252+ // Map names of lowlevel sensor values to highlevel telemetry data fields
253+ (*this ->sensor_field_mapping )[string (" dht.0.temp" )] = string (" temp-outside" );
254+ (*this ->sensor_field_mapping )[string (" dht.0.hum" )] = string (" humidity-outside" );
255+ (*this ->sensor_field_mapping )[string (" ds18b20.0" )] = string (" temp-brood" );
256+
257+ }
258+
259+ DataManager *datamgr = new DataManager();
260+
261+
262+ // static dataset (v1) backward compatibility: Make opaque CSV header string from list of field name elements
263+ const char *datasetHeader = join(*datamgr->field_labels, ' ,' ).c_str();
264+
211265
212266// -------------------------+----
213267// variables you can modify | END
@@ -355,7 +409,7 @@ const char datasetHeader[] = "Datum/Zeit,Gewicht,Aussen-Temperatur,Aussen-Feucht
355409#endif
356410
357411
358- void setup () {
412+ void setup () {
359413 // debug and GSM
360414 Serial.begin (9600 );
361415 #ifdef isDebug
@@ -443,6 +497,7 @@ void setup () {
443497 // some simplificatiion for your convenience
444498 // build uploadPath and upload header
445499 #if defined (isGSM) || defined (isWifi) || defined (isEthernet)
500+
446501 // build uploadPath
447502 snprintf (uploadPath, sizeof (uploadPath), uploadScheme,
448503 uploadDomain,
@@ -585,7 +640,7 @@ void setup () {
585640// timestamp
586641#ifdef isRTC
587642 #ifdef isWifi
588- void getTimestamp () {
643+ void getTimestamp (Measurement& measurement ) {
589644 // set timestamp later by server
590645 memcpy (timestampChar, " timestamp-by-server" , sizeof (" timestamp-by-server" ));
591646 /*
@@ -646,14 +701,16 @@ void setup () {
646701 */
647702 }
648703 #else
649- void getTimestamp () {
704+ void getTimestamp (Measurement& measurement ) {
650705
651706 // Get the current datetime
652707 DateTime currentTime = RTC.now ();
653708
654709 // Write to char array
655710 snprintf (timestampChar, sizeof (timestampChar), " %d/%02d/%02d %02d:%02d:%02d" , currentTime.year (), currentTime.month (), currentTime.date (), currentTime.hour (), currentTime.minute (), currentTime.second ()); // write to char array
656711
712+ // Write to data container
713+ measurement.time = timestampChar;
657714 }
658715 #endif
659716#endif
@@ -692,7 +749,7 @@ void setup () {
692749
693750// weight
694751#ifdef isScale
695- void getWeight () {
752+ void getWeight (Measurement& measurement ) {
696753 // clear running median samples
697754 weightSamples.clear ();
698755
@@ -753,12 +810,14 @@ void setup () {
753810 // Write to char array
754811 dtostrf (weight, 8 , 3 , weightMedianChar);
755812
813+ // Write to data container
814+ measurement.data [" weight" ] = weight;
756815 }
757816#endif
758817
759818// temperature array / DS18B20
760819#ifdef isTemperatureArray
761- void getTemperature () {
820+ void getTemperature (Measurement& measurement ) {
762821 // request temperature on all devices on the bus
763822 temperatureSensors.setWaitForConversion (false ); // makes it async
764823 // initiatie temperature retrieval
@@ -783,13 +842,16 @@ void setup () {
783842 // Write to char array
784843 dtostrf (temperatureC, 5 , 1 , temperatureArrayChar[i]);
785844
845+ // Write to data container
846+ String key = " ds18b20." + String (i); // e.g. ds18b20.0, ds18b20.1, etc.
847+ measurement.data [key.c_str ()] = temperatureC;
786848 }
787849 }
788850#endif
789851
790852// humidity and temperature / DHTxx
791853#ifdef isHumidityTemperature
792- void getHumidityTemperature () {
854+ void getHumidityTemperature (Measurement& measurement ) {
793855 // read humidity and temperature data
794856 // loop through all devices
795857 for (int i=0 ; i<humidityNumDevices; i++) {
@@ -812,6 +874,10 @@ void setup () {
812874 // Read sensor the second time, this should deliver the appropriate values
813875 int chk = DHT.readHumidityType (humidityPins[i]);
814876
877+ // Prepare keys for data container, e.g. dht.0.temp, dht.0.hum, dht.1.temp, dht.1.hum, etc.
878+ String key_temp = " dht." + String (i) + " .temp" ;
879+ String key_hum = " dht." + String (i) + " .hum" ;
880+
815881 switch (chk) {
816882 // this is the normal case, all is working smoothly
817883 case DHTLIB_OK:
@@ -821,6 +887,9 @@ void setup () {
821887 dtostrf (DHT.temperature , 5 , 1 , temperatureChar[i]);
822888 dtostrf (DHT.humidity , 5 , 1 , humidityChar[i]);
823889
890+ // Write to data container
891+ measurement.data [key_temp.c_str ()] = DHT.temperature ;
892+ measurement.data [key_hum.c_str ()] = DHT.humidity ;
824893
825894 break ;
826895
@@ -865,7 +934,7 @@ void setup () {
865934// read value should be 411 (3.14V / 0.402V) and the max analog
866935// read value should be 785 (6V / 0.767V).
867936#ifdef isBattery
868- void getVoltage () {
937+ void getVoltage (Measurement& measurement ) {
869938 int batteryValue = analogRead (batteryPin); // read battery as analog value
870939
871940 // Compute voltage based on voltage divider resistors
@@ -874,6 +943,8 @@ void setup () {
874943 // Write to char array
875944 dtostrf (voltage,5 ,2 ,voltageChar); // write to char array
876945
946+ // Write to data container
947+ measurement.data [" voltage" ] = voltage;
877948
878949 }
879950#endif
@@ -895,28 +966,32 @@ void setup () {
895966// Main program
896967// ------------
897968
898- void loop () {
969+ void loop () {
970+
971+ // Data container to collect one reading
972+ Measurement *measurement = new Measurement ();
973+
899974 // update data
900975 //
901976 // update timestamp
902977 #ifdef isRTC
903- getTimestamp ();
978+ getTimestamp (*measurement );
904979 #endif
905980 // update weight
906981 #ifdef isScale
907- getWeight ();
982+ getWeight (*measurement );
908983 #endif
909984 // update humidity and temperature, DHTxx
910985 #ifdef isHumidityTemperature
911- getHumidityTemperature ();
986+ getHumidityTemperature (*measurement );
912987 #endif
913988 // update temperature array
914989 #ifdef isTemperatureArray
915- getTemperature ();
990+ getTemperature (*measurement );
916991 #endif
917992 // update battery voltage
918993 #ifdef isBattery
919- getVoltage ();
994+ getVoltage (*measurement );
920995 #endif
921996
922997/*
@@ -949,7 +1024,10 @@ void loop () {
9491024 Serial.println(voltageChar);
9501025*/
9511026
952- // build dataset
1027+ // ----------------------------
1028+ // ** static dataset (v1): data
1029+ // ----------------------------
1030+
9531031 // calculate size of dataset
9541032 char dataset[0
9551033 #ifdef isRTC
@@ -997,11 +1075,36 @@ void loop () {
9971075 strcat (dataset, " ," );
9981076 strcat (dataset, voltageChar);
9991077 #endif
1078+
10001079 // debug
10011080 #ifdef isDebug
1081+ Serial.println (" static dataset (v1)" );
10021082 Serial.println (dataset);
10031083 #endif
10041084
1085+
1086+ // -----------------------------
1087+ // ** dynamic dataset (v2): data
1088+ // -----------------------------
1089+
1090+ std::string data_header = datamgr->csv_header ();
1091+ std::string data_record = datamgr->csv_data (*measurement);
1092+
1093+ // debug
1094+ #ifdef isDebug
1095+ Serial.println (" static dataset (v2)" );
1096+ // Serial.println(data_record);
1097+ #endif
1098+
1099+
1100+ // ---------------
1101+ // ** Telemetry **
1102+ // ---------------
1103+
1104+ // TODO: Use HTTP POST
1105+ // TODO: Use MQTT
1106+ // TODO: Use SSL if possible
1107+
10051108 #if defined (isGSM) || defined (isWifi) || defined (isEthernet)
10061109 // replace spaces with plus for proper URL
10071110 for (int i = 0 ; dataset[i] != 0 ; i++) {
0 commit comments