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
31 static std::unique_ptr<FocuserDriver> focuserDriver(new FocuserDriver());
32
FocuserDriver()33 FocuserDriver::FocuserDriver()
34 {
35 // Let's specify the driver version
36 setVersion(1, 0);
37
38 // What capabilities do we support?
39 FI::SetCapability(FOCUSER_CAN_ABORT |
40 FOCUSER_CAN_ABS_MOVE |
41 FOCUSER_CAN_REL_MOVE |
42 FOCUSER_CAN_SYNC);
43 }
44
initProperties()45 bool FocuserDriver::initProperties()
46 {
47 INDI::Focuser::initProperties();
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",
52 MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE);
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
61 addDebugControl();
62
63 // Set limits as per documentation
64 FocusAbsPosN[0].min = 0;
65 FocusAbsPosN[0].max = 999999;
66 FocusAbsPosN[0].step = 1000;
67
68 FocusRelPosN[0].min = 0;
69 FocusRelPosN[0].max = 999;
70 FocusRelPosN[0].step = 100;
71
72 FocusSpeedN[0].min = 1;
73 FocusSpeedN[0].max = 254;
74 FocusSpeedN[0].step = 10;
75
76 return true;
77 }
78
getDefaultName()79 const char *FocuserDriver::getDefaultName()
80 {
81 return "Focuser Driver";
82 }
83
updateProperties()84 bool FocuserDriver::updateProperties()
85 {
86 if (isConnected())
87 {
88 // Read these values before defining focuser interface properties
89 readPosition();
90 }
91
92 INDI::Focuser::updateProperties();
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
Handshake()120 bool FocuserDriver::Handshake()
121 {
122 // This functin 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
sendCommand(const char * cmd,char * res,int cmd_len,int res_len)139 bool 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
hexDump(char * buf,const char * data,int size)198 void 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
ISNewSwitch(const char * dev,const char * name,ISState * states,char * names[],int n)207 bool 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
getStartupValues()224 bool 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
MoveAbsFocuser(uint32_t targetTicks)235 IPState FocuserDriver::MoveAbsFocuser(uint32_t targetTicks)
236 {
237 // Issue here the command necessary to move the focuser to targetTicks
238 return IPS_BUSY;
239 }
240
MoveRelFocuser(FocusDirection dir,uint32_t ticks)241 IPState FocuserDriver::MoveRelFocuser(FocusDirection dir, uint32_t ticks)
242 {
243 m_TargetDiff = ticks * ((dir == FOCUS_INWARD) ? -1 : 1);
244 return MoveAbsFocuser(FocusAbsPosN[0].value + m_TargetDiff);
245 }
246
AbortFocuser()247 bool FocuserDriver::AbortFocuser()
248 {
249 return sendCommand("FOOBAR");
250 }
251
TimerHit()252 void FocuserDriver::TimerHit()
253 {
254 if (isConnected() == false)
255 return;
256
257 // What is the last read position?
258 double currentPosition = FocusAbsPosN[0].value;
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.s == IPS_BUSY || FocusRelPosNP.s == IPS_BUSY) && isMoving() == false)
267 {
268 FocusAbsPosNP.s = IPS_OK;
269 FocusRelPosNP.s = IPS_OK;
270 IDSetNumber(&FocusAbsPosNP, nullptr);
271 IDSetNumber(&FocusRelPosNP, nullptr);
272 }
273 // If there was a different between last and current positions, let's update all clients
274 else if (currentPosition != FocusAbsPosN[0].value)
275 {
276 IDSetNumber(&FocusAbsPosNP, nullptr);
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
287 SetTimer(getCurrentPollingPeriod());
288 }
289
isMoving()290 bool 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
readTemperature()302 bool 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
readPosition()324 bool 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 FocusAbsPosN[0].value = pos;
348
349 return true;
350 }
351
readStepping()352 bool 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
SyncFocuser(uint32_t ticks)375 bool 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
setStepping(SteppingMode mode)382 bool 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
saveConfigItems(FILE * fp)389 bool FocuserDriver::saveConfigItems(FILE *fp)
390 {
391 INDI::Focuser::saveConfigItems(fp);
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 }
399