39static std::unique_ptr<WandererCover> wanderercover(
new WandererCover());
43#define WANDERER_RESPONSE_SIZE 1
44#define COMMAND_WAITING_TIME 120
46#define FIRST_SUPPORTED_VERSION "20220920"
48#define TAB_NAME_CONFIGURATION "Dust cover configuration"
50#define CLOSE_COVER_COMMAND "1000\n"
51#define HANDSHAKE_COMMAND "1500001\n"
52#define OPEN_COVER_COMMAND "1001\n"
53#define TURN_OFF_LIGHT_PANEL_COMMAND "9999\n"
55# define SET_CURRENT_POSITION_TO_OPEN_POSITION "257\n"
56# define SET_CURRENT_POSITION_TO_CLOSED_POSITION "256\n"
66 LOG_INFO(
"Initializing Wanderer Cover properties...");
75 IUFillText(&StatusT[0],
"Cover",
"Cover",
nullptr);
76 IUFillText(&StatusT[1],
"Light",
"Light",
nullptr);
80 IUFillText(&FirmwareT[0],
"Version",
"Version",
nullptr);
84 IUFillSwitch(&ControlPositionPositiveDegreesConfigurationV[PLUS_1_DEGREE],
"PLUS_1_DEGREE",
"+ 1°",
ISS_OFF);
85 IUFillSwitch(&ControlPositionPositiveDegreesConfigurationV[PLUS_10_DEGREE],
"PLUS_10_DEGREE",
"+ 10°",
ISS_OFF);
86 IUFillSwitch(&ControlPositionPositiveDegreesConfigurationV[PLUS_50_DEGREE],
"PLUS_50_DEGREE",
"+ 50°",
ISS_OFF);
87 IUFillSwitchVector(&ControlPositionPositiveDegreesConfigurationVP, ControlPositionPositiveDegreesConfigurationV, 3,
91 IUFillSwitch(&ControlPositionNegativeDegreesConfigurationV[MINUS_1_DEGREE],
"MINUS_1_DEGREE",
"- 1°",
ISS_OFF);
92 IUFillSwitch(&ControlPositionNegativeDegreesConfigurationV[MINUS_10_DEGREE],
"MINUS_10_DEGREE",
"- 10°",
ISS_OFF);
93 IUFillSwitch(&ControlPositionNegativeDegreesConfigurationV[MINUS_50_DEGREE],
"MINUS_50_DEGREE",
"- 50°",
ISS_OFF);
94 IUFillSwitchVector(&ControlPositionNegativeDegreesConfigurationVP, ControlPositionNegativeDegreesConfigurationV, 3,
98 IUFillSwitch(&DefinePositionConfigurationV[SET_CURRENT_POSITION_OPEN],
"Set current position as open",
99 " 1 - Set current position as open",
ISS_OFF);
100 IUFillSwitch(&DefinePositionConfigurationV[SET_CURRENT_POSITION_CLOSE],
"Set current position as close",
101 "2 - Set current position as close",
ISS_OFF);
118 LOG_INFO(
"Properties initialization complete");
122bool WandererCover::Handshake()
124 LOG_INFO(
"Starting handshake with device...");
130 IUSaveText(&FirmwareT[0],
"Simulation version");
133 updateCoverStatus((
char*)
"0");
135 setLightBoxStatusAsSwitchedOff();
137 NumberOfStepsBeetweenOpenAndCloseState = 0;
138 setNumberOfStepsStatusValue(NumberOfStepsBeetweenOpenAndCloseState);
144 tcflush(PortFD, TCIOFLUSH);
145 int nbytes_read_name = 0, nbytes_written = 0, rc = -1;
152 LOGF_ERROR(
"Serial write error: %s", errorMessage);
160 LOGF_ERROR(
"Device read error: %s", errorMessage);
163 name[nbytes_read_name - 1] =
'\0';
166 int nbytes_read_version = 0;
167 char version[64] = {0};
172 LOGF_ERROR(
"Device read error: %s", errorMessage);
173 LOGF_ERROR(
"You have an old firmware. This version is not supported. You should update the device as described here : %s",
174 "https://www.wandererastro.com/en/col.jsp?id=106");
178 displayConfigurationMessage();
179 version[nbytes_read_version - 1] =
'\0';
186 char cover_state[64] = {0};
187 int nbytes_read_cover_state = 0;
193 LOGF_ERROR(
"Device read error: %s", errorMessage);
196 cover_state[nbytes_read_cover_state - 1] =
'\0';
197 LOGF_INFO(
"Cover state : %s", cover_state);
198 updateCoverStatus(cover_state);
201 char number_of_steps[64] = {0};
202 int nbytes_read_number_of_steps = 0;
208 LOGF_ERROR(
"Device read error: %s", errorMessage);
211 number_of_steps[nbytes_read_number_of_steps - 1] =
'\0';
212 LOGF_INFO(
"Number of steps between open and close states : %s", cover_state);
213 NumberOfStepsBeetweenOpenAndCloseState = sscanf(number_of_steps,
"%d", &NumberOfStepsBeetweenOpenAndCloseState);
214 if (NumberOfStepsBeetweenOpenAndCloseState == 0)
216 LOGF_ERROR(
"The number of steps is 0 meaning the falt panel may hit an obstacle. You should define opening and closing position first.",
219 setNumberOfStepsStatusValue(NumberOfStepsBeetweenOpenAndCloseState);
221 setLightBoxStatusAsSwitchedOff();
223 LOGF_INFO(
"Handshake successful:%s", name);
224 tcflush(PortFD, TCIOFLUSH);
228void WandererCover::updateCoverStatus(
char* res)
233 setParkCapStatusAsClosed();
235 else if (strcmp(res,
"1") == 0 &&
isCoverOpen ==
false)
238 setParkCapStatusAsOpen();
240 else if (strcmp(res,
"255") == 0)
242 LOGF_INFO(
"No cover status information available. You should first open/close the cover.",
"");
287 return "Wanderer Cover v3";
322 if (processConfigurationButtonSwitch(dev, name, states, names, n))
343bool WandererCover::getStartupData()
367 LOG_DEBUG(
"=== Starting ParkCap() - Attempting to close dust cap ===");
369 LOGF_DEBUG(
"Checking number of steps: %d", NumberOfStepsBeetweenOpenAndCloseState);
370 if (NumberOfStepsBeetweenOpenAndCloseState == 0)
372 LOG_ERROR(
"Number of steps is 0 - Configuration required");
373 LOGF_ERROR(
"The number of steps is 0 meaning the flat panel may hit an obstacle. You should define opening and closing position first.",
"");
380 LOG_INFO(
"Cover is already closed - No action needed");
387 LOG_INFO(
"In simulation mode - Setting status to closed");
388 setParkCapStatusAsClosed();
392 LOG_DEBUG(
"Sending close command to device");
396 LOG_ERROR(
"Failed to send close command");
400 LOGF_DEBUG(
"Checking response for errors: %s", response);
401 if (hasWandererSentAnError(response))
403 LOG_ERROR(
"Device returned error - Configuration required");
404 LOG_ERROR(
"You need to configure Open and closed position first in 'Dust cover configuration' tab.");
408 LOG_DEBUG(
"Close command successful - Updating status");
409 setParkCapStatusAsClosed();
411 LOG_DEBUG(
"=== ParkCap() completed successfully ===");
417 LOG_DEBUG(
"=== Starting UnParkCap() - Attempting to open dust cap ===");
419 LOGF_DEBUG(
"Checking number of steps: %d", NumberOfStepsBeetweenOpenAndCloseState);
420 if (NumberOfStepsBeetweenOpenAndCloseState == 0)
422 LOG_ERROR(
"Number of steps is 0 - Configuration required");
423 LOG_ERROR(
"The number of steps is 0 meaning the flat panel may hit an obstacle. You should define opening and closing position first.");
430 LOG_INFO(
"Cover is already open - No action needed");
437 LOG_INFO(
"In simulation mode - Setting status to open");
438 setParkCapStatusAsOpen();
442 LOG_DEBUG(
"Sending open command to device");
446 LOG_ERROR(
"Failed to send open command");
450 LOGF_DEBUG(
"Checking response for errors: %s", response);
451 if (hasWandererSentAnError(response))
453 LOG_ERROR(
"Device returned error - Displaying configuration message");
454 displayConfigurationMessage();
458 LOG_DEBUG(
"Open command successful - Updating status");
459 setParkCapStatusAsOpen();
461 LOG_DEBUG(
"=== UnParkCap() completed successfully ===");
465void WandererCover::setParkCapStatusAsClosed()
478void WandererCover::setParkCapStatusAsOpen()
493 LOGF_INFO(
"Setting light box state to: %s", enable ?
"ON" :
"OFF");
496 LOG_INFO(enable ?
"Light box is already on." :
"Light box is already off.");
506 return switchOffLightBox();
512bool WandererCover::switchOffLightBox()
516 setLightBoxStatusAsSwitchedOff();
524 setLightBoxStatusAsSwitchedOff();
528void WandererCover::setLightBoxStatusAsSwitchedOff()
538 LOG_INFO(
"Light panel switched off");
544 LOGF_INFO(
"Setting light box brightness to %d", value);
547 setLightBoxBrightnesStatusToValue(value);
551 char command[5] = {0};
552 snprintf(command, 5,
"%03d\n", value);
553 if (!sendCommand(command, response,
false))
556 setLightBoxBrightnesStatusToValue(value);
561bool WandererCover::setCurrentPositionToOpenPosition()
563 LOG_INFO(
"Setting current position as open position...");
566 LOG_INFO(
"Current position set to open position");
567 NumberOfDegreesSinceLastOpenPositionSet = 0;
574 NumberOfDegreesSinceLastOpenPositionSet = 0;
576 LOG_INFO(
"Open position successfully set");
580bool WandererCover::setCurrentPositionToClosedPosition()
582 LOG_INFO(
"Setting current position as closed position...");
583 int cumulative_angle_value = ((abs(NumberOfDegreesSinceLastOpenPositionSet) * 222.22) / 10) + 10000;
584 LOGF_INFO(
"Calculated cumulative angle: %d", cumulative_angle_value);
588 LOG_INFO(
"Current position set to closed position");
589 LOGF_INFO(
"Sending cumulative angle of %d", cumulative_angle_value);
590 setNumberOfStepsStatusValue(cumulative_angle_value);
591 setParkCapStatusAsClosed();
598 if (!sendCommand(std::to_string(cumulative_angle_value).c_str(), response,
false))
601 setNumberOfStepsStatusValue(cumulative_angle_value);
603 setParkCapStatusAsClosed();
605 LOG_INFO(
"Closed position successfully set");
609void WandererCover::setLightBoxBrightnesStatusToValue(uint16_t value)
615 setLightBoxStatusAsSwitchedOff();
626 LOGF_INFO(
"Brightness set to %d.", value);
629void WandererCover::setNumberOfStepsStatusValue(
int value)
631 LOGF_DEBUG(
"Current number of steps value configured between open and closed position : %d", value);
632 NumberOfStepsBeetweenOpenAndCloseState = value;
638bool WandererCover::sendCommand(std::string command,
char *response,
bool waitForAnswer)
640 LOGF_INFO(
"Sending command: %s", command.c_str());
641 int nbytes_read = 0, nbytes_written = 0, rc = -1;
642 std::string command_termination =
"\n";
648 LOGF_ERROR(
"Serial write error: %s", errorMessage);
656 LOGF_ERROR(
"Device read error: %s", errorMessage);
663 LOGF_INFO(
"Received response: %s", response);
668IPState WandererCover::moveDustCap(
int degrees)
670 LOGF_INFO(
"Moving dust cap by %d degrees", degrees);
671 if (degrees < -360 or degrees > 360)
673 LOGF_ERROR(
"Degrees must be between -360 and 360 : %d", degrees);
678 LOGF_INFO(
"Moving dust cap cover of %d degrees", degrees);
679 NumberOfDegreesSinceLastOpenPositionSet += degrees;
680 LOGF_INFO(
"Number of degrees since last open position set : %d", NumberOfDegreesSinceLastOpenPositionSet);
684 int stepping_offset = 100000;
687 stepping_offset = -100000;
690 int command_value = (degrees * 222.22) + stepping_offset;
693 if (!sendCommand(std::to_string(command_value).c_str(), response,
true))
696 NumberOfDegreesSinceLastOpenPositionSet += degrees;
697 LOGF_DEBUG(
"Number of degrees since last open position set : %d", NumberOfDegreesSinceLastOpenPositionSet);
698 LOG_INFO(
"Dust cap movement complete");
702bool WandererCover::processConfigurationButtonSwitch(
const char *dev,
const char *name,
ISState *states,
char *names[],
705 LOG_INFO(
"Processing configuration button press...");
709 if (!strcmp(ControlPositionPositiveDegreesConfigurationVP.
name, name))
711 IUUpdateSwitch(&ControlPositionPositiveDegreesConfigurationVP, states, names, n);
712 if (ControlPositionPositiveDegreesConfigurationV[PLUS_1_DEGREE].s )
715 ControlPositionPositiveDegreesConfigurationV[PLUS_1_DEGREE].s =
ISS_OFF;
717 else if (ControlPositionPositiveDegreesConfigurationV[PLUS_10_DEGREE].s ==
ISS_ON)
720 ControlPositionPositiveDegreesConfigurationV[PLUS_10_DEGREE].s =
ISS_OFF;
722 else if (ControlPositionPositiveDegreesConfigurationV[PLUS_50_DEGREE].s ==
ISS_ON)
725 ControlPositionPositiveDegreesConfigurationV[PLUS_50_DEGREE].s =
ISS_OFF;
727 IDSetSwitch(&ControlPositionPositiveDegreesConfigurationVP,
nullptr);
729 LOG_INFO(
"Configuration button processing complete");
733 if (!strcmp(ControlPositionNegativeDegreesConfigurationVP.
name, name))
735 IUUpdateSwitch(&ControlPositionNegativeDegreesConfigurationVP, states, names, n);
736 if (ControlPositionNegativeDegreesConfigurationV[MINUS_1_DEGREE].s )
739 ControlPositionNegativeDegreesConfigurationV[MINUS_1_DEGREE].s =
ISS_OFF;
741 else if (ControlPositionNegativeDegreesConfigurationV[MINUS_10_DEGREE].s ==
ISS_ON)
744 ControlPositionNegativeDegreesConfigurationV[MINUS_10_DEGREE].s =
ISS_OFF;
746 else if (ControlPositionNegativeDegreesConfigurationV[MINUS_50_DEGREE].s ==
ISS_ON)
749 ControlPositionNegativeDegreesConfigurationV[MINUS_50_DEGREE].s =
ISS_OFF;
751 IDSetSwitch(&ControlPositionNegativeDegreesConfigurationVP,
nullptr);
753 LOG_INFO(
"Configuration button processing complete");
757 if (!strcmp(DefinePositionConfigurationVP.
name, name))
760 if (DefinePositionConfigurationV[SET_CURRENT_POSITION_OPEN].s ==
ISS_ON)
762 setCurrentPositionToOpenPosition();
763 DefinePositionConfigurationV[SET_CURRENT_POSITION_OPEN].s =
ISS_OFF;
765 else if (DefinePositionConfigurationV[SET_CURRENT_POSITION_CLOSE].s ==
ISS_ON)
767 setCurrentPositionToClosedPosition();
768 DefinePositionConfigurationV[SET_CURRENT_POSITION_CLOSE].s =
ISS_OFF;
770 IDSetSwitch(&DefinePositionConfigurationVP,
nullptr);
771 LOG_INFO(
"Configuration button processing complete");
776 LOG_INFO(
"Configuration button processing complete");
785bool WandererCover::hasWandererSentAnError(
char* response)
787 const std::string response_string( response );
788 size_t found = response_string.find(
"Error");
789 if (found != std::string::npos)
795void WandererCover::displayConfigurationMessage()
797 LOG_WARN(
" - Once thesest steps are done, the dust cover will remember the park and unpark positions.");
798 LOG_WARN(
" - Click on 'Set current position as close' to define the park position");
799 LOG_WARN(
" - Use again the select list to move your cover panel in close position on the scope");
800 LOG_WARN(
" - Click on 'Set current position as open' to define the unpark position");
801 LOG_WARN(
" - Use the select controller to move your panel to the open position");
802 LOG_WARN(
"In order to do so, go to 'Dust cover configurtation' tab and do the following steps :");
803 LOG_WARN(
"Before first use, or when you change your setup, you need to configure Open and closed position first in 'Dust cover configurtation' tab.");
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the initial connection to the devic...
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
Provides interface to implement remotely controlled dust cover.
const char * getDeviceName() const
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
virtual void ISGetProperties(const char *dev)
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool ISSnoopDevice(XMLEle *root)
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
uint32_t getPollingPeriod() const
getPollingPeriod Return the polling period.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
bool isSimulation() const
void syncDriverInfo()
syncDriverInfo sends the current driver information to the client.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
INDI::PropertySwitch ParkCapSP
bool updateProperties()
updateProperties Defines or Delete properties based on default device connection status
void initProperties(const char *group, uint32_t capabilities=0)
Initialize dust cap properties. It is recommended to call this function within initProperties() of yo...
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process dust cap switch properties.
void initProperties(const char *group, uint32_t capabilities)
Initialize light box properties. It is recommended to call this function within initProperties() of y...
void ISGetProperties(const char *deviceName)
isGetLightBoxProperties Get light box properties
INDI::PropertyNumber LightIntensityNP
bool saveConfigItems(FILE *fp)
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process light box switch properties.
bool processText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process light box text properties.
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process light box number properties.
INDI::PropertySwitch LightSP
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
Provides interface to implement controllable light box/switch device.
virtual bool SetLightBoxBrightness(uint16_t value) override
setBrightness Set light level. Must be implemented in the child class, if supported.
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool EnableLightBox(bool enable) override
EnableLightBox Turn on/off on a light box. Must be implemented in the child class.
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual IPState ParkCap() override
Park dust cap (close cover). Must be implemented by child.
virtual IPState UnParkCap() override
unPark dust cap (open cover). Must be implemented by child.
const char * getDefaultName() override
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Implementations for common driver routines.
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)
#define TAB_NAME_CONFIGURATION
#define CLOSE_COVER_COMMAND
#define HANDSHAKE_COMMAND
#define WANDERER_RESPONSE_SIZE
#define OPEN_COVER_COMMAND
#define SET_CURRENT_POSITION_TO_CLOSED_POSITION
#define COMMAND_WAITING_TIME
#define TURN_OFF_LIGHT_PANEL_COMMAND
#define SET_CURRENT_POSITION_TO_OPEN_POSITION