diff --git a/Actual_values.png b/Actual_values.png new file mode 100644 index 0000000..f603842 Binary files /dev/null and b/Actual_values.png differ diff --git a/Config_Influx.png b/Config_Influx.png new file mode 100644 index 0000000..e6eac46 Binary files /dev/null and b/Config_Influx.png differ diff --git a/Loginfo.png b/Loginfo.png new file mode 100644 index 0000000..44bcb21 Binary files /dev/null and b/Loginfo.png differ diff --git a/README_FORK.md b/README_FORK.md new file mode 100644 index 0000000..f5e6458 --- /dev/null +++ b/README_FORK.md @@ -0,0 +1,71 @@ +# Fork of MultiGeiger +# Adoptions in this fork: +this fork is based on the master branch of the original MultiGeiger software (https://github.com/ecocurious2/MultiGeiger.git) as of 08.04.2022 (Firmware V1.17.0-dev) + +# Motivation: +Having a Air Quality sensor from the Luftdaten project (sensor.community) installed, I know about problems with dropping Wifi connections, memory leaks, +interrupted data transfer to the internet portals, etc. +And I also know, without being able to monitor my sensor outside at its final installation position, I would have been lost. + +E.g. to search a good installation position outside, I use a power bank for the sensor and my cellphone to display the 'Actual Values' page, and I directly see the Wifi quality. + +Then you install it there in a provisional manner and watch the values from inside via Wifi for a couple of days. + +If sending to the portals fails, you can see the http return code on the log page. + +You can actually monitor all values which are normally sent to the serial port via the log info page via WiFi now, +no need to connect a laptop anymore via USB (which would imply a restart, resetting the sensor, which could solve/hide an actual problem ) ... + +As long as you have WiFi, you can monitor your Sensor, even if your Internet connection is broken down. + +I have an Influx-DB running here on a raspberry, where I log all my weather data locally. Of course I wanted to include this sensor as well. + +This fork bascically ports some functionality from the 'Luftdaten-Sensor' (sensor.community) to the MultiGeiger, +with some simplifications and enhancements in the code ... + +It does NOT touch the original code concerning gathering the data from the tube, etc. This is all untouched ! + +Nicola from the ecocurious team was so nice to share the ecocurious logo for this fork. + +# This implementation adds : + * local html pages which are accessible via WiFi. Just connect to the sensor in your WLAN and you're on the first page ... + + * first page is showing actual sensor values (Dose rate, cps, HV pulses), Wifi-Data (Signal/Quality), free Memory, Firmware Version & Date … + + ![Alt text](./Actual_values.png) + + * if BME280/680 is available, it also shows temperature, humidity & pressure + + * the page refreshes automatically every 10s + + * second page shows log infos which are normally only available through serial connection + + ![Alt text](./Loginfo.png) + + * the loginfo is updated automatically in the central frame and can be scrolled + + * The loglevel can be changed temporarily (runtime, not saved) + + * a configurable http connection to a influx database included in the original configuration page + + ![Alt text](./Config_Influx.png) + + * (extendable) translation system for the texts displayed on the local WLAN pages. + + * Languages DE, EN and IT are available already. + + * Preferred language to be set on compile time + + * in the configuration page where Text attributes were showing only '?????' upon first run. + +# Compilation : + +I use 'VS code' and platformio for compilation, upload and monitoring (I'm working under Fedora). +* You can copy the platformio-example.ini to platformio.ini, compile one of the (currently) 3 language options, and upload it. +* The only thing to be adopted in platformio.ini is probably upload_port & monitor_port +* Necessary libraries should be pulled in automatically. +* Furthermore you have to copy userdefines-example.h to userdefines.h and adopt the content before compilation. + +I have only experience with the WiFi version of this sensor, so the pages will not be visible on the LoRaWan sensor option due to missing WiFi (I guess). + + diff --git a/multigeiger/display.cpp b/multigeiger/display.cpp index b9abe1e..708b785 100644 --- a/multigeiger/display.cpp +++ b/multigeiger/display.cpp @@ -87,9 +87,10 @@ static const char *status_chars[STATUS_MAX] = { ".t3T?", // ST_TTN_OFF, ST_TTN_IDLE, ST_TTN_ERROR, ST_TTN_SENDING, ST_TTN_INIT // group BlueTooth ".B4b?", // ST_BLE_OFF, ST_BLE_CONNECTED, ST_BLE_ERROR, ST_BLE_CONNECTABLE, ST_BLE_INIT + // group InfluxDB + ".i5I?", // ST_INFLUX_OFF, ST_INFLUX_IDLE, ST_INFLUX_ERROR, ST_INFLUX_SENDING,ST_INFLUX_INIT // group other - ".", // ST_NODISPLAY - ".", // ST_NODISPLAY + ".", // ST_NODISPLAY ".H7", // ST_NODISPLAY, ST_HV_OK, ST_HV_ERROR }; diff --git a/multigeiger/display.h b/multigeiger/display.h index 26c67be..86db585 100644 --- a/multigeiger/display.h +++ b/multigeiger/display.h @@ -52,6 +52,12 @@ void display_statusline(String txt); #define ST_BLE_INIT 4 // status index 5 is still free +#define STATUS_INFLUX 5 +#define ST_INFLUX_OFF 0 +#define ST_INFLUX_IDLE 1 +#define ST_INFLUX_ERROR 2 +#define ST_INFLUX_SENDING 3 +#define ST_INFLUX_INIT 4 // status index 6 is still free diff --git a/multigeiger/log.cpp b/multigeiger/log.cpp index 3615ce4..e3f2b97 100644 --- a/multigeiger/log.cpp +++ b/multigeiger/log.cpp @@ -1,18 +1,19 @@ // low level log() call - outputs to serial/usb #include - +#include "string.h" #include "log.h" #include "clock.h" +#include "utils.h" // the GEIGER: prefix is is to easily differentiate our output from other esp32 output (e.g. wifi messages) #define LOG_PREFIX_FORMAT "GEIGER: %s " #define LOG_PREFIX_LEN (7+1+19+1) // chars, without the terminating \0 -static int log_level = NOLOG; // messages at level >= log_level will be output +int log_level = NOLOG; // messages at level >= log_level will be output void log(int level, const char *format, ...) { - if (level < log_level) + if (level > log_level) return; va_list args; @@ -21,12 +22,18 @@ void log(int level, const char *format, ...) { sprintf(buf, LOG_PREFIX_FORMAT, utctime()); vsprintf(buf + LOG_PREFIX_LEN, format, args); va_end(args); - Serial.println(buf); + Debug.println(buf); } void setup_log(int level) { - Serial.begin(115200); - while (!Serial) {}; + Debug.begin(115200); // Output to Serial at 115200 baud + while (!Debug) {}; log(NOLOG, "Logging initialized at level %d.", level); // this will always be output log_level = level; } +int getloglevel(void){ + return log_level; +} +void setloglevel(int level){ + log_level=level; +} diff --git a/multigeiger/log.h b/multigeiger/log.h index bbaf8e2..a0f96a3 100644 --- a/multigeiger/log.h +++ b/multigeiger/log.h @@ -4,14 +4,32 @@ #define _LOG_H_ // log levels -#define DEBUG 0 -#define INFO 1 -#define WARNING 2 -#define ERROR 3 -#define CRITICAL 4 -#define NOLOG 999 // only to set log_level, so log() never creates output +// reverted the numbers to meet logic in log_data.h (see #define Serial_None 0 ...) +// DEBUG is max info, reducing output step by step till NOLOG +/* TR, 20.04.2022 : +1) there's no CRITICAL and no WARNING . remove both and use numbers for Serial_Print_Mode? +and NOLOG really logs nothing then ... +2) Serial_Print_Mode should be included in this sequence (--> settable through the loglevel!). + It's not clear to me, why Serial_Print_Mode is set fix at compilation time ?!? + Org: +#define DEBUG 5 +#define INFO 4 +#define WARNING 3 // there is nothing with WARNING ... +#define ERROR 2 +#define CRITICAL 1 // there is nothing with CRITICAL ... +#define NOLOG 0 // minimum info, only display alarms +*/ + +#define DEBUG 5 +#define INFO 4 +#define MED_INFO 3 +#define MIN_INFO 2 +#define ERROR 1 +#define NOLOG 0 + void log(int level, const char *format, ...); void setup_log(int level); - +int getloglevel(void); +void setloglevel(int level); #endif // _LOG_H_ diff --git a/multigeiger/log_data.cpp b/multigeiger/log_data.cpp index c7cb19f..11dbe8c 100644 --- a/multigeiger/log_data.cpp +++ b/multigeiger/log_data.cpp @@ -8,7 +8,6 @@ int Serial_Print_Mode; static const char *Serial_Logging_Name = "Simple Multi-Geiger"; static const char *dashes = "------------------------------------------------------------------------------------------------------------------------"; - static const char *Serial_Logging_Header = " %10s %15s %10s %9s %9s %8s %9s %9s %9s %5s %5s %6s"; static const char *Serial_Logging_Body = "DATA %10d %15d %10f %9f %9d %8d %9d %9f %9f %5.1f %5.1f %6.0f"; static const char *Serial_One_Minute_Log_Header = " %4s %10s %29s"; @@ -20,48 +19,52 @@ void setup_log_data(int mode) { // Write Header of Table, depending on the logging mode: bool data_log_enabled = (Serial_Print_Mode == Serial_Logging) || (Serial_Print_Mode == Serial_One_Minute_Log) || (Serial_Print_Mode == Serial_Statistics_Log); if (data_log_enabled) { - log(INFO, dashes); - log(INFO, "%s, Version %s", Serial_Logging_Name, VERSION_STR); - log(INFO, dashes); + log(Serial_Print_Mode, dashes); + log(Serial_Print_Mode, "%s, Version %s", Serial_Logging_Name, VERSION_STR); + log(Serial_Print_Mode, dashes); } } - +//Serial_Logging void log_data(int GMC_counts, int time_difference, float Count_Rate, float Dose_Rate, int HV_pulse_count, int accumulated_GMC_counts, int accumulated_time, float accumulated_Count_Rate, float accumulated_Dose_Rate, float t, float h, float p) { static int counter = 0; if (counter++ % 20 == 0) { // output the header now and then, so table is better readable - log(INFO, Serial_Logging_Header, - "GMC_counts", "Time_difference", "Count_Rate", "Dose_Rate", "HV Pulses", "Accu_GMC", "Accu_Time", "Accu_Rate", "Accu_Dose", "Temp", "Humi", "Press"); - log(INFO, Serial_Logging_Header, - "[Counts]", "[ms]", "[cps]", "[uSv/h]", "[-]", "[Counts]", "[ms]", "[cps]", "[uSv/h]", "[C]", "[%]", "[hPa]"); - log(INFO, dashes); + write_log_header(); } log(INFO, Serial_Logging_Body, GMC_counts, time_difference, Count_Rate, Dose_Rate, HV_pulse_count, accumulated_GMC_counts, accumulated_time, accumulated_Count_Rate, accumulated_Dose_Rate, t, h, p); } - +//Serial_One_Minute_Log void log_data_one_minute(int time_s, int cpm, int counts) { static int counter = 0; if (counter++ % 20 == 0) { // output the header now and then, so table is better readable - log(INFO, Serial_One_Minute_Log_Header, + log(MED_INFO, Serial_One_Minute_Log_Header, "Time", "Count_Rate", "Counts"); - log(INFO, Serial_One_Minute_Log_Header, + log(MED_INFO, Serial_One_Minute_Log_Header, "[s]", "[cpm]", "[Counts per last measurement]"); - log(INFO, dashes); + log(MED_INFO, dashes); } - log(INFO, Serial_One_Minute_Log_Body, + log(MED_INFO, Serial_One_Minute_Log_Body, time_s, cpm, counts); } - +// Serial_Statistics_Log void log_data_statistics(int count_time_between) { static int counter = 0; if (counter++ % 20 == 0) { // output the header now and then, so table is better readable - log(INFO, "Time between two impacts"); - log(INFO, "[usec]"); - log(INFO, dashes); + log(MIN_INFO, "Time between two impacts"); + log(MIN_INFO, "[usec]"); + log(MIN_INFO, dashes); } - log(INFO, "%d", count_time_between); + log(MIN_INFO, "%d", count_time_between); } +//Header for Serial_Logging +void write_log_header(){ + log(INFO, Serial_Logging_Header, + "GMC_counts", "Time_difference", "Count_Rate", "Dose_Rate", "HV Pulses", "Accu_GMC", "Accu_Time", "Accu_Rate", "Accu_Dose", "Temp", "Humi", "Press"); + log(INFO, Serial_Logging_Header, + "[Counts]", "[ms]", "[cps]", "[uSv/h]", "[-]", "[Counts]", "[ms]", "[cps]", "[uSv/h]", "[C]", "[%]", "[hPa]"); + log(INFO, dashes); +} \ No newline at end of file diff --git a/multigeiger/log_data.h b/multigeiger/log_data.h index 46ad12d..fe92b99 100644 --- a/multigeiger/log_data.h +++ b/multigeiger/log_data.h @@ -4,11 +4,30 @@ #define _LOG_DATA_H_ // Values for Serial_Print_Mode to configure Serial (USB) output mode. +/* TR, 20.04.2022 : +Serial_Print_Mode should be included in NOLOG...DEBUG sequence (--> settable through the loglevel!). + It's not clear to me, why Serial_Print_Mode is set fix at compilation time ... + Current settings are : +Serial_None 0 // No Serial output +Serial_Debug 1 // Only debug and error messages +Serial_Logging 2 // Log measurements as a table (default) +Serial_One_Minute_Log 3 // One Minute logging +Serial_Statistics_Log 4 // Logs time [us] between two events, 1/s + +Proposal: +rename standard log levels and include Serial_xxx : +NOLOG 0 --> incl. Serial_None, display ALARMS, else turn off logging +ERROR 1 --> incl. 0 +MIN_INFO 2 --> incl. Serial_Statistics_Log + 0 + 1 +MED_INFO 3 --> incl. Serial_One_Minute_Log + 0 + 1 +MAX_INFO 4 --> incl. Serial_Logging + 0 + 1 +DEBUG 5 --> incl. Serial_Debug + 0 + 1 + 4 +*/ #define Serial_None 0 // No Serial output -#define Serial_Debug 1 // Only debug and error messages -#define Serial_Logging 2 // Log measurements as a table +#define Serial_Debug 5 // Only debug and error messages +#define Serial_Logging 4 // Log measurements as a table #define Serial_One_Minute_Log 3 // One Minute logging -#define Serial_Statistics_Log 4 // Logs time [us] between two events +#define Serial_Statistics_Log 2 // Logs time [us] between two events extern int Serial_Print_Mode; @@ -18,5 +37,5 @@ void log_data(int GMC_counts, int time_difference, float Count_Rate, float Dose_ float t, float h, float p); void log_data_one_minute(int time_s, int cpm, int counts); void log_data_statistics(int count_time_between); - +void write_log_header(void); #endif // _LOG_DATA_H_ diff --git a/multigeiger/multigeiger.ino b/multigeiger/multigeiger.ino index 8031f20..e7643cc 100644 --- a/multigeiger/multigeiger.ino +++ b/multigeiger/multigeiger.ino @@ -18,6 +18,7 @@ #include "ble.h" #include "chkhardware.h" #include "clock.h" +#include "utils.h" // Measurement interval (default 2.5min) [sec] #define MEASUREMENT_INTERVAL 150 @@ -37,10 +38,16 @@ // DIP switches static Switches switches; - +float Count_Rate; +float Dose_Rate; +int hv_pulses; +unsigned long starttime; +bool have_thp = false; +float temperature = 0.0, humidity = 0.0, pressure = 0.0; void setup() { bool isLoraBoard = init_hwtest(); + starttime = millis(); // store the start time setup_log(DEFAULT_LOG_LEVEL); setup_display(isLoraBoard); setup_switches(isLoraBoard); @@ -50,7 +57,8 @@ void setup() { setup_speaker(playSound, ledTick && switches.led_on, speakerTick && switches.speaker_on); setup_transmission(VERSION_STR, ssid, isLoraBoard); setup_ble(ssid, sendToBle && switches.ble_on); - setup_log_data(SERIAL_DEBUG); + //setup_log_data(SERIAL_DEBUG); + setup_log_data(DEFAULT_LOG_LEVEL); setup_tube(); } @@ -123,7 +131,7 @@ void publish(unsigned long current_ms, unsigned long current_counts, unsigned lo return; } last_timestamp = current_ms; - int hv_pulses = current_hv_pulses - last_hv_pulses; + hv_pulses = current_hv_pulses - last_hv_pulses; last_hv_pulses = current_hv_pulses; int counts = current_counts - last_counts; last_counts = current_counts; @@ -136,8 +144,8 @@ void publish(unsigned long current_ms, unsigned long current_counts, unsigned lo float GMC_factor_uSvph = tubes[TUBE_TYPE].cps_to_uSvph; // calculate the current count rate and dose rate - float Count_Rate = (dt != 0) ? (float)counts * 1000.0 / (float)dt : 0.0; - float Dose_Rate = Count_Rate * GMC_factor_uSvph; + Count_Rate = (dt != 0) ? (float)counts * 1000.0 / (float)dt : 0.0; + Dose_Rate = Count_Rate * GMC_factor_uSvph; // calculate the count rate and dose rate over the complete time from start accumulated_Count_Rate = (accumulated_time != 0) ? (float)accumulated_GMC_counts * 1000.0 / (float)accumulated_time : 0.0; @@ -151,15 +159,19 @@ void publish(unsigned long current_ms, unsigned long current_counts, unsigned lo // Sound local alarm? if (soundLocalAlarm && GMC_factor_uSvph > 0) { if (accumulated_Dose_Rate > localAlarmThreshold) { - log(WARNING, "Local alarm: Accumulated dose of %.3f µSv/h above threshold at %.3f µSv/h", accumulated_Dose_Rate, localAlarmThreshold); +// the following is not a WARNING in the sense of a possible system failure ! This is a safety alarm and should really be displayed anytime !!!! +// log(WARNING, "Local alarm: Accumulated dose of %.3f µSv/h above threshold at %.3f µSv/h", accumulated_Dose_Rate, localAlarmThreshold); + log(NOLOG, "Local alarm: Accumulated dose of %.3f µSv/h above threshold at %.3f µSv/h", accumulated_Dose_Rate, localAlarmThreshold); alarm(); } else if (Dose_Rate > (accumulated_Dose_Rate * localAlarmFactor)) { - log(WARNING, "Local alarm: Current dose of %.3f > %d x accumulated dose of %.3f µSv/h", Dose_Rate, localAlarmFactor, accumulated_Dose_Rate); +// same as above +// log(WARNING, "Local alarm: Current dose of %.3f > %d x accumulated dose of %.3f µSv/h", Dose_Rate, localAlarmFactor, accumulated_Dose_Rate); + log(NOLOG, "Local alarm: Current dose of %.3f > %d x accumulated dose of %.3f µSv/h", Dose_Rate, localAlarmFactor, accumulated_Dose_Rate); alarm(); } } - if (Serial_Print_Mode == Serial_Logging) { + if (Serial_Print_Mode >= Serial_Logging) { log_data(counts, dt, Count_Rate, Dose_Rate, hv_pulses, accumulated_GMC_counts, accumulated_time, accumulated_Count_Rate, accumulated_Dose_Rate, temperature, humidity, pressure); @@ -212,6 +224,7 @@ void read_THP(unsigned long current_ms, } void transmit(unsigned long current_ms, unsigned long current_counts, unsigned long gm_count_timestamp, unsigned long current_hv_pulses, + bool have_thp, float temperature, float humidity, float pressure, int wifi_status) { static unsigned long last_counts = 0; static unsigned long last_hv_pulses = 0; @@ -233,10 +246,14 @@ void transmit(unsigned long current_ms, unsigned long current_counts, unsigned l int hv_pulses = current_hv_pulses - last_hv_pulses; last_hv_pulses = current_hv_pulses; + // calculate the current count rate and dose rate + float GMC_factor_uSvph = tubes[TUBE_TYPE].cps_to_uSvph; + float Count_Rate = (dt != 0) ? (float)counts * 1000.0 / (float)dt : 0.0; + float dose_rate = Count_Rate * GMC_factor_uSvph; log(DEBUG, "Measured GM: cpm= %d HV=%d", current_cpm, hv_pulses); - transmit_data(tubes[TUBE_TYPE].type, tubes[TUBE_TYPE].nbr, dt, hv_pulses, counts, current_cpm, + transmit_data(tubes[TUBE_TYPE].type, tubes[TUBE_TYPE].nbr, dt, hv_pulses, counts, current_cpm, dose_rate, have_thp, temperature, humidity, pressure, wifi_status); } } @@ -244,8 +261,9 @@ void transmit(unsigned long current_ms, unsigned long current_counts, unsigned l void loop() { static bool hv_error = false; // true means a HV capacitor charging issue - static bool have_thp = false; - static float temperature = 0.0, humidity = 0.0, pressure = 0.0; + //static bool have_thp = false; + have_thp = false; + temperature = 0.0; humidity = 0.0; pressure = 0.0; unsigned long current_ms = millis(); // to save multiple calls to millis() @@ -282,7 +300,7 @@ void loop() { // do any other periodic updates for uplinks poll_transmission(); - + Serial_Print_Mode = getloglevel(); publish(current_ms, gm_counts, gm_count_timestamp, hv_pulses, temperature, humidity, pressure); if (Serial_Print_Mode == Serial_One_Minute_Log) diff --git a/multigeiger/transl.h b/multigeiger/transl.h new file mode 100644 index 0000000..23524a8 --- /dev/null +++ b/multigeiger/transl.h @@ -0,0 +1,24 @@ +#ifndef transl_h +#define transl_h +/* TR, 07.04.2022 + add new translation strings to ALL files translations_xx.h !!! + do NOT add translation_xx.h to any of your routines, but transl.h + Steps to add a new language : + 1. create a new file translation_xx.h where xx is the language code + 2. add this file name to transl.h + 3. add a new chapter into platformio.ini (like [env:geiger_en] (copy&paste), and only adopt the buildflag '-DTRANSL_XX' to the new language + this defines TRANSL_XX which can be queried in transl.h during runtime in its if-elif and includes the corresponding translation_xx.h. +*/ + +#if defined(TRANSL_DE) +#include "translations_de.h" +#elif defined(TRANSL_EN) +#include "translations_en.h" +#elif defined(TRANSL_IT) +#include "translations_it.h" +#else +#warning No language defined +#include "translations_en.h" +#endif + +#endif diff --git a/multigeiger/translations_de.h b/multigeiger/translations_de.h new file mode 100644 index 0000000..194217a --- /dev/null +++ b/multigeiger/translations_de.h @@ -0,0 +1,55 @@ +/* TR, 07.04.2022 + add new translation strings to ALL files translations_xx.h !!! + do NOT add translation_xx.h to any of your routines, but transl.h + Steps to add a new language : + 1. create a new file translation_xx.h where xx is the language code + 2. add this file name to transl.h + 3. add a new chapter into platformio.ini (like [env:geiger_en] (copy&paste), and only adopt the buildflag '-DTRANSL_XX' to the new language + this defines TRANSL_XX which can be queried in transl.h during runtime in its if-elif and includes the corresponding translation_xx.h. +*/ +#include + +#define LANGUAGE "DE" + +const char TRA_CURRENT_DATA[] PROGMEM = "Aktuelle Werte"; +const char TRA_DEBUG_DATA[] PROGMEM = "Debug Info"; +const char TRA_SET_LOGLEVEL_TO[] PROGMEM = "

Setze Loglevel auf {lvl}

"; +const char TRA_LOGLEVEL_IS[] PROGMEM = "

Loglevel ist: {lvl}

"; +#define TRA_ACT_VAL_HEADLINE "Aktuelle Werte" +#define TRA_BUTTON_NOLOG "NoLog" +//#define TRA_BUTTON_CRITICAL "KRITISCH" +#define TRA_BUTTON_ERROR "Fehler" +//#define TRA_BUTTON_WARNING "WARNUNG" +//#define TRA_BUTTON_INFO "INFO" +#define TRA_BUTTON_MININFO "min.Info" +#define TRA_BUTTON_MEDINFO "med.Info" +#define TRA_BUTTON_MAXINFO "max.Info" +#define TRA_BUTTON_DEBUG "Debug" +#define TRA_BUTTON_CONFIG "Konfiguration" +#define TRA_BUTTON_BACK "Zurück zur Startseite" +#define TRA_BUTTON_LOG_PAGE "LogInfo Seite" +#define TRA_BUTTON_REFRESH "Aktualisieren" +#define TRA_REFRESH_INFO "man.Aktualisieren" +#define TRA_LOG_PAGE_INFO "LogInfo Seite öffnen" +#define TRA_CONFIG_INFO "Konfiguration öffnen" +#define TRA_NOLOG_INFO "nur Alarme" +#define TRA_DEBUG_INFO "alles, mit Debuginfo" +const char TRA_SEND_TO_INFO [] PROGMEM = "Sende an {ext} ..."; +const char TRA_SENT_TO_INFO [] PROGMEM = "An {ext} gesandt, Status: {s}, http: "; + +#define TRA_SENSOR "Sensor" +#define TRA_PARAMETER "Parameter" +#define TRA_VALUE "Wert" +#define TRA_MES_RESTART "Starte neu..." +#define TRA_MES_CONF_SAVED "Konfig. gespeichert" +const char TRA_CPS[] PROGMEM="cps"; +const char TRA_DOSERATE[] PROGMEM="Dosisleistung"; +const char TRA_HV_PULSES[] PROGMEM="HochVolt Pulse"; +const char TRA_TEMP[] PROGMEM="Temperatur"; +const char TRA_PRESSURE[] PROGMEM = "Luftdruck"; +const char TRA_HUMIDITY[] PROGMEM = "rel. Luftfeuchte"; +const char TRA_WIFISIGNAL[] PROGMEM = "Signal"; +const char TRA_WIFIQUALITY[] PROGMEM = "Qualität"; +const char TRA_ESP_FREE_MEM[] PROGMEM = "Freier Speicher"; +const char TRA_ESP_UPTIME[] PROGMEM = "Laufzeit"; + diff --git a/multigeiger/translations_en.h b/multigeiger/translations_en.h new file mode 100644 index 0000000..9671540 --- /dev/null +++ b/multigeiger/translations_en.h @@ -0,0 +1,54 @@ +/* TR, 07.04.2022 + add new translation strings to ALL files translations_xx.h !!! + do NOT add translation_xx.h to any of your routines, but transl.h + Steps to add a new language : + 1. create a new file translation_xx.h where xx is the language code + 2. add this file name to transl.h + 3. add a new chapter into platformio.ini (like [env:geiger_en] (copy&paste), and only adopt the buildflag '-DTRANSL_XX' to the new language + this defines TRANSL_XX which can be queried in transl.h during runtime in its if-elif and includes the corresponding translation_xx.h. +*/ +#include + +#define LANGUAGE "EN" + +const char TRA_CURRENT_DATA[] PROGMEM = "Actual values"; +const char TRA_DEBUG_DATA[] PROGMEM = "Debug Info"; +const char TRA_SET_LOGLEVEL_TO[] PROGMEM = "

Seting Loglevel to {lvl}

"; +const char TRA_LOGLEVEL_IS[] PROGMEM = "

Loglevel is: {lvl}

"; +#define TRA_ACT_VAL_HEADLINE "Actual Values" +#define TRA_BUTTON_NOLOG "NoLog" +//#define TRA_BUTTON_CRITICAL "CRITICAL" +#define TRA_BUTTON_ERROR "Error" +//#define TRA_BUTTON_WARNING "WARNING" +//#define TRA_BUTTON_INFO "INFO" +#define TRA_BUTTON_MININFO "min.Info" +#define TRA_BUTTON_MEDINFO "med.Info" +#define TRA_BUTTON_MAXINFO "max.Info" +#define TRA_BUTTON_DEBUG "Debug" +#define TRA_BUTTON_CONFIG "Configuration" +#define TRA_BUTTON_BACK "Back to Homepage" +#define TRA_BUTTON_LOG_PAGE "LogInfo page" +#define TRA_BUTTON_REFRESH "Refresh" +#define TRA_REFRESH_INFO "manual Refresh" +#define TRA_LOG_PAGE_INFO "open LogInfo page" +#define TRA_CONFIG_INFO "open Configuration" +#define TRA_NOLOG_INFO "alarms only" +#define TRA_DEBUG_INFO "all, incl. Debug info" +const char TRA_SEND_TO_INFO [] PROGMEM = "Sending to {ext} ..."; +const char TRA_SENT_TO_INFO [] PROGMEM = "Sent to {ext}, status: {s}, http: "; + +#define TRA_SENSOR "Sensor" +#define TRA_PARAMETER "Parameter" +#define TRA_VALUE "Value" +#define TRA_MES_RESTART "Restarting..." +#define TRA_MES_CONF_SAVED "Config saved." +const char TRA_CPS[] PROGMEM="cps"; +const char TRA_DOSERATE[] PROGMEM="Dose rate"; +const char TRA_HV_PULSES[] PROGMEM="High Voltage pulses"; +const char TRA_TEMP[] PROGMEM="Temperature"; +const char TRA_PRESSURE[] PROGMEM = "Air pressure"; +const char TRA_HUMIDITY[] PROGMEM = "rel. Humidity"; +const char TRA_WIFISIGNAL[] PROGMEM = "Signal"; +const char TRA_WIFIQUALITY[] PROGMEM = "Quality"; +const char TRA_ESP_FREE_MEM[] PROGMEM = "Free Memory"; +const char TRA_ESP_UPTIME[] PROGMEM = "Uptime"; diff --git a/multigeiger/translations_it.h b/multigeiger/translations_it.h new file mode 100644 index 0000000..c665abc --- /dev/null +++ b/multigeiger/translations_it.h @@ -0,0 +1,54 @@ +/* TR, 07.04.2022 + add new translation strings to ALL files translations_xx.h !!! + do NOT add translation_xx.h to any of your routines, but transl.h + Steps to add a new language : + 1. create a new file translation_xx.h where xx is the language code + 2. add this file name to transl.h + 3. add a new chapter into platformio.ini (like [env:geiger_en] (copy&paste), and only adopt the buildflag '-DTRANSL_XX' to the new language + this defines TRANSL_XX which can be queried in transl.h during runtime in its if-elif and includes the corresponding translation_xx.h. +*/ +#include + +#define LANGUAGE "IT" + +const char TRA_CURRENT_DATA[] PROGMEM = "Valori attuali"; +const char TRA_DEBUG_DATA[] PROGMEM = "Informazioni Debug"; +const char TRA_SET_LOGLEVEL_TO[] PROGMEM = "

Cambia livello log a {lvl}

"; +const char TRA_LOGLEVEL_IS[] PROGMEM = "

Livello log è: {lvl}

"; +#define TRA_ACT_VAL_HEADLINE "Valori Attuali" +#define TRA_BUTTON_NOLOG "Nessuno" +//#define TRA_BUTTON_CRITICAL "CRITICI" +#define TRA_BUTTON_ERROR "Errori" +//#define TRA_BUTTON_WARNING "AVVISI" +//#define TRA_BUTTON_INFO "INFO" +#define TRA_BUTTON_MININFO "min.Info" +#define TRA_BUTTON_MEDINFO "med.Info" +#define TRA_BUTTON_MAXINFO "max.Info" +#define TRA_BUTTON_DEBUG "Debug" +#define TRA_BUTTON_CONFIG "Configurazione" +#define TRA_BUTTON_BACK "Ritorno a Homepage" +#define TRA_BUTTON_LOG_PAGE "Pagina LogInfo" +#define TRA_BUTTON_REFRESH "Riavviare" +#define TRA_REFRESH_INFO "Riavviare adesso" +#define TRA_LOG_PAGE_INFO "Apri pagina Loginfo" +#define TRA_CONFIG_INFO "Apri configurazione" +#define TRA_NOLOG_INFO "Solo allerte" +#define TRA_DEBUG_INFO "Tutto, debug incluso" +const char TRA_SEND_TO_INFO [] PROGMEM = "Inviando a {ext} ..."; +const char TRA_SENT_TO_INFO [] PROGMEM = "Inviato a {ext}, status: {s}, http: "; + +#define TRA_SENSOR "Sensore" +#define TRA_PARAMETER "Parametro" +#define TRA_VALUE "Valore" +#define TRA_MES_RESTART "Avviando..." +#define TRA_MES_CONF_SAVED "Configurazione salvata" +const char TRA_CPS[] PROGMEM="cps"; +const char TRA_DOSERATE[] PROGMEM="Rateo di dose"; +const char TRA_HV_PULSES[] PROGMEM="numero pulsioni ad alto voltaggio"; +const char TRA_TEMP[] PROGMEM="Temperatura"; +const char TRA_PRESSURE[] PROGMEM = "Pressione d'aria"; +const char TRA_HUMIDITY[] PROGMEM = "Umidità rel."; +const char TRA_WIFISIGNAL[] PROGMEM = "Segnale"; +const char TRA_WIFIQUALITY[] PROGMEM = "Qualità"; +const char TRA_ESP_FREE_MEM[] PROGMEM = "memoria disponibile"; +const char TRA_ESP_UPTIME[] PROGMEM = "Tempo di esercizio"; diff --git a/multigeiger/transmission.cpp b/multigeiger/transmission.cpp index fc278f6..12be77e 100644 --- a/multigeiger/transmission.cpp +++ b/multigeiger/transmission.cpp @@ -15,7 +15,7 @@ #include "transmission.h" #include "ca_certs.h" - +#include "utils.h" // Hosts for data delivery // use http for now, could we use https? @@ -30,7 +30,7 @@ #define CUSTOMSRV "https://ptsv2.com/t/xxxxx-yyyyyyyyyy/post" // Get your own toilet URL and put it here before setting this to true. #define SEND2CUSTOMSRV false - +extern uint16_t influxPort; static String http_software_version; static unsigned int lora_software_version; static String chipID; @@ -41,7 +41,7 @@ typedef struct https_client { HTTPClient *hc; } HttpsClient; -static HttpsClient c_madavi, c_sensorc, c_customsrv; +static HttpsClient c_madavi, c_sensorc, c_customsrv, c_influxsrv; void setup_transmission(const char *version, char *ssid, bool loraHardware) { chipID = String(ssid); @@ -69,9 +69,14 @@ void setup_transmission(const char *version, char *ssid, bool loraHardware) { c_customsrv.wc->setCACert(ca_certs); c_customsrv.hc = new HTTPClient; + c_influxsrv.wc = new WiFiClientSecure; + c_influxsrv.wc->setCACert(ca_certs); + c_influxsrv.hc = new HTTPClient; + set_status(STATUS_SCOMM, sendToCommunity ? ST_SCOMM_INIT : ST_SCOMM_OFF); set_status(STATUS_MADAVI, sendToMadavi ? ST_MADAVI_INIT : ST_MADAVI_OFF); set_status(STATUS_TTN, sendToLora ? ST_TTN_INIT : ST_TTN_OFF); + set_status(STATUS_INFLUX, sendToInflux ? ST_INFLUX_INIT : ST_INFLUX_OFF); } void poll_transmission() { @@ -97,12 +102,11 @@ void prepare_http(HttpsClient *client, const char *host) { int send_http(HttpsClient *client, String body) { if (DEBUG_SERVER_SEND) log(DEBUG, "http request body: %s", body.c_str()); - int httpResponseCode = client->hc->POST(body); if (httpResponseCode > 0) { - String response = client->hc->getString(); if (DEBUG_SERVER_SEND) { log(DEBUG, "http code: %d", httpResponseCode); + String response = client->hc->getString(); log(DEBUG, "http response: %s", response.c_str()); } } else { @@ -114,7 +118,7 @@ int send_http(HttpsClient *client, String body) { int send_http_geiger(HttpsClient *client, const char *host, unsigned int timediff, unsigned int hv_pulses, unsigned int gm_counts, unsigned int cpm, int xpin) { - char body[1000]; + char body[500]; prepare_http(client, host); if (xpin != XPIN_NO_XPIN) { client->hc->addHeader("X-PIN", String(xpin)); @@ -130,7 +134,7 @@ int send_http_geiger(HttpsClient *client, const char *host, unsigned int timedif ] } )====="; - snprintf(body, 1000, json_format, + snprintf(body, 500, json_format, http_software_version.c_str(), cpm, hv_pulses, @@ -140,7 +144,7 @@ int send_http_geiger(HttpsClient *client, const char *host, unsigned int timedif } int send_http_thp(HttpsClient *client, const char *host, float temperature, float humidity, float pressure, int xpin) { - char body[1000]; + char body[300]; prepare_http(client, host); if(xpin != XPIN_NO_XPIN) { client->hc->addHeader("X-PIN", String(xpin)); @@ -155,7 +159,7 @@ int send_http_thp(HttpsClient *client, const char *host, float temperature, floa ] } )====="; - snprintf(body, 1000, json_format, + snprintf(body, 300, json_format, http_software_version.c_str(), temperature, humidity, @@ -166,7 +170,7 @@ int send_http_thp(HttpsClient *client, const char *host, float temperature, floa // two extra functions for MADAVI, because MADAVI needs the sensorname in value_type to recognize the sensors int send_http_geiger_2_madavi(HttpsClient *client, String tube_type, unsigned int timediff, unsigned int hv_pulses, unsigned int gm_counts, unsigned int cpm) { - char body[1000]; + char body[500]; prepare_http(client, MADAVI); tube_type = tube_type.substring(10); const char *json_format = R"=====( @@ -180,7 +184,7 @@ int send_http_geiger_2_madavi(HttpsClient *client, String tube_type, unsigned in ] } )====="; - snprintf(body, 1000, json_format, + snprintf(body, 500, json_format, http_software_version.c_str(), tube_type.c_str(), cpm, tube_type.c_str(), hv_pulses, @@ -190,7 +194,7 @@ int send_http_geiger_2_madavi(HttpsClient *client, String tube_type, unsigned in } int send_http_thp_2_madavi(HttpsClient *client, float temperature, float humidity, float pressure) { - char body[1000]; + char body[500]; prepare_http(client, MADAVI); const char *json_format = R"=====( { @@ -202,7 +206,7 @@ int send_http_thp_2_madavi(HttpsClient *client, float temperature, float humidit ] } )====="; - snprintf(body, 1000, json_format, + snprintf(body, 500, json_format, http_software_version.c_str(), temperature, humidity, @@ -243,58 +247,164 @@ int send_ttn_thp(float temperature, float humidity, float pressure) { ttnData[4] = ((int)(pressure / 10)) & 0xFF; return lorawan_send(2, ttnData, 5, false, NULL, NULL, NULL); } +//************influx-DB******************** +int send_http_influx(HttpsClient *client, String body) { + if (DEBUG_SERVER_SEND) + log(DEBUG, "http request body: %s", body.c_str()); + if(strlen(influxUser) >0 || strlen(influxPassword)>0) + client->hc->setAuthorization(influxUser,influxPassword); + int httpResponseCode = client->hc->POST(body); + if (httpResponseCode >= HTTP_CODE_OK && httpResponseCode <= HTTP_CODE_ALREADY_REPORTED) { + log(DEBUG,"Sent to influx-db, status: ok, http: %d", httpResponseCode); + } else if (httpResponseCode >= HTTP_CODE_BAD_REQUEST) { + log(DEBUG,"Sent to influx-db, status: error, http: %d", httpResponseCode); + log(DEBUG,"Details: %s", client->hc->getString().c_str()); + } + client->hc->end(); + return httpResponseCode; +} + +int send_http_geiger_2_influx(HttpsClient *client, const char *host, unsigned int timediff, unsigned int hv_pulses, + unsigned int gm_counts, unsigned int cpm, float Dose_Rate, int have_thp, float temperature, float humidity, float pressure) { + char body[300]; + if (host[4] == 's') // https + client->hc->begin(*client->wc, host); + else // http + client->hc->begin(influxServer,influxPort,influxPath); + client->hc->addHeader("Content-Type", "application/x-www-form-urlencoded"); + client->hc->addHeader("X-Sensor", chipID); -void transmit_data(String tube_type, int tube_nbr, unsigned int dt, unsigned int hv_pulses, unsigned int gm_counts, unsigned int cpm, + client->hc->addHeader("", String(influxMeasurement)); + if(!have_thp){ + snprintf(body, 300, "%s cpm=%d,hv_pulses=%d,gm_count=%d,timediff=%d,dose_rate=%f\n", influxMeasurement, + cpm, + hv_pulses, + gm_counts, + timediff,Dose_Rate); + } + else{ + snprintf(body, 300, "%s cpm=%d,hv_pulses=%d,gm_count=%d,timediff=%d,dose_rate=%f,temperature=%f,humidity=%f,pressure=%f\n", influxMeasurement, + cpm, + hv_pulses, + gm_counts, + timediff,Dose_Rate,temperature,humidity,pressure); + + } + log(DEBUG,"Influx-Body: %s",body); + return send_http_influx(client, body); + +} +void transmit_data(String tube_type, int tube_nbr, unsigned int dt, unsigned int hv_pulses, unsigned int gm_counts, unsigned int cpm,float Dose_Rate, int have_thp, float temperature, float humidity, float pressure, int wifi_status) { int rc1, rc2; + RESERVE_STRING (logstr,SMALL_STR); + bool transfer_ok; #if SEND2CUSTOMSRV - bool customsrv_ok; - log(INFO, "Sending to CUSTOMSRV ..."); - rc1 = send_http_geiger(&c_customsrv, CUSTOMSRV, dt, hv_pulses, gm_counts, cpm, XPIN_NO_XPIN); - rc2 = have_thp ? send_http_thp(&c_customsrv, CUSTOMSRV, temperature, humidity, pressure, XPIN_NO_XPIN) : 200; - customsrv_ok = (rc1 == 200) && (rc2 == 200); - log(INFO, "Sent to CUSTOMSRV, status: %s, http: %d %d", customsrv_ok ? "ok" : "error", rc1, rc2); + //bool customsrv_ok; + //log(INFO, "Sending to CUSTOMSRV ..."); + logstr=FPSTR(TRA_SEND_TO_INFO); + logstr.replace("{ext}", F("CUSTOMSRV")); + log(INFO, logstr.c_str()); + + rc1 = send_http_geiger(&c_customsrv, CUSTOMSRV, dt, hv_pulses, gm_counts, cpm, XPIN_NO_XPIN); + rc2 = have_thp ? send_http_thp(&c_customsrv, CUSTOMSRV, temperature, humidity, pressure, XPIN_NO_XPIN) : 200; + transfer_ok = (rc1 == 200) && (rc2 == 200):true:false; + //log(INFO, "Sent to CUSTOMSRV, status: %s, http: %d %d", customsrv_ok ? "ok" : "error", rc1, rc2); + logstr=FPSTR(TRA_SENT_TO_INFO); + logstr.replace("{ext}", F("CUSTOMSRV")); + logstr.replace("{s}", transfer_ok ? "ok" : "error"); + logstr += rc1; + logstr += F(", "); + logstr += rc2; + log(INFO, logstr.c_str()); #endif if(sendToMadavi && (wifi_status == ST_WIFI_CONNECTED)) { - bool madavi_ok; - log(INFO, "Sending to Madavi ..."); + //bool madavi_ok; + //log(INFO, "Sending to Madavi ..."); + logstr=FPSTR(TRA_SEND_TO_INFO); + logstr.replace("{ext}", F("Madavi")); + log(INFO, logstr.c_str()); + set_status(STATUS_MADAVI, ST_MADAVI_SENDING); display_status(); rc1 = send_http_geiger_2_madavi(&c_madavi, tube_type, dt, hv_pulses, gm_counts, cpm); rc2 = have_thp ? send_http_thp_2_madavi(&c_madavi, temperature, humidity, pressure) : 200; delay(300); - madavi_ok = (rc1 == 200) && (rc2 == 200); - log(INFO, "Sent to Madavi, status: %s, http: %d %d", madavi_ok ? "ok" : "error", rc1, rc2); - set_status(STATUS_MADAVI, madavi_ok ? ST_MADAVI_IDLE : ST_MADAVI_ERROR); + transfer_ok = (rc1 == 200) && (rc2 == 200)?true:false; + //log(INFO, "Sent to Madavi, status: %s, http: %d %d", madavi_ok ? "ok" : "error", rc1, rc2); + logstr=FPSTR(TRA_SENT_TO_INFO); + logstr.replace("{ext}", F("Madavi")); + logstr.replace("{s}", transfer_ok ? "ok" : "error"); + logstr += rc1; + logstr += F(", "); + logstr += rc2; + log(INFO, logstr.c_str()); + set_status(STATUS_MADAVI, transfer_ok ? ST_MADAVI_IDLE : ST_MADAVI_ERROR); display_status(); } if(sendToCommunity && (wifi_status == ST_WIFI_CONNECTED)) { - bool scomm_ok; - log(INFO, "Sending to sensor.community ..."); + //bool scomm_ok; + //log(INFO, "Sending to sensor.community ..."); + logstr=FPSTR(TRA_SEND_TO_INFO); + logstr.replace("{ext}", F("sensor.community")); + log(INFO, logstr.c_str()); set_status(STATUS_SCOMM, ST_SCOMM_SENDING); display_status(); rc1 = send_http_geiger(&c_sensorc, SENSORCOMMUNITY, dt, hv_pulses, gm_counts, cpm, XPIN_RADIATION); rc2 = have_thp ? send_http_thp(&c_sensorc, SENSORCOMMUNITY, temperature, humidity, pressure, XPIN_BME280) : 201; delay(300); - scomm_ok = (rc1 == 201) && (rc2 == 201); - log(INFO, "Sent to sensor.community, status: %s, http: %d %d", scomm_ok ? "ok" : "error", rc1, rc2); - set_status(STATUS_SCOMM, scomm_ok ? ST_SCOMM_IDLE : ST_SCOMM_ERROR); + transfer_ok = (rc1 == 201) && (rc2 == 201)?true:false; + //log(INFO, "Sent to sensor.community, status: %s, http: %d %d", scomm_ok ? "ok" : "error", rc1, rc2); + logstr=FPSTR(TRA_SENT_TO_INFO); + logstr.replace("{ext}", F("sensor.community")); + logstr.replace("{s}", transfer_ok ? "ok" : "error"); + logstr += rc1; + logstr += F(", "); + logstr += rc2; + log(INFO, logstr.c_str()); + set_status(STATUS_SCOMM, transfer_ok ? ST_SCOMM_IDLE : ST_SCOMM_ERROR); display_status(); } if(isLoraBoard && sendToLora && (strcmp(appeui, "") != 0)) { // send only, if we have LoRa credentials - bool ttn_ok; - log(INFO, "Sending to TTN ..."); + //bool ttn_ok; + //log(INFO, "Sending to TTN ..."); + logstr=FPSTR(TRA_SEND_TO_INFO); + logstr.replace("{ext}", "TTN"); + log(INFO, logstr.c_str()); set_status(STATUS_TTN, ST_TTN_SENDING); display_status(); rc1 = send_ttn_geiger(tube_nbr, dt, gm_counts); rc2 = have_thp ? send_ttn_thp(temperature, humidity, pressure) : TX_STATUS_UPLINK_SUCCESS; - ttn_ok = (rc1 == TX_STATUS_UPLINK_SUCCESS) && (rc2 == TX_STATUS_UPLINK_SUCCESS); - set_status(STATUS_TTN, ttn_ok ? ST_TTN_IDLE : ST_TTN_ERROR); + transfer_ok = (rc1 == TX_STATUS_UPLINK_SUCCESS) && (rc2 == TX_STATUS_UPLINK_SUCCESS)?true:false; + set_status(STATUS_TTN, transfer_ok ? ST_TTN_IDLE : ST_TTN_ERROR); display_status(); } +//InfuxDB + if (sendToInflux && (wifi_status == ST_WIFI_CONNECTED)) { + //bool influx_ok; + //log(INFO, "Sending to Influx-DB ..."); + logstr=FPSTR(TRA_SEND_TO_INFO); + logstr.replace("{ext}", F("Influx-DB")); + log(INFO, logstr.c_str()); + set_status(STATUS_INFLUX, ST_INFLUX_SENDING); + display_status(); + + log(DEBUG,"Measured data: cpm=%d, HV=%d, DoseRate=%f,gm_count=%d,timediff=%d", cpm, hv_pulses,Dose_Rate,gm_counts,dt); + rc1 = send_http_geiger_2_influx(&c_influxsrv, influxServer, dt, hv_pulses, gm_counts, cpm, Dose_Rate,have_thp,temperature, humidity, pressure); + + transfer_ok = (rc1 >= HTTP_CODE_OK && rc1 <= HTTP_CODE_ALREADY_REPORTED)?true:false; + //log(INFO, "Sent to influx server %s, status: %s, http: %d", influxServer, influx_ok ? "ok" : "error", rc1); + logstr=FPSTR(TRA_SENT_TO_INFO); + logstr.replace("{ext}", F("Influx-DB")); + logstr.replace("{s}", transfer_ok ? "ok" : "error"); + logstr += rc1; + log(INFO, logstr.c_str()); + set_status(STATUS_INFLUX, transfer_ok ? ST_INFLUX_IDLE : ST_INFLUX_ERROR); + display_status(); + } } diff --git a/multigeiger/transmission.h b/multigeiger/transmission.h index 428d18d..820c4b0 100644 --- a/multigeiger/transmission.h +++ b/multigeiger/transmission.h @@ -15,7 +15,7 @@ #define XPIN_BME280 11 void setup_transmission(const char *version, char *ssid, bool lora); -void transmit_data(String tube_type, int tube_nbr, unsigned int dt, unsigned int hv_pulses, unsigned int gm_counts, unsigned int cpm, +void transmit_data(String tube_type, int tube_nbr, unsigned int dt, unsigned int hv_pulses, unsigned int gm_counts, unsigned int cpm,float Dose_Rate, int have_thp, float temperature, float humidity, float pressure, int wifi_status); // The Arduino LMIC wants to be polled from loop(). This takes care of that on LoRa boards. diff --git a/multigeiger/userdefines-example.h b/multigeiger/userdefines-example.h index def914b..03ced6b 100644 --- a/multigeiger/userdefines-example.h +++ b/multigeiger/userdefines-example.h @@ -64,6 +64,10 @@ // 0x2A39: Heart Rate Control Point --> allows to reset "energy expenditure", as required by service definition #define SEND2BLE false +// Send data to an influx-db ? +#define SEND2INFLUX false +#define INFLUX_PORT 8086 + // Play an alarm sound when radiation level is too high? // Activates when either accumulated dose rate reaches the set threshold (see below) // or when the current dose rate is higher than the accumulated dose rate by the set factor (see below). diff --git a/multigeiger/utils.cpp b/multigeiger/utils.cpp index d44e373..931cf93 100644 --- a/multigeiger/utils.cpp +++ b/multigeiger/utils.cpp @@ -1,8 +1,9 @@ // Misc. utilities #include - +#include #include "log.h" +#include "log_data.h" #include "utils.h" // ** convert hexstring to len bytes of data @@ -44,3 +45,77 @@ void reverseByteArray(unsigned char *data, int len) { } } +/*********************************************************************** + * Log output via Hardwareserial to the serial port AND on log webpage * + ***********************************************************************/ + +LoggingSerial Debug; + +LoggingSerial::LoggingSerial() + : HardwareSerial(0) { + m_buffer = xQueueCreate(XLARGE_STR, sizeof(uint8_t)); +} + +size_t LoggingSerial::write(uint8_t c){ + xQueueSendToBack(m_buffer, ( void * ) &c, ( TickType_t ) 0); + return HardwareSerial::write(c); +} + +size_t LoggingSerial::write(const uint8_t *buffer, size_t size){ + for(int i = 0; i < size; i++) { + xQueueSendToBack(m_buffer, ( void * ) &buffer[i], ( TickType_t ) 0); + } + return HardwareSerial::write(buffer, size); +} + +String LoggingSerial::popLines(){ + String r; + uint8_t c; + while (xQueueReceive(m_buffer, &(c ), (TickType_t) 0 )) { + r += (char) c; + + if (c == '\n' && r.length() > 10) + break; + } + return r; +} +void LoggingSerial::Reset(){ + xQueueReset(m_buffer); + //write_log_header(); +} + +//taken from Luftdaten.info +String delayToString(unsigned time_ms) { + + char buf[64]; + String s; + + if (time_ms > 2 * 1000 * 60 * 60 * 24) { + sprintf_P(buf, PSTR("%d days, "), time_ms / (1000 * 60 * 60 * 24)); + s += buf; + time_ms %= 1000 * 60 * 60 * 24; + } + + if (time_ms > 2 * 1000 * 60 * 60) { + sprintf_P(buf, PSTR("%d h, "), time_ms / (1000 * 60 * 60)); + s += buf; + time_ms %= 1000 * 60 * 60; + } + + if (time_ms > 2 * 1000 * 60) { + sprintf_P(buf, PSTR("%d min, "), time_ms / (1000 * 60)); + s += buf; + time_ms %= 1000 * 60; + } + + if (time_ms > 2 * 1000) { + sprintf_P(buf, PSTR("%d s, "), time_ms / 1000); + s += buf; + } + + if (s.length() > 2) { + s = s.substring(0, s.length() - 2); + } + + return s; +} \ No newline at end of file diff --git a/multigeiger/utils.h b/multigeiger/utils.h index 65eb729..4e7893a 100644 --- a/multigeiger/utils.h +++ b/multigeiger/utils.h @@ -2,9 +2,37 @@ #ifndef UTILS_H #define UTILS_H +#include // Prototypes int hex2data(unsigned char *data, const char *hexstring, unsigned int len); void reverseByteArray(unsigned char *data, int len); +String delayToString(unsigned time_ms); + +constexpr unsigned SMALL_STR = 64-1; +constexpr unsigned MED_STR = 256-1; +constexpr unsigned LARGE_STR = 512-1; +constexpr unsigned XLARGE_STR = 1024-1; + +#define RESERVE_STRING(name, size) String name((const char*)nullptr); name.reserve(size) + +/***************************************************************** + * Debug output * + *****************************************************************/ + +class LoggingSerial : public HardwareSerial { + +public: + LoggingSerial(); + size_t write(uint8_t c) override; + size_t write(const uint8_t *buffer, size_t size) override; + String popLines(); + void Reset(); + +private: + QueueHandle_t m_buffer; +}; + +extern class LoggingSerial Debug; #endif diff --git a/multigeiger/webconf.cpp b/multigeiger/webconf.cpp index 1ca60ac..dd2c772 100644 --- a/multigeiger/webconf.cpp +++ b/multigeiger/webconf.cpp @@ -1,13 +1,17 @@ // Web Configuration related code // also: OTA updates - +#include "version.h" #include "log.h" #include "speaker.h" +#include "transl.h" #include "IotWebConf.h" #include "IotWebConfTParameter.h" #include #include "userdefines.h" +#include "utils.h" +#include "tube.h" +#include "webconf.h" // Checkboxes have 'selected' if checked, so we need 9 byte for this string. #define CHECKBOX_LEN 9 @@ -20,6 +24,7 @@ bool sendToCommunity = SEND2SENSORCOMMUNITY; bool sendToMadavi = SEND2MADAVI; bool sendToLora = SEND2LORA; bool sendToBle = SEND2BLE; +bool sendToInflux = SEND2INFLUX; bool soundLocalAlarm = LOCAL_ALARM_SOUND; char speakerTick_c[CHECKBOX_LEN]; @@ -29,6 +34,7 @@ char showDisplay_c[CHECKBOX_LEN]; char sendToCommunity_c[CHECKBOX_LEN]; char sendToMadavi_c[CHECKBOX_LEN]; char sendToLora_c[CHECKBOX_LEN]; +char sendToInflux_c[CHECKBOX_LEN]; char sendToBle_c[CHECKBOX_LEN]; char soundLocalAlarm_c[CHECKBOX_LEN]; @@ -40,6 +46,24 @@ static bool isLoraBoard; float localAlarmThreshold = LOCAL_ALARM_THRESHOLD; int localAlarmFactor = (int)LOCAL_ALARM_FACTOR; +uint16_t influxPort = INFLUX_PORT; + +char influxServer[100] = ""; +char influxPath[100] = ""; +char influxUser[65] = ""; +char influxPassword[IOTWEBCONF_PASSWORD_LEN+1] = ""; +char influxMeasurement[100] = ""; + +extern float Count_Rate; +extern float Dose_Rate; +extern unsigned long starttime; +extern int log_level; +extern int hv_pulses; +extern float temperature; +extern float humidity; +extern float pressure; + + iotwebconf::ParameterGroup grpMisc = iotwebconf::ParameterGroup("misc", "Misc. Settings"); iotwebconf::CheckboxParameter startSoundParam = iotwebconf::CheckboxParameter("Start sound", "startSound", playSound_c, CHECKBOX_LEN, playSound); iotwebconf::CheckboxParameter speakerTickParam = iotwebconf::CheckboxParameter("Speaker tick", "speakerTick", speakerTick_c, CHECKBOX_LEN, speakerTick); @@ -50,12 +74,26 @@ iotwebconf::ParameterGroup grpTransmission = iotwebconf::ParameterGroup("transmi iotwebconf::CheckboxParameter sendToCommunityParam = iotwebconf::CheckboxParameter("Send to sensor.community", "send2Community", sendToCommunity_c, CHECKBOX_LEN, sendToCommunity); iotwebconf::CheckboxParameter sendToMadaviParam = iotwebconf::CheckboxParameter("Send to madavi.de", "send2Madavi", sendToMadavi_c, CHECKBOX_LEN, sendToMadavi); iotwebconf::CheckboxParameter sendToBleParam = iotwebconf::CheckboxParameter("Send to BLE (Reboot required!)", "send2ble", sendToBle_c, CHECKBOX_LEN, sendToBle); +iotwebconf::CheckboxParameter sendToInfluxParam = iotwebconf::CheckboxParameter("Send to influx-db", "send2Influx", sendToInflux_c, CHECKBOX_LEN, sendToInflux); +// influx-db parameters +iotwebconf::ParameterGroup grpInfluxDB = iotwebconf::ParameterGroup("influxdb", "Influx-DB Settings"); +iotwebconf::TextParameter influxServerParam = iotwebconf::TextParameter("Server", "influxserver", influxServer, 99,'\0',"influx-Server name"); +iotwebconf::TextParameter influxPathParam = iotwebconf::TextParameter("Path", "influxpath", influxPath, 99,'\0',"e.g. /write?db=myInfluxDB&precision=s"); +iotwebconf::IntTParameter influxPortParam = + iotwebconf::Builder>("influxPort"). + label("Port"). + defaultValue(influxPort). + min(1).max(65535). + step(1).placeholder("1..65535").build(); +iotwebconf::TextParameter influxUserParam = iotwebconf::TextParameter("User", "influxuser", influxUser, 64,'\0',"Username"); +iotwebconf::PasswordParameter influxPasswordParam = iotwebconf::PasswordParameter("Password", "influxpassword", influxPassword, IOTWEBCONF_PASSWORD_LEN,'\0',"Password"); +iotwebconf::TextParameter influxMeasurementParam = iotwebconf::TextParameter("Measurement", "influxmeasurement", influxMeasurement, 99,'\0',"Measurement"); iotwebconf::ParameterGroup grpLoRa = iotwebconf::ParameterGroup("lora", "LoRa Settings"); iotwebconf::CheckboxParameter sendToLoraParam = iotwebconf::CheckboxParameter("Send to LoRa (=>TTN)", "send2lora", sendToLora_c, CHECKBOX_LEN, sendToLora); -iotwebconf::TextParameter deveuiParam = iotwebconf::TextParameter("DEVEUI", "deveui", deveui, 17); -iotwebconf::TextParameter appeuiParam = iotwebconf::TextParameter("APPEUI", "appeui", appeui, 17); -iotwebconf::TextParameter appkeyParam = iotwebconf::TextParameter("APPKEY", "appkey", appkey, 33); +iotwebconf::TextParameter deveuiParam = iotwebconf::TextParameter("DEVEUI", "deveui", deveui, 17,'\0',"Device EUI"); +iotwebconf::TextParameter appeuiParam = iotwebconf::TextParameter("APPEUI", "appeui", appeui, 17,'\0',"Application EUI"); +iotwebconf::TextParameter appkeyParam = iotwebconf::TextParameter("APPKEY", "appkey", appkey, 33,'\0',"App Key"); iotwebconf::ParameterGroup grpAlarm = iotwebconf::ParameterGroup("alarm", "Local Alarm Setting"); iotwebconf::CheckboxParameter soundLocalAlarmParam = iotwebconf::CheckboxParameter("Enable local alarm sound", "soundLocalAlarm", soundLocalAlarm_c, CHECKBOX_LEN, soundLocalAlarm); @@ -100,8 +138,9 @@ unsigned long getESPchipID() { pid[0] = (uint8_t)pespid[5]; pid[1] = (uint8_t)pespid[4]; pid[2] = (uint8_t)pespid[3]; - log(INFO, "ID: %08X", id); - log(INFO, "MAC: %04X%08X", (uint16_t)(espid >> 32), (uint32_t)espid); + /* removed, is shown now in the web header, MAC is wrong/reverted here, by the way */ + //log(INFO, "ID: %08X", id); + //log(INFO, "MAC: %04X%08X", (uint16_t)(espid >> 32), (uint32_t)espid); return id; } @@ -111,34 +150,108 @@ char *buildSSID() { sprintf(ssid, "ESP32-%d", id); return ssid; } +int32_t calcWiFiSignalQuality(int32_t rssi) { + // Treat 0 or positive values as 0% + if (rssi >= 0 || rssi < -100) { + rssi = -100; + } + if (rssi > -50) { + rssi = -50; + } + return (rssi + 100) * 2; +} +//****************************************************************************** +// output of one line with on the startpage with "|sensor|datatype|value unit|" +//****************************************************************************** +void add_value_to_table(String& content, const __FlashStringHelper* sensor, const __FlashStringHelper* param, const String& value, const char* unit) { + RESERVE_STRING(s, MED_STR); + s = FPSTR(WEB_PAGE_DATA_LINE); + s.replace("{s}", sensor); + s.replace("{d}", param); + s.replace("{val}", value); + s.replace("{u}", String(unit)); + content += s; +} +//****************************************************************************** +// Start page +//****************************************************************************** void handleRoot(void) { // Handle web requests to "/" path. // -- Let IotWebConf test and handle captive portal requests. if (iotWebConf.handleCaptivePortal()) { // -- Captive portal requests were already served. return; } - const char *index = - "" - "" - "" - "" - "MultiGeiger Configuration" - "" - "" - "

Configuration

" - "

" - "Go to the config page to change settings or update firmware." - "

" - "" - "\n"; - server.send(200, "text/html;charset=UTF-8", index); + server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); + server.sendHeader(F("Pragma"), F("no-cache")); + server.sendHeader(F("Expires"), F("0")); + + // Enable Pagination (Chunked Transfer) + server.setContentLength(CONTENT_LENGTH_UNKNOWN); + server.send(200, FPSTR(CONTENT_TYPE_TXT_HTML), ""); + RESERVE_STRING (index,XLARGE_STR); + char tmp[50]; + const int last_signal_strength = WiFi.RSSI(); + index = FPSTR(WEB_PAGE_HEAD); + index.replace("{t}", FPSTR(TRA_CURRENT_DATA)); + +// Paginate page after ~ 1500 Bytes + server.sendContent(index); + index = emptyString; + index = FPSTR(WEB_PAGE_HEADLINE); + index.replace("{id}", theName); + index.replace("{mac}", WiFi.macAddress()); + index.replace("{fw}", VERSION_STR); + server.sendContent(index); + index = emptyString; + +index =F("

" TRA_ACT_VAL_HEADLINE "

" +"" +"" +"" +"" +"" +""); + +sprintf(tmp,"%.3f",Count_Rate); +add_value_to_table(index,F(tubes[TUBE_TYPE].type),FPSTR(TRA_CPS),tmp,"c/s"); + +sprintf(tmp,"%.3f",Dose_Rate); +add_value_to_table(index,F(tubes[TUBE_TYPE].type),FPSTR(TRA_DOSERATE),tmp,"µSv/h"); + +sprintf(tmp,"%d",hv_pulses); +add_value_to_table(index,F(tubes[TUBE_TYPE].type),FPSTR(TRA_HV_PULSES),tmp,""); + +index +=F(""); +// Paginate page after ~ 1500 Bytes + server.sendContent(index); + index = emptyString; +if(have_thp){ + sprintf(tmp,"%.1f",temperature); + add_value_to_table(index,F("BMEx80"),FPSTR(TRA_TEMP),tmp,"°C"); + sprintf(tmp,"%.1f",pressure); + add_value_to_table(index,F("BMEx80"),FPSTR(TRA_PRESSURE),tmp,"hPa"); + sprintf(tmp,"%.1f",humidity); + add_value_to_table(index,F("BMEx80"),FPSTR(TRA_HUMIDITY),tmp,"%"); + index +=F(""); +} +add_value_to_table(index,F("WiFi"),FPSTR(TRA_WIFISIGNAL),String(last_signal_strength),"dBm"); +add_value_to_table(index,F("WiFi"),FPSTR(TRA_WIFIQUALITY),String(calcWiFiSignalQuality(last_signal_strength)),"%"); +add_value_to_table(index,F("ESP32"),FPSTR(TRA_ESP_FREE_MEM),String(ESP.getFreeHeap()),"Byte"); +add_value_to_table(index,F("ESP32"),FPSTR(TRA_ESP_UPTIME),delayToString(millis() - starttime),""); + +index += F("
" TRA_SENSOR "" TRA_PARAMETER "" TRA_VALUE "
 
 

"); +index += FPSTR(WEB_PAGE_START_BUTTONS); +index +=F("
\n"); + + server.sendContent(index); + //server.send(200, FPSTR(CONTENT_TYPE_TXT_HTML), index); // looks like user wants to do some configuration or maybe flash firmware. // while accessing the flash, we need to turn ticking off to avoid exceptions. // user needs to save the config (or flash firmware + reboot) to turn it on again. // note: it didn't look like there is an easy way to put this call at the right place // (start of fw flash / start of config save) - this is why it is here. - tick_enable(false); + //tick_enable(false);TR : move to setup_webconf(). Then it's only off while in configution mode, and not during 'browsing' ... } static char lastWiFiSSID[IOTWEBCONF_WORD_LEN] = ""; @@ -146,7 +259,7 @@ static char lastWiFiSSID[IOTWEBCONF_WORD_LEN] = ""; void loadConfigVariables(void) { // check if WiFi SSID has changed. If so, restart cpu. Otherwise, the program will not use the new SSID if ((strcmp(lastWiFiSSID, "") != 0) && (strcmp(lastWiFiSSID, iotWebConf.getWifiSsidParameter()->valueBuffer) != 0)) { - log(INFO, "Doing restart..."); + log(INFO, TRA_MES_RESTART); ESP.restart(); } strcpy(lastWiFiSSID, iotWebConf.getWifiSsidParameter()->valueBuffer); @@ -159,19 +272,122 @@ void loadConfigVariables(void) { sendToMadavi = sendToMadaviParam.isChecked(); sendToLora = sendToLoraParam.isChecked(); sendToBle = sendToBleParam.isChecked(); + sendToInflux = sendToInfluxParam.isChecked(); + influxPort = influxPortParam.value(); + strcpy(influxServer,influxServerParam.valueBuffer); + strcpy(influxPath,influxPathParam.valueBuffer); + strcpy(influxUser,influxUserParam.valueBuffer); + strcpy(influxPassword,influxPasswordParam.valueBuffer); + strcpy(influxMeasurement,influxMeasurementParam.valueBuffer); soundLocalAlarm = soundLocalAlarmParam.isChecked(); localAlarmThreshold = localAlarmThresholdParam.value(); localAlarmFactor = localAlarmFactorParam.value(); } void configSaved(void) { - log(INFO, "Config saved. "); + log(INFO, TRA_MES_CONF_SAVED); loadConfigVariables(); tick_enable(true); } +//****************************************************************************** +// Aufruf original iotWebConf.handleConfig, aber vorher tick abschalten ... +//****************************************************************************** +void handleConfig(void){ + tick_enable(false); + iotWebConf.handleConfig(); +} + +//****************************************************************************** +// Ausgabe der seriellen Daten auf der Debugseite +//****************************************************************************** +void handleSerial(void){ + String s(Debug.popLines()); + server.send(s.length() ? 200 : 204, "text/plain", s); +} + +//****************************************************************************** +// Debug-Seite +//****************************************************************************** +void handleDebug(void){ + server.sendHeader(F("Cache-Control"), F("no-cache, no-store, must-revalidate")); + server.sendHeader(F("Pragma"), F("no-cache")); + server.sendHeader(F("Expires"), F("0")); + // Enable Pagination (Chunked Transfer) + server.setContentLength(CONTENT_LENGTH_UNKNOWN); + server.send(200, FPSTR(CONTENT_TYPE_TXT_HTML), ""); + char s[10]; + RESERVE_STRING(page_content, LARGE_STR); + Debug.Reset(); + + page_content = FPSTR(WEB_PAGE_HEAD); + page_content.replace("{t}", FPSTR(TRA_DEBUG_DATA)); + //page_content.replace("{lng}", F("DE")); + + server.sendContent(page_content); + + page_content = emptyString; + page_content = FPSTR(WEB_PAGE_HEADLINE); + page_content.replace("{id}", theName); + page_content.replace("{mac}", WiFi.macAddress()); + page_content.replace("{fw}", VERSION_STR); + + server.sendContent(page_content); + page_content = emptyString; + + page_content += FPSTR(TRA_LOGLEVEL_IS); + int lvl=NOLOG; + if (server.hasArg("lvl")) { + lvl = server.arg("lvl").toInt(); + setloglevel(lvl); + }else { lvl=log_level;} + if (lvl == 5) strcpy(s,"Debug"); + else if (lvl == 4) strcpy(s,"max.Info"); + else if (lvl == 3) strcpy(s,"med.Info"); + else if (lvl == 2) strcpy(s,"min.Info"); + else if (lvl == 1) strcpy(s,"Error"); + else strcpy(s,"NoLog"); + + page_content.replace("{lvl}", String(s)); + + page_content += F("
");
+	page_content += Debug.popLines();
+
+  page_content += FPSTR(WEB_PAGE_DBG_SCRIPT);
+  page_content += F("
"); + server.sendContent(page_content); + page_content = emptyString; + + page_content += FPSTR(TRA_SET_LOGLEVEL_TO); + page_content.replace("{lvl}", F("...")); + page_content += FPSTR(WEB_PAGE_DBG_BUTTONS); + page_content += F("" TRA_BUTTON_BACK "
"); + server.sendContent(page_content); +} +/***************************************************************** + * Webserver Images * + *****************************************************************/ +static void webserver_static() { + server.sendHeader(F("Cache-Control"), F("max-age=2592000, public")); + + if (server.arg(String('r')) == F("logo")) { + server.send_P(200, CONTENT_TYPE_IMAGE_PNG, + LOGO_PNG, LOGO_PNG_SIZE); + } + else if (server.arg(String('r')) == F("css")) { + server.send_P(200, CONTENT_TYPE_TEXT_CSS, + WEB_PAGE_STATIC_CSS, sizeof(WEB_PAGE_STATIC_CSS)-1); + } else { + iotWebConf.handleNotFound(); + } +} + +//****************************************************************************** +// Config settings +//****************************************************************************** void setup_webconf(bool loraHardware) { isLoraBoard = loraHardware; + iotWebConf.setConfigSavedCallback(&configSaved); // *INDENT-OFF* <- for 'astyle' to not format the following 3 lines iotWebConf.setupUpdateServer( @@ -194,6 +410,14 @@ void setup_webconf(bool loraHardware) { grpTransmission.addItem(&sendToMadaviParam); grpTransmission.addItem(&sendToBleParam); iotWebConf.addParameterGroup(&grpTransmission); + grpInfluxDB.addItem(&sendToInfluxParam); + grpInfluxDB.addItem(&influxServerParam); + grpInfluxDB.addItem(&influxPathParam); + grpInfluxDB.addItem(&influxPortParam); + grpInfluxDB.addItem(&influxUserParam); + grpInfluxDB.addItem(&influxPasswordParam); + grpInfluxDB.addItem(&influxMeasurementParam); + iotWebConf.addParameterGroup(&grpInfluxDB); if (isLoraBoard) { grpLoRa.addItem(&sendToLoraParam); grpLoRa.addItem(&deveuiParam); @@ -216,7 +440,13 @@ void setup_webconf(bool loraHardware) { // -- Set up required URL handlers on the web server. server.on("/", handleRoot); - server.on("/config", [] { iotWebConf.handleConfig(); }); + // using our own /config which simply turns of ticks first, then calls iotWebConf.handleConfig() + // server.on("/config", [] { iotWebConf.handleConfig(); }); //TR : original code + //server.on("/config", [] { handleConfig(); }); //works, but slow ??? + server.on("/config", handleConfig); + server.on("/debug", handleDebug); //debug page + server.on("/serial", handleSerial ); //needed for the serial ring buffer on the debug page + server.on(F(STATIC_PREFIX), webserver_static); // need to copy static data (logo,css) for speed reasons server.onNotFound([]() { iotWebConf.handleNotFound(); }); diff --git a/multigeiger/webconf.h b/multigeiger/webconf.h index ad68aa3..314be4e 100644 --- a/multigeiger/webconf.h +++ b/multigeiger/webconf.h @@ -5,6 +5,7 @@ #define _WEBCONF_H_ #include "IotWebConf.h" +#include "transl.h" extern bool speakerTick; extern bool playSound; @@ -13,12 +14,19 @@ extern bool showDisplay; extern bool sendToCommunity; extern bool sendToMadavi; extern bool sendToLora; +extern bool sendToInflux; extern bool sendToBle; extern bool soundLocalAlarm; +extern bool have_thp; extern char appeui[]; extern char deveui[]; extern char appkey[]; +extern char influxServer[]; +extern char influxPath[]; +extern char influxUser[]; +extern char influxPassword[]; +extern char influxMeasurement[]; extern float localAlarmThreshold; extern int localAlarmFactor; @@ -28,4 +36,117 @@ extern IotWebConf iotWebConf; void setup_webconf(bool loraHardware); -#endif // _WEBCONF_H_ +//#define LANGUAGE "DE" + +//const char CURRENT_DATA[] PROGMEM = "Aktuelle Werte"; +//const char DEBUG_DATA[] PROGMEM = "Debug Info"; +//const char SET_LOGLEVEL_TO[] PROGMEM = "

Setze Loglevel auf {lvl}

"; +//const char LOGLEVEL_IS[] PROGMEM = "

Loglevel ist: {lvl}

"; +const char CONTENT_TYPE_TXT_HTML[] PROGMEM = "text/html;charset=UTF-8"; +const char CONTENT_TYPE_IMAGE_PNG[] PROGMEM = "image/png"; +const char CONTENT_TYPE_TEXT_CSS[] PROGMEM = "text/css"; +#define STATIC_PREFIX "/" LANGUAGE "_s1" + +// gets preloaded +const char WEB_PAGE_STATIC_CSS[] PROGMEM ="body{font-family:Arial;margin:0}\ +.button{background-color:#1c87c9;border:none;color:white;padding:8px 16px;text-align:center;text-decoration:none;display:inline-block;font-size:16px;margin:4px 2px;cursor:pointer;border-radius:8px;box-shadow:5px 5px #999;}\ +.button:active{background-color:#3e8e41;box-shadow:3px 3px #666;transform:translateY(4px);}\ +div.debuglist{margin:10px;border:3px outset blue;background-color:GhostWhite;box-shadow:5px 5px #999;text-align:left;width:95%;height:300px;overflow-x:auto;overflow-y:auto;padding:5px;box-shadow:5px 5px #999;}\ +div.canvas{background-color:LightCyan;box-shadow:5px 5px #999;width:97%;}\ +.content{margin:10px}.r{text-align:right}.v{box-shadow:5px 5px #999}\ +.b{padding:10px}"; +const char WEB_PAGE_HEAD[] PROGMEM = "{t}\ +\ +\ +"; +/* styles inside the string. slow ... +const char WEB_PAGE_HEAD[] PROGMEM = "{t}\ +\ +"; +*/ + +/* no logo, but text eco curious text only, styles inside string +const char WEB_PAGE_HEADLINE[] PROGMEM = "
\ +\ +\ +\ +\ +
ecoMulti-Geiger
curious
ID: {id}
MAC: {mac}
Firmware: {fw}
"; +*/ + const char WEB_PAGE_DBG_BUTTONS[] PROGMEM = "\ +\ +\ +\ +\ +\ +\ +
" TRA_BUTTON_NOLOG "" TRA_BUTTON_ERROR "" TRA_BUTTON_MININFO "" TRA_BUTTON_MEDINFO "" TRA_BUTTON_MAXINFO "" TRA_BUTTON_DEBUG "
"; + +const char WEB_PAGE_START_BUTTONS[] PROGMEM = "\ +
" TRA_BUTTON_CONFIG "" TRA_BUTTON_LOG_PAGE "" TRA_BUTTON_REFRESH "
"; + +const char WEB_PAGE_DBG_SCRIPT[] PROGMEM = ""; + +const char WEB_PAGE_DATA_LINE[] PROGMEM = "{s}{d}{val} {u}"; + + +const char WEB_PAGE_HEADLINE[] PROGMEM = "
\ +" TRA_BUTTON_BACK "

Multi-Geiger


\ +ID: {id}
MAC: {mac}
Firmware: {fw} (" __DATE__ " " __TIME__ ")
"; +constexpr const unsigned int LOGO_PNG_SIZE = 692; + +// gets preloaded +const char LOGO_PNG[] PROGMEM = { +0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52, +0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x24, 0x04, 0x03, 0x00, 0x00, 0x00, 0x8D, 0x94, 0x82, +0x0B, 0x00, 0x00, 0x00, 0x1B, 0x50, 0x4C, 0x54, 0x45, 0xD0, 0x5D, 0x17, 0xF8, 0xFC, 0xF8, 0x0A, +0xB4, 0xEC, 0x89, 0xBF, 0x2C, 0xA1, 0xDD, 0xCD, 0x61, 0xCE, 0xF4, 0x4F, 0x35, 0x8D, 0x60, 0x6F, +0x97, 0x7C, 0xA2, 0x44, 0xDB, 0xB1, 0x52, 0xEF, 0x00, 0x00, 0x00, 0x01, 0x74, 0x52, 0x4E, 0x53, +0x00, 0x40, 0xE6, 0xD8, 0x66, 0x00, 0x00, 0x02, 0x47, 0x49, 0x44, 0x41, 0x54, 0x38, 0x8D, 0x8D, +0x53, 0xC1, 0x8A, 0xDC, 0x30, 0x0C, 0x35, 0x13, 0x87, 0xED, 0x67, 0x04, 0x91, 0xB4, 0xD7, 0x80, +0x43, 0x77, 0x8F, 0x26, 0x93, 0xB9, 0x87, 0x10, 0xDF, 0x4B, 0x0F, 0xDE, 0x1E, 0xCB, 0x1E, 0x42, +0x8F, 0x0B, 0x81, 0xFD, 0xEE, 0xEA, 0x49, 0xB1, 0x93, 0x42, 0x93, 0xD6, 0xEC, 0x8E, 0x35, 0xF6, +0x7B, 0x7A, 0x4F, 0x92, 0xC7, 0x98, 0xBC, 0xAC, 0x73, 0x9D, 0x4F, 0x81, 0x9C, 0x14, 0x29, 0x38, +0x5B, 0x0E, 0x4B, 0x19, 0x1A, 0x14, 0xD8, 0xDB, 0x0B, 0x86, 0x00, 0x80, 0xB8, 0xA7, 0x40, 0x76, +0xE7, 0x2F, 0x45, 0xEE, 0x92, 0x5D, 0x91, 0xDD, 0x96, 0xE3, 0x42, 0xC6, 0x22, 0x1F, 0x7F, 0x00, +0xD9, 0x5A, 0x26, 0x43, 0xCD, 0x5B, 0xB5, 0x78, 0xE6, 0xAB, 0x13, 0xA9, 0xF6, 0x2E, 0xA0, 0xD5, +0x79, 0x11, 0x28, 0x2E, 0x9C, 0x15, 0xE2, 0xE0, 0xEE, 0x5A, 0xB5, 0xD2, 0xA9, 0xAC, 0x24, 0x39, +0xA3, 0x68, 0xD1, 0x2E, 0x51, 0xC4, 0xA9, 0xF9, 0x17, 0x65, 0x7D, 0x71, 0x2F, 0x42, 0xF1, 0x9B, +0xEC, 0xAA, 0xE7, 0xFF, 0xA3, 0xE2, 0x93, 0x8A, 0x0D, 0xCD, 0x78, 0x41, 0x29, 0xD2, 0xD5, 0x6E, +0xEC, 0x99, 0x78, 0xFD, 0xBA, 0xA0, 0xAC, 0x1D, 0x3E, 0x93, 0x13, 0xD7, 0x16, 0x2E, 0x82, 0xB2, +0xAC, 0x17, 0x2A, 0x4E, 0x25, 0x8A, 0x6D, 0x40, 0xC3, 0x57, 0xA5, 0xFC, 0x38, 0xA5, 0x60, 0xE8, +0x32, 0x41, 0x2E, 0xDB, 0x0D, 0xF8, 0xF2, 0x11, 0x69, 0x7E, 0x50, 0x8C, 0xE7, 0x94, 0xAD, 0xFE, +0xFC, 0x60, 0x8A, 0x18, 0x6B, 0x63, 0x6E, 0x4B, 0x7C, 0x3F, 0xA5, 0xE4, 0x67, 0xA9, 0x81, 0xB7, +0x31, 0x8E, 0xDC, 0x84, 0x25, 0xFE, 0x3C, 0xA5, 0x08, 0xB4, 0x4B, 0x7A, 0xAD, 0xF9, 0x14, 0x23, +0xBE, 0x5C, 0x52, 0x8C, 0xBD, 0xFB, 0x2D, 0x18, 0x38, 0x78, 0x4A, 0x94, 0x6F, 0x7F, 0x81, 0x0E, +0x41, 0xA0, 0xF6, 0xCF, 0xD3, 0xA7, 0xF8, 0x8A, 0xED, 0xE3, 0x48, 0xB1, 0x61, 0x90, 0x7D, 0xE2, +0x56, 0x56, 0xC6, 0x12, 0x49, 0x5C, 0x4D, 0x34, 0x4E, 0x34, 0x13, 0xED, 0xC6, 0x70, 0x6D, 0x6E, +0xD4, 0x98, 0x92, 0x91, 0xB5, 0x41, 0x8C, 0xE5, 0xCB, 0x8D, 0xC2, 0xF8, 0x40, 0x34, 0x10, 0x95, +0x31, 0x7A, 0xA5, 0xF0, 0xA1, 0x52, 0x90, 0x1C, 0xB1, 0x30, 0xA8, 0x66, 0x15, 0xBF, 0xA9, 0xF0, +0x9A, 0x85, 0xD2, 0x72, 0x4F, 0x44, 0x45, 0x29, 0x56, 0x91, 0x22, 0xC2, 0x69, 0xE9, 0x60, 0x8C, +0x54, 0x65, 0x89, 0xAE, 0x1D, 0xDC, 0x12, 0xF9, 0x44, 0x8D, 0x41, 0x08, 0xFF, 0x3D, 0xB2, 0x97, +0x02, 0xCA, 0x2A, 0xCD, 0xC0, 0x07, 0xE5, 0xE7, 0x88, 0xF9, 0x2C, 0xDF, 0xAB, 0xA4, 0x72, 0x03, +0xB5, 0x99, 0x19, 0x84, 0x82, 0x68, 0x3E, 0xD4, 0xC2, 0x01, 0x6B, 0x96, 0xA4, 0x94, 0xB7, 0xA4, +0x52, 0x33, 0x4B, 0x5A, 0x2B, 0xED, 0x30, 0xDE, 0x1C, 0x6A, 0xE1, 0x1C, 0x50, 0xA1, 0x2F, 0xA0, +0xBC, 0x51, 0x56, 0x41, 0xC3, 0x66, 0x0F, 0xCA, 0x28, 0xAD, 0x2E, 0xF7, 0x5A, 0xAA, 0x4D, 0x85, +0xAC, 0xEB, 0xBC, 0xB6, 0x7D, 0x2F, 0xBF, 0xD9, 0x29, 0x07, 0x95, 0x6A, 0x53, 0x21, 0x9D, 0x5A, +0x95, 0xE7, 0xD2, 0x93, 0x8C, 0xF0, 0x40, 0x49, 0xB5, 0x64, 0x15, 0x93, 0x87, 0xAB, 0x14, 0x13, +0x44, 0x46, 0x10, 0xE6, 0x81, 0x51, 0x8A, 0x4D, 0x05, 0x58, 0xE9, 0xBA, 0x97, 0x5A, 0x47, 0x51, +0xE9, 0x41, 0x31, 0x16, 0xCD, 0x99, 0x10, 0x5A, 0x69, 0xF2, 0x88, 0x3D, 0x1B, 0xF3, 0x94, 0x02, +0x69, 0x08, 0xE3, 0x6C, 0x10, 0x04, 0x46, 0x59, 0xDB, 0x5E, 0x10, 0x0D, 0x5F, 0xEE, 0xC6, 0xF0, +0x3C, 0x46, 0x54, 0x8C, 0x60, 0x78, 0x30, 0xAC, 0xE7, 0x1B, 0xD8, 0xD5, 0x57, 0xC0, 0x52, 0xBA, +0xEF, 0xC6, 0xFC, 0x8D, 0x0E, 0x0F, 0x04, 0x10, 0xDE, 0x67, 0x31, 0x35, 0x29, 0x32, 0x5D, 0x64, +0x63, 0x29, 0xD9, 0x28, 0x0F, 0x18, 0x94, 0xF4, 0xC6, 0x4C, 0xEA, 0x36, 0xF6, 0xE6, 0x30, 0x17, +0xAF, 0x49, 0x6A, 0xA3, 0x59, 0x43, 0x6A, 0x32, 0x7A, 0x52, 0x4E, 0xFA, 0x10, 0x50, 0xE9, 0x23, +0xE0, 0x0F, 0xBF, 0xA5, 0x80, 0x3E, 0x2A, 0x03, 0x7D, 0x9A, 0x6D, 0x98, 0x85, 0x3B, 0x9A, 0xDF, +0x39, 0xC4, 0xB9, 0x22, 0x26, 0xEB, 0x96, 0x83, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, +0xAE, 0x42, 0x60, 0x82 +}; +#endif // _WEBCONF_H_ \ No newline at end of file diff --git a/platformio-example.ini b/platformio-example.ini index 765db24..36dcc40 100644 --- a/platformio-example.ini +++ b/platformio-example.ini @@ -8,26 +8,61 @@ ; Please visit documentation for the other options and examples ; https://docs.platformio.org/page/projectconf.html ; -; ******* DON'T EDIT THIS FILE! ******** -; ***** Copy it to platformio.ini and edit that! ***** -; +;TR 07.04.2022 +; Steps to add a new translation language for the internal sensor pages: +; 1. create a new file translation_xx.h where xx is the language code +; 2. add this file name to transl.h +; 3. add a new chapter in platformio.ini, like [env:geiger_en] (copy&paste), and +; - adopt headline [env:geiger_xx] +; - the buildflag '-DTRANSL_XX' to the new language +; this defines TRANSL_XX which can be queried in transl.h during runtime in its if-elsif and includes the corresponding translation_xx.h. +;--> add new translation strings to ALL files translations_xx.h !!! +;--> do NOT add translation_xx.h to any of your routines, but transl.h + [platformio] src_dir = multigeiger - -[env:geiger] -board = heltec_wireless_stick +[common] build_flags = -D CFG_eu868=1 -D CFG_sx1276_radio=1 -D ARDUINO_LMIC_PROJECT_CONFIG_H_SUPPRESS=1 -platform = espressif32 -framework = arduino -monitor_speed=115200 lib_deps= U8g2 - Adafruit BME680 Library@^2.0.0 + Adafruit BME680 Library Adafruit BME280 Library Adafruit Unified Sensor - IotWebConf@^3.1.0 + IotWebConf MCCI LoRaWAN LMIC library h2zero/NimBLE-Arduino + ;Adafruit BME680 Library@^2.0.0 using latest now + ;IotWebConf@^3.1.0 using latest now + ;EspSoftwareSerial using hardwareserial instead +[env] +monitor_speed=115200 +;set/adopt your upload and monitor port here, like ... +;upload_port = /dev/ttyUSB0 +;monitor_port = /dev/ttyUSB0 + +[env:geiger] +;lang = DE +board = heltec_wireless_stick +build_flags = ${common.build_flags} '-DTRANSL_DE' +platform = espressif32 +framework = arduino +lib_deps= ${common.lib_deps} + +[env:geiger_en] +;lang = EN +board = heltec_wireless_stick +build_flags = ${common.build_flags} '-DTRANSL_EN' +platform = espressif32 +framework = arduino +lib_deps= ${common.lib_deps} + +[env:geiger_it] +;lang = IT +board = heltec_wireless_stick +build_flags = ${common.build_flags} '-DTRANSL_IT' +platform = espressif32 +framework = arduino +lib_deps= ${common.lib_deps}