diff --git a/drivers.xml b/drivers.xml
index 0914131b28..642fc737e5 100644
--- a/drivers.xml
+++ b/drivers.xml
@@ -437,9 +437,13 @@
indi_simulator_rotator
1.0
-
+
indi_wanderer_lite_rotator
1.0
+
+
+ indi_wanderer_rotator_mini
+ 1.0
indi_microtouch_focus
diff --git a/drivers/rotator/CMakeLists.txt b/drivers/rotator/CMakeLists.txt
index 86b012a8e0..6b2ee9e293 100644
--- a/drivers/rotator/CMakeLists.txt
+++ b/drivers/rotator/CMakeLists.txt
@@ -62,6 +62,15 @@ add_executable(indi_wanderer_lite_rotator ${WandererRotatorLite_SRC})
target_link_libraries(indi_wanderer_lite_rotator indidriver)
install(TARGETS indi_wanderer_lite_rotator RUNTIME DESTINATION bin)
+# ############### Wanderer Rotator Mini ################
+SET(WandererRotatorMini_SRC
+ wanderer_rotator_mini.cpp)
+
+add_executable(indi_wanderer_rotator_mini ${WandererRotatorMini_SRC})
+target_link_libraries(indi_wanderer_rotator_mini indidriver)
+install(TARGETS indi_wanderer_rotator_mini RUNTIME DESTINATION bin)
+
+
# ############### Integra85 Focusing Rotator ################
SET(integra_SRC
integra.cpp)
diff --git a/drivers/rotator/wanderer_rotator_mini.cpp b/drivers/rotator/wanderer_rotator_mini.cpp
new file mode 100644
index 0000000000..4c3e4758d1
--- /dev/null
+++ b/drivers/rotator/wanderer_rotator_mini.cpp
@@ -0,0 +1,375 @@
+/*******************************************************************************
+ Copyright(c) 2024 Frank Wang. All rights reserved.
+
+ WandererRotator Mini V1/V2
+
+ 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 "wanderer_rotator_mini.h"
+#include "indicom.h"
+#include "connectionplugins/connectionserial.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+// We declare an auto pointer to WandererRotatorMini.
+static std::unique_ptr wandererrotatormini(new WandererRotatorMini());
+
+WandererRotatorMini::WandererRotatorMini()
+{
+ setVersion(1, 0);
+
+}
+bool WandererRotatorMini::initProperties()
+{
+
+ INDI::Rotator::initProperties();
+
+ SetCapability(ROTATOR_CAN_REVERSE | ROTATOR_CAN_ABORT | ROTATOR_CAN_HOME | ROTATOR_HAS_BACKLASH);
+
+ addAuxControls();
+ // Calibrate
+ SetZeroSP[0].fill("Set_Zero", "Mechanical Zero", ISS_OFF);
+ SetZeroSP.fill(getDeviceName(), "Set_Zero", "Set Current As", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1,60, IPS_IDLE);
+
+
+ serialConnection->setDefaultBaudRate(Connection::Serial::B_19200);
+
+
+ return true;
+}
+
+bool WandererRotatorMini::updateProperties()
+{
+ INDI::Rotator::updateProperties();
+
+ if (isConnected())
+ {
+ defineProperty(SetZeroSP);
+ if(firmware<20240208)
+ {
+ LOG_ERROR("The firmware is outdated, please upgrade to the latest firmware, or the driver cannot function properly!");
+ }
+ }
+ else
+ {
+ deleteProperty(SetZeroSP);
+ }
+ return true;
+}
+
+bool WandererRotatorMini::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
+{
+
+ if (dev && !strcmp(dev, getDeviceName()))
+ {
+ if (SetZeroSP.isNameMatch(name))
+ {
+ SetZeroSP.setState(sendCommand("1500002") ? IPS_OK : IPS_ALERT);
+ SetZeroSP.apply();
+ GotoRotatorN[0].value=0;
+ IDSetNumber(&GotoRotatorNP, nullptr);
+ LOG_INFO("Virtual Mechanical Angle is set to zero.");
+ return true;
+ }
+ }
+ return Rotator::ISNewSwitch(dev, name, states, names, n);
+}
+
+
+
+const char *WandererRotatorMini::getDefaultName()
+{
+ return "WandererRotator Mini";
+}
+
+bool WandererRotatorMini::Handshake()
+{
+ PortFD = serialConnection->getPortFD();
+ tcflush(PortFD, TCIOFLUSH);
+ int nbytes_read_name = 0,nbytes_written=0,rc=-1;
+ char name[64] = {0};
+
+ if ((rc = tty_write_string(PortFD, "1500001", &nbytes_written)) != TTY_OK)
+ {
+ char errorMessage[MAXRBUF];
+ tty_error_msg(rc, errorMessage, MAXRBUF);
+ LOGF_ERROR("Serial write error: %s", errorMessage);
+ return false;
+ }
+
+ //Device Model//////////////////////////////////////////////////////////////////////////////////////////////////////
+ if ((rc = tty_read_section(PortFD, name, 'A', 3, &nbytes_read_name)) != TTY_OK)
+ {
+ tcflush(PortFD, TCIOFLUSH);
+ if ((rc = tty_write_string(PortFD, "1500001", &nbytes_written)) != TTY_OK)
+ {
+ char errorMessage[MAXRBUF];
+ tty_error_msg(rc, errorMessage, MAXRBUF);
+ LOGF_ERROR("Serial write error: %s", errorMessage);
+ return false;
+ }
+ 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 WandererRotator, please check the serial port!","Updated");
+ LOGF_ERROR("Device read error: %s", errorMessage);
+ return false;
+ }
+ }
+ name[nbytes_read_name - 1] = '\0';
+ if(strcmp(name, "WandererRotatorMini")!=0)
+ {
+ LOGF_ERROR("The device is not WandererRotator Mini!","Updated");
+ LOGF_INFO("The device is %s",name);
+ return false;
+ }
+ // 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';
+ LOGF_INFO("Firmware Version:%s", version);
+ firmware=std::atoi(version);
+ // Angle//////////////////////////////////////////////////////////////////////////////////////////
+ char M_angle[64] = {0};
+ int nbytes_read_M_angle= 0;
+ tty_read_section(PortFD, M_angle, 'A', 5, &nbytes_read_M_angle);
+ M_angle[nbytes_read_M_angle - 1] = '\0';
+ M_angleread = std::strtod(M_angle,NULL);
+
+ if(abs(M_angleread)>400000)
+ {
+ rc=sendCommand("1500002");
+ LOG_WARN("Virtual Mechanical Angle is too large, it is now set to zero!");
+ rc=sendCommand("1500001");
+ rc=tty_read_section(PortFD, M_angle, 'A', 5, &nbytes_read_M_angle);
+ rc=tty_read_section(PortFD, M_angle, 'A', 5, &nbytes_read_M_angle);
+ rc=tty_read_section(PortFD, M_angle, 'A', 5, &nbytes_read_M_angle);
+ M_angle[nbytes_read_M_angle - 1] = '\0';
+ M_angleread = std::strtod(M_angle,NULL);
+
+ }
+ GotoRotatorN[0].value=abs(M_angleread/1000);
+ tcflush(PortFD, TCIOFLUSH);
+ return true;
+}
+
+
+IPState WandererRotatorMini::MoveRotator(double angle)
+{
+ angle = angle - GotoRotatorN[0].value;
+ if (angle * positionhistory < 0 && angle > 0)
+ {
+ angle = angle + backlash;
+ }
+ if (angle * positionhistory < 0 && angle < 0)
+ {
+ angle = angle - backlash;
+ }
+ char cmd[16];
+ int position = (int)(reversecoefficient * angle * 1142);
+ positionhistory = angle;
+ snprintf(cmd, 16, "%d", position);
+ Move(cmd);
+
+ return IPS_BUSY;
+}
+
+
+
+bool WandererRotatorMini::AbortRotator()
+{
+
+ haltcommand = true;
+
+ int nbytes_written = 0, rc = -1;
+ tcflush(PortFD, TCIOFLUSH);
+ if ((rc = tty_write_string(PortFD, "Stop", &nbytes_written)) != TTY_OK)
+ {
+ char errorMessage[MAXRBUF];
+ tty_error_msg(rc, errorMessage, MAXRBUF);
+ LOGF_ERROR("Serial write error: %s", errorMessage);
+ return false;
+ }
+
+ SetTimer(100);
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+/// \brief WandererRotatorMini::HomeRotator
+/// \return
+///
+IPState WandererRotatorMini::HomeRotator()
+{
+
+ double angle = -1 * reversecoefficient * GotoRotatorN[0].value;
+ positionhistory = -1* GotoRotatorN[0].value;
+ char cmd[16];
+ int position = (int)(angle * 1142);
+ snprintf(cmd, 16, "%d", position);
+ GotoRotatorNP.s = IPS_BUSY;
+ Move(cmd);
+ LOG_INFO("Moving to zero...");
+ return IPS_OK;
+}
+
+
+bool WandererRotatorMini::ReverseRotator(bool enabled)
+{
+
+ if (enabled)
+ {
+ if(M_angleread>0)
+ {
+ HomeRotator();
+ LOG_WARN("The rotator will first move to zero and then reverse the rotation direction to prevent cable wrap...");
+ }
+ reversecoefficient = -1;
+ ReverseState = true;
+ return true;
+ }
+ else
+ {
+ if(M_angleread<0)
+ {
+ HomeRotator();
+ LOG_WARN("The rotator will first move to zero and then reverse the rotation direction to prevent cable wrap...");
+ }
+ reversecoefficient = 1;
+ ReverseState = false;
+ return true;
+ }
+ return false;
+}
+
+
+
+void WandererRotatorMini::TimerHit()
+{
+
+ if (GotoRotatorNP.s == IPS_BUSY || haltcommand == true)
+ {
+
+ if(nowtime", cmd);
+ if ((rc = tty_write_string(PortFD, cmd, &nbytes_written)) != TTY_OK)
+ {
+ char errorMessage[MAXRBUF];
+ tty_error_msg(rc, errorMessage, MAXRBUF);
+ LOGF_ERROR("Serial write error: %s", errorMessage);
+ return false;
+ }
+ SetTimer(2000);
+ nowtime=0;
+ estime=abs(std::atoi(cmd)/1142*260);
+ return true;
+}
+
+
+bool WandererRotatorMini::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;
+}
+
+
+bool WandererRotatorMini::SetRotatorBacklash(int32_t steps)
+{
+ backlash = (double)(steps / 1142);
+ return true;
+}
+
+bool WandererRotatorMini::SetRotatorBacklashEnabled(bool enabled)
+{
+ if(enabled)
+ {
+ return SetRotatorBacklash(RotatorBacklashN[0].value);
+ }
+ else
+ {
+ return SetRotatorBacklash(0);
+ }
+
+}
+
+
diff --git a/drivers/rotator/wanderer_rotator_mini.h b/drivers/rotator/wanderer_rotator_mini.h
new file mode 100644
index 0000000000..280ba1deb7
--- /dev/null
+++ b/drivers/rotator/wanderer_rotator_mini.h
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ Copyright(c) 2024 Frank Wang. All rights reserved.
+
+ WandererRotator Mini V1/V2
+
+ 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 "indirotator.h"
+#include "indirotatorinterface.h"
+#include "indipropertyswitch.h"
+class WandererRotatorMini : public INDI::Rotator
+{
+public:
+ WandererRotatorMini();
+
+ virtual bool initProperties() override;
+ virtual bool updateProperties() override;
+ virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override;
+
+
+
+
+protected:
+ const char * getDefaultName() override;
+ virtual IPState MoveRotator(double angle) override;
+ virtual IPState HomeRotator() override;
+ virtual bool ReverseRotator(bool enabled) override;
+
+ virtual bool AbortRotator() override;
+ virtual void TimerHit() override;
+ virtual bool SetRotatorBacklash(int32_t steps) override;
+ virtual bool SetRotatorBacklashEnabled(bool enabled) override;
+
+
+
+
+private:
+ int firmware=0;
+ double M_angleread=0;
+ double initangle=0;
+ bool Handshake() override;
+ INDI::PropertySwitch SetZeroSP{1};
+ bool sendCommand(std::string command);
+ bool Move(const char *cmd);
+ bool haltcommand = false;
+ bool ReverseState=false;
+ int reversecoefficient=1;
+ double backlash=0.5;
+ double positionhistory=0;
+ int estime=0;
+ int nowtime=0;
+
+};
+
+
+
+
+
+