Instrument Neutral Distributed Interface INDI 2.2.5
indi_astrolink4mini2.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2022 astrojolo.com
3 .
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7 .
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12 .
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
17*******************************************************************************/
19
20#include "indicom.h"
21
22#define VERSION_MAJOR 0
23#define VERSION_MINOR 2
24
25#define ASTROLINK4_LEN 200
26#define ASTROLINK4_TIMEOUT 3
27
28#define POLLTIME 500
29
33std::unique_ptr<IndiAstroLink4mini2> indiFocuserLink(new IndiAstroLink4mini2());
34
42
44{
45 return "AstroLink 4 mini II";
46}
47
51bool IndiAstroLink4mini2::Handshake()
52{
53 PortFD = serialConnection->getPortFD();
54
55 char res[ASTROLINK4_LEN] = {0};
56 if (sendCommand("#", res))
57 {
58 if (strncmp(res, "#:AstroLink4mini", 16) != 0)
59 {
60 DEBUG(INDI::Logger::DBG_ERROR, "Device not recognized.");
61 return false;
62 }
63 else
64 {
65 DEBUG(INDI::Logger::DBG_DEBUG, "Handshake success");
67 return true;
68 }
69 }
70 return false;
71}
72
74{
75 if (isConnected())
76 {
77 readDevice();
79 }
80}
81
86{
88
90
91 char focuserSelectLabel[15];
92 memset(focuserSelectLabel, 0, 15);
93 setFindex(IUGetConfigOnSwitchName(getDeviceName(), FocuserSelectSP.getName(), focuserSelectLabel, 15) == 0 ? 0 : 1);
94
100 //FOCUSER_HAS_BACKLASH);
101
103 WI::initProperties(ENVIRONMENT_TAB, ENVIRONMENT_TAB);
104
108
109 serialConnection = new Connection::Serial(this);
110 serialConnection->registerHandshake([&]()
111 {
112 return Handshake();
113 });
114 registerConnection(serialConnection);
115
116 serialConnection->setDefaultPort("/dev/ttyUSB0");
117 serialConnection->setDefaultBaudRate(serialConnection->B_38400);
118
119 FocuserSelectSP[FOC_SEL_1].fill("FOC_SEL_1", "Focuser 1", (getFindex() == 0 ? ISS_ON : ISS_OFF));
120 FocuserSelectSP[FOC_SEL_2].fill("FOC_SEL_2", "Focuser 2", (getFindex() > 0 ? ISS_ON : ISS_OFF));
121 FocuserSelectSP.fill(getDeviceName(), "FOCUSER_SELECT", "Focuser select", FOCUS_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
122
123 // Power readings
124 PowerDataNP[POW_VIN].fill("VIN", "Input voltage [V]", "%.1f", 0, 15, 10, 0);
125 PowerDataNP[POW_REG].fill("REG", "Regulated voltage [V]", "%.1f", 0, 15, 10, 0);
126 PowerDataNP[POW_ITOT].fill("ITOT", "Total current [A]", "%.1f", 0, 15, 10, 0);
127 PowerDataNP[POW_AH].fill("AH", "Energy consumed [Ah]", "%.1f", 0, 1000, 10, 0);
128 PowerDataNP[POW_WH].fill("WH", "Energy consumed [Wh]", "%.1f", 0, 10000, 10, 0);
129 PowerDataNP.fill(getDeviceName(), "POWER_DATA", "Power data", POWER_TAB, IP_RO, 60, IPS_IDLE);
130
131 // Power lines
132 Power1SP[PWR1BTN_ON].fill("PWR1BTN_ON", "ON", ISS_OFF);
133 Power1SP[PWR1BTN_OFF].fill( "PWR1BTN_OFF", "OFF", ISS_ON);
134 Power1SP.fill(getDeviceName(), "DC1", "Port 1", POWER_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
135
136 Power2SP[PWR2BTN_ON].fill("PWR2BTN_ON", "ON", ISS_OFF);
137 Power2SP[PWR2BTN_OFF].fill("PWR2BTN_OFF", "OFF", ISS_ON);
138 Power2SP.fill(getDeviceName(), "DC2", "Port 2", POWER_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
139
140 Power3SP[PWR3BTN_ON].fill( "PWR3BTN_ON", "ON", ISS_OFF);
141 Power3SP[PWR3BTN_OFF].fill("PWR3BTN_OFF", "OFF", ISS_ON);
142 Power3SP.fill(getDeviceName(), "DC3", "Port 3", POWER_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
143
144 PWMNP[PWM1_VAL].fill("PWM1_VAL", "A", "%3.0f", 0, 100, 10, 0);
145 PWMNP[PWM2_VAL].fill("PWM2_VAL", "B", "%3.0f", 0, 100, 10, 0);
146 PWMNP.fill(getDeviceName(), "PWM", "PWM", POWER_TAB, IP_RW, 60, IPS_IDLE);
147
148 PowerDefaultOnSP[POW_DEF_ON1].fill("POW_DEF_ON1", "DC1", ISS_OFF);
149 PowerDefaultOnSP[POW_DEF_ON2].fill("POW_DEF_ON2", "DC2", ISS_OFF);
150 PowerDefaultOnSP[POW_DEF_ON3].fill("POW_DEF_ON3", "DC3", ISS_OFF);
151 PowerDefaultOnSP.fill(getDeviceName(), "POW_DEF_ON", "Power default ON", POWER_TAB, IP_RW, ISR_NOFMANY, 60, IPS_IDLE);
152
153 // focuser settings
154 Focuser1SettingsNP[FS1_SPEED].fill("FS1_SPEED", "Speed [pps]", "%.0f", 10, 200, 1, 100);
155 Focuser1SettingsNP[FS1_CURRENT].fill("FS1_CURRENT", "Current [mA]", "%.0f", 100, 2000, 100, 400);
156 Focuser1SettingsNP[FS1_HOLD].fill("FS1_HOLD", "Hold torque [%]", "%.0f", 0, 100, 10, 0);
157 Focuser1SettingsNP[FS1_STEP_SIZE].fill("FS1_STEP_SIZE", "Step size [um]", "%.2f", 0, 100, 0.1, 5.0);
158 Focuser1SettingsNP[FS1_COMPENSATION].fill("FS1_COMPENSATION", "Compensation [steps/C]", "%.2f", -1000, 1000, 1, 0);
159 Focuser1SettingsNP[FS1_COMP_THRESHOLD].fill("FS1_COMP_THRESHOLD", "Compensation threshold [steps]", "%.0f", 1, 1000, 10,
160 10);
161 Focuser1SettingsNP.fill(getDeviceName(), "FOCUSER1_SETTINGS", "Focuser 1 settings", FOC1_SETTINGS_TAB, IP_RW, 60, IPS_IDLE);
162
163 Focuser2SettingsNP[FS2_SPEED].fill("FS2_SPEED", "Speed [pps]", "%.0f", 10, 200, 1, 100);
164 Focuser2SettingsNP[FS2_CURRENT].fill("FS2_CURRENT", "Current [mA]", "%.0f", 100, 2000, 100, 400);
165 Focuser2SettingsNP[FS2_HOLD].fill("FS2_HOLD", "Hold torque [%]", "%.0f", 0, 100, 10, 0);
166 Focuser2SettingsNP[FS2_STEP_SIZE].fill("FS2_STEP_SIZE", "Step size [um]", "%.2f", 0, 100, 0.1, 5.0);
167 Focuser2SettingsNP[FS2_COMPENSATION].fill("FS2_COMPENSATION", "Compensation [steps/C]", "%.2f", -1000, 1000, 1, 0);
168 Focuser2SettingsNP[FS2_COMP_THRESHOLD].fill("FS2_COMP_THRESHOLD", "Compensation threshold [steps]", "%.0f", 1, 1000, 10,
169 10);
170 Focuser2SettingsNP.fill(getDeviceName(), "FOCUSER2_SETTINGS", "Focuser 2 settings", FOC2_SETTINGS_TAB, IP_RW, 60, IPS_IDLE);
171
172 Focuser1ModeSP[FS1_MODE_UNI].fill("FS1_MODE_UNI", "Unipolar", ISS_ON);
173 Focuser1ModeSP[FS1_MODE_MICRO_L].fill("FS1_MODE_MICRO_L", "Microstep 1/8", ISS_OFF);
174 Focuser1ModeSP[FS1_MODE_MICRO_H].fill( "FS1_MODE_MICRO_H", "Microstep 1/32", ISS_OFF);
175 Focuser1ModeSP.fill(getDeviceName(), "FOCUSER1_MODE", "Focuser mode", FOC1_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
176
177 Focuser2ModeSP[FS2_MODE_UNI].fill("FS2_MODE_UNI", "Unipolar", ISS_ON);
178 Focuser2ModeSP[FS2_MODE_MICRO_L].fill("FS2_MODE_MICRO_L", "Microstep 1/8", ISS_OFF);
179 Focuser2ModeSP[FS2_MODE_MICRO_H].fill("FS2_MODE_MICRO_H", "Microstep 1/32", ISS_OFF);
180 Focuser2ModeSP.fill(getDeviceName(), "FOCUSER2_MODE", "Focuser mode", FOC2_SETTINGS_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
181
182 // Environment Group
183 addParameter("WEATHER_TEMPERATURE", "Temperature (C)", -15, 35, 15);
184 addParameter("WEATHER_HUMIDITY", "Humidity %", 0, 100, 15);
185 addParameter("WEATHER_DEWPOINT", "Dew Point (C)", 0, 100, 15);
186
187 return true;
188}
189
191{
192 // Call parent update properties first
194
195 if (isConnected())
196 {
199 defineProperty(FocuserSelectSP);
200 defineProperty(Focuser1SettingsNP);
201 defineProperty(Focuser2SettingsNP);
202 defineProperty(Focuser1ModeSP);
203 defineProperty(Focuser2ModeSP);
204 defineProperty(PowerDataNP);
205 defineProperty(Power1SP);
206 defineProperty(Power2SP);
207 defineProperty(Power3SP);
208 defineProperty(PWMNP);
209 defineProperty(PowerDefaultOnSP);
210 }
211 else
212 {
213 deleteProperty(PowerDataNP);
214 deleteProperty(Focuser1SettingsNP);
215 deleteProperty(Focuser2SettingsNP);
216 deleteProperty(Focuser1ModeSP);
217 deleteProperty(Focuser2ModeSP);
218 deleteProperty(FocuserSelectSP);
219 deleteProperty(Power1SP);
220 deleteProperty(Power2SP);
221 deleteProperty(Power3SP);
222 deleteProperty(PWMNP);
223 deleteProperty(PowerDefaultOnSP);
226 }
227
228 return true;
229}
230
231bool IndiAstroLink4mini2::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
232{
233 if (dev && !strcmp(dev, getDeviceName()))
234 {
235 char cmd[ASTROLINK4_LEN] = {0};
236 char res[ASTROLINK4_LEN] = {0};
237
238 // Handle PWM
239 if (PWMNP.isNameMatch(name))
240 {
241 bool allOk = true;
242 if (PWMNP[PWM1_VAL].getValue() != values[0])
243 {
244 sprintf(cmd, "B:0:%d", static_cast<uint8_t>(values[0]));
245 allOk = allOk && sendCommand(cmd, res);
246 }
247 if (PWMNP[PWM2_VAL].getValue() != values[1])
248 {
249 sprintf(cmd, "B:1:%d", static_cast<uint8_t>(values[1]));
250 allOk = allOk && sendCommand(cmd, res);
251 }
252 PWMNP.setState((allOk) ? IPS_BUSY : IPS_ALERT);
253 if (allOk)
254 PWMNP.update(values, names, n);
255 PWMNP.apply();
256 return true;
257 }
258
259 // Focuser settings
260 if (Focuser1SettingsNP.isNameMatch(name))
261 {
262 bool allOk = true;
263 std::map<int, std::string> updates;
264 updates[U_FOC1_STEP] = doubleToStr(values[FS1_STEP_SIZE] * 100.0);
265 updates[U_FOC1_COMPSTEPS] = doubleToStr(values[FS1_COMPENSATION] * 100.0);
266 updates[U_FOC1_COMPTRIGGER] = doubleToStr(values[FS1_COMP_THRESHOLD]);
267 updates[U_FOC1_SPEED] = intToStr(values[FS1_SPEED]);
268 updates[U_FOC1_ACC] = intToStr(values[FS1_SPEED] * 5.0);
269 updates[U_FOC1_CUR] = intToStr(values[FS1_CURRENT] / 10.0);
270 updates[U_FOC1_HOLD] = intToStr(values[FS1_HOLD]);
271 allOk = allOk && updateSettings("u", "U", updates);
272 updates.clear();
273 if (allOk)
274 {
275 Focuser1SettingsNP.setState(IPS_BUSY);
276 Focuser1SettingsNP.update(values, names, n);
277 Focuser1SettingsNP.apply();
278 DEBUGF(INDI::Logger::DBG_SESSION, "Focuser 1 temperature compensation is %s",
279 (values[FS1_COMPENSATION] > 0) ? "enabled" : "disabled");
280 return true;
281 }
282 Focuser1SettingsNP.setState(IPS_ALERT);
283 return true;
284 }
285
286 if (Focuser2SettingsNP.isNameMatch(name))
287 {
288 bool allOk = true;
289 std::map<int, std::string> updates;
290 updates[U_FOC2_STEP] = doubleToStr(values[FS2_STEP_SIZE] * 100.0);
291 updates[U_FOC2_COMPSTEPS] = doubleToStr(values[FS2_COMPENSATION] * 100.0);
292 updates[U_FOC2_COMPTRIGGER] = doubleToStr(values[FS2_COMP_THRESHOLD]);
293 updates[U_FOC2_SPEED] = intToStr(values[FS2_SPEED]);
294 updates[U_FOC2_ACC] = intToStr(values[FS2_SPEED] * 5.0);
295 updates[U_FOC2_CUR] = intToStr(values[FS2_CURRENT] / 10.0);
296 updates[U_FOC2_HOLD] = intToStr(values[FS2_HOLD]);
297 allOk = allOk && updateSettings("u", "U", updates);
298 updates.clear();
299 if (allOk)
300 {
301 Focuser2SettingsNP.setState(IPS_BUSY);
302 Focuser2SettingsNP.update(values, names, n);
303 Focuser2SettingsNP.apply();
304 DEBUGF(INDI::Logger::DBG_SESSION, "Focuser 2 temperature compensation is %s",
305 (values[FS2_COMPENSATION] > 0) ? "enabled" : "disabled");
306 return true;
307 }
308 Focuser2SettingsNP.setState(IPS_ALERT);
309 return true;
310 }
311
312 if (strstr(name, "FOCUS"))
313 return FI::processNumber(dev, name, values, names, n);
314 }
315 return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
316}
317
318bool IndiAstroLink4mini2::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
319{
320 if (dev && !strcmp(dev, getDeviceName()))
321 {
322 char cmd[ASTROLINK4_LEN] = {0};
323 char res[ASTROLINK4_LEN] = {0};
324
325 // handle power line 1
326 if (Power1SP.isNameMatch(name))
327 {
328 sprintf(cmd, "C:0:%s", (strcmp(Power1SP[PWR1BTN_ON].getName(), names[0])) ? "0" : "1");
329 bool allOk = sendCommand(cmd, res);
330 Power1SP.setState(allOk ? IPS_BUSY : IPS_ALERT);
331 if (allOk)
332 Power1SP.update(states, names, n);
333
334 Power1SP.apply();
335 return true;
336 }
337
338 // handle power line 2
339 if (Power2SP.isNameMatch(name))
340 {
341 sprintf(cmd, "C:1:%s", (strcmp(Power2SP[PWR2BTN_ON].getName(), names[0])) ? "0" : "1");
342 bool allOk = sendCommand(cmd, res);
343 Power2SP.setState(allOk ? IPS_BUSY : IPS_ALERT);
344 if (allOk)
345 Power2SP.update(states, names, n);
346
347 Power2SP.apply();
348 return true;
349 }
350
351 // handle power line 3
352 if (Power3SP.isNameMatch(name))
353 {
354 sprintf(cmd, "C:2:%s", (strcmp(Power3SP[PWR3BTN_ON].getName(), names[0])) ? "0" : "1");
355 bool allOk = sendCommand(cmd, res);
356 Power3SP.setState(allOk ? IPS_BUSY : IPS_ALERT);
357 if (allOk)
358 Power3SP.update(states, names, n);
359
360 Power3SP.apply();
361 return true;
362 }
363
364 // Power default on
365 if (PowerDefaultOnSP.isNameMatch(name))
366 {
367 std::map<int, std::string> updates;
368 updates[U_OUT1_DEF] = (states[0] == ISS_ON) ? "1" : "0";
369 updates[U_OUT2_DEF] = (states[1] == ISS_ON) ? "1" : "0";
370 updates[U_OUT3_DEF] = (states[2] == ISS_ON) ? "1" : "0";
371 if (updateSettings("u", "U", updates))
372 {
373 PowerDefaultOnSP.setState(IPS_BUSY);
374 PowerDefaultOnSP.update(states, names, n);
375 PowerDefaultOnSP.apply();
376 return true;
377 }
378 PowerDefaultOnSP.setState(IPS_ALERT);
379 return true;
380 }
381
382 // Focuser Mode
383 if (Focuser1ModeSP.isNameMatch(name))
384 {
385 std::string value = "0";
386 if (!strcmp(Focuser1ModeSP[FS1_MODE_UNI].getName(), names[0]))
387 value = "0";
388 if (!strcmp(Focuser1ModeSP[FS1_MODE_MICRO_L].getName(), names[0]))
389 value = "1";
390 if (!strcmp(Focuser1ModeSP[FS1_MODE_MICRO_H].getName(), names[0]))
391 value = "2";
392 if (updateSettings("u", "U", U_FOC1_MODE, value.c_str()))
393 {
394 Focuser1ModeSP.setState(IPS_BUSY);
395 Focuser1ModeSP.update(states, names, n);
396 Focuser1ModeSP.apply();
397 return true;
398 }
399 Focuser1ModeSP.setState(IPS_ALERT);
400 return true;
401 }
402 if (Focuser2ModeSP.isNameMatch(name))
403 {
404 std::string value = "0";
405 if (!strcmp(Focuser2ModeSP[FS2_MODE_UNI].getName(), names[0]))
406 value = "0";
407 if (!strcmp(Focuser2ModeSP[FS2_MODE_MICRO_L].getName(), names[0]))
408 value = "1";
409 if (!strcmp(Focuser2ModeSP[FS2_MODE_MICRO_H].getName(), names[0]))
410 value = "2";
411 if (updateSettings("u", "U", U_FOC2_MODE, value.c_str()))
412 {
413 Focuser2ModeSP.setState(IPS_BUSY);
414 Focuser2ModeSP.update(states, names, n);
415 Focuser2ModeSP.apply();
416 return true;
417 }
418 Focuser2ModeSP.setState(IPS_ALERT);
419 return true;
420 }
421
422 // Stepper select
423 if (FocuserSelectSP.isNameMatch(name))
424 {
425 setFindex((strcmp(FocuserSelectSP[0].getName(), names[0])) ? 1 : 0);
426 DEBUGF(INDI::Logger::DBG_DEBUG, "Focuser index set by switch to %i", getFindex());
430 FocuserSelectSP.setState(FocusMaxPosNP.getState());
431
432 FocuserSelectSP.update(states, names, n);
433 FocuserSelectSP.apply();
437
438 return true;
439 }
440
441 if (strstr(name, "FOCUS"))
442 return FI::processSwitch(dev, name, states, names, n);
443 }
444 return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
445}
446
447bool IndiAstroLink4mini2::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
448{
449 return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
450}
451
453{
454 FocuserSelectSP.save(fp);
457 return true;
458}
459
464{
465 char cmd[ASTROLINK4_LEN] = {0}, res[ASTROLINK4_LEN] = {0};
466 snprintf(cmd, ASTROLINK4_LEN, "R:%i:%u", getFindex(), targetTicks);
467 return (sendCommand(cmd, res)) ? IPS_BUSY : IPS_ALERT;
468}
469
471{
472 return MoveAbsFocuser(dir == FOCUS_INWARD ? FocusAbsPosNP[0].getValue() - ticks : FocusAbsPosNP[0].getValue() + ticks);
473}
474
476{
477 char cmd[ASTROLINK4_LEN] = {0}, res[ASTROLINK4_LEN] = {0};
478 snprintf(cmd, ASTROLINK4_LEN, "H:%i", getFindex());
479 return (sendCommand(cmd, res));
480}
481
483{
484 int index = getFindex() > 0 ? U_FOC2_REV : U_FOC1_REV;
485 if (updateSettings("u", "U", index, (enabled) ? "1" : "0"))
486 {
488 return true;
489 }
490 else
491 {
492 return false;
493 }
494}
495
497{
498 char cmd[ASTROLINK4_LEN] = {0}, res[ASTROLINK4_LEN] = {0};
499 snprintf(cmd, ASTROLINK4_LEN, "P:%i:%u", getFindex(), ticks);
500 if (sendCommand(cmd, res))
501 {
503 return true;
504 }
505 else
506 {
507 return false;
508 }
509}
510
512{
513 int index = getFindex() > 0 ? U_FOC2_MAX : U_FOC1_MAX;
514 if (updateSettings("u", "U", index, std::to_string(ticks).c_str()))
515 {
517 return true;
518 }
519 else
520 {
521 return false;
522 }
523}
524
526{
527 INDI_UNUSED(steps);
528 return true;
529}
530
532{
533 INDI_UNUSED(enabled);
534 return true;
535}
536
540bool IndiAstroLink4mini2::sendCommand(const char *cmd, char *res)
541{
542 int nbytes_read = 0, nbytes_written = 0, tty_rc = 0;
543 char command[ASTROLINK4_LEN];
544
545 if (isSimulation())
546 {
547 if (strncmp(cmd, "#", 1) == 0)
548 sprintf(res, "%s\n", "#:AstroLink4mini");
549 if (strncmp(cmd, "q", 1) == 0)
550 sprintf(res, "%s\n",
551 "q:AL4MII:1234:0:5678:0:3.14:1:23.12:45:9.11:1:19.19:35:80:1:0:1:12.11:7.62:20.01:132.11:33:0:0:0:1:-10.1:7.7:1:19.19:35:8.22:1:1:18.11");
552 if (strncmp(cmd, "p", 1) == 0)
553 sprintf(res, "%s\n", "p:1234");
554 if (strncmp(cmd, "i", 1) == 0)
555 sprintf(res, "%s\n", "i:0");
556 if (strncmp(cmd, "u", 1) == 0)
557 sprintf(res, "%s\n",
558 "u:1:1:80:120:30:50:200:800:200:800:0:2:10000:80000:0:0:50:18:30:15:5:10:10:0:1:0:0:0:0:0:0:0:40:90:10:1100:14000:10000:100:0");
559 if (strncmp(cmd, "A", 1) == 0)
560 sprintf(res, "%s\n", "A:4.5.0 mini II");
561 if (strncmp(cmd, "R", 1) == 0)
562 sprintf(res, "%s\n", "R:");
563 if (strncmp(cmd, "C", 1) == 0)
564 sprintf(res, "%s\n", "C:");
565 if (strncmp(cmd, "B", 1) == 0)
566 sprintf(res, "%s\n", "B:");
567 if (strncmp(cmd, "H", 1) == 0)
568 sprintf(res, "%s\n", "H:");
569 if (strncmp(cmd, "P", 1) == 0)
570 sprintf(res, "%s\n", "P:");
571 if (strncmp(cmd, "U", 1) == 0)
572 sprintf(res, "%s\n", "U:");
573 if (strncmp(cmd, "S", 1) == 0)
574 sprintf(res, "%s\n", "S:");
575 }
576 else
577 {
578 tcflush(PortFD, TCIOFLUSH);
579 sprintf(command, "%s\n", cmd);
581 if ((tty_rc = tty_write_string(PortFD, command, &nbytes_written)) != TTY_OK)
582 return false;
583
584 if (!res)
585 {
586 tcflush(PortFD, TCIOFLUSH);
587 return true;
588 }
589
590 if ((tty_rc = tty_nread_section(PortFD, res, ASTROLINK4_LEN, stopChar, ASTROLINK4_TIMEOUT, &nbytes_read)) != TTY_OK
591 || nbytes_read == 1)
592 return false;
593
594 tcflush(PortFD, TCIOFLUSH);
595 res[nbytes_read - 1] = '\0';
596 DEBUGF(INDI::Logger::DBG_DEBUG, "RES %s", res);
597 if (tty_rc != TTY_OK)
598 {
599 char errorMessage[MAXRBUF];
600 tty_error_msg(tty_rc, errorMessage, MAXRBUF);
601 LOGF_ERROR("Serial error: %s", errorMessage);
602 return false;
603 }
604 }
605 return (cmd[0] == res[0]);
606}
607
608bool IndiAstroLink4mini2::readDevice()
609{
610 char res[ASTROLINK4_LEN] = {0};
611 if (sendCommand("q", res))
612 {
613 std::vector<std::string> result = split(res, ":");
614 result.erase(result.begin());
615
616 int focuserPosition = std::stoi(result[getFindex() == 1 ? Q_FOC2_POS : Q_FOC1_POS]);
617 int stepsToGo = std::stod(result[getFindex() == 1 ? Q_FOC2_TO_GO : Q_FOC1_TO_GO]);
618 FocusAbsPosNP[0].setValue(focuserPosition);
619 if (stepsToGo == 0)
620 {
624 }
625 else
626 {
629 }
631
632 if (result.size() > 5)
633 {
634 if (std::stod(result[Q_SENS1_PRESENT]) > 0)
635 {
636 setParameterValue("WEATHER_TEMPERATURE", std::stod(result[Q_SENS1_TEMP]));
637 setParameterValue("WEATHER_HUMIDITY", std::stod(result[Q_SENS1_HUM]));
638 setParameterValue("WEATHER_DEWPOINT", std::stod(result[Q_SENS1_DEW]));
640 }
641 else
642 {
644 }
645
647
648 if (Power1SP.getState() != IPS_OK || Power2SP.getState() != IPS_OK || Power3SP.getState() != IPS_OK)
649 {
650 Power1SP[PWR1BTN_ON].setState((std::stod(result[Q_OUT1]) > 0) ? ISS_ON : ISS_OFF);
651 Power1SP[PWR1BTN_OFF].setState((std::stod(result[Q_OUT1]) == 0) ? ISS_ON : ISS_OFF);
652 Power1SP.setState(IPS_OK);
653 Power1SP.apply();
654 Power2SP[PWR2BTN_ON].setState((std::stod(result[Q_OUT2]) > 0) ? ISS_ON : ISS_OFF);
655 Power2SP[PWR2BTN_OFF].setState((std::stod(result[Q_OUT2]) == 0) ? ISS_ON : ISS_OFF);
656 Power2SP.setState(IPS_OK);
657 Power2SP.apply();
658 Power3SP[PWR3BTN_ON].setState((std::stod(result[Q_OUT3]) > 0) ? ISS_ON : ISS_OFF);
659 Power3SP[PWR3BTN_OFF].setState((std::stod(result[Q_OUT3]) == 0) ? ISS_ON : ISS_OFF);
660 Power3SP.setState(IPS_OK);
661 Power3SP.apply();
662 }
663
664 PWMNP[PWM1_VAL].setValue(std::stod(result[Q_PWM1]));
665 PWMNP[PWM2_VAL].setValue(std::stod(result[Q_PWM2]));
666 PWMNP.setState(IPS_OK);
667 PWMNP.apply();
668
669 PowerDataNP[POW_ITOT].setValue(std::stod(result[Q_ITOT]));
670 PowerDataNP[POW_REG].setValue(std::stod(result[Q_VREG]));
671 PowerDataNP[POW_VIN].setValue(std::stod(result[Q_VIN]));
672 PowerDataNP[POW_AH].setValue(std::stod(result[Q_AH]));
673 PowerDataNP[POW_WH].setValue(std::stod(result[Q_WH]));
674 PowerDataNP.setState(IPS_OK);
675 PowerDataNP.apply();
676 }
677 }
678
679 // update settings data if was changed
680 if (PowerDefaultOnSP.getState() != IPS_OK || FocusMaxPosNP.getState() != IPS_OK || FocusReverseSP.getState() != IPS_OK
681 || FocuserSelectSP.getState() != IPS_OK || Focuser1SettingsNP.getState() != IPS_OK
682 || Focuser2SettingsNP.getState() != IPS_OK || Focuser1ModeSP.getState() != IPS_OK || Focuser2ModeSP.getState() != IPS_OK)
683 {
684 if (sendCommand("u", res))
685 {
686 std::vector<std::string> result = split(res, ":");
687
688 if (PowerDefaultOnSP.getState() != IPS_OK)
689 {
690 PowerDefaultOnSP[POW_DEF_ON1].setState((std::stod(result[U_OUT1_DEF]) > 0) ? ISS_ON : ISS_OFF);
691 PowerDefaultOnSP[POW_DEF_ON2].setState((std::stod(result[U_OUT2_DEF]) > 0) ? ISS_ON : ISS_OFF);
692 PowerDefaultOnSP[POW_DEF_ON3].setState((std::stod(result[U_OUT3_DEF]) > 0) ? ISS_ON : ISS_OFF);
693 PowerDefaultOnSP.setState(IPS_OK);
694 PowerDefaultOnSP.apply();
695 }
696
697 if (Focuser1SettingsNP.getState() != IPS_OK)
698 {
699
700 DEBUGF(INDI::Logger::DBG_DEBUG, "Update settings, focuser 1, res %s", res);
701 Focuser1SettingsNP[FS1_STEP_SIZE].setValue(std::stod(result[U_FOC1_STEP]) / 100.0);
702 Focuser1SettingsNP[FS1_COMPENSATION].setValue(std::stod(result[U_FOC1_COMPSTEPS]) / 100.0);
703 Focuser1SettingsNP[FS1_COMP_THRESHOLD].setValue(std::stod(result[U_FOC1_COMPTRIGGER]));
704 Focuser1SettingsNP[FS1_SPEED].setValue(std::stod(result[U_FOC1_SPEED]));
705 Focuser1SettingsNP[FS1_CURRENT].setValue(std::stod(result[U_FOC1_CUR]) * 10.0);
706 Focuser1SettingsNP[FS1_HOLD].setValue(std::stod(result[U_FOC1_HOLD]));
707 Focuser1SettingsNP.setState(IPS_OK);
708 Focuser1SettingsNP.apply();
709 }
710
711 if (Focuser2SettingsNP.getState() != IPS_OK)
712 {
713 DEBUGF(INDI::Logger::DBG_DEBUG, "Update settings, focuser 2, res %s", res);
714 Focuser2SettingsNP[FS2_STEP_SIZE].setValue(std::stod(result[U_FOC2_STEP]) / 100.0);
715 Focuser2SettingsNP[FS2_COMPENSATION].setValue(std::stod(result[U_FOC2_COMPSTEPS]) / 100.0);
716 Focuser2SettingsNP[FS2_COMP_THRESHOLD].setValue(std::stod(result[U_FOC2_COMPTRIGGER]));
717 Focuser2SettingsNP[FS2_SPEED].setValue(std::stod(result[U_FOC2_SPEED]));
718 Focuser2SettingsNP[FS2_CURRENT].setValue(std::stod(result[U_FOC2_CUR]) * 10.0);
719 Focuser2SettingsNP[FS2_HOLD].setValue(std::stod(result[U_FOC2_HOLD]));
720 Focuser2SettingsNP.setState(IPS_OK);
721 Focuser2SettingsNP.apply();
722 }
723
724 if (Focuser1ModeSP.getState() != IPS_OK)
725 {
726 Focuser1ModeSP[FS1_MODE_UNI].setState(Focuser1ModeSP[FS1_MODE_MICRO_L].s = Focuser1ModeSP[FS1_MODE_MICRO_H].s = ISS_OFF);
727 if (!strcmp("0", result[U_FOC1_MODE].c_str()))
728 Focuser1ModeSP[FS1_MODE_UNI].setState(ISS_ON);
729 if (!strcmp("1", result[U_FOC1_MODE].c_str()))
730 Focuser1ModeSP[FS1_MODE_MICRO_L].setState(ISS_ON);
731 if (!strcmp("2", result[U_FOC1_MODE].c_str()))
732 Focuser1ModeSP[FS1_MODE_MICRO_H].setState(ISS_ON);
733 Focuser1ModeSP.setState(IPS_OK);
734 Focuser1ModeSP.apply();
735 }
736
737 if (Focuser2ModeSP.getState() != IPS_OK)
738 {
739 Focuser2ModeSP[FS2_MODE_UNI].setState(Focuser2ModeSP[FS2_MODE_MICRO_L].s = Focuser2ModeSP[FS2_MODE_MICRO_H].s = ISS_OFF);
740 if (!strcmp("0", result[U_FOC2_MODE].c_str()))
741 Focuser2ModeSP[FS2_MODE_UNI].setState(ISS_ON);
742 if (!strcmp("1", result[U_FOC2_MODE].c_str()))
743 Focuser2ModeSP[FS2_MODE_MICRO_L].setState(ISS_ON);
744 if (!strcmp("2", result[U_FOC2_MODE].c_str()))
745 Focuser2ModeSP[FS2_MODE_MICRO_H].setState(ISS_ON);
746 Focuser2ModeSP.setState(IPS_OK);
747 Focuser2ModeSP.apply();
748 }
749
751 {
752 DEBUGF(INDI::Logger::DBG_DEBUG, "Update maxpos, focuser %i, res %s", getFindex(), res);
753 int index = getFindex() > 0 ? U_FOC2_MAX : U_FOC1_MAX;
754 FocusMaxPosNP[0].setValue(std::stod(result[index]));
757 }
759 {
760 DEBUGF(INDI::Logger::DBG_DEBUG, "Update reverse, focuser %i, res %s", getFindex(), res);
761 int index = getFindex() > 0 ? U_FOC2_REV : U_FOC1_REV;
762 FocusReverseSP[INDI_ENABLED].setState((std::stoi(result[index]) > 0) ? ISS_ON : ISS_OFF);
763 FocusReverseSP[INDI_DISABLED].setState((std::stoi(result[index]) == 0) ? ISS_ON : ISS_OFF);
766 }
767 FocuserSelectSP.setState(IPS_OK);
768 FocuserSelectSP.apply();
769 }
770 }
771
772 return true;
773}
774
778std::vector<std::string> IndiAstroLink4mini2::split(const std::string &input, const std::string &regex)
779{
780 // passing -1 as the submatch index parameter performs splitting
781 std::regex re(regex);
782 std::sregex_token_iterator
783 first{input.begin(), input.end(), re, -1},
784 last;
785 return {first, last};
786}
787
788std::string IndiAstroLink4mini2::doubleToStr(double val)
789{
790 char buf[10];
791 sprintf(buf, "%.0f", val);
792 return std::string(buf);
793}
794
795std::string IndiAstroLink4mini2::intToStr(double val)
796{
797 char buf[10];
798 sprintf(buf, "%i", (int)val);
799 return std::string(buf);
800}
801
802bool IndiAstroLink4mini2::updateSettings(const char *getCom, const char *setCom, int index, const char *value)
803{
804 std::map<int, std::string> values;
805 values[index] = value;
806 return updateSettings(getCom, setCom, values);
807}
808
809bool IndiAstroLink4mini2::updateSettings(const char *getCom, const char *setCom, std::map<int, std::string> values)
810{
811 char cmd[ASTROLINK4_LEN] = {0}, res[ASTROLINK4_LEN] = {0};
812 snprintf(cmd, ASTROLINK4_LEN, "%s", getCom);
813 if (sendCommand(cmd, res))
814 {
815 std::string concatSettings = "";
816 std::vector<std::string> result = split(res, ":");
817 if (result.size() >= values.size())
818 {
819 result[0] = setCom;
820 for (std::map<int, std::string>::iterator it = values.begin(); it != values.end(); ++it)
821 result[it->first] = it->second;
822
823 for (const auto &piece : result)
824 concatSettings += piece + ":";
825
826 snprintf(cmd, ASTROLINK4_LEN, "%s", concatSettings.c_str());
827 if (sendCommand(cmd, res))
828 return true;
829 }
830 }
831 return false;
832}
833
834int IndiAstroLink4mini2::getFindex()
835{
836 return focuserIndex;
837}
838
839void IndiAstroLink4mini2::setFindex(int index)
840{
841 DEBUGF(INDI::Logger::DBG_DEBUG, "Focuser index set to %i", index);
842 focuserIndex = index;
843}
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...
void setDefaultPort(const char *port)
setDefaultPort Set default port. Call this function in initProperties() of your driver if you want to...
bool isConnected() const
const char * getDeviceName() const
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
void addConfigurationControl()
Add Configuration control to the driver.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
void addSimulationControl()
Add Simulation control to the driver.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
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.
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
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.
void addDebugControl()
Add Debug control to the driver.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
Provides interface to implement focuser functionality.
INDI::PropertyNumber FocusRelPosNP
INDI::PropertySwitch FocusReverseSP
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
void initProperties(const char *groupName)
Initialize focuser properties. It is recommended to call this function within initProperties() of you...
INDI::PropertyNumber FocusMaxPosNP
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process focus number properties.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process focus switch properties.
INDI::PropertyNumber FocusAbsPosNP
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void save(FILE *f) const
save Save property to configuration file.
const char * getName() const
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
Provides interface to implement weather reporting and safety monitoring functionality.
void addParameter(std::string name, std::string label, double numMinOk, double numMaxOk, double percWarning, bool flipWarning=false)
Add a physical weather measurable parameter to the weather driver.
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
INDI::PropertyNumber ParametersNP
void initProperties(const char *statusGroup, const char *paramsGroup)
Initialize focuser properties. It is recommended to call this function within initProperties() of you...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool SetFocuserBacklashEnabled(bool enabled) override
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool sendCommand(const char *cmd, char *res)
Serial commands.
virtual bool SetFocuserMaxPosition(uint32_t ticks) override
SetFocuserMaxPosition Set Focuser Maximum position limit in the hardware.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
virtual bool initProperties() override
Overrides.
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
Focuser interface.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
virtual const char * getDefaultName() override
const char * FOCUS_TAB
FOCUS_TAB Where all the properties for focuser are located.
#define ASTROLINK4_LEN
#define ASTROLINK4_TIMEOUT
#define VERSION_MAJOR
#define POLLTIME
#define VERSION_MINOR
std::unique_ptr< IndiAstroLink4mini2 > indiFocuserLink(new IndiAstroLink4mini2())
Delegates.
#define U_FOC2_REV
#define Q_OUT2
#define U_FOC1_COMPTRIGGER
#define Q_FOC2_POS
#define U_FOC2_STEP
#define U_FOC1_STEP
#define U_OUT3_DEF
#define Q_FOC2_TO_GO
#define Q_WH
#define U_FOC1_SPEED
#define U_FOC1_COMPSTEPS
#define Q_AH
#define U_FOC2_CUR
#define U_FOC2_MAX
#define U_FOC1_ACC
#define U_FOC1_CUR
#define U_FOC1_REV
#define U_FOC1_MAX
#define U_OUT1_DEF
#define Q_SENS1_TEMP
#define Q_PWM2
#define U_FOC2_ACC
#define Q_VIN
#define U_FOC2_HOLD
#define Q_FOC1_TO_GO
#define Q_PWM1
#define Q_FOC1_POS
#define U_OUT2_DEF
#define U_FOC2_COMPTRIGGER
#define Q_ITOT
#define U_FOC2_MODE
#define Q_VREG
#define Q_OUT3
#define Q_OUT1
#define Q_SENS1_HUM
#define U_FOC2_SPEED
#define U_FOC1_MODE
#define Q_SENS1_DEW
#define U_FOC1_HOLD
#define Q_SENS1_PRESENT
#define U_FOC2_COMPSTEPS
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_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_NOFMANY
Definition indiapi.h:173
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
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#define MAXRBUF
Definition indidriver.c:52
int IUGetConfigOnSwitchName(const char *dev, const char *property, char *name, size_t size)
IUGetConfigOnSwitchLabel Opens configuration file and returns the name of the ON switch property,...
Definition indidriver.c:783
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
Definition indilogger.h:56
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define DEBUGF(priority, msg,...)
Definition indilogger.h:57
uint8_t cmd[4]
Definition pwc-ioctl.h:2