diff --git a/drivers.xml b/drivers.xml
index 22471bcfef..7a1f10f910 100644
--- a/drivers.xml
+++ b/drivers.xml
@@ -749,6 +749,10 @@
indi_wandererbox_pro_v3
1.0
+
+ indi_wandererbox_plus_v3
+ 1.0
+
diff --git a/drivers/auxiliary/CMakeLists.txt b/drivers/auxiliary/CMakeLists.txt
index b68e845325..6bf1480ad9 100644
--- a/drivers/auxiliary/CMakeLists.txt
+++ b/drivers/auxiliary/CMakeLists.txt
@@ -1,6 +1,14 @@
IF(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
INSTALL(FILES 99-indi_auxiliary.rules DESTINATION ${UDEVRULES_INSTALL_DIR})
ENDIF()
+# ########## Wanderer Box Plus V3 ###############
+SET(indi_wandererbox_plus_v3_SRC
+ wandererbox_plus_v3.cpp)
+
+add_executable(indi_wandererbox_plus_v3 ${indi_wandererbox_plus_v3_SRC})
+target_link_libraries(indi_wandererbox_plus_v3 indidriver)
+install(TARGETS indi_wandererbox_plus_v3 RUNTIME DESTINATION bin)
+
# ########## Wanderer Box Pro V3 ###############
SET(indi_wandererbox_pro_v3_SRC
wandererbox_pro_v3.cpp)
diff --git a/drivers/auxiliary/wandererbox_plus_v3.cpp b/drivers/auxiliary/wandererbox_plus_v3.cpp
new file mode 100644
index 0000000000..0cc1ba1e9f
--- /dev/null
+++ b/drivers/auxiliary/wandererbox_plus_v3.cpp
@@ -0,0 +1,686 @@
+/*******************************************************************************
+ Copyright(c) 2024 Frank Wang. All rights reserved.
+
+ WandererBox Plus V3
+
+ 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 2 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 Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+*******************************************************************************/
+
+#include "wandererbox_plus_v3.h"
+#include "indicom.h"
+#include "connectionplugins/connectionserial.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// We declare an auto pointer to WandererBoxPlusV3.
+static std::unique_ptr WandererBoxplusv3(new WandererBoxPlusV3());
+
+
+
+WandererBoxPlusV3::WandererBoxPlusV3()
+{
+ setVersion(1, 0);
+}
+
+bool WandererBoxPlusV3::initProperties()
+{
+
+ INDI::DefaultDevice::initProperties();
+ setDriverInterface(AUX_INTERFACE);
+
+
+ addAuxControls();
+
+
+ // Calibrate
+ CalibrateSP[0].fill("Calibrate", "Calibrate Current", ISS_OFF);
+ CalibrateSP.fill(getDeviceName(), "Calibrate_DEVICE", "Calibrate Current", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1,60, IPS_IDLE);
+
+ // Power Monitor
+ PowerMonitorNP[VOLTAGE].fill("VOLTAGE", "Voltage (V)", "%4.2f", 0, 999, 100, 0);
+ PowerMonitorNP[TOTAL_CURRENT].fill("TOTAL_CURRENT", "Total Current (A)", "%4.2f", 0, 999, 100, 0);
+ PowerMonitorNP.fill(getDeviceName(), "POWER_Monitor", "Power Monitor", MAIN_CONTROL_TAB, IP_RO,60, IPS_IDLE);
+
+
+ // USB Control
+ USBControlSP[INDI_ENABLED].fill("INDI_ENABLED", "On", ISS_OFF);
+ USBControlSP[INDI_DISABLED].fill("INDI_DISABLED", "Off", ISS_OFF);
+ USBControlSP.fill(getDeviceName(), "USB", "USB", MAIN_CONTROL_TAB, IP_RW,ISR_1OFMANY, 60, IPS_IDLE);
+
+
+ // DC3
+ DC3ControlNP[DC3].fill( "DC3", "Dew Heater (PWM)", "%.2f", 0, 255, 5, 0);
+ DC3ControlNP.fill(getDeviceName(), "PWM", "DC3", DC3_TAB, IP_RW, 60, IPS_IDLE);
+
+ // DC2SET
+ setDC2voltageNP[setDC2voltage].fill( "DC2SET", "Adjustable Voltage", "%.2f", 5, 13.2, 0.1, 0);
+ setDC2voltageNP.fill(getDeviceName(), "DC2voltageSET", "Set DC2", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE);
+ // DC2 Control
+ dc2ControlSP[INDI_ENABLED].fill( "INDI_ENABLED", "On", ISS_OFF);
+ dc2ControlSP[INDI_DISABLED].fill( "INDI_DISABLED", "Off", ISS_ON);
+ dc2ControlSP.fill(getDeviceName(), "DC2", "DC2", MAIN_CONTROL_TAB, IP_RW,ISR_1OFMANY, 60, IPS_IDLE);
+
+ // DC4-6 Control
+ DC4_6ControlSP[INDI_ENABLED].fill( "INDI_ENABLED", "On", ISS_OFF);
+ DC4_6ControlSP[INDI_DISABLED].fill( "INDI_DISABLED", "Off", ISS_ON);
+ DC4_6ControlSP.fill(getDeviceName(), "DC4-6", "DC4-6", MAIN_CONTROL_TAB, IP_RW,ISR_1OFMANY, 60, IPS_IDLE);
+
+ // DC3 TEMP Difference Control
+ DC3diffSP[DC3_Manual].fill( "Manual", "Manual", ISS_ON);
+ DC3diffSP[DC3_DPD_Mode].fill( "DPD_Mode", "DPD Mode", ISS_OFF);
+ DC3diffSP[DC3_CT_Mode].fill( "CT_Mode", "CT Mode", ISS_OFF);
+ DC3diffSP.fill(getDeviceName(), "DC3_DIFF", "DC3 Dew Mode", DC3_TAB, IP_RW,ISR_1OFMANY, 60, IPS_IDLE);
+
+ DC3diffSETNP[DC3DIFFSET].fill( "DC3 Auto Control", "Dew Point Difference(C)", "%.2f", 10, 30, 1, 0);
+ DC3diffSETNP.fill(getDeviceName(), "DC3_DIFF_SET", "DPD Mode", DC3_TAB, IP_RW, 60, IPS_IDLE);
+
+ DC3constSETNP[DC3CONSTSET].fill( "DC3 Auto Control", "Temperature(C)", "%.2f", 0, 40, 1, 0);
+ DC3constSETNP.fill(getDeviceName(), "DC3_CONST_SET", "CT Mode", DC3_TAB, IP_RW, 60, IPS_IDLE);
+
+ //ENV
+ ENVMonitorNP[Probe1_Temp].fill( "Probe1_Temp", "Probe1 Temperature (C)", "%4.2f", 0, 999, 100, 0);
+ ENVMonitorNP[ENV_Humidity].fill( "ENV_Humidity", "Ambient Humidity %", "%4.2f", 0, 999, 100, 0);
+ ENVMonitorNP[ENV_Temp].fill( "ENV_Temp", "Ambient Temperature (C)", "%4.2f", 0, 999, 100, 0);
+ ENVMonitorNP[DEW_Point].fill( "DEW_Point", "Dew Point (C)", "%4.2f", 0, 999, 100, 0);
+ ENVMonitorNP.fill(getDeviceName(), "ENV_Monitor", "Environment",ENVIRONMENT_TAB, IP_RO,60, IPS_IDLE);
+ serialConnection = new Connection::Serial(this);
+ serialConnection->setDefaultBaudRate(Connection::Serial::B_19200);
+ serialConnection->registerHandshake([&]()
+ {
+ return getData();
+ });
+ registerConnection(serialConnection);
+
+ return true;
+}
+
+bool WandererBoxPlusV3::getData()
+{
+ try
+ {
+ PortFD = serialConnection->getPortFD();
+ tcflush(PortFD, TCIOFLUSH);
+ int nbytes_read_name = 0,rc=-1;
+ char name[64] = {0};
+
+ //Device Model//////////////////////////////////////////////////////////////////////////////////////////////////////
+ if ((rc = tty_read_section(PortFD, name, 'A', 3, &nbytes_read_name)) != TTY_OK)
+ {
+ char errorMessage[MAXRBUF];
+ tty_error_msg(rc, errorMessage, MAXRBUF);
+ LOGF_INFO("No data received, the device may not be WandererBox Plus V3, please check the serial port!","Updated");
+ LOGF_ERROR("Device read error: %s", errorMessage);
+ return false;
+ }
+ name[nbytes_read_name - 1] = '\0';
+ if(strcmp(name, "ZXWBProV3")==0||strcmp(name, "WandererCoverV4")==0||strcmp(name, "UltimateV2")==0||strcmp(name, "PlusV2")==0)
+ {
+ LOGF_INFO("The device is not WandererBox Plus V3!","Updated");
+ return false;
+ }
+ if(strcmp(name, "ZXWBPlusV3")!=0)
+ throw std::exception();
+ // Frimware version/////////////////////////////////////////////////////////////////////////////////////////////
+ int nbytes_read_version = 0;
+ char version[64] = {0};
+ tty_read_section(PortFD, version, 'A', 5, &nbytes_read_version);
+
+ version[nbytes_read_version - 1] = '\0';
+ firmware=std::atoi(version);
+
+
+ // Temp probe 1//////////////////////////////////////////////////////////////////////////////////////////
+ char temp1[64] = {0};
+ int nbytes_read_temp1= 0;
+ tty_read_section(PortFD, temp1, 'A', 5, &nbytes_read_temp1);
+ temp1[nbytes_read_temp1 - 1] = '\0';
+ temp1read = std::strtod(temp1,NULL);
+
+ // DHTH//////////////////////////////////////////////////////////////////////////////////////////
+ char DHTH[64] = {0};
+ int nbytes_read_DHTH= 0;
+ tty_read_section(PortFD, DHTH, 'A', 5, &nbytes_read_DHTH);
+ DHTH[nbytes_read_DHTH - 1] = '\0';
+ DHTHread = std::strtod(DHTH,NULL);
+
+ // DHTT//////////////////////////////////////////////////////////////////////////////////////////
+ char DHTT[64] = {0};
+ int nbytes_read_DHTT= 0;
+ tty_read_section(PortFD, DHTT, 'A', 5, &nbytes_read_DHTT);
+ DHTT[nbytes_read_DHTT - 1] = '\0';
+ DHTTread = std::strtod(DHTT,NULL);
+ updateENV(temp1read,DHTHread,DHTTread);
+
+ // Total current//////////////////////////////////////////////////////////////////////////////////////////
+ char Tcurrent[64] = {0};
+ int nbytes_read_Tcurrent= 0;
+ tty_read_section(PortFD, Tcurrent, 'A', 5, &nbytes_read_Tcurrent);
+ Tcurrent[nbytes_read_Tcurrent - 1] = '\0';
+ Tcurrentread = std::strtod(Tcurrent,NULL);
+
+ // Voltage//////////////////////////////////////////////////////////////////////////////////////////
+ char voltage[64] = {0};
+ int nbytes_read_voltage= 0;
+ tty_read_section(PortFD, voltage, 'A', 5, &nbytes_read_voltage);
+ voltage[nbytes_read_voltage - 1] = '\0';
+ voltageread = std::strtod(voltage,NULL);
+ updatePower(Tcurrentread,voltageread);
+
+ // USB//////////////////////////////////////////////////////////////////////////////////////////
+ char USB[64] = {0};
+ int nbytes_read_USB= 0;
+ tty_read_section(PortFD, USB, 'A', 5, &nbytes_read_USB);
+ USB[nbytes_read_USB - 1] = '\0';
+ USBread = std::stoi(USB);
+ updateUSB(USBread);
+
+
+ // DC2//////////////////////////////////////////////////////////////////////////////////////////
+ char DC2[64] = {0};
+ int nbytes_read_DC2= 0;
+ tty_read_section(PortFD, DC2, 'A', 5, &nbytes_read_DC2);
+ DC2[nbytes_read_DC2 - 1] = '\0';
+ DC2read = std::stoi(DC2);
+ updateDC2(DC2read);
+
+ // DC3//////////////////////////////////////////////////////////////////////////////////////////
+ char DC3[64] = {0};
+ int nbytes_read_DC3= 0;
+ tty_read_section(PortFD, DC3, 'A', 5, &nbytes_read_DC3);
+ DC3[nbytes_read_DC3 - 1] = '\0';
+ DC3read = std::stoi(DC3);
+ updateDC3(DC3read);
+
+
+ // DC4_6//////////////////////////////////////////////////////////////////////////////////////////
+ char DC4_6[64] = {0};
+ int nbytes_read_DC4_6= 0;
+ tty_read_section(PortFD, DC4_6, 'A', 5, &nbytes_read_DC4_6);
+ DC4_6[nbytes_read_DC4_6 - 1] = '\0';
+ DC4_6read =std::stoi(DC4_6);
+ updateDC4_6(DC4_6read);
+
+
+ // DC2SET//////////////////////////////////////////////////////////////////////////////////////////
+ char DC2SET[64] = {0};
+ int nbytes_read_DC2SET= 0;
+ tty_read_section(PortFD, DC2SET, 'A', 5, &nbytes_read_DC2SET);
+ DC2SET[nbytes_read_DC2SET - 1] = '\0';
+ //LOGF_INFO("All Data Updated","Updated");
+ DC2SETread = std::stoi(DC2SET);
+ updateDC2SET(DC2SETread);
+
+
+ //DC3 DEW CONTROL
+ if(DC3diffSP[DC3_DPD_Mode].getState() == ISS_ON)
+ {
+ if(temp1read=20240216)
+ {
+ defineProperty(CalibrateSP);
+ LOGF_INFO("Firmware version: %d", firmware);
+ }
+ else
+ {
+ LOGF_INFO("The firmware is outdated, please upgrade to the latest firmware, or power reading calibration will be unavailable.","failed");
+ }
+ defineProperty(PowerMonitorNP);
+
+
+ defineProperty(USBControlSP);
+
+ defineProperty(setDC2voltageNP);
+ defineProperty(dc2ControlSP);
+
+ defineProperty(DC4_6ControlSP);
+
+
+ defineProperty(DC3diffSP);
+
+
+
+ //DC3////////////////////
+ if(DC3diffSP[DC3_DPD_Mode].getState() == ISS_ON)
+ {
+ deleteProperty(DC3constSETNP);
+ deleteProperty(DC3ControlNP);
+ defineProperty(DC3diffSETNP);
+ }
+ else if(DC3diffSP[DC3_CT_Mode].getState() == ISS_ON)
+ {
+ deleteProperty(DC3ControlNP);
+ deleteProperty(DC3diffSETNP);
+ defineProperty(DC3constSETNP);
+ }
+ else
+ {
+ defineProperty(DC3ControlNP);
+ deleteProperty(DC3diffSETNP);
+ deleteProperty(DC3constSETNP);
+ }
+ defineProperty(ENVMonitorNP);
+
+
+ }
+ else
+ {
+
+ deleteProperty(CalibrateSP);
+ deleteProperty(PowerMonitorNP);
+ deleteProperty(ENVMonitorNP);
+
+ deleteProperty(dc2ControlSP);
+ deleteProperty(setDC2voltageNP);
+ deleteProperty(DC4_6ControlSP);
+
+ deleteProperty(USBControlSP);
+
+
+ deleteProperty(DC3ControlNP);
+
+
+ deleteProperty(DC3diffSP);
+ deleteProperty(DC3diffSETNP);
+
+
+
+ }
+ return true;
+}
+
+bool WandererBoxPlusV3::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
+{
+ // Calibrate
+ if (CalibrateSP.isNameMatch(name))
+ {
+ CalibrateSP.setState(sendCommand("66300744") ? IPS_OK : IPS_ALERT);
+ CalibrateSP.apply();
+ LOG_INFO("Calibrating Current Readings...");
+ return true;
+ }
+
+
+ // DC2 Control
+ if (dc2ControlSP.isNameMatch(name))
+ {
+ dc2ControlSP.update(states, names, n);
+ dc2ControlSP.setState(IPS_ALERT);
+ char cmd[128] = {0};
+ snprintf(cmd, 128, "12%d", (dc2ControlSP[INDI_ENABLED].getState() == ISS_ON) ? 1 : 0);
+ dc2ControlSP.setState( sendCommand(cmd) ? IPS_OK : IPS_ALERT);
+ dc2ControlSP.apply();
+
+ return true;
+ }
+ // DC4-6 Control
+ if (DC4_6ControlSP.isNameMatch(name))
+ {
+ DC4_6ControlSP.update(states, names, n);
+ DC4_6ControlSP.setState(IPS_ALERT);
+ char cmd[128] = {0};
+ snprintf(cmd, 128, "10%d", (DC4_6ControlSP[INDI_ENABLED].getState() == ISS_ON) ? 1 : 0);
+ DC4_6ControlSP.setState( sendCommand(cmd) ? IPS_OK : IPS_ALERT);
+ DC4_6ControlSP.apply();
+
+ return true;
+ }
+
+ // USB Control
+ if (USBControlSP.isNameMatch(name))
+ {
+ USBControlSP.update(states, names, n);
+ USBControlSP.setState(IPS_ALERT);
+ char cmd[128] = {0};
+ snprintf(cmd, 128, "11%d", (USBControlSP[INDI_ENABLED].getState() == ISS_ON) ? 1 : 0);
+ USBControlSP.setState( sendCommand(cmd) ? IPS_OK : IPS_ALERT);
+
+ return true;
+ }
+
+ // DC3 DEW
+ if (DC3diffSP.isNameMatch(name))
+ {
+ DC3diffSP.update(states, names, n);
+ DC3diffSP.setState(IPS_ALERT);
+ if(DC3diffSP[DC3_DPD_Mode].getState() == ISS_ON&&ENVMonitorNP[Probe1_Temp].value!=-127&&isnan(ENVMonitorNP[DEW_Point].value)==0)
+ {
+ DC3DIFFMODE=true;
+ DC3CONSTMODE=false;
+ deleteProperty(DC3ControlNP);
+ deleteProperty(DC3constSETNP);
+ defineProperty(DC3diffSETNP);
+
+ DC3diffSETNP.setState(IPS_OK);
+ DC3diffSETNP.apply();
+ DC3diffSP.setState(IPS_OK) ;
+ DC3diffSP.apply();
+ LOGF_INFO("Dew Point Difference Mode for DC3 activated! WandererBox will keep the dew heater at the temperature higher than the dew point by the set value.","Updated");
+ return true;
+ }
+ else if(DC3diffSP[DC3_DPD_Mode].getState() == ISS_ON&&(ENVMonitorNP[Probe1_Temp].value==-127||isnan(ENVMonitorNP[DEW_Point].value)==1))
+ {
+ DC3DIFFMODE=false;
+ DC3CONSTMODE=false;
+ DC3diffSP[DC3_Manual].setState( ISS_ON);
+ LOGF_INFO("Manual Mode for DC3 activated! Please adjust the duty cycle manually, you can also use DC3 as an ordinary switch.","Updated");
+ DC3diffSP.apply();
+ }
+ else if(DC3diffSP[DC3_CT_Mode].getState() == ISS_ON&&ENVMonitorNP[Probe1_Temp].value!=-127)
+ {
+ DC3CONSTMODE=true;
+ DC3DIFFMODE=false;
+ deleteProperty(DC3diffSETNP);
+ deleteProperty(DC3ControlNP);
+ defineProperty(DC3constSETNP);
+
+ DC3constSETNP.setState(IPS_OK);
+ DC3constSETNP.apply();
+ DC3diffSP.setState(IPS_OK) ;
+ DC3diffSP.apply();
+ LOGF_INFO("Constant Temperature Mode for DC3 activated! WandererBox will keep the dew heater at the set temperature.","Updated");
+ return true;
+ }
+ else if(DC3diffSP[DC3_CT_Mode].getState() == ISS_ON&&ENVMonitorNP[Probe1_Temp].value==-127)
+ {
+ DC3DIFFMODE=false;
+ DC3CONSTMODE=false;
+ DC3diffSP[DC3_Manual].setState( ISS_ON);
+ LOGF_INFO("Manual Mode for DC3 activated! Please adjust the duty cycle manually, you can also use DC3 as an ordinary switch.","Updated");
+ DC3diffSP.apply();
+ }
+ else
+ {
+ DC3DIFFMODE=false;
+ DC3CONSTMODE=false;
+ defineProperty(DC3ControlNP);
+ deleteProperty(DC3diffSETNP);
+ deleteProperty(DC3constSETNP);
+ DC3diffSP.setState(IPS_OK) ;
+ DC3diffSP.apply();
+ LOGF_INFO("Manual Mode for DC3 activated! Please adjust the duty cycle manually, you can also use DC3 as an ordinary switch.","Updated");
+ return true;
+ }
+
+ }
+
+
+ return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
+}
+
+bool WandererBoxPlusV3::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
+{
+ if (dev && !strcmp(dev, getDeviceName()))
+ {
+ // DC3
+ if (DC3ControlNP.isNameMatch(name))
+ {
+ bool rc1 = false;
+ for (int i = 0; i < n; i++)
+ {
+ rc1 = setDewPWM(3, static_cast(values[i]));
+ }
+
+ DC3ControlNP.setState( (rc1) ? IPS_OK : IPS_ALERT);
+ if (DC3ControlNP.getState() == IPS_OK)
+ DC3ControlNP.update(values, names, n);
+ DC3ControlNP.apply();
+ return true;
+ }
+ if (DC3diffSETNP.isNameMatch(name))
+ {
+
+ DC3diffSETNP.setState(IPS_OK) ;
+ if (DC3diffSETNP.getState() == IPS_OK)
+ DC3diffSETNP.update(values, names, n);
+ DC3diffSETNP.apply();
+ return true;
+ }
+ if (DC3constSETNP.isNameMatch(name))
+ {
+
+ DC3constSETNP.setState(IPS_OK) ;
+ if (DC3constSETNP.getState() == IPS_OK)
+ DC3constSETNP.update(values, names, n);
+ DC3constSETNP.apply();
+ return true;
+ }
+
+ // DC2voltageSET
+ if (setDC2voltageNP.isNameMatch(name))
+ {
+ bool rc1 = false;
+ for (int i = 0; i < n; i++)
+ {
+ rc1 = setDewPWM(20, static_cast(10*values[i]));
+ }
+
+ setDC2voltageNP.setState( (rc1) ? IPS_OK : IPS_ALERT);
+ if (setDC2voltageNP.getState() == IPS_OK)
+ setDC2voltageNP.update(values, names, n);
+ setDC2voltageNP.apply();
+ return true;
+ }
+
+
+ }
+ return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
+}
+
+bool WandererBoxPlusV3::setDewPWM(uint8_t id, uint8_t value)
+{
+ char cmd[64] = {0};
+ snprintf(cmd, 64, "%d%03d", id, value);
+ if (sendCommand(cmd))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+const char *WandererBoxPlusV3::getDefaultName()
+{
+ return "WandererBox Pro V3";
+}
+
+
+bool WandererBoxPlusV3::sendCommand(std::string command)
+{
+ int nbytes_written = 0, rc = -1;
+ std::string command_termination = "\n";
+ LOGF_DEBUG("CMD: %s", command.c_str());
+ if ((rc = tty_write_string(PortFD, (command + command_termination).c_str(), &nbytes_written)) != TTY_OK)
+ {
+ char errorMessage[MAXRBUF];
+ tty_error_msg(rc, errorMessage, MAXRBUF);
+ LOGF_ERROR("Serial write error: %s", errorMessage);
+ return false;
+ }
+ return true;
+}
+
+void WandererBoxPlusV3::TimerHit()
+{
+ if (!isConnected())
+ {
+ SetTimer(2500);
+ return;
+ }
+
+ getData();
+ SetTimer(2500);
+}
+
+bool WandererBoxPlusV3::saveConfigItems(FILE * fp)
+{
+ INDI::DefaultDevice::saveConfigItems(fp);
+
+ DC3diffSP.save(fp);
+ DC3diffSETNP.save(fp);
+ DC3constSETNP.save(fp);
+ DC3ControlNP.save(fp);
+
+ setDC2voltageNP.save(fp);
+ return true;
+}
+
diff --git a/drivers/auxiliary/wandererbox_plus_v3.h b/drivers/auxiliary/wandererbox_plus_v3.h
new file mode 100644
index 0000000000..53ccc5d551
--- /dev/null
+++ b/drivers/auxiliary/wandererbox_plus_v3.h
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ Copyright(c) 2024 Frank Wang. All rights reserved.
+
+ WandererBox Plus V3
+
+ 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 2 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 Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+*******************************************************************************/
+
+#pragma once
+
+#include "defaultdevice.h"
+#include
+#include
+
+namespace Connection
+{
+class Serial;
+}
+
+class WandererBoxPlusV3 : public INDI::DefaultDevice
+{
+public:
+ WandererBoxPlusV3();
+ virtual ~WandererBoxPlusV3() = default;
+
+ virtual bool initProperties() override;
+ virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override;
+ virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override;
+ virtual bool updateProperties() override;
+
+
+
+protected:
+ const char *getDefaultName() override;
+ virtual bool saveConfigItems(FILE *fp) override;
+ virtual void TimerHit() override;
+
+
+private:
+
+ int firmware=0;
+ bool sendCommand(std::string command);
+ bool DC3DIFFMODE=false;
+ bool DC3CONSTMODE=false;
+ bool getData();
+ static constexpr const char *ENVIRONMENT_TAB {"Sensors"};
+ static constexpr const char *DC3_TAB {"DC3"};
+ //Current Calibrate
+ INDI::PropertySwitch CalibrateSP{1};
+
+
+ //Temp1
+ double temp1read = 0;
+ //DHTH
+ double DHTHread = 0;
+ //DHTT
+ void updateENV(double temp1,double DHTH,double DHTT);
+ double DHTTread = 0;
+ //Total Current
+ double Tcurrentread = 0;
+ //Power Monitor
+ void updatePower(double Tcurrent,double voltage);
+ double voltageread = 0;
+ //USB
+ void updateUSB(int value);
+ int USBread = 0;
+ //DC2
+ void updateDC2(int value);
+ int DC2read = 0;
+ //DC3
+ void updateDC3(int value);
+ int DC3read = 0;
+ //DC4_6
+ void updateDC4_6(int value);
+ int DC4_6read = 0;
+ //DC2set
+ void updateDC2SET(double value);
+ int DC2SETread = 0;
+
+ bool setDewPWM(uint8_t id, uint8_t value);
+
+ //DC Control//////////////////////////////////////////////////////////////////////////////////
+ INDI::PropertySwitch dc2ControlSP{2};
+ INDI::PropertySwitch DC4_6ControlSP{2};
+ //USB Control//////////////////////////////////////////////////////////////////////////////////
+ INDI::PropertySwitch USBControlSP{2};
+ //DC3 Control////////////////////////////////////////////////////////////////
+ INDI::PropertyNumber DC3ControlNP{1};
+ enum
+ {
+ DC3,
+ };
+ //DC3 DIFF////////////////////////////////////////////////////////////////
+ INDI::PropertySwitch DC3diffSP{3};
+ enum
+ {
+ DC3_Manual,
+ DC3_DPD_Mode,
+ DC3_CT_Mode,
+ };
+
+ INDI::PropertyNumber DC3diffSETNP{1};
+ enum
+ {
+ DC3DIFFSET,
+ };
+ INDI::PropertyNumber DC3constSETNP{1};
+ enum
+ {
+ DC3CONSTSET,
+ };
+
+
+
+ //DC2 Voltage Control////////////////////////////////////////////////////////////////
+ INDI::PropertyNumber setDC2voltageNP{1};
+ enum
+ {
+ setDC2voltage,
+ };
+
+ // Power Monitor
+
+ INDI::PropertyNumber PowerMonitorNP{2};
+ enum
+ {
+ VOLTAGE,
+ TOTAL_CURRENT,
+ };
+
+ // ENV Monitor
+
+ INDI::PropertyNumber ENVMonitorNP{4};
+ enum
+ {
+ Probe1_Temp,
+ ENV_Humidity,
+ ENV_Temp,
+ DEW_Point,
+ };
+
+
+
+ int PortFD{ -1 };
+
+ Connection::Serial *serialConnection{ nullptr };
+};