Tasmota/tasmota/tasmota_xsns_sensor/xsns_105_lox_o2.ino

160 lines
4.8 KiB
C++

/*
xsns_105_lox_o2.ino - Support for LuminOx Sealed Optical Oxygen Sensor on Tasmota
Copyright (C) 2023 Anton ACE Elizarov
https://github.com/ACE1046
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_LOX_O2
/*********************************************************************************************\
* LuminOx Sealed Optical Oxygen Sensor LOX-02-S
*
* LuminOx requires no additional signal conditioning circuitry and connects
* directly to the interfacing microcontroller via a 3.3V-level USART link
* All USART communication is preformed using ascii characters
* By default, stream mode is initiated on sensor power-up and will supply an output string
* approximately once every second. This provides the data for ppO2, Temperature, Pressure, O2 and
* sensor status. Format is fixed, shown below:
* O xxxx.x T yxx.x P xxxx % xxx.xx e xxxx\r\n
* i.e.
* O 0198.5 T +21.7 P 0983 % 020.19 e 0000\r\n
\*********************************************************************************************/
#define XSNS_105 105
#define LOX_O2_BAUDRATE 9600
#include <TasmotaSerial.h>
TasmotaSerial *LOXSerial = nullptr;
#define RESPONSE_LEN (sizeof("O 0198.5 T +21.7 P 0983 % 020.19 e 0000\r\n") - 1) // not including terminating zero
struct LOX_O2
{
float ppO2 = 0.0;
float temperature = 0.0;
uint32_t pressure = 0;
float O2 = 0.0;
uint32 error = 0;
} *lox_o2 = nullptr;
/********************************************************************************************/
void LOXInit()
{
if (PinUsed(GPIO_LOX_O2_RX))
{
lox_o2 = (LOX_O2 *)calloc(1, sizeof(struct LOX_O2));
LOXSerial = new TasmotaSerial(Pin(GPIO_LOX_O2_RX), -1, 1);
if (LOXSerial->begin(LOX_O2_BAUDRATE))
{
if (LOXSerial->hardwareSerial())
ClaimSerial();
}
#ifdef ESP32
AddLog(LOG_LEVEL_DEBUG, PSTR("LOX: Serial UART%d"), LOXSerial->getUart());
#endif
}
}
void LOXParse(uint8_t *buf)
{
if (!lox_o2) return;
// O 0198.5 T +21.7 P 0983 % 020.19 e 0000
if (buf[0] != 'O' || buf[9] != 'T' || buf[17] != 'P' || buf[24] != '%' || buf[33] != 'e') return; // check for valid response
lox_o2->pressure = strtoul((char *)buf+19, nullptr, 10);
lox_o2->ppO2 = CharToFloat((char *)buf+2);
lox_o2->temperature = CharToFloat((char *)buf+11);
lox_o2->O2 = CharToFloat((char *)buf+26);
lox_o2->error = strtoul((char *)buf+35, nullptr, 10);
}
void LOXJson()
{
if (!lox_o2) return;
if (lox_o2->pressure > 0 && lox_o2->error == 0)
{
float temperature = ConvertTemp(lox_o2->temperature);
ResponseAppend_P(PSTR(",\"LOX\":{\"" D_JSON_PRESSURE "\":%i,\"ppO2\":%1_f,\"" D_JSON_TEMPERATURE "\":%1_f,\"" D_JSON_O2 "\":%2_f"),
lox_o2->pressure, &lox_o2->ppO2, &temperature, &lox_o2->O2);
ResponseJsonEnd();
}
}
void LOXRead()
{
uint8_t buf[RESPONSE_LEN+1];
uint32_t in_buf = 0;
if (!LOXSerial || !lox_o2) return;
buf[RESPONSE_LEN] = 0;
while (LOXSerial->available() >= RESPONSE_LEN)
{
in_buf = 0;
while (LOXSerial->available() && in_buf < RESPONSE_LEN)
{
char c = LOXSerial->read();
buf[in_buf++] = c;
if (c == '\n') break;
}
if (in_buf == RESPONSE_LEN) LOXParse(buf);
}
}
#ifdef USE_WEBSERVER
const char types[] = "LOX";
void LOXShow(void)
{
if (!LOXSerial || !lox_o2) return;
//AddLog(LOG_LEVEL_DEBUG, PSTR("LOX: %s"), value);
WSContentSend_PD(PSTR("{s}%s " D_PRESSURE "{m} %i " D_UNIT_PRESSURE "{e}"), types, lox_o2->pressure);
WSContentSend_PD(PSTR("{s}%s ppO2{m} %1_f " D_UNIT_PRESSURE "{e}"), types, &lox_o2->ppO2);
WSContentSend_Temp(types, ConvertTemp(lox_o2->temperature));
WSContentSend_PD(PSTR("{s}%s " D_O2 "{m} %2_f %%{e}"), types, &lox_o2->O2);
}
#endif // USE_WEBSERVER
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xsns105(uint32_t function) {
bool result = false;
switch (function) {
case FUNC_INIT:
LOXInit();
break;
case FUNC_EVERY_SECOND:
LOXRead();
break;
case FUNC_JSON_APPEND:
LOXJson();
break;
#ifdef USE_WEBSERVER
case FUNC_WEB_SENSOR:
LOXShow();
break;
#endif // USE_WEBSERVER
}
return result;
}
#endif // USE_LOX