diff --git a/docs/source/Plugin/P103.rst b/docs/source/Plugin/P103.rst index 4560326cb7..e2c984bca9 100644 --- a/docs/source/Plugin/P103.rst +++ b/docs/source/Plugin/P103.rst @@ -27,13 +27,134 @@ Datasheet: |P103_datasheet| Description ----------- +Several Atlas Scientific EZO sensors are supported by this plugin. Only I2C communication is supported, some sensors use that by default, and most other sensors can be re-configured from RS232 to I2C communication. +Currently there is support for: +* **pH**: Potential of Hydrogen (Acidity) -.. Events -.. ~~~~~~ +* **ORP**: Oxidation Reduction Potential + +* **EC**: Electrical Conductivity + +* **DO**: Dissolved Oxigen + +* **HUM**: Humidity (Can also provide Temperature and Dewpoint measurements) + +Device configuration +-------------------- + +.. image:: P103_DeviceConfiguration.png + +* **Name**: A unique name should be entered here. + +* **Enabled**: The device can be disabled or enabled. When not enabled the device should not use any resources. + +I2C options +^^^^^^^^^^^ + +The available settings here depend on the build used. At least the **Force Slow I2C speed** option is available, but selections for the I2C Multiplexer can also be shown. For details see the :ref:`Hardware_page` + +When loading the Device configuration page, the plugin will try to detect the type of sensor connected based on the selected I2C addresses. If no sensor is found, the message in red will be shown. + +If no sensor is connected, the configuration can still be shown by selecting the **Setup without sensor** checkbox, choosing the I2C address for the intended sensor, and submitting the page. + +Device settings +^^^^^^^^^^^^^^^ + +Board +^^^^^ + +This section shows relevant information, obtained from the sensor if actually connected. + +* **Status LED**: The status led on the sensor can be enabled or disabled, using this checkbox. + +For each board there are different device parameters available, but when using the device simulation, no actual device information is available, so no data is shown. + +Board pH +^^^^^^^^ + +.. image:: P103_Device_pH.png + +pH Calibration +^^^^^^^^^^^^^^ + +For the pH sensor, some calibration may be needed. The simulated device doesn't have any calibration data set. + +.. image:: P103_Device_pH_Calibration.png + +For compensation of temperature, either a fixed temperature or a value from a connected temperature sensor can be selected. For this connected temperature sensor, the ``[#]`` notation can be used. This will will be evaluated when the value is needed. + +Board ORP +^^^^^^^^^ + +.. image:: P103_Device_ORP.png + +ORP Calibration +^^^^^^^^^^^^^^^ + +For the ORP sensor, some calibration may be needed. The simulated device doesn't have any calibration data set. + +.. image:: P103_Device_ORP_Calibration.png + +For compensation of temperature, either a fixed temperature or a value from a connected temperature sensor can be selected. For this connected temperature sensor, the ``[#]`` notation can be used. This will will be evaluated when the value is needed. + +Board EC +^^^^^^^^ + +.. image:: P103_Device_EC.png + +EC Calibration +^^^^^^^^^^^^^^ + +For the EC sensor, some calibration may be needed. The simulated device doesn't have any calibration data set. + +.. image:: P103_Device_EC_Calibration.png + +For compensation of temperature, either a fixed temperature or a value from a connected temperature sensor can be selected. For this connected temperature sensor, the ``[#]`` notation can be used. This will will be evaluated when the value is needed. + +Board DO +^^^^^^^^ + +.. image:: P103_Device_DO.png + +DO Calibration +^^^^^^^^^^^^^^ + +For the DO sensor, some calibration may be needed. The simulated device doesn't have any calibration data set. + +.. image:: P103_Device_DO_Calibration.png + +Board HUM +^^^^^^^^^ + +.. image:: P103_Device_HUM.png + +HUM Calibration +^^^^^^^^^^^^^^^ + +For the HUM sensor, no calibration can be adjusted, but options to enable the Temperature and/or Dew-point measurements are available. + +.. image:: P103_Device_HUM_Options.png + +To make the Temperature and Dew-point available for use, extra values are available: + +.. image:: P103_Device_HUM_Values.png + +Data Acquisition +^^^^^^^^^^^^^^^^ + +This group of settings, **Single event with all values**, **Send to Controller** and **Interval** settings are standard available configuration items. Send to Controller is only visible when one or more Controllers are configured. + +* **Interval** By default, Interval will be set to 60 sec. The data will be collected and optionally sent to any configured controllers using this interval. + +Values +^^^^^^ + +For all sensors, the **SensorData** and **Voltage** values are available. For the **HUM** Humidity sensor, also **Temperature** and **Dew-point** values are available, but have to be enabled in options to return a sensible value. + +In selected builds, per Value **Stats** options are available, that when enabled, will gather the measured data and present most recent data in a graph, as described here: :ref:`Task Value Statistics: ` -.. .. include:: P103_events.repl @@ -43,4 +164,6 @@ Change log .. versionchanged:: 2.0 ... + |added| 2023-10-17 Add HUM sensor + |added| 2020-04-25 diff --git a/docs/source/Plugin/P103_DeviceConfiguration.png b/docs/source/Plugin/P103_DeviceConfiguration.png new file mode 100644 index 0000000000..08b3cb3848 Binary files /dev/null and b/docs/source/Plugin/P103_DeviceConfiguration.png differ diff --git a/docs/source/Plugin/P103_Device_DO.png b/docs/source/Plugin/P103_Device_DO.png new file mode 100644 index 0000000000..45ee0f97bf Binary files /dev/null and b/docs/source/Plugin/P103_Device_DO.png differ diff --git a/docs/source/Plugin/P103_Device_DO_Calibration.png b/docs/source/Plugin/P103_Device_DO_Calibration.png new file mode 100644 index 0000000000..aca47b6a7e Binary files /dev/null and b/docs/source/Plugin/P103_Device_DO_Calibration.png differ diff --git a/docs/source/Plugin/P103_Device_EC.png b/docs/source/Plugin/P103_Device_EC.png new file mode 100644 index 0000000000..f68c3220e0 Binary files /dev/null and b/docs/source/Plugin/P103_Device_EC.png differ diff --git a/docs/source/Plugin/P103_Device_EC_Calibration.png b/docs/source/Plugin/P103_Device_EC_Calibration.png new file mode 100644 index 0000000000..3809304f0a Binary files /dev/null and b/docs/source/Plugin/P103_Device_EC_Calibration.png differ diff --git a/docs/source/Plugin/P103_Device_HUM.png b/docs/source/Plugin/P103_Device_HUM.png new file mode 100644 index 0000000000..7bd7d3b2d4 Binary files /dev/null and b/docs/source/Plugin/P103_Device_HUM.png differ diff --git a/docs/source/Plugin/P103_Device_HUM_Options.png b/docs/source/Plugin/P103_Device_HUM_Options.png new file mode 100644 index 0000000000..ea727f8c11 Binary files /dev/null and b/docs/source/Plugin/P103_Device_HUM_Options.png differ diff --git a/docs/source/Plugin/P103_Device_HUM_Values.png b/docs/source/Plugin/P103_Device_HUM_Values.png new file mode 100644 index 0000000000..926054d61e Binary files /dev/null and b/docs/source/Plugin/P103_Device_HUM_Values.png differ diff --git a/docs/source/Plugin/P103_Device_ORP.png b/docs/source/Plugin/P103_Device_ORP.png new file mode 100644 index 0000000000..e760cf7091 Binary files /dev/null and b/docs/source/Plugin/P103_Device_ORP.png differ diff --git a/docs/source/Plugin/P103_Device_ORP_Calibration.png b/docs/source/Plugin/P103_Device_ORP_Calibration.png new file mode 100644 index 0000000000..eafe628803 Binary files /dev/null and b/docs/source/Plugin/P103_Device_ORP_Calibration.png differ diff --git a/docs/source/Plugin/P103_Device_pH.png b/docs/source/Plugin/P103_Device_pH.png new file mode 100644 index 0000000000..389477d8c0 Binary files /dev/null and b/docs/source/Plugin/P103_Device_pH.png differ diff --git a/docs/source/Plugin/P103_Device_pH_Calibration.png b/docs/source/Plugin/P103_Device_pH_Calibration.png new file mode 100644 index 0000000000..2dbcb770fc Binary files /dev/null and b/docs/source/Plugin/P103_Device_pH_Calibration.png differ diff --git a/docs/source/Plugin/_plugin_substitutions_p10x.repl b/docs/source/Plugin/_plugin_substitutions_p10x.repl index 87af9e5666..84cc1e7d1b 100644 --- a/docs/source/Plugin/_plugin_substitutions_p10x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p10x.repl @@ -37,16 +37,16 @@ .. |P102_compileinfo| replace:: `.` .. |P102_usedlibraries| replace:: https://github.com/olehs/PZEM004T -.. |P103_name| replace:: :cyan:`Atlas Scientific EZO pH` +.. |P103_name| replace:: :cyan:`Atlas Scientific EZO pH ORP EC DO HUM` .. |P103_type| replace:: :cyan:`Environment` -.. |P103_typename| replace:: :cyan:`Environment - Atlas Scientific EZO pH` +.. |P103_typename| replace:: :cyan:`Environment - Atlas Scientific EZO pH ORP EC DO HUM` .. |P103_porttype| replace:: `.` .. |P103_status| replace:: :yellow:`CLIMATE` -.. |P103_github| replace:: P103_Atlas_EZO_pH.ino -.. _P103_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P103_Atlas_EZO_pH.ino +.. |P103_github| replace:: P103_Atlas_EZO_pH_ORP_EC_DO.ino +.. _P103_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino .. |P103_usedby| replace:: `.` .. |P103_shortinfo| replace:: `.` -.. |P103_maintainer| replace:: TD-er +.. |P103_maintainer| replace:: `TD-er tonhuisman` .. |P103_compileinfo| replace:: `.` .. |P103_usedlibraries| replace:: `.` .. |P103_datasheet| replace:: https://atlas-scientific.com/files/pH_EZO_Datasheet.pdf diff --git a/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino b/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino index 1a3b491587..bda8579e01 100644 --- a/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino +++ b/src/_P103_Atlas_EZO_pH_ORP_EC_DO.ino @@ -2,17 +2,28 @@ #ifdef USES_P103 -// ########################################################################### -// ################## Plugin 103 : Atlas Scientific EZO pH ORP EC DO sensors # -// ########################################################################### - -// datasheet at https://atlas-scientific.com/files/pH_EZO_Datasheet.pdf -// datasheet at https://atlas-scientific.com/files/ORP_EZO_Datasheet.pdf -// datasheet at https://atlas-scientific.com/files/EC_EZO_Datasheet.pdf -// datasheet at https://atlas-scientific.com/files/DO_EZO_Datasheet.pdf +// ######################################################################################## +// ################## Plugin 103 : Atlas Scientific EZO pH ORP EC DO HUM RTD FLOW sensors # +// ######################################################################################## + +// datasheet at https://atlas-scientific.com/files/pH_EZO_Datasheet.pdf (0x63, pH level) +// datasheet at https://atlas-scientific.com/files/ORP_EZO_Datasheet.pdf (0x62, Oxidation Reduction Potential) +// datasheet at https://atlas-scientific.com/files/EC_EZO_Datasheet.pdf (0x64, electric conductivity) +// datasheet at https://atlas-scientific.com/files/DO_EZO_Datasheet.pdf (0x61, dissolved oxigen) +// datasheet at https://files.atlas-scientific.com/EZO-HUM-C-Datasheet.pdf (0x6F, humidity) +// datasheet at https://files.atlas-scientific.com/EZO_RTD_Datasheet.pdf (0x66, thermosensors) +// datasheet at https://files.atlas-scientific.com/flow_EZO_Datasheet.pdf (0x68, flow meter) // only i2c mode is supported /** Changelog: + * 2024-10-19 tonhuisman: Fix javascript errors, some code improvements + * 2023-10-23 tonhuisman: Handle EZO-HUM firmware issue of including 'Dew,' in the result values + * // TODO Rewrite plugin using PluginDataStruct so it will allow proper async handling of commands requiring 300 msec delay before reading + * responses + * 2023-10-22 tonhuisman: Fix more irregularities, read configured EZO-HUM output options, and add options to enable/disable + * Temperature and Dew point values + * 2023-10-22 tonhuisman: Fix some irregularities, add logging for status read (UI) and value(s) read (INFO log) + * 2023-10-17 tonhuisman: Add support for EZO HUM, RTD and FLOW sensor modules (I2C only!) (RTD, FLOW disabled, default to UART mode) * 2023-01-08 tonhuisman: Replace ambiguous #define UNKNOWN, move support functions to plugin_struct source * 2023-01-07 tonhuisman: Refactored strings (a.o. shorter names for WEBFORM_LOAD and WEBFORM_SAVE events), separate javascript function * instead of repeated code, extract red/orange/green messages into functions @@ -20,35 +31,55 @@ * Reuse char arrays instead of instantiating a new one */ +# include "src/PluginStructs/P103_data_struct.h" + # define PLUGIN_103 # define PLUGIN_ID_103 103 -# define PLUGIN_NAME_103 "Environment - Atlas EZO pH ORP EC DO" +# define PLUGIN_NAME_103 "Environment - Atlas EZO pH ORP EC DO HUM" +# if P103_USE_RTD +" RTD" +# endif // if P103_USE_RTD +# if P103_USE_FLOW +" FLOW" +# endif // if P103_USE_FLOW # define PLUGIN_VALUENAME1_103 "SensorData" # define PLUGIN_VALUENAME2_103 "Voltage" - -# include "src/PluginStructs/P103_data_struct.h" +# define PLUGIN_VALUENAME3_103 "Temperature" // TODO Only used for HUM TODO: Fix for EZO-FLOW extra reading +# define PLUGIN_VALUENAME4_103 "Dewpoint" // Only used for HUM boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) { boolean success = false; - AtlasEZO_Sensors_e board_type = AtlasEZO_Sensors_e::UNKNOWN; + AtlasEZO_Sensors_e board_type = AtlasEZO_Sensors_e::UNKNOWN; + const uint8_t i2cAddressValues[] = { 0x63, 0x62, 0x64, 0x61, 0x6F + # if P103_USE_RTD + , 0x66 + # endif // if P103_USE_RTD + # if P103_USE_FLOW + , 0x68 + # endif // if P103_USE_FLOW + }; + constexpr int i2c_nr_elements = NR_ELEMENTS(i2cAddressValues); + + char boarddata[ATLAS_EZO_RETURN_ARRAY_SIZE]{}; + + bool _HUMhasHum = true; // EZO-HUM options (& defaults) + bool _HUMhasTemp = false; + bool _HUMhasDew = false; switch (function) { case PLUGIN_DEVICE_ADD: { - Device[++deviceCount].Number = PLUGIN_ID_103; - Device[deviceCount].Type = DEVICE_TYPE_I2C; - Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_DUAL; - Device[deviceCount].Ports = 0; - Device[deviceCount].PullUpOption = false; - Device[deviceCount].InverseLogicOption = false; - Device[deviceCount].FormulaOption = true; - Device[deviceCount].ValueCount = 2; - Device[deviceCount].SendDataOption = true; - Device[deviceCount].TimerOption = true; - Device[deviceCount].GlobalSyncOption = true; + Device[++deviceCount].Number = PLUGIN_ID_103; + Device[deviceCount].Type = DEVICE_TYPE_I2C; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_DUAL; + Device[deviceCount].Ports = 0; + Device[deviceCount].FormulaOption = true; + Device[deviceCount].ValueCount = 2; + Device[deviceCount].SendDataOption = true; + Device[deviceCount].TimerOption = true; break; } @@ -62,21 +93,45 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) { strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_103)); strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_103)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_103)); // Only used for HUM + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[3], PSTR(PLUGIN_VALUENAME4_103)); // Only used for HUM + break; + } + + case PLUGIN_SET_DEFAULTS: + { + P103_NR_OUTPUT_VALUES = 2; + + break; + } + + case PLUGIN_GET_DEVICEVALUECOUNT: + { + event->Par1 = P103_NR_OUTPUT_VALUES; // Depends on sensor + + success = true; + break; } case PLUGIN_I2C_HAS_ADDRESS: case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: { - const uint8_t i2cAddressValues[] = { 0x61, 0x62, 0x63, 0x64 }; // , 0x65, 0x66, 0x67}; // Disabled unsupported devices as discussed - // here: https://github.com/letscontrolit/ESPEasy/pull/3733 (review - // comment by TD-er) + // Disabled unsupported devices as discussed + // here: https://github.com/letscontrolit/ESPEasy/pull/3733 (review comment by TD-er) if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) { - addFormSelectorI2C(F("i2c"), P103_ATLASEZO_I2C_NB_OPTIONS, i2cAddressValues, P103_I2C_ADDRESS); - addFormNote(F("pH: 0x63, ORP: 0x62, EC: 0x64, DO: 0x61. The plugin is able to detect the type of device automatically.")); + addFormSelectorI2C(F("i2c"), i2c_nr_elements, i2cAddressValues, P103_I2C_ADDRESS); + addFormNote(F("pH: 0x63, ORP: 0x62, EC: 0x64, DO: 0x61, HUM: 0x6F" + # if P103_USE_RTD + ", RTD: 0x66" + # endif // if P103_USE_RTD + # if P103_USE_FLOW + ", FLOW: 0x68" + # endif // if P103_USE_FLOW + ". The plugin can recognize the type of device.")); } else { - success = intArrayContains(P103_ATLASEZO_I2C_NB_OPTIONS, i2cAddressValues, event->Par1); + success = intArrayContains(i2c_nr_elements, i2cAddressValues, event->Par1); } break; } @@ -96,35 +151,69 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) P103_addDisabler(); // JS function disabler(clear,single,l,h,dry,nul,atm) - char boarddata[ATLAS_EZO_RETURN_ARRAY_SIZE] = { 0 }; + addFormCheckBox(F("Setup without sensor"), F("uncon"), P103_UNCONNECTED_SETUP == 1); - if (P103_send_I2C_command(P103_I2C_ADDRESS, F("i"), boarddata)) - { - String boardInfo(boarddata); + if (P103_send_I2C_command(P103_I2C_ADDRESS, F("i"), boarddata) || P103_UNCONNECTED_SETUP) { + const String boardInfo(boarddata); addRowLabel(F("Board type")); - String board = boardInfo.substring(boardInfo.indexOf(',') + 1, boardInfo.lastIndexOf(',')); - String version = boardInfo.substring(boardInfo.lastIndexOf(',') + 1); - addHtml(board); + String board = parseStringKeepCase(boardInfo, 2); + const String version = parseStringKeepCase(boardInfo, 3); - String boardTypes = F("pH ORP EC D.O."); - AtlasEZO_Sensors_e boardIDs[] = { + const String boardTypes = F("pH ORP EC D.O.HUM RTD FLO"); // Unsupported boards are still ignored + const AtlasEZO_Sensors_e boardIDs[] = { AtlasEZO_Sensors_e::PH, AtlasEZO_Sensors_e::ORP, AtlasEZO_Sensors_e::EC, AtlasEZO_Sensors_e::DO, + AtlasEZO_Sensors_e::HUM, + # if P103_USE_RTD + AtlasEZO_Sensors_e::RTD, + # endif // if P103_USE_RTD + # if P103_USE_FLOW + AtlasEZO_Sensors_e::FLOW, + # endif // if P103_USE_FLOW }; int bType = boardTypes.indexOf(board); - if (bType > -1) { + if ((board.isEmpty() || (bType == -1)) && P103_UNCONNECTED_SETUP) { + // Not recognized, lets assume I2C address is correct, so we can setup the options + for (uint8_t i = 0; i < i2c_nr_elements; ++i) { + if (i2cAddressValues[i] == P103_I2C_ADDRESS) { + bType = i * 4; // Divided in the next check + break; + } + } + } + + if ((bType > -1) && ((size_t)(bType / 4) < NR_ELEMENTS(boardIDs))) { board_type = boardIDs[bType / 4]; + board = toString(board_type); } - P103_BOARD_TYPE = static_cast(board_type); + addHtml(board); - if (board_type == AtlasEZO_Sensors_e::UNKNOWN) - { - P103_html_red(F(" WARNING : Board type should be 'pH', 'ORP', 'EC' or 'DO', check your i2c address? ")); + P103_BOARD_TYPE = static_cast(board_type); + const int output_values[] = { 2, 2, 2, 2, 2, 4 + # if P103_USE_RTD + , 2 + # endif // if P103_USE_RTD + # if P103_USE_FLOW + , 3 + # endif // if P103_USE_FLOW + }; + P103_NR_OUTPUT_VALUES = output_values[P103_BOARD_TYPE]; + + + if (board_type == AtlasEZO_Sensors_e::UNKNOWN) { + P103_html_red(F(" WARNING : Board type should be 'pH', 'ORP', 'EC', 'DO', 'HUM'" + # if P103_USE_RTD + ", 'RTD'" + # endif // if P103_USE_RTD + # if P103_USE_FLOW + ", 'FLOW'" + # endif // if P103_USE_FLOW + ", check your i2c address?")); } addRowLabel(F("Board version")); addHtml(version); @@ -132,14 +221,18 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) addHtml(F("'); - } - else - { + } else { P103_html_red(F("Unable to send command to device")); - if (board_type == AtlasEZO_Sensors_e::UNKNOWN) - { - P103_html_red(F(" WARNING : Board type should be 'pH', 'ORP', 'EC' or 'DO', check your i2c address? ")); + if (board_type == AtlasEZO_Sensors_e::UNKNOWN) { + P103_html_red(F(" WARNING : Board type should be 'pH', 'ORP', 'EC', 'DO', 'HUM'" + # if P103_USE_RTD + ", 'RTD'" + # endif // if P103_USE_RTD + # if P103_USE_FLOW + ", 'FLOW'" + # endif // if P103_USE_FLOW + ", check your i2c address?")); } success = false; break; @@ -147,87 +240,80 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) memset(boarddata, 0, ATLAS_EZO_RETURN_ARRAY_SIZE); // Cleanup - if (P103_send_I2C_command(P103_I2C_ADDRESS, F("Status"), boarddata)) - { - String boardStatus(boarddata); + if (P103_send_I2C_command(P103_I2C_ADDRESS, F("Status"), boarddata) || P103_UNCONNECTED_SETUP) { + const String boardStatus(boarddata); - addRowLabel(F("Board restart code")); + addRowLabel(F("Board status")); + addHtml(boardStatus); - # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, boardStatus); - # endif // ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, concat(F("Board status: "), boardStatus)); + # endif // ifndef BUILD_NO_DEBUG - char *statuschar = strchr(boarddata, ','); + addRowLabel(F("Board restart code")); - if (statuschar != nullptr) - { - switch (boarddata[statuschar - boarddata + 1]) - { - case 'P': - { - addHtml(F("powered off")); - break; - } - case 'S': - { - addHtml(F("software reset")); - break; - } - case 'B': - { - addHtml(F("brown out")); - break; - } - case 'W': - { - addHtml(F("watch dog")); - break; - } - case 'U': - default: - { - addHtml(F("unknown")); - break; - } - } + const String stat = parseStringKeepCase(boardStatus, 2); + + if (!stat.isEmpty()) { + addHtml(P103_statusToString(stat[0])); } addRowLabel(F("Board voltage")); - addHtml(boardStatus.substring(boardStatus.lastIndexOf(',') + 1)); + addHtml(parseString(boardStatus, 3)); addUnit('V'); addRowLabel(F("Sensor Data")); - addHtmlFloat(UserVar[event->BaseVarIndex]); + addHtmlFloat(UserVar.getFloat(event->TaskIndex, 0)); - switch (board_type) - { + switch (board_type) { case AtlasEZO_Sensors_e::PH: - { addUnit(F("pH")); break; - } case AtlasEZO_Sensors_e::ORP: - { addUnit(F("mV")); break; - } case AtlasEZO_Sensors_e::EC: - { addUnit(F("µS")); break; - } case AtlasEZO_Sensors_e::DO: - { addUnit(F("mg/L")); break; - } + case AtlasEZO_Sensors_e::HUM: + addUnit(F("%RH")); + memset(boarddata, 0, ATLAS_EZO_RETURN_ARRAY_SIZE); // Cleanup + + if (P103_getHUMOutputOptions(event, + _HUMhasHum, + _HUMhasTemp, + _HUMhasDew)) { + if (_HUMhasTemp) { + addRowLabel(F("Temperature")); + addHtmlFloat(UserVar.getFloat(event->TaskIndex, 2)); + addUnit(F("°C")); + } + + if (_HUMhasDew) { + addRowLabel(F("Dew point")); + addHtmlFloat(UserVar.getFloat(event->TaskIndex, 3)); + addUnit(F("°C")); + } + } + break; + # if P103_USE_RTD + case AtlasEZO_Sensors_e::RTD: + addUnit(F("°C")); // TODO Read current scale (C/F/K) from device, show flow + break; + # endif // if P103_USE_RTD + # if P103_USE_FLOW + case AtlasEZO_Sensors_e::FLOW: + addUnit(F("mL/min")); + break; + # endif // if P103_USE_FLOW case AtlasEZO_Sensors_e::UNKNOWN: break; } - } - else - { - P103_html_red(F("Unable to send status command to device")); + } else { + P103_html_red(F("Unable to send Status command to device")); success = false; break; } @@ -236,35 +322,30 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) addFormCheckBox(F("Status LED"), F("status_led"), P103_STATUS_LED); // Ability to see and change EC Probe Type (e.g., 0.1, 1.0, 10) - if (board_type == AtlasEZO_Sensors_e::EC) - { + if (board_type == AtlasEZO_Sensors_e::EC) { memset(boarddata, 0, ATLAS_EZO_RETURN_ARRAY_SIZE); // Cleanup - if (P103_send_I2C_command(P103_I2C_ADDRESS, F("K,?"), boarddata)) - { - String ecProbeType(boarddata); + if (P103_send_I2C_command(P103_I2C_ADDRESS, F("K,?"), boarddata)) { + const String ecProbeType(boarddata); - addFormTextBox(F("EC Probe Type"), F("ec_probe_type"), ecProbeType.substring(ecProbeType.lastIndexOf(',') + 1), 32); + addFormTextBox(F("EC Probe Type"), F("ec_probe_type"), parseStringKeepCase(ecProbeType, 2), 32); addFormCheckBox(F("Set Probe Type"), F("en_set_probe_type"), false); } } // calibrate - switch (board_type) - { + switch (board_type) { case AtlasEZO_Sensors_e::PH: { addFormSubHeader(F("pH Calibration")); - addFormNote(F( - "Calibration for pH-Probe could be 1 (single), 2 (single, low) or 3 point (single, low, high). The sequence is important.")); + addFormNote(F("Calibration for pH-Probe could be 1 (single), 2 (single, low) or 3 point (single, low, high)." + " The sequence is important.")); const int nb_calibration_points = P103_addCreate3PointCalibration(board_type, event, P103_I2C_ADDRESS, F("pH"), 0.0, 14.0, 2, 0.01); - if (nb_calibration_points > 1) - { + if (nb_calibration_points > 1) { memset(boarddata, 0, ATLAS_EZO_RETURN_ARRAY_SIZE); // Cleanup - if (P103_send_I2C_command(P103_I2C_ADDRESS, F("Slope,?"), boarddata)) - { + if (P103_send_I2C_command(P103_I2C_ADDRESS, F("Slope,?"), boarddata)) { addFormNote(concat(F("Answer to 'Slope' command : "), String(boarddata))); } } @@ -272,53 +353,66 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) } case AtlasEZO_Sensors_e::ORP: - { addFormSubHeader(F("ORP Calibration")); P103_addCreateSinglePointCalibration(board_type, event, P103_I2C_ADDRESS, F("mV"), 0.0, 1500.0, 0, 1.0); break; - } case AtlasEZO_Sensors_e::EC: - { addFormSubHeader(F("EC Calibration")); P103_addCreateDryCalibration(); P103_addCreate3PointCalibration(board_type, event, P103_I2C_ADDRESS, F("µS"), 0.0, 500000.0, 0, 1.0); break; - } case AtlasEZO_Sensors_e::DO: - { addFormSubHeader(F("DO Calibration")); P103_addDOCalibration(P103_I2C_ADDRESS); break; - } + + case AtlasEZO_Sensors_e::HUM: // No calibration + # if P103_USE_RTD + case AtlasEZO_Sensors_e::RTD: // TODO Decide what calibration data to retrieve/store + # endif // if P103_USE_RTD + # if P103_USE_FLOW + case AtlasEZO_Sensors_e::FLOW: // TODO Size/type of flow meter, default: 1/2", Flow rate, Conversion factor, Output values: total, + // flow rate + # endif // if P103_USE_FLOW case AtlasEZO_Sensors_e::UNKNOWN: break; } - // Clear calibration - P103_addClearCalibration(); + if ((AtlasEZO_Sensors_e::PH == board_type) || + (AtlasEZO_Sensors_e::ORP == board_type) || + (AtlasEZO_Sensors_e::EC == board_type)) { + // Clear calibration option, only when using calibration + P103_addClearCalibration(); - // Temperature compensation - if ((board_type == AtlasEZO_Sensors_e::PH) || - (board_type == AtlasEZO_Sensors_e::EC) || - (board_type == AtlasEZO_Sensors_e::DO)) - { + // } + + // Temperature compensation + // if ((AtlasEZO_Sensors_e::PH == board_type) || + // (AtlasEZO_Sensors_e::ORP == board_type) || + // (AtlasEZO_Sensors_e::EC == board_type)) { ESPEASY_RULES_FLOAT_TYPE value{}; addFormSubHeader(F("Temperature compensation")); - char deviceTemperatureTemplate[40] = { 0 }; + char deviceTemperatureTemplate[40]{}; LoadCustomTaskSettings(event->TaskIndex, reinterpret_cast(&deviceTemperatureTemplate), sizeof(deviceTemperatureTemplate)); ZERO_TERMINATE(deviceTemperatureTemplate); addFormTextBox(F("Temperature "), F("_template"), deviceTemperatureTemplate, sizeof(deviceTemperatureTemplate)); - addFormNote(F("You can use a formula and idealy refer to a temp sensor (directly, via ESPEasyP2P or MQTT import)," - " e.g. '[Pool#Temperature]'. If you don't have a sensor, you could type a fixed value like '25' or '25.5'.")); + addFormNote(F("You can use a formula and ideally refer to a temp sensor" + # ifndef LIMIT_BUILD_SIZE + " (directly, via ESPEasyP2P or MQTT import)," + " e.g. '[Pool#Temperature]'. If you don't have a sensor, you could" + # else // ifndef LIMIT_BUILD_SIZE + " or" + # endif // ifndef LIMIT_BUILD_SIZE + " type a fixed value like '25' or '25.5'." + )); String deviceTemperatureTemplateString(deviceTemperatureTemplate); - String pooltempString(parseTemplate(deviceTemperatureTemplateString, 40)); + const String pooltempString(parseTemplate(deviceTemperatureTemplateString)); - if (Calculate(pooltempString, value) != CalculateReturnCode::OK) - { + if (Calculate(pooltempString, value) != CalculateReturnCode::OK) { addFormNote(F("Formula parse error. Using fixed value!")); value = P103_FIXED_TEMP_VALUE; } @@ -326,6 +420,12 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) addFormNote(strformat(F("Actual value: %.2f"), value)); } + if (AtlasEZO_Sensors_e::HUM == board_type) { + addFormSubHeader(F("HUM Options")); + addFormCheckBox(F("Enable Temperature reading"), F("hum_temp"), _HUMhasTemp); + addFormCheckBox(F("Enable Dew point reading"), F("hum_dew"), _HUMhasTemp); + } + success = true; break; } @@ -334,29 +434,20 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) { board_type = static_cast(P103_BOARD_TYPE); - P103_I2C_ADDRESS = getFormItemInt(F("i2c")); + P103_I2C_ADDRESS = getFormItemInt(F("i2c")); + P103_UNCONNECTED_SETUP = isFormItemChecked(F("uncon")) ? 1 : 0; P103_SENSOR_VERSION = getFormItemFloat(F("sensorVersion")); - char boarddata[ATLAS_EZO_RETURN_ARRAY_SIZE] = { 0 }; + P103_STATUS_LED = isFormItemChecked(F("status_led")) ? 1 : 0; - if (isFormItemChecked(F("status_led"))) - { - P103_send_I2C_command(P103_I2C_ADDRESS, F("L,1"), boarddata); - } - else - { - P103_send_I2C_command(P103_I2C_ADDRESS, F("L,0"), boarddata); - } - P103_STATUS_LED = isFormItemChecked(F("status_led")); + P103_send_I2C_command(P103_I2C_ADDRESS, concat(F("L,"), P103_STATUS_LED), boarddata); - if ((board_type == AtlasEZO_Sensors_e::EC) && isFormItemChecked(F("en_set_probe_type"))) - { + if ((board_type == AtlasEZO_Sensors_e::EC) && isFormItemChecked(F("en_set_probe_type"))) { # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("isFormItemChecked")); # endif // ifndef BUILD_NO_DEBUG - String probeType(F("K,")); - probeType += webArg(F("ec_probe_type")); + const String probeType = concat(F("K,"), webArg(F("ec_probe_type"))); memset(boarddata, 0, ATLAS_EZO_RETURN_ARRAY_SIZE); // Cleanup P103_send_I2C_command(P103_I2C_ADDRESS, probeType, boarddata); } @@ -368,60 +459,52 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) P103_CALIBRATION_LOW = getFormItemFloat(F("ref_cal_L")); P103_CALIBRATION_HIGH = getFormItemFloat(F("ref_cal_H")); - if (isFormItemChecked(F("en_cal_clear"))) - { + if (isFormItemChecked(F("en_cal_clear"))) { cmd += F("clear"); triggerCalibrate = true; - } - else if (isFormItemChecked(F("en_cal_dry"))) - { + } else if (isFormItemChecked(F("en_cal_dry"))) { cmd += F("dry"); triggerCalibrate = true; - } - else if (isFormItemChecked(F("en_cal_single"))) - { - if (board_type == AtlasEZO_Sensors_e::PH) - { + } else if (isFormItemChecked(F("en_cal_single"))) { + if (board_type == AtlasEZO_Sensors_e::PH) { cmd += F("mid,"); } cmd += P103_CALIBRATION_SINGLE; triggerCalibrate = true; - } - else if (isFormItemChecked(F("en_cal_L"))) - { + } else if (isFormItemChecked(F("en_cal_L"))) { cmd += F("low,"); cmd += P103_CALIBRATION_LOW; triggerCalibrate = true; - } - else if (isFormItemChecked(F("en_cal_H"))) - { + } else if (isFormItemChecked(F("en_cal_H"))) { cmd += F("high,"); cmd += P103_CALIBRATION_HIGH; triggerCalibrate = true; } - else if (isFormItemChecked(F("en_cal_atm"))) - { + + if (isFormItemChecked(F("en_cal_atm"))) { triggerCalibrate = true; } - else if (isFormItemChecked(F("en_cal_0"))) - { + + if (isFormItemChecked(F("en_cal_0"))) { cmd += '0'; triggerCalibrate = true; } - if (triggerCalibrate) - { + if (triggerCalibrate && + ((AtlasEZO_Sensors_e::PH == board_type) || + (AtlasEZO_Sensors_e::EC == board_type) || + (AtlasEZO_Sensors_e::DO == board_type)) + ) { memset(boarddata, 0, ATLAS_EZO_RETURN_ARRAY_SIZE); // Cleanup P103_send_I2C_command(P103_I2C_ADDRESS, cmd, boarddata); } - if ((board_type == AtlasEZO_Sensors_e::PH) || - (board_type == AtlasEZO_Sensors_e::EC) || - (board_type == AtlasEZO_Sensors_e::DO)) - { - char deviceTemperatureTemplate[40] = { 0 }; - String tmpString = webArg(F("_template")); + if ((AtlasEZO_Sensors_e::PH == board_type) || + (AtlasEZO_Sensors_e::EC == board_type) || + (AtlasEZO_Sensors_e::DO == board_type)) { + char deviceTemperatureTemplate[40]{}; + String tmpString = webArg(F("_template")); safe_strncpy(deviceTemperatureTemplate, tmpString.c_str(), sizeof(deviceTemperatureTemplate) - 1); ZERO_TERMINATE(deviceTemperatureTemplate); // be sure that our string ends with a \0 @@ -429,6 +512,25 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) sizeof(deviceTemperatureTemplate))); } + if ((AtlasEZO_Sensors_e::HUM == board_type) && P103_getHUMOutputOptions(event, + _HUMhasHum, + _HUMhasTemp, + _HUMhasDew)) { + if (!_HUMhasHum) { // If humidity not enabled, then enable it + P103_send_I2C_command(P103_I2C_ADDRESS, F("O,Hum,1"), boarddata); + } + bool _humOpt = isFormItemChecked(F("hum_temp")); + + if (_humOpt != _HUMhasTemp) { + P103_send_I2C_command(P103_I2C_ADDRESS, concat(F("O,T,"), _humOpt ? 1 : 0), boarddata); + } + _humOpt = isFormItemChecked(F("hum_dew")); + + if (_humOpt != _HUMhasDew) { + P103_send_I2C_command(P103_I2C_ADDRESS, concat(F("O,Dew,"), _humOpt ? 1 : 0), boarddata); + } + } + success = true; break; } @@ -445,41 +547,75 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) String readCommand; - if ((board_type == AtlasEZO_Sensors_e::PH) || - (board_type == AtlasEZO_Sensors_e::EC) || - (board_type == AtlasEZO_Sensors_e::DO)) + if ((AtlasEZO_Sensors_e::PH == board_type) || + (AtlasEZO_Sensors_e::EC == board_type) || + (AtlasEZO_Sensors_e::DO == board_type)) { // first set the temperature of reading - char deviceTemperatureTemplate[40] = { 0 }; + char deviceTemperatureTemplate[40]{}; LoadCustomTaskSettings(event->TaskIndex, reinterpret_cast(&deviceTemperatureTemplate), sizeof(deviceTemperatureTemplate)); ZERO_TERMINATE(deviceTemperatureTemplate); String deviceTemperatureTemplateString(deviceTemperatureTemplate); - String temperatureString(parseTemplate(deviceTemperatureTemplateString, 40)); + const String temperatureString(parseTemplate(deviceTemperatureTemplateString)); readCommand = F("RT,"); ESPEASY_RULES_FLOAT_TYPE temperatureReading{}; - if (Calculate(temperatureString, temperatureReading) != CalculateReturnCode::OK) - { + if (Calculate(temperatureString, temperatureReading) != CalculateReturnCode::OK) { temperatureReading = P103_FIXED_TEMP_VALUE; } readCommand += temperatureReading; } - else if (board_type == AtlasEZO_Sensors_e::ORP) - { + else if ((AtlasEZO_Sensors_e::ORP == board_type) || + (AtlasEZO_Sensors_e::HUM == board_type) + # if P103_USE_RTD + || (AtlasEZO_Sensors_e::RTD == board_type) + # endif // if P103_USE_RTD + # if P103_USE_FLOW + || (AtlasEZO_Sensors_e::FLOW == board_type) + # endif // if P103_USE_FLOW + ) { readCommand = F("R,"); } // ok, now we can read the sensor data - char boarddata[ATLAS_EZO_RETURN_ARRAY_SIZE] = { 0 }; + memset(boarddata, 0, ATLAS_EZO_RETURN_ARRAY_SIZE); // Cleanup UserVar.setFloat(event->TaskIndex, 0, -1); - if (P103_send_I2C_command(P103_I2C_ADDRESS, readCommand, boarddata)) - { + if (P103_send_I2C_command(P103_I2C_ADDRESS, readCommand, boarddata)) { String sensorString(boarddata); + addLog(LOG_LEVEL_INFO, concat(F("P103: READ result: "), sensorString)); + float sensor_f{}; + + if (string2float(parseString(sensorString, 1), sensor_f)) { + UserVar.setFloat(event->TaskIndex, 0, sensor_f); + } + + if (board_type == AtlasEZO_Sensors_e::HUM) { // TODO Fix reading Dew point without Temperature enabled + if (string2float(parseString(sensorString, 2), sensor_f)) { + UserVar.setFloat(event->TaskIndex, 2, sensor_f); + } + String dewVal = parseString(sensorString, 3); + + if (equals(dewVal, F("dew"))) { // Handle EZO-HUM firmware bug including 'Dew,' in the result string + dewVal = parseString(sensorString, 4); + } + + if (string2float(dewVal, sensor_f)) { + UserVar.setFloat(event->TaskIndex, 3, sensor_f); + } + } + + # if P103_USE_FLOW + + if ((board_type == AtlasEZO_Sensors_e::FLOW) && + string2float(parseString(sensorString, 2), sensor_f)) { + UserVar.setFloat(event->TaskIndex, 2, sensor_f); + } + # endif // if P103_USE_FLOW string2float(sensorString, sensor_f); UserVar.setFloat(event->TaskIndex, 0, sensor_f); } @@ -488,10 +624,9 @@ boolean Plugin_103(uint8_t function, struct EventStruct *event, String& string) memset(boarddata, 0, ATLAS_EZO_RETURN_ARRAY_SIZE); // Cleanup UserVar.setFloat(event->TaskIndex, 1, -1); - if (P103_send_I2C_command(P103_I2C_ADDRESS, F("Status"), boarddata)) - { + if (P103_send_I2C_command(P103_I2C_ADDRESS, F("Status"), boarddata)) { String voltage(boarddata); - float volt_f{}; + float volt_f{}; string2float(voltage.substring(voltage.lastIndexOf(',') + 1), volt_f); UserVar.setFloat(event->TaskIndex, 1, volt_f); } diff --git a/src/src/PluginStructs/P103_data_struct.cpp b/src/src/PluginStructs/P103_data_struct.cpp index 7635e230b9..19d95a5a88 100644 --- a/src/src/PluginStructs/P103_data_struct.cpp +++ b/src/src/PluginStructs/P103_data_struct.cpp @@ -2,9 +2,40 @@ #ifdef USES_P103 -// Call this function with two char arrays, one containing the command -// The other containing an allocatted char array for answer -// Returns true on success, false otherwise +const __FlashStringHelper* toString(AtlasEZO_Sensors_e sensor) { + switch (sensor) { + case AtlasEZO_Sensors_e::PH: return F("pH (Potential of Hydrogen)"); + case AtlasEZO_Sensors_e::ORP: return F("ORP (Oxidation Reduction Potential)"); + case AtlasEZO_Sensors_e::EC: return F("EC (Electric conductivity)"); + case AtlasEZO_Sensors_e::DO: return F("DO (Dissolved Oxigen)"); + case AtlasEZO_Sensors_e::HUM: return F("HUM (Humidity)"); + # if P103_USE_RTD + case AtlasEZO_Sensors_e::RTD: return F("RTD (Thermosensor)"); + # endif // if P103_USE_RTD + # if P103_USE_FLOW + case AtlasEZO_Sensors_e::FLOW: return F("FLOW (Flow meter)"); + # endif // if P103_USE_FLOW + case AtlasEZO_Sensors_e::UNKNOWN: break; + } + return F("Unknown"); +} + +const __FlashStringHelper* P103_statusToString(char status) { + switch (status) { + case 'P': + return F("powered off"); + case 'S': + return F("software reset"); + case 'B': + return F("brown out"); + case 'W': + return F("watch dog"); + case 'U': + default: + break; + } + return F("unknown"); +} bool P103_send_I2C_command(uint8_t I2Caddress, const String& cmd, char *sensordata) { @@ -23,6 +54,7 @@ bool P103_send_I2C_command(uint8_t I2Caddress, const String& cmd, char *sensorda } # endif // ifndef BUILD_NO_DEBUG Wire.beginTransmission(I2Caddress); + for (size_t i = 0; i < cmd.length(); ++i) { Wire.write(static_cast(cmd[i])); } @@ -30,7 +62,14 @@ bool P103_send_I2C_command(uint8_t I2Caddress, const String& cmd, char *sensorda if (error != 0) { - addLog(LOG_LEVEL_ERROR, F("Wire.endTransmission() returns error: Check Atlas shield, pH, ORP, EC and DO are supported.")); + addLog(LOG_LEVEL_ERROR, F("Wire.endTransmission() returns error: Check Atlas shield, pH, ORP, EC, DO, HUM" + # if P103_USE_RTD + ", RTD" + # endif // if P103_USE_RTD + # if P103_USE_FLOW + ", FLOW" + # endif // if P103_USE_FLOW + " are supported.")); return false; } @@ -53,7 +92,7 @@ bool P103_send_I2C_command(uint8_t I2Caddress, const String& cmd, char *sensorda in_char = Wire.read(); if (in_char == 0) - { // if we receive a null caracter, we're done + { // if we receive a null character, we're done while (Wire.available()) { // purge the data line if needed Wire.read(); @@ -63,7 +102,7 @@ bool P103_send_I2C_command(uint8_t I2Caddress, const String& cmd, char *sensorda } else { - if (sensor_bytes_received > ATLAS_EZO_RETURN_ARRAY_SIZE) + if (sensor_bytes_received >= ATLAS_EZO_RETURN_ARRAY_SIZE) { addLog(LOG_LEVEL_ERROR, F("< result array to short!")); return false; @@ -77,7 +116,6 @@ bool P103_send_I2C_command(uint8_t I2Caddress, const String& cmd, char *sensorda switch (i2c_response_code) { case 1: - { # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { @@ -85,7 +123,6 @@ bool P103_send_I2C_command(uint8_t I2Caddress, const String& cmd, char *sensorda } # endif // ifndef BUILD_NO_DEBUG break; - } case 2: # ifndef BUILD_NO_DEBUG @@ -112,8 +149,8 @@ bool P103_send_I2C_command(uint8_t I2Caddress, const String& cmd, char *sensorda int P103_getCalibrationPoints(uint8_t i2cAddress) { - int nb_calibration_points = -1; - char sensordata[ATLAS_EZO_RETURN_ARRAY_SIZE] = { 0 }; + int nb_calibration_points = -1; + char sensordata[ATLAS_EZO_RETURN_ARRAY_SIZE]{}; if (P103_send_I2C_command(i2cAddress, F("Cal,?"), sensordata)) { @@ -152,25 +189,29 @@ void P103_html_green(const String& message) { void P103_addDisabler() { // disabler(): argument(s) on 'true' will set that checkbox to false! non-boolean ignores argument // addHtml(F("\n")); // Minified: addHtml(F("\n")); } @@ -178,7 +219,7 @@ void P103_addClearCalibration() { addRowLabel(F("Clear calibration")); addFormCheckBox(F("Clear"), F("en_cal_clear"), false); - addHtml(F("\n")); + addHtml(F("\n")); addFormNote(F("Attention! This will reset all calibrated data. New calibration will be needed!!!")); } @@ -196,11 +237,11 @@ int P103_addDOCalibration(uint8_t I2Cchoice) addRowLabel(F("Calibrate to atmospheric oxygen levels")); addFormCheckBox(F("Enable"), F("en_cal_atm"), false); - addHtml(F("\n")); + addHtml(F("\n")); addRowLabel(F("Calibrate device to 0 dissolved oxygen")); addFormCheckBox(F("Enable"), F("en_cal_0"), false); - addHtml(F("\n")); + addHtml(F("\n")); if (nb_calibration_points > 0) { @@ -218,7 +259,7 @@ void P103_addCreateDryCalibration() { addRowLabel(F("Dry calibration")); addFormCheckBox(F("Enable"), F("en_cal_dry"), false); - addHtml(F("\n")); + addHtml(F("\n")); addFormNote(F("Dry calibration must always be done first!")); addFormNote(F("Calibration for pH-Probe could be 1 (single) or 2 point (low, high).")); } @@ -263,7 +304,7 @@ int P103_addCreateSinglePointCalibration(AtlasEZO_Sensors_e board_type, } } addFormCheckBox(F("Enable"), F("en_cal_single"), false); - addHtml(F("\n")); + addHtml(F("\n")); return nb_calibration_points; } @@ -292,7 +333,7 @@ int P103_addCreate3PointCalibration(AtlasEZO_Sensors_e board_type, P103_html_orange(F("Not yet calibrated")); } addFormCheckBox(F("Enable"), F("en_cal_L"), false); - addHtml(F("\n")); + addHtml(F("\n")); addRowLabel(F("High calibration")); addFormFloatNumberBox(F("Ref high point"), F("ref_cal_H"), P103_CALIBRATION_HIGH, min, max, nrDecimals, stepsize); @@ -308,9 +349,39 @@ int P103_addCreate3PointCalibration(AtlasEZO_Sensors_e board_type, P103_html_orange(F("Not yet calibrated")); } addFormCheckBox(F("Enable"), F("en_cal_H"), false); - addHtml(F("\n")); + addHtml(F("\n")); return nb_calibration_points; } +bool P103_getHUMOutputOptions(struct EventStruct *event, + bool & _HUMhasHum, + bool & _HUMhasTemp, + bool & _HUMhasDew) { + bool result = false; + + char boarddata[ATLAS_EZO_RETURN_ARRAY_SIZE]{}; + + if ((result = P103_send_I2C_command(P103_I2C_ADDRESS, F("O,?"), boarddata))) { + String outputs(boarddata); + int o = 2; + String outPar = parseString(outputs, o); + + while (!outPar.isEmpty()) { + if (equals(outPar, F("hum"))) { + _HUMhasHum = true; + } else + if (equals(outPar, F("t"))) { + _HUMhasTemp = true; + } else + if (equals(outPar, F("dew"))) { + _HUMhasDew = true; + } + o++; + outPar = parseString(outputs, o); + } + } + return result; +} + #endif // ifdef USES_P103 diff --git a/src/src/PluginStructs/P103_data_struct.h b/src/src/PluginStructs/P103_data_struct.h index f615e38964..5dd82a4d64 100644 --- a/src/src/PluginStructs/P103_data_struct.h +++ b/src/src/PluginStructs/P103_data_struct.h @@ -8,17 +8,40 @@ # include "../Globals/RulesCalculate.h" +# define P103_USE_RTD 0 // Defaults to UART, so disabled for now +# define P103_USE_FLOW 0 // Defaults to UART, so disabled for now + +// # ifdef PLUGIN_SET_MAX // Enable RTD and FLOW for MAX builds +// # if !P103_USE_RTD +// # undef P103_USE_RTD +// # define P103_USE_RTD 1 +// # endif // if !P103_USE_RTD +// # if !P103_USE_FLOW +// # undef P103_USE_FLOW +// # define P103_USE_FLOW 1 +// # endif // if !P103_USE_FLOW +// # endif // ifdef PLUGIN_SET_MAX + enum class AtlasEZO_Sensors_e : uint8_t { UNKNOWN = 0u, PH = 1u, ORP = 2u, EC = 3u, DO = 4u, + HUM = 5u, + # if P103_USE_RTD + RTD = 6u, // Defaults to UART, so disabled for now + # endif // if P103_USE_RTD + # if P103_USE_FLOW + FLOW = 7u, // Defaults to UART, so disabled for now + # endif // if P103_USE_FLOW }; # define P103_BOARD_TYPE PCONFIG(0) # define P103_I2C_ADDRESS PCONFIG(1) # define P103_STATUS_LED PCONFIG(2) +# define P103_NR_OUTPUT_VALUES PCONFIG(3) +# define P103_UNCONNECTED_SETUP PCONFIG(4) # define P103_SENSOR_VERSION PCONFIG_FLOAT(0) # define P103_CALIBRATION_SINGLE PCONFIG_FLOAT(1) # define P103_CALIBRATION_LOW PCONFIG_FLOAT(2) @@ -26,13 +49,15 @@ enum class AtlasEZO_Sensors_e : uint8_t { # define ATLAS_EZO_RETURN_ARRAY_SIZE 33 // Max expected result 32 bytes + \0 -# define P103_ATLASEZO_I2C_NB_OPTIONS 4 // was: 6 see comment below at 'const int i2cAddressValues' - # define P103_FIXED_TEMP_VALUE 20 // Temperature correction for pH and EC sensor if no temperature is given from calculation -bool P103_send_I2C_command(uint8_t I2Caddress, - const String& cmd, - char *sensordata); // Forward declarations +// Forward declarations +const __FlashStringHelper* toString(AtlasEZO_Sensors_e sensor); +const __FlashStringHelper* P103_statusToString(char status); + +bool P103_send_I2C_command(uint8_t I2Caddress, + const String& cmd, + char *sensordata); void P103_addDisabler(); void P103_html_color_message(const __FlashStringHelper *color, @@ -60,6 +85,10 @@ int P103_addCreate3PointCalibration(AtlasEZO_Sensors_e board_type, float max, uint8_t nrDecimals, float stepsize); +bool P103_getHUMOutputOptions(struct EventStruct *event, + bool & _HUMhasHum, + bool & _HUMhasTemp, + bool & _HUMhasDew); #endif // ifdef USED_P103 #endif // ifndef PLUGINSTRUCTS_P103_DATA_STRUCT_H