diff --git a/CHANGELOG.md b/CHANGELOG.md index fd5d56543..ba2c480bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file. - Scripter array transfer via UFS (#24060) - ESP8266 GPIOViewer memory map if enabled with `#define GV_USE_ESPINFO` - Berry `tcp.write()` add `offset` and `len` (#24076) +- NeoPool command `NPReadLSB`, `NPReadMSB`, `NPWriteLSB`, `NWriteMSB` for directly read/write LSB/MSB of 16-bit register ### Breaking Changed diff --git a/tasmota/tasmota_xsns_sensor/xsns_83_neopool.ino b/tasmota/tasmota_xsns_sensor/xsns_83_neopool.ino index f51bfa134..49014b7a9 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_83_neopool.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_83_neopool.ino @@ -1,7 +1,7 @@ /* xsns_83_neopool.ino - Sugar Valley NeoPool Control System Modbus support for Tasmota - Copyright (C) 2023 Norbert Richter + Copyright (C) 2025 Norbert Richter 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 @@ -50,7 +50,6 @@ * Only one Modbus client (master) can be connected to the Modbus connector * of the same name. It is not possible to operate several clients on * connectors with the same name. - * * The differently labelled Modbus connectors are completely independent * physical Modbus interfaces. Data traffic on one of the connector is * invisible on the other connectors. One exception is the DISPLAY connector, @@ -58,9 +57,7 @@ * Since only one Modbus client can operate one Modbus server at a time, the * DISPLAY connector is useless for our purposes as long as the internal LCD * is connected to one of the two DISPLAY connectors at the same time. - * - * Conclusion: - * Use the WIFI or EXTERNAL connector only. + * Conclusion: Use the WIFI or EXTERNAL connector only. \****************************************************************************/ #define XSNS_83 83 @@ -290,7 +287,7 @@ enum NeoPoolRegister { // Contains the configuration parameters for the screen controllers (language, colours, sound, etc). MBF_PAR_UICFG_MACHINE = 0x0600, // 0x0600* Machine type (see MBV_PAR_MACH_* and kNeoPoolMachineNames[]) MBF_PAR_UICFG_LANGUAGE, // 0x0601* Selected language (see MBV_PAR_LANG_*) - MBF_PAR_UICFG_BACKLIGHT, // 0x0602* Display backlight function (see MBV_PAR_BACKLIGHT_*) + MBF_PAR_UICFG_BACKLIGHT, // 0x0602* Display backlight brightness (in %, upper part (8-bit MSB)=0-100) and function (lower part 8-bit LSB, see MBV_PAR_BACKLIGHT_*) MBF_PAR_UICFG_SOUND, // 0x0603* mask Audible alerts (see MBMSK_PAR_SOUND_*) MBF_PAR_UICFG_PASSWORD, // 0x0604* System password encoded in BCD MBF_PAR_UICFG_VISUAL_OPTIONS, // 0x0605* mask Stores the different display options for the user interface menus (bitmask). Some bits allow you to hide options that are normally visible (bits 0 to 3) while other bits allow you to show options that are normally hidden (bits 9 to 15) @@ -517,7 +514,7 @@ enum NeoPoolConstAndBitMask { MBV_PAR_LANG_HUNGARIAN = 10, MBV_PAR_LANG_RUSSIAN = 11, - // MBF_PAR_UICFG_BACKLIGHT + // MBF_PAR_UICFG_BACKLIGHT (LSB) MBV_PAR_BACKLIGHT_15SEC = 0, // Backlight off after 15 sec MBV_PAR_BACKLIGHT_30SEC = 1, // Backlight off after 30 sec MBV_PAR_BACKLIGHT_60SEC = 2, // Backlight off after 60 sec @@ -1174,15 +1171,23 @@ const char HTTP_SNS_NEOPOOL_STATUS_ACTIVE[] PROGMEM = "filter:invert(1)"; * * NPRead {} * NPReadL {} - * read 16/32-bit register (cnt = 1..30|1..15), cnt = 1 if omitted - * NPRead read 16-bit register - * NPReadL read 32-bit register + * NPReadLSB {} + * NPReadMSB {} + * read 16|32-bit register (cnt = 1..30|1..15), cnt = 1 if omitted + * NPRead read 16-bit register + * NPReadL read 32-bit register + * NPReadLSB read 16-bit register LSB only (like NPRead, but only reads the least significant byte (LSB) of 16-bit register) + * NPReadMSB read 16-bit register MSB only (like NPRead, but only reads the most significant byte (MSB) of 16-bit register) * * NPWrite {...} * NPWriteL {...} - * NPWrite write 16-bit register (data = 0..65535) - * NPWriteL write 32-bit register (data = 0..4294967295) - * The max. number of parameters is 10 + * NPWriteLSB {...} + * NPWriteMSB {...} + * write 16|32-bit register (the max. number of parameters is 20|10) + * NPWrite write 16-bit register (data = 0..65535) + * NPWriteL write 32-bit register (data = 0..4294967295) + * NPWriteLSB write 16-bit register LSB only (data = 0..255, like NPWrite, but only writes the least significant byte (LSB) of 16-bit register) + * NPWriteMSB write 16-bit register MSB only (data = 0..255, like NPWrite, but only writes the most significant byte (LSB) of 16-bit register) * * NPBit {} * NPBitL {} @@ -1253,8 +1258,12 @@ const char HTTP_SNS_NEOPOOL_STATUS_ACTIVE[] PROGMEM = "filter:invert(1)"; #define D_CMND_NP_RESULT "Result" #define D_CMND_NP_READ "Read" #define D_CMND_NP_READL "ReadL" +#define D_CMND_NP_READLSB D_CMND_NP_READ "LSB" +#define D_CMND_NP_READMSB D_CMND_NP_READ "MSB" #define D_CMND_NP_WRITE "Write" #define D_CMND_NP_WRITEL "WriteL" +#define D_CMND_NP_WRITELSB D_CMND_NP_WRITE "LSB" +#define D_CMND_NP_WRITEMSB D_CMND_NP_WRITE "MSB" #define D_CMND_NP_BIT "Bit" #define D_CMND_NP_BITL "BitL" #define D_CMND_NP_FILTRATION "Filtration" @@ -1289,8 +1298,12 @@ const char kNPCommands[] PROGMEM = D_PRFX_NEOPOOL "|" // Prefix D_CMND_NP_RESULT "|" D_CMND_NP_READ "|" D_CMND_NP_READL "|" + D_CMND_NP_READLSB "|" + D_CMND_NP_READMSB "|" D_CMND_NP_WRITE "|" D_CMND_NP_WRITEL "|" + D_CMND_NP_WRITELSB "|" + D_CMND_NP_WRITEMSB "|" D_CMND_NP_BIT "|" D_CMND_NP_BITL "|" D_CMND_NP_FILTRATION "|" @@ -1326,6 +1339,10 @@ void (* const NPCommand[])(void) PROGMEM = { &CmndNeopoolResult, &CmndNeopoolReadReg, &CmndNeopoolReadReg, + &CmndNeopoolReadReg, + &CmndNeopoolReadReg, + &CmndNeopoolWriteReg, + &CmndNeopoolWriteReg, &CmndNeopoolWriteReg, &CmndNeopoolWriteReg, &CmndNeopoolBit, @@ -2520,7 +2537,7 @@ void NeoPoolShow(bool json) * Command implementation \****************************************************************************/ -void NeopoolReadWriteResponse(uint16_t addr, uint16_t *data, uint16_t cnt, bool fbits32, int16_t bit) +void NeopoolReadWriteResponse(uint16_t addr, uint16_t *data, uint16_t cnt, bool fbits32, bool flsb, bool fmsb, int16_t bit) { const char *data_fmt; uint32_t ldata; @@ -2531,27 +2548,32 @@ void NeopoolReadWriteResponse(uint16_t addr, uint16_t *data, uint16_t cnt, bool data_fmt = PSTR("%ld"); if (NEOPOOL_RESULT_HEX == NeoPoolSettings.result) { - data_fmt = fbits32 ? PSTR("\"0x%08X\"") : PSTR("\"0x%04X\""); + data_fmt = fbits32 ? PSTR("\"0x%08X\"") : (!flsb && !fmsb) ? PSTR("\"0x%04X\"") : PSTR("\"0x%02X\""); } - ldata = (uint32_t)data[0]; - if (fbits32) { - ldata |= (uint32_t)data[1] << 16; + char sdel[2] = {0}; + if ( cnt > 1 ) { + ResponseAppend_P(PSTR("[")); + } + for(uint16_t i=0; i 1 ) { + ResponseAppend_P(PSTR("%s"), sdel); + } + ldata = (uint32_t)data[(fbits32+1)*i]; + if (fbits32) { + ldata |= (uint32_t)data[(fbits32+1)*i+1] << 16; + } + else if (flsb) { + ldata &= 0xff; + } + else if (fmsb) { + ldata >>= 8; + ldata &= 0xff; + } + ResponseAppend_P(data_fmt, ldata); + *sdel = ','; } if ( cnt > 1 ) { - char sdel[2] = {0}; - ResponseAppend_P(PSTR("[")); - for(uint16_t i=0; i= 0) { ResponseAppend_P(PSTR(",\"" D_NEOPOOL_JSON_BIT "%d\":%ld"), bit, (ldata>>bit) & 1); @@ -2587,6 +2609,8 @@ void CmndNeopoolReadReg(void) uint32_t value[2] = { 0 }; uint32_t params_cnt = ParseParameters(nitems(value), value); bool fbits32 = !strcasecmp_P(XdrvMailbox.command, PSTR(D_PRFX_NEOPOOL D_CMND_NP_READL)); + bool flsb = !strcasecmp_P(XdrvMailbox.command, PSTR(D_PRFX_NEOPOOL D_CMND_NP_READLSB)); + bool fmsb = !strcasecmp_P(XdrvMailbox.command, PSTR(D_PRFX_NEOPOOL D_CMND_NP_READMSB)); cnt = 1; if (2 == params_cnt) { @@ -2599,7 +2623,7 @@ void CmndNeopoolReadReg(void) return; } } - NeopoolReadWriteResponse(addr, data, cnt, fbits32, -1); + NeopoolReadWriteResponse(addr, data, cnt, fbits32, flsb, fmsb, -1); } @@ -2609,16 +2633,34 @@ void CmndNeopoolWriteReg(void) uint32_t value[(nitems(data)/2)+1] = { 0 }; uint32_t params_cnt = ParseParameters(nitems(value), value); bool fbits32 = !strcasecmp_P(XdrvMailbox.command, PSTR(D_PRFX_NEOPOOL D_CMND_NP_WRITEL)); + bool flsb = !strcasecmp_P(XdrvMailbox.command, PSTR(D_PRFX_NEOPOOL D_CMND_NP_WRITELSB)); + bool fmsb = !strcasecmp_P(XdrvMailbox.command, PSTR(D_PRFX_NEOPOOL D_CMND_NP_WRITEMSB)); - if (params_cnt > 1) { + cnt = params_cnt-1; + if (params_cnt > 1 && cnt < (fbits32 ? (nitems(data)/2) : nitems(data))) { addr = value[0]; - cnt = params_cnt-1; + if (flsb || fmsb) { + if (NEOPOOL_MODBUS_OK != NeoPoolReadRegister(addr, data, fbits32 ? (cnt*2) : cnt)) { + NeopoolResponseError(); + return; + } + } for (uint32_t i = 0; i < cnt; i++) { if (fbits32) { data[i*2] = value[i+1]; // LSB data[i*2+1] = value[i+1]>>16; // MSB } else { - data[i] = value[i+1]; + if (flsb) { + data[i] &= 0xff00; + data[i] |= (value[i+1] & 0xff); + } + else if (fmsb) { + data[i] &= 0x00ff; + data[i] |= (value[i+1] & 0xff)<<8; + } + else { + data[i] = value[i+1]; + } } } if (NEOPOOL_MODBUS_OK != NeoPoolWriteRegister(addr, data, fbits32 ? cnt*2 : cnt)) { @@ -2630,7 +2672,7 @@ void CmndNeopoolWriteReg(void) NeopoolResponseError(); return; } - NeopoolReadWriteResponse(addr, data, cnt, fbits32, -1); + NeopoolReadWriteResponse(addr, data, cnt, fbits32, flsb, fmsb, -1); } @@ -2674,7 +2716,7 @@ void CmndNeopoolBit(void) NeopoolResponseError(); return; } - NeopoolReadWriteResponse(addr, &data, 1, fbits32, bit); + NeopoolReadWriteResponse(addr, &data, 1, fbits32, false, false, bit); return; }