Skip to content

Commit cc594f8

Browse files
committed
Do not use CTS with standalone Celestron controllers
1 parent 47609e7 commit cc594f8

5 files changed

Lines changed: 55 additions & 127 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ TAGS
77
build/
88
build-*/
99
.claude-worktrees/*
10-
10+
.claude/*
1111
# Gedit backup files
1212
*~
1313
*.orig

drivers/power/indi-celestron-dewpower/celestron_dewpower.cpp

Lines changed: 37 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
#include <memory>
66
#include <termios.h>
77
#include <cstring>
8-
#include <sys/ioctl.h>
98
#include <chrono>
9+
#include <thread>
1010

1111
static std::unique_ptr<CelestronDewPower> celestronDewPower(new CelestronDewPower());
1212

@@ -149,8 +149,6 @@ bool CelestronDewPower::Handshake()
149149

150150
LOG_INFO("Setting serial speed to 9600 baud.");
151151

152-
m_IsRTSCTS = detectRTSCTS(); // Detect RTS/CTS if applicable
153-
154152
// Get version
155153
if (!getDewPowerControllerVersion())
156154
{
@@ -181,8 +179,15 @@ bool CelestronDewPower::Handshake()
181179
{
182180
if (response.command() == PORTCTRL_GET_PORT_INFO)
183181
{
182+
// The controller only reports full telemetry (enabled/
183+
// shorted/power/voltage, >= 7 bytes) when a port is
184+
// actively drawing current; an idle port -- the normal
185+
// state at connection time -- replies with just the
186+
// 1-byte port type. Only require that one byte here so
187+
// capability detection doesn't silently misclassify
188+
// every idle port as DC output (portType 0).
184189
AUXBuffer data = response.getDataBuffer();
185-
if (data.size() >= 7)
190+
if (!data.empty())
186191
{
187192
uint8_t portType = data[0];
188193
m_PortTypes[i] = portType;
@@ -358,6 +363,13 @@ bool CelestronDewPower::processResponse(AUXCommand &m)
358363
case PORTCTRL_SET_LED_BRIGHTNESS:
359364
// These are "set" commands, no specific data to process in response beyond success/failure
360365
break;
366+
case PORTCTRL_NAK:
367+
{
368+
AUXBuffer data = m.getDataBuffer();
369+
LOGF_DEBUG("Controller NAK'd command 0x%02X (not applicable to that port).",
370+
data.empty() ? 0 : data[0]);
371+
break;
372+
}
361373
default:
362374
break;
363375
}
@@ -440,7 +452,7 @@ int CelestronDewPower::sendBuffer(AUXBuffer buf)
440452
{
441453
int n;
442454

443-
if (aux_tty_write((char * )buf.data(), buf.size(), CTS_TIMEOUT, &n) != TTY_OK)
455+
if (aux_tty_write((char * )buf.data(), buf.size(), &n) != TTY_OK)
444456
return 0;
445457

446458
std::this_thread::sleep_for(std::chrono::milliseconds(50));
@@ -478,60 +490,18 @@ bool CelestronDewPower::sendAUXCommand(AUXCommand &command)
478490
////////////////////////////////////////////////////////////////////////////////
479491
// Wrap functions around the standard driver communication functions tty_read
480492
// and tty_write.
481-
// When the communication is serial, these wrap functions implement the
482-
// Celestron hardware handshake used by telescope serial ports AUX and PC.
483-
// When the communication is by network, these wrap functions are trasparent.
484-
// Read and write calls are passed, as is, to the standard functions tty_read
485-
// and tty_write.
486-
// 16-Feb-2020 Fabrizio Pollastri <mxgbot@gmail.com>
493+
//
494+
// This controller only ever communicates over a USB CDC-ACM virtual serial
495+
// port (/dev/ttyACM*), never a real RS-232 half-duplex AUX/PC port, so
496+
// unlike INDI's CelestronAUX telescope driver (which this AUX protocol was
497+
// adapted from) there is no hardware RTS/CTS flow control or byte-echo to
498+
// handle here: USB CDC-ACM modem-control lines are virtual and commonly
499+
// read back as permanently asserted regardless of real hardware state,
500+
// which previously caused a false-positive half-duplex detection here and
501+
// broke every connection. Plain full-duplex tty_read/tty_write is correct,
502+
// matching drivers/focuser/celestronauxpacket.cpp which talks to other
503+
// Celestron AUX-bus accessories the same way.
487504
////////////////////////////////////////////////////////////////////////////////
488-
void CelestronDewPower::setRTS(bool rts)
489-
{
490-
if (ioctl(PortFD, TIOCMGET, &m_ModemControl) == -1)
491-
LOGF_ERROR("Error getting handshake lines %s(%d).", strerror(errno), errno);
492-
if (rts)
493-
m_ModemControl |= TIOCM_RTS;
494-
else
495-
m_ModemControl &= ~TIOCM_RTS;
496-
if (ioctl(PortFD, TIOCMSET, &m_ModemControl) == -1)
497-
LOGF_ERROR("Error setting handshake lines %s(%d).", strerror(errno), errno);
498-
}
499-
500-
/////////////////////////////////////////////////////////////////////////////////////
501-
///
502-
/////////////////////////////////////////////////////////////////////////////////////
503-
bool CelestronDewPower::waitCTS(float timeout)
504-
{
505-
float step = timeout / 20.;
506-
for (; timeout >= 0; timeout -= step)
507-
{
508-
std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<int>(step)));
509-
if (ioctl(PortFD, TIOCMGET, &m_ModemControl) == -1)
510-
{
511-
LOGF_ERROR("Error getting handshake lines %s(%d).", strerror(errno), errno);
512-
return 0;
513-
}
514-
if (m_ModemControl & TIOCM_CTS)
515-
return 1;
516-
}
517-
return 0;
518-
}
519-
520-
/////////////////////////////////////////////////////////////////////////////////////
521-
///
522-
/////////////////////////////////////////////////////////////////////////////////////
523-
bool CelestronDewPower::detectRTSCTS()
524-
{
525-
setRTS(1);
526-
bool retval = waitCTS(300.);
527-
setRTS(0);
528-
return retval;
529-
}
530-
531-
532-
/////////////////////////////////////////////////////////////////////////////////////
533-
///
534-
/////////////////////////////////////////////////////////////////////////////////////
535505
bool CelestronDewPower::tty_set_speed(speed_t speed)
536506
{
537507
struct termios tty_setting;
@@ -578,11 +548,6 @@ int CelestronDewPower::aux_tty_read(char *buf, int bufsiz, int timeout, int *n)
578548
int errcode;
579549
DEBUGF(DBG_SERIAL, "aux_tty_read: %d", PortFD);
580550

581-
// if hardware flow control is required, set RTS to off to receive: PC port
582-
// bahaves as half duplex.
583-
if (m_IsRTSCTS)
584-
setRTS(0);
585-
586551
if((errcode = tty_read(PortFD, buf, bufsiz, timeout, n)) != TTY_OK)
587552
{
588553
char errmsg[MAXRBUF] = {0};
@@ -596,27 +561,11 @@ int CelestronDewPower::aux_tty_read(char *buf, int bufsiz, int timeout, int *n)
596561
/////////////////////////////////////////////////////////////////////////////////////
597562
///
598563
/////////////////////////////////////////////////////////////////////////////////////
599-
int CelestronDewPower::aux_tty_write(char *buf, int bufsiz, float timeout, int *n)
564+
int CelestronDewPower::aux_tty_write(char *buf, int bufsiz, int *n)
600565
{
601-
int errcode, ne;
566+
int errcode;
602567
char errmsg[MAXRBUF];
603568

604-
//DEBUGF(DBG_CAUX, "aux_tty_write: %d", PortFD);
605-
606-
// if hardware flow control is required, set RTS to on then wait for CTS
607-
// on to write: PC port bahaves as half duplex. RTS may be already on.
608-
if (m_IsRTSCTS)
609-
{
610-
DEBUG(DBG_SERIAL, "aux_tty_write: set RTS");
611-
setRTS(1);
612-
DEBUG(DBG_SERIAL, "aux_tty_write: wait CTS");
613-
if (!waitCTS(timeout))
614-
{
615-
LOGF_ERROR("Error getting handshake lines %s(%d).\n", strerror(errno), errno);
616-
return TTY_TIME_OUT;
617-
}
618-
}
619-
620569
errcode = tty_write(PortFD, buf, bufsiz, n);
621570

622571
if (errcode != TTY_OK)
@@ -626,32 +575,6 @@ int CelestronDewPower::aux_tty_write(char *buf, int bufsiz, float timeout, int *
626575
return errcode;
627576
}
628577

629-
// if hardware flow control is required, Wait for tx complete, set RTS to
630-
// off, to receive (half duplex).
631-
if (m_IsRTSCTS)
632-
{
633-
DEBUG(DBG_SERIAL, "aux_tty_write: clear RTS");
634-
std::this_thread::sleep_for(std::chrono::milliseconds(50));
635-
setRTS(0);
636-
637-
// ports requiring hardware flow control echo all sent characters,
638-
// verify them.
639-
DEBUG(DBG_SERIAL, "aux_tty_write: verify echo");
640-
if ((errcode = tty_read(PortFD, errmsg, *n, READ_TIMEOUT, &ne)) != TTY_OK)
641-
{
642-
tty_error_msg(errcode, errmsg, MAXRBUF);
643-
LOGF_ERROR("%s", errmsg);
644-
return errcode;
645-
}
646-
647-
if (*n != ne)
648-
return TTY_WRITE_ERROR;
649-
650-
for (int i = 0; i < ne; i++)
651-
if (buf[i] != errmsg[i])
652-
return TTY_WRITE_ERROR;
653-
}
654-
655578
return TTY_OK;
656579
}
657580

@@ -739,6 +662,8 @@ bool CelestronDewPower::getPortInfo(uint8_t portNumber)
739662
if (response.command() == PORTCTRL_GET_PORT_INFO)
740663
{
741664
// RESP: <0 type><1 enabled><2 isShorted><3:4 power(mW)><5:6 VoltageLevel (mV)>
665+
// Only present in full when the port is actively drawing
666+
// current; an idle port replies with just the 1-byte type.
742667
AUXBuffer data = response.getDataBuffer();
743668
if (data.size() >= 7)
744669
{
@@ -749,6 +674,11 @@ bool CelestronDewPower::getPortInfo(uint8_t portNumber)
749674
portNumber, data[0], data[1], data[2], (data[3] << 8) | data[4], (data[5] << 8) | data[6]);
750675
return true;
751676
}
677+
else if (!data.empty())
678+
{
679+
LOGF_DEBUG("Port %d Info: Type=%d (idle, no telemetry)", portNumber, data[0]);
680+
return true;
681+
}
752682
}
753683
}
754684
return false;

drivers/power/indi-celestron-dewpower/celestron_dewpower.h

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@
3333
#include "celestron_dewpower_auxproto.h"
3434

3535
#include <vector>
36-
#include <thread>
37-
#include <chrono>
3836
#include <termios.h>
39-
#include <sys/ioctl.h>
4037

4138
class CelestronDewPower : public INDI::DefaultDevice, public INDI::PowerInterface, public INDI::WeatherInterface
4239
{
@@ -78,13 +75,10 @@ class CelestronDewPower : public INDI::DefaultDevice, public INDI::PowerInterfac
7875
bool processResponse(AUXCommand &m); // Added processResponse
7976

8077
// Serial port specific functions
81-
void setRTS(bool rts);
82-
bool waitCTS(float timeout);
83-
bool detectRTSCTS();
8478
bool tty_set_speed(speed_t speed);
8579
void hex_dump(char *buf, AUXBuffer data, size_t size);
8680
int aux_tty_read(char *buf, int bufsiz, int timeout, int *n);
87-
int aux_tty_write(char *buf, int bufsiz, float timeout, int *n);
81+
int aux_tty_write(char *buf, int bufsiz, int *n);
8882

8983
// Device specific commands
9084
bool getDewPowerControllerVersion();
@@ -112,10 +106,6 @@ class CelestronDewPower : public INDI::DefaultDevice, public INDI::PowerInterfac
112106
POWER_STATUS_OVERVOLTAGE
113107
};
114108

115-
// Communication state variables from CelestronAUX
116-
bool m_IsRTSCTS {false};
117-
int m_ModemControl {0};
118-
119109
// Store last received data to avoid unnecessary updates
120110
AUXBuffer lastInputPowerData;
121111
std::vector<AUXBuffer> lastPortInfoData;
@@ -136,6 +126,4 @@ class CelestronDewPower : public INDI::DefaultDevice, public INDI::PowerInterfac
136126
// Constants
137127
// seconds
138128
static constexpr uint8_t READ_TIMEOUT {1};
139-
// ms
140-
static constexpr uint8_t CTS_TIMEOUT {100};
141129
};

drivers/power/indi-celestron-dewpower/celestron_dewpower_auxproto.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,6 @@
2929
#include <unistd.h>
3030
#include <stdio.h>
3131

32-
#define READ_TIMEOUT 1 // s
33-
#define CTS_TIMEOUT 100 // ms
34-
#define RTS_DELAY 50 // ms
35-
3632
#define BUFFER_SIZE 512
3733
int MAX_CMD_LEN = 32;
3834

@@ -231,6 +227,8 @@ const char * AUXCommand::commandName(AUXCommands command) const
231227
return "PORTCTRL_SET_LED_BRIGHTNESS";
232228
case PORTCTRL_GET_LED_BRIGHTNESS:
233229
return "PORTCTRL_GET_LED_BRIGHTNESS";
230+
case PORTCTRL_NAK:
231+
return "PORTCTRL_NAK";
234232
default :
235233
return nullptr;
236234
}

drivers/power/indi-celestron-dewpower/celestron_dewpower_auxproto.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,26 @@ enum AUXCommands
5454
PORTCTRL_GET_VERSION = 0xFE,
5555

5656
PORTCTRL_SET_LED_BRIGHTNESS = 0x20,
57-
PORTCTRL_GET_LED_BRIGHTNESS = 0x21
57+
PORTCTRL_GET_LED_BRIGHTNESS = 0x21,
58+
59+
// Observed 2026-08-20 on real hardware: NAK/error response, sent back
60+
// instead of the requested command when that command does not apply
61+
// to the addressed port (e.g. PORTCTRL_GET_DH_PORT_INFO on a non-dew
62+
// port). Response data is 1 byte: the command that was rejected.
63+
PORTCTRL_NAK = 0xF0
5864
};
5965

6066
enum AUXTargets
6167
{
6268
HC = 0x04,
6369
APP = 0x20,
64-
DEW_POWER_CTRL = 0xc0 // New target for Celestron Dew Heater & Power Controller
70+
// Confirmed 2026-08-20 against a real Celestron Dew Heater & Power
71+
// Controller (CGX-attached, 3-port unit) via AUX destination-address
72+
// sweep + GET_VERSION/GET_NUMBER_OF_PORTS/GET_INPUT_POWER/
73+
// GET_ENVIRONMENT/GET_PORT_INFO round-trip. Not in the older
74+
// documented AUX target list (drivers/focuser/celestronauxpacket.h),
75+
// which only goes up to LIGHT = 0xBF.
76+
DEW_POWER_CTRL = 0x17
6577
};
6678

6779
#define CAUX_DEFAULT_IP "1.2.3.4"

0 commit comments

Comments
 (0)