Instrument Neutral Distributed Interface INDI 2.2.5
focuser_driver.cpp
Go to the documentation of this file.
1/*
2 FocuserDriver Focuser
3
4 Copyright(c) 2019 Jasem Mutlaq. All rights reserved.
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#include "focuser_driver.h"
22
23#include "indicom.h"
24
25#include <cstring>
26#include <termios.h>
27#include <memory>
28#include <thread>
29#include <chrono>
30
31static std::unique_ptr<FocuserDriver> focuserDriver(new FocuserDriver());
32
34{
35 // Let's specify the driver version
36 setVersion(1, 0);
37
38 // What capabilities do we support?
43}
44
46{
48
49 // Focuser temperature
50 IUFillNumber(&TemperatureN[0], "TEMPERATURE", "Celsius", "%6.2f", -100, 100, 0, 0);
51 IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature",
53
54 // Stepping Modes
55 IUFillSwitch(&SteppingModeS[STEPPING_FULL], "STEPPING_FULL", "Full", ISS_ON);
56 IUFillSwitch(&SteppingModeS[STEPPING_HALF], "STEPPING_HALF", "Half", ISS_OFF);
57 IUFillSwitchVector(&SteppingModeSP, SteppingModeS, 2, getDeviceName(), "STEPPING_MODE", "Mode",
58 STEPPING_TAB, IP_RW, ISR_1OFMANY, 0, IPS_OK);
59
60
62
63 // Set limits as per documentation
64 FocusAbsPosNP[0].setMin(0);
65 FocusAbsPosNP[0].setMax(999999);
66 FocusAbsPosNP[0].setStep(1000);
67
68 FocusRelPosNP[0].setMin(0);
69 FocusRelPosNP[0].setMax(999);
70 FocusRelPosNP[0].setStep(100);
71
72 FocusSpeedNP[0].setMin(1);
73 FocusSpeedNP[0].setMax(254);
74 FocusSpeedNP[0].setStep(10);
75
76 return true;
77}
78
80{
81 return "Focuser Driver";
82}
83
85{
86 if (isConnected())
87 {
88 // Read these values before defining focuser interface properties
89 readPosition();
90 }
91
93
94 if (isConnected())
95 {
96 if (readTemperature())
97 defineProperty(&TemperatureNP);
98
99 bool rc = getStartupValues();
100
101 // Settings
102 defineProperty(&SteppingModeSP);
103
104 if (rc)
105 LOG_INFO("FocuserDriver is ready.");
106 else
107 LOG_WARN("Failed to query startup values.");
108 }
109 else
110 {
111 if (TemperatureNP.s == IPS_OK)
112 deleteProperty(TemperatureNP.name);
113
114 deleteProperty(SteppingModeSP.name);
115 }
116
117 return true;
118}
119
121{
122 // This function is ensure that we have communication with the focuser
123 // Below we send it 0x6 byte and check for 'S' in the return. Change this
124 // to be valid for your driver. It could be anything, you can simply put this below
125 // return readPosition()
126 // since this will try to read the position and if successful, then communicatoin is OK.
127 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
128
129 // Ack
130 cmd[0] = 0x6;
131
132 bool rc = sendCommand(cmd, res, 1, 1);
133 if (rc == false)
134 return false;
135
136 return res[0] == 'S';
137}
138
139bool FocuserDriver::sendCommand(const char * cmd, char * res, int cmd_len, int res_len)
140{
141 int nbytes_written = 0, nbytes_read = 0, rc = -1;
142
143 tcflush(PortFD, TCIOFLUSH);
144
145 if (cmd_len > 0)
146 {
147 char hex_cmd[DRIVER_LEN * 3] = {0};
148 hexDump(hex_cmd, cmd, cmd_len);
149 LOGF_DEBUG("CMD <%s>", hex_cmd);
150 rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
151 }
152 else
153 {
154 LOGF_DEBUG("CMD <%s>", cmd);
155 rc = tty_write_string(PortFD, cmd, &nbytes_written);
156 }
157
158 if (rc != TTY_OK)
159 {
160 char errstr[MAXRBUF] = {0};
161 tty_error_msg(rc, errstr, MAXRBUF);
162 LOGF_ERROR("Serial write error: %s.", errstr);
163 return false;
164 }
165
166 if (res == nullptr)
167 return true;
168
169 if (res_len > 0)
170 rc = tty_read(PortFD, res, res_len, DRIVER_TIMEOUT, &nbytes_read);
171 else
172 rc = tty_nread_section(PortFD, res, DRIVER_LEN, DRIVER_STOP_CHAR, DRIVER_TIMEOUT, &nbytes_read);
173
174 if (rc != TTY_OK)
175 {
176 char errstr[MAXRBUF] = {0};
177 tty_error_msg(rc, errstr, MAXRBUF);
178 LOGF_ERROR("Serial read error: %s.", errstr);
179 return false;
180 }
181
182 if (res_len > 0)
183 {
184 char hex_res[DRIVER_LEN * 3] = {0};
185 hexDump(hex_res, res, res_len);
186 LOGF_DEBUG("RES <%s>", hex_res);
187 }
188 else
189 {
190 LOGF_DEBUG("RES <%s>", res);
191 }
192
193 tcflush(PortFD, TCIOFLUSH);
194
195 return true;
196}
197
198void FocuserDriver::hexDump(char * buf, const char * data, int size)
199{
200 for (int i = 0; i < size; i++)
201 sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
202
203 if (size > 0)
204 buf[3 * size - 1] = '\0';
205}
206
207bool FocuserDriver::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
208{
209 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
210 {
211 // Stepping Mode
212 if (!strcmp(name, SteppingModeSP.name))
213 {
214 IUUpdateSwitch(&SteppingModeSP, states, names, n);
215 SteppingModeSP.s = IPS_OK;
216 IDSetSwitch(&SteppingModeSP, nullptr);
217 return true;
218 }
219 }
220
221 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
222}
223
224bool FocuserDriver::getStartupValues()
225{
226 bool rc1 = readStepping();
227 //bool rc2 = readFoo();
228 //bool rc2 = readBar();
229
230 //return (rc1 && rc2 && rc3);
231
232 return rc1;
233}
234
236{
237 // Issue here the command necessary to move the focuser to targetTicks
238 return IPS_BUSY;
239}
240
242{
243 m_TargetDiff = ticks * ((dir == FOCUS_INWARD) ? -1 : 1);
244 return MoveAbsFocuser(FocusAbsPosNP[0].getValue() + m_TargetDiff);
245}
246
248{
249 return sendCommand("FOOBAR");
250}
251
253{
254 if (isConnected() == false)
255 return;
256
257 // What is the last read position?
258 double currentPosition = FocusAbsPosNP[0].getValue();
259
260 // Read the current position
261 readPosition();
262
263 // Check if we have a pending motion
264 // if isMoving() is false, then we stopped, so we need to set the Focus Absolute
265 // and relative properties to OK
266 if ( (FocusAbsPosNP.getState() == IPS_BUSY || FocusRelPosNP.getState() == IPS_BUSY) && isMoving() == false)
267 {
272 }
273 // If there was a different between last and current positions, let's update all clients
274 else if (currentPosition != FocusAbsPosNP[0].getValue())
275 {
277 }
278
279 // Read temperature periodically
280 if (TemperatureNP.s == IPS_OK && m_TemperatureCounter++ == DRIVER_TEMPERATURE_FREQ)
281 {
282 m_TemperatureCounter = 0;
283 if (readTemperature())
284 IDSetNumber(&TemperatureNP, nullptr);
285 }
286
288}
289
290bool FocuserDriver::isMoving()
291{
292 char res[DRIVER_LEN] = {0};
293
294 bool rc = sendCommand("FOOBAR", res, 1, 1);
295
296 if (rc && !strcmp(res, "STOPPED"))
297 return true;
298
299 return false;
300}
301
302bool FocuserDriver::readTemperature()
303{
304 char res[DRIVER_LEN] = {0};
305
306 // This assumes we need to read 4 BYTES for the temperature. It can be anything
307 // If the response is terminated by the DRIVER_STOP_CHAR, we can simply call
308 // sendCommand("FOOBAR", res)
309 if (sendCommand("FOOBAR", res, strlen("FOOBAR"), 4) == false)
310 return false;
311
312 float temperature = -1000;
313 sscanf(res, "%f", &temperature);
314
315 if (temperature < -100)
316 return false;
317
318 TemperatureN[0].value = temperature;
319 TemperatureNP.s = IPS_OK;
320
321 return true;
322}
323
324bool FocuserDriver::readPosition()
325{
326 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
327
328 cmd[0] = 0xA;
329 cmd[1] = 0xB;
330 cmd[2] = 0xC;
331
332 // since the command above is not NULL-TERMINATED, we need to specify the number of bytes (3)
333 // in the send command below. We also specify 7 bytes to be read which can be changed to any value.
334 if (sendCommand(cmd, res, 3, 7) == false)
335 return false;
336
337 // For above, in case instead the response is terminated by DRIVER_STOP_CHAR, then the command would be
338 // (sendCommand(cmd, res, 3) == false)
339 // return false;
340
341 int32_t pos = 1e6;
342 sscanf(res, "%d", &pos);
343
344 if (pos == 1e6)
345 return false;
346
347 FocusAbsPosNP[0].setValue(pos);
348
349 return true;
350}
351
352bool FocuserDriver::readStepping()
353{
354 char res[DRIVER_LEN] = {0};
355
356 if (sendCommand("FOOBAR", res, 3, 1) == false)
357 return false;
358
359 int32_t mode = 1e6;
360 sscanf(res, "%d", &mode);
361
362 if (mode == 1e6)
363 return false;
364
365 // Assuming the above function returns 10 for full step, and 11 for half step
366 // we can update the switch status as follows
367 SteppingModeS[STEPPING_FULL].s = (mode == 10) ? ISS_ON : ISS_OFF;
368 SteppingModeS[STEPPING_HALF].s = (mode == 10) ? ISS_OFF : ISS_ON;
369 SteppingModeSP.s = IPS_OK;
370
371 return true;
372}
373
374
375bool FocuserDriver::SyncFocuser(uint32_t ticks)
376{
377 char cmd[DRIVER_LEN] = {0};
378 snprintf(cmd, DRIVER_LEN, "#:SYNC+%06d#", ticks);
379 return sendCommand(cmd);
380}
381
382bool FocuserDriver::setStepping(SteppingMode mode)
383{
384 char cmd[DRIVER_LEN] = {0};
385 snprintf(cmd, DRIVER_LEN, "#FOOBAR%01d#", mode);
386 return sendCommand(cmd);
387}
388
390{
392
393 // We need to reserve and save stepping mode
394 // so that the next time the driver is loaded, it is remembered and applied.
395 IUSaveConfigSwitch(fp, &SteppingModeSP);
396
397 return true;
398}
double temperature
bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
const char * getDefaultName() override
bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
bool AbortFocuser() override
AbortFocuser all focus motion.
bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
bool saveConfigItems(FILE *fp) override
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
bool isConnected() const
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.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
INDI::PropertyNumber FocusRelPosNP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusSpeedNP
INDI::PropertyNumber FocusAbsPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool saveConfigItems(FILE *fp) override
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:759
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition indidevapi.c:490
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.
Definition indidevapi.c:376
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition indidevapi.c:398
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.
Definition indidevapi.c:453
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:369