Instrument Neutral Distributed Interface INDI  1.9.5
sestosenso2.cpp
Go to the documentation of this file.
1 /*
2  SestoSenso 2 Focuser
3  Copyright (C) 2020 Piotr Zyziuk
4  Copyright (C) 2020 Jasem Mutlaq (Added Esatto support)
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 
22 #include "sestosenso2.h"
23 
24 #include "indicom.h"
25 
26 #include <cmath>
27 #include <cstring>
28 #include <memory>
29 #include <algorithm>
30 
31 #include <assert.h>
32 #include <termios.h>
33 #include <unistd.h>
35 #include <sys/ioctl.h>
36 
37 static std::unique_ptr<SestoSenso2> sesto(new SestoSenso2());
38 
39 static const char *MOTOR_TAB = "Motor";
40 static const char *ENVIRONMENT_TAB = "Environment";
41 
42 struct MotorRates
43 {
44  // Rate values: 1-10
45  uint32_t accRate = 0, runSpeed = 0, decRate = 0;
46 };
47 
49 {
50  // Current values: 1-10
51  uint32_t accCurrent = 0, runCurrent = 0, decCurrent = 0;
52  // Hold current: 1-5
53  uint32_t holdCurrent = 0;
54 };
55 
56 // Settings names for the default motor settings presets
57 const char *MOTOR_PRESET_NAMES[] = { "light", "medium", "slow" };
58 
60 {
61  setVersion(0, 7);
62  // Can move in Absolute & Relative motions, can AbortFocuser motion.
64 
65  m_MotionProgressTimer.callOnTimeout(std::bind(&SestoSenso2::checkMotionProgressCallback, this));
66  m_MotionProgressTimer.setSingleShot(true);
67 
68  m_HallSensorTimer.callOnTimeout(std::bind(&SestoSenso2::checkHallSensorCallback, this));
69  m_HallSensorTimer.setSingleShot(true);
70  m_HallSensorTimer.setInterval(1000);
71 }
72 
74 {
75 
77 
78  FocusBacklashN[0].min = 0;
79  FocusBacklashN[0].max = 10000;
80  FocusBacklashN[0].step = 1;
81  FocusBacklashN[0].value = 0;
82 
83  setConnectionParams();
84 
85  // Firmware information
86  IUFillText(&FirmwareT[FIRMWARE_SN], "SERIALNUMBER", "Serial Number", "");
87  IUFillText(&FirmwareT[FIRMWARE_VERSION], "VERSION", "Version", "");
88  IUFillTextVector(&FirmwareTP, FirmwareT, 2, getDeviceName(), "FOCUS_FIRMWARE", "Firmware", CONNECTION_TAB, IP_RO, 0,
89  IPS_IDLE);
90 
91  // Voltage Information
92  IUFillNumber(&VoltageInN[0], "VOLTAGEIN", "Volts", "%.2f", 0, 100, 0., 0.);
93  IUFillNumberVector(&VoltageInNP, VoltageInN, 1, getDeviceName(), "VOLTAGE_IN", "Voltage in", ENVIRONMENT_TAB, IP_RO, 0,
94  IPS_IDLE);
95 
96  // Focuser temperature
97  IUFillNumber(&TemperatureN[TEMPERATURE_MOTOR], "TEMPERATURE", "Motor (c)", "%.2f", -50, 70., 0., 0.);
98  IUFillNumber(&TemperatureN[TEMPERATURE_EXTERNAL], "TEMPERATURE_ETX", "External (c)", "%.2f", -50, 70., 0., 0.);
99  IUFillNumberVector(&TemperatureNP, TemperatureN, 2, getDeviceName(), "FOCUS_TEMPERATURE", "Temperature", ENVIRONMENT_TAB,
100  IP_RO, 0, IPS_IDLE);
101 
102  // Current Speed
103  IUFillNumber(&SpeedN[0], "SPEED", "steps/s", "%.f", 0, 7000., 1, 0);
104  IUFillNumberVector(&SpeedNP, SpeedN, 1, getDeviceName(), "FOCUS_SPEED", "Motor Speed", MAIN_CONTROL_TAB, IP_RO, 0,
105  IPS_IDLE);
106 
107  // Focuser calibration
108  IUFillText(&CalibrationMessageT[0], "CALIBRATION", "Calibration stage", "Press START to begin the Calibration.");
109  IUFillTextVector(&CalibrationMessageTP, CalibrationMessageT, 1, getDeviceName(), "CALIBRATION_MESSAGE", "Calibration",
111 
112  // Calibration
113  IUFillSwitch(&CalibrationS[CALIBRATION_START], "CALIBRATION_START", "Start", ISS_OFF);
114  IUFillSwitch(&CalibrationS[CALIBRATION_NEXT], "CALIBRATION_NEXT", "Next", ISS_OFF);
115  IUFillSwitchVector(&CalibrationSP, CalibrationS, 2, getDeviceName(), "FOCUS_CALIBRATION", "Calibration", MAIN_CONTROL_TAB,
116  IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
117 
118  IUFillText(&BacklashMessageT[0], "BACKLASH", "Backlash stage", "Press START to measure backlash.");
119  IUFillTextVector(&BacklashMessageTP, BacklashMessageT, 1, getDeviceName(), "BACKLASH_MESSAGE", "Backlash",
121 
122  // Backlash
123  IUFillSwitch(&BacklashS[BACKLASH_START], "BACKLASH_START", "Start", ISS_OFF);
124  IUFillSwitch(&BacklashS[BACKLASH_NEXT], "BACKLASH_NEXT", "Next", ISS_OFF);
125  IUFillSwitchVector(&BacklashSP, BacklashS, 2, getDeviceName(), "FOCUS_BACKLASH", "Backlash", MAIN_CONTROL_TAB,
126  IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
127 
128  // Speed Moves
129  IUFillSwitch(&FastMoveS[FASTMOVE_IN], "FASTMOVE_IN", "Move In", ISS_OFF);
130  IUFillSwitch(&FastMoveS[FASTMOVE_OUT], "FASTMOVE_OUT", "Move out", ISS_OFF);
131  IUFillSwitch(&FastMoveS[FASTMOVE_STOP], "FASTMOVE_STOP", "Stop", ISS_OFF);
132  IUFillSwitchVector(&FastMoveSP, FastMoveS, 3, getDeviceName(), "FAST_MOVE", "Calibration Move", MAIN_CONTROL_TAB, IP_RW,
133  ISR_ATMOST1, 0, IPS_IDLE);
134 
135  // Hold state
136  IUFillSwitch(&MotorHoldS[MOTOR_HOLD_ON], "HOLD_ON", "Hold On", ISS_OFF);
137  IUFillSwitch(&MotorHoldS[MOTOR_HOLD_OFF], "HOLD_OFF", "Hold Off", ISS_OFF);
138  IUFillSwitchVector(&MotorHoldSP, MotorHoldS, 2, getDeviceName(), "MOTOR_HOLD", "Motor Hold", MAIN_CONTROL_TAB,
139  IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
140 
141  // Override the default Max. Position to make it Read-Only
142  IUFillNumberVector(&FocusMaxPosNP, FocusMaxPosN, 1, getDeviceName(), "FOCUS_MAX", "Max. Position", MAIN_CONTROL_TAB, IP_RO,
143  0, IPS_IDLE);
144 
145  // Home Position
146  IUFillSwitch(&HomeS[0], "FOCUS_HOME_GO", "Go", ISS_OFF);
147  IUFillSwitchVector(&HomeSP, HomeS, 1, getDeviceName(), "FOCUS_HOME", "Home", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 60,
148  IPS_IDLE);
149 
150  // Motor rate
151  IUFillNumber(&MotorRateN[MOTOR_RATE_ACC], "ACC", "Acceleration", "%.f", 1, 10, 1, 1);
152  IUFillNumber(&MotorRateN[MOTOR_RATE_RUN], "RUN", "Run Speed", "%.f", 1, 10, 1, 2);
153  IUFillNumber(&MotorRateN[MOTOR_RATE_DEC], "DEC", "Deceleration", "%.f", 1, 10, 1, 1);
154  IUFillNumberVector(&MotorRateNP, MotorRateN, 3, getDeviceName(), "MOTOR_RATE", "Motor Rate", MOTOR_TAB, IP_RW, 0,
155  IPS_IDLE);
156 
157  // Motor current
158  IUFillNumber(&MotorCurrentN[MOTOR_CURR_ACC], "CURR_ACC", "Acceleration", "%.f", 1, 10, 1, 7);
159  IUFillNumber(&MotorCurrentN[MOTOR_CURR_RUN], "CURR_RUN", "Run", "%.f", 1, 10, 1, 7);
160  IUFillNumber(&MotorCurrentN[MOTOR_CURR_DEC], "CURR_DEC", "Deceleration", "%.f", 1, 10, 1, 7);
161  IUFillNumber(&MotorCurrentN[MOTOR_CURR_HOLD], "CURR_HOLD", "Hold", "%.f", 0, 5, 1, 3);
162  IUFillNumberVector(&MotorCurrentNP, MotorCurrentN, 4, getDeviceName(), "MOTOR_CURRENT", "Current", MOTOR_TAB, IP_RW, 0,
163  IPS_IDLE);
164 
165  // Load motor preset
166  IUFillSwitch(&MotorApplyPresetS[MOTOR_APPLY_LIGHT], "MOTOR_APPLY_LIGHT", "Light", ISS_OFF);
167  IUFillSwitch(&MotorApplyPresetS[MOTOR_APPLY_MEDIUM], "MOTOR_APPLY_MEDIUM", "Medium", ISS_OFF);
168  IUFillSwitch(&MotorApplyPresetS[MOTOR_APPLY_HEAVY], "MOTOR_APPLY_HEAVY", "Heavy", ISS_OFF);
169  IUFillSwitchVector(&MotorApplyPresetSP, MotorApplyPresetS, 3, getDeviceName(), "MOTOR_APPLY_PRESET", "Apply Preset",
170  MOTOR_TAB, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
171 
172  // Load user preset
173  IUFillSwitch(&MotorApplyUserPresetS[MOTOR_APPLY_USER1], "MOTOR_APPLY_USER1", "User 1", ISS_OFF);
174  IUFillSwitch(&MotorApplyUserPresetS[MOTOR_APPLY_USER2], "MOTOR_APPLY_USER2", "User 2", ISS_OFF);
175  IUFillSwitch(&MotorApplyUserPresetS[MOTOR_APPLY_USER3], "MOTOR_APPLY_USER3", "User 3", ISS_OFF);
176  IUFillSwitchVector(&MotorApplyUserPresetSP, MotorApplyUserPresetS, 3, getDeviceName(), "MOTOR_APPLY_USER_PRESET",
177  "Apply Custom", MOTOR_TAB, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
178 
179  // Save user preset
180  IUFillSwitch(&MotorSaveUserPresetS[MOTOR_SAVE_USER1], "MOTOR_SAVE_USER1", "User 1", ISS_OFF);
181  IUFillSwitch(&MotorSaveUserPresetS[MOTOR_SAVE_USER2], "MOTOR_SAVE_USER2", "User 2", ISS_OFF);
182  IUFillSwitch(&MotorSaveUserPresetS[MOTOR_SAVE_USER3], "MOTOR_SAVE_USER3", "User 3", ISS_OFF);
183  IUFillSwitchVector(&MotorSaveUserPresetSP, MotorSaveUserPresetS, 3, getDeviceName(), "MOTOR_SAVE_USER_PRESET",
184  "Save Custom", MOTOR_TAB, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
185 
186  // Relative and absolute movement
187  FocusRelPosN[0].min = 0.;
188  FocusRelPosN[0].max = 50000.;
189  FocusRelPosN[0].value = 0;
190  FocusRelPosN[0].step = 1000;
191 
192  FocusAbsPosN[0].min = 0.;
193  FocusAbsPosN[0].max = 200000.;
194  FocusAbsPosN[0].value = 0;
195  FocusAbsPosN[0].step = 1000;
196 
197  FocusMaxPosN[0].value = 2097152;
198  PresetN[0].max = FocusMaxPosN[0].value;
199  PresetN[1].max = FocusMaxPosN[0].value;
200  PresetN[2].max = FocusMaxPosN[0].value;
201 
202  addAuxControls();
203 
205 
206  return true;
207 }
208 
210 {
211  if (isConnected() && updateMaxLimit() == false)
212  LOGF_WARN("Check you have the latest %s firmware. Focuser requires calibration.", getDeviceName());
213 
215 
216  if (isConnected())
217  {
218  defineProperty(&SpeedNP);
219  defineProperty(&CalibrationMessageTP);
220  defineProperty(&CalibrationSP);
221  defineProperty(&BacklashMessageTP);
222  defineProperty(&BacklashSP);
223  defineProperty(&HomeSP);
224  defineProperty(&MotorRateNP);
225  defineProperty(&MotorCurrentNP);
226  defineProperty(&MotorHoldSP);
227  defineProperty(&MotorApplyPresetSP);
228  defineProperty(&MotorApplyUserPresetSP);
229  defineProperty(&MotorSaveUserPresetSP);
230 
231  defineProperty(&FirmwareTP);
232 
233  if (updateTemperature())
234  defineProperty(&TemperatureNP);
235 
236  if (updateVoltageIn())
237  defineProperty(&VoltageInNP);
238 
239  if (getStartupValues())
240  LOG_INFO("Parameters updated, focuser ready for use.");
241  else
242  LOG_WARN("Failed to inquire parameters. Check logs.");
243  }
244  else
245  {
246  if (TemperatureNP.s == IPS_OK)
247  deleteProperty(TemperatureNP.name);
248  deleteProperty(FirmwareTP.name);
249  deleteProperty(VoltageInNP.name);
250  deleteProperty(CalibrationMessageTP.name);
251  deleteProperty(CalibrationSP.name);
252  deleteProperty(BacklashMessageTP.name);
253  deleteProperty(BacklashSP.name);
254  deleteProperty(SpeedNP.name);
255  deleteProperty(HomeSP.name);
256  deleteProperty(MotorRateNP.name);
257  deleteProperty(MotorCurrentNP.name);
258  deleteProperty(MotorHoldSP.name);
259  deleteProperty(MotorApplyPresetSP.name);
260  deleteProperty(MotorApplyUserPresetSP.name);
261  deleteProperty(MotorSaveUserPresetSP.name);
262  }
263 
264  return true;
265 }
266 
268 {
269  if (Ack())
270  {
271  LOGF_INFO("%s is online. Getting focus parameters...", getDeviceName());
272  return true;
273  }
274 
275  LOG_INFO("Error retrieving data from device, please ensure focuser is powered and the port is correct.");
276  return false;
277 }
278 
280 {
281  // if (isSimulation() == false)
282  // command->goHome();
283 
284  return INDI::Focuser::Disconnect();
285 }
286 
288 {
289  backlashTicks = static_cast<uint32_t>(abs(steps));
290  backlashDirection = steps < 0 ? FOCUS_INWARD : FOCUS_OUTWARD;
291  oldbacklashDirection = backlashDirection;
292  return true;
293 }
294 
296 {
297  return "Sesto Senso 2";
298 }
299 
300 bool SestoSenso2::updateTemperature()
301 {
302  char res[SESTO_LEN] = {0};
303  double temperature = 0;
304 
305  if (isSimulation())
306  strncpy(res, "23.45", SESTO_LEN);
307  else if (command->getMotorTemp(res) == false)
308  return false;
309 
310  try
311  {
312  temperature = std::stod(res);
313  }
314  catch(...)
315  {
316  LOGF_WARN("Failed to process temperature response: %s (%d bytes)", res, strlen(res));
317  return false;
318  }
319 
320  if (temperature > 90)
321  return false;
322 
323  TemperatureN[TEMPERATURE_MOTOR].value = temperature;
324  TemperatureNP.s = IPS_OK;
325 
326  // External temperature - Optional
327  if (command->getExternalTemp(res))
328  {
329  TemperatureN[TEMPERATURE_EXTERNAL].value = -273.15;
330  try
331  {
332  temperature = std::stod(res);
333  }
334  catch(...)
335  {
336  LOGF_DEBUG("Failed to process external temperature response: %s (%d bytes)", res, strlen(res));
337  }
338 
339  if (temperature < 90)
340  TemperatureN[TEMPERATURE_EXTERNAL].value = temperature;
341  }
342 
343  return true;
344 }
345 
346 
347 bool SestoSenso2::updateMaxLimit()
348 {
349  char res[SESTO_LEN] = {0};
350 
351  if (isSimulation())
352  return true;
353 
354  if (command->getMaxPosition(res) == false)
355  return false;
356 
357  int maxLimit = 0;
358 
359  sscanf(res, "%d", &maxLimit);
360 
361  if (maxLimit > 0)
362  {
363  FocusMaxPosN[0].max = maxLimit;
364  if (FocusMaxPosN[0].value > maxLimit)
365  FocusMaxPosN[0].value = maxLimit;
366 
367  FocusAbsPosN[0].min = 0;
368  FocusAbsPosN[0].max = maxLimit;
369  FocusAbsPosN[0].value = 0;
370  FocusAbsPosN[0].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
371 
372  FocusRelPosN[0].min = 0.;
373  FocusRelPosN[0].max = FocusAbsPosN[0].step * 10;
374  FocusRelPosN[0].value = 0;
375  FocusRelPosN[0].step = FocusAbsPosN[0].step;
376 
377  PresetN[0].max = maxLimit;
378  PresetN[0].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
379  PresetN[1].max = maxLimit;
380  PresetN[1].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
381  PresetN[2].max = maxLimit;
382  PresetN[2].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
383 
384 
386  return true;
387  }
388 
389 
391  return false;
392 }
393 
394 bool SestoSenso2::updatePosition()
395 {
396  char res[SESTO_LEN] = {0};
397  if (isSimulation())
398  snprintf(res, SESTO_LEN, "%u", static_cast<uint32_t>(FocusAbsPosN[0].value));
399  else if (command->getAbsolutePosition(res) == false)
400  return false;
401 
402  try
403  {
404  int32_t currentPos = std::stoi(res);
405  if(backlashDirection == FOCUS_INWARD)
406  {
407  currentPos += backlashTicks;
408  }
409  else
410  {
411  currentPos -= backlashTicks;
412  }
413  FocusAbsPosN[0].value = currentPos;
415  return true;
416  }
417  catch(...)
418  {
419  LOGF_WARN("Failed to process position response: %s (%d bytes)", res, strlen(res));
421  return false;
422  }
423 }
424 
425 bool SestoSenso2::updateVoltageIn()
426 {
427  char res[SESTO_LEN] = {0};
428  double voltageIn = 0;
429 
430  if (isSimulation())
431  strncpy(res, "12.00", SESTO_LEN);
432  else if (command->getVoltageIn(res) == false)
433  return false;
434 
435  try
436  {
437  voltageIn = std::stod(res);
438  }
439  catch(...)
440  {
441  LOGF_WARN("Failed to process voltage response: %s (%d bytes)", res, strlen(res));
442  return false;
443  }
444 
445  if (voltageIn > 24)
446  return false;
447 
448  VoltageInN[0].value = voltageIn;
449  VoltageInNP.s = (voltageIn >= 11.0) ? IPS_OK : IPS_ALERT;
450 
451  return true;
452 }
453 
454 bool SestoSenso2::fetchMotorSettings()
455 {
456  // Fetch driver state and reflect in INDI
457  MotorRates ms;
458  MotorCurrents mc;
459  bool motorHoldActive = false;
460 
461  if (isSimulation())
462  {
463  ms.accRate = 1;
464  ms.runSpeed = 2;
465  ms.decRate = 1;
466  mc.accCurrent = 3;
467  mc.runCurrent = 4;
468  mc.decCurrent = 3;
469  mc.holdCurrent = 2;
470  }
471  else
472  {
473  if (!command->getMotorSettings(ms, mc, motorHoldActive))
474  {
475  MotorRateNP.s = IPS_IDLE;
476  MotorCurrentNP.s = IPS_IDLE;
477  MotorHoldSP.s = IPS_IDLE;
478  return false;
479  }
480  }
481 
482  MotorRateN[MOTOR_RATE_ACC].value = ms.accRate;
483  MotorRateN[MOTOR_RATE_RUN].value = ms.runSpeed;
484  MotorRateN[MOTOR_RATE_DEC].value = ms.decRate;
485  MotorRateNP.s = IPS_OK;
486  IDSetNumber(&MotorRateNP, nullptr);
487 
488  MotorCurrentN[MOTOR_CURR_ACC].value = mc.accCurrent;
489  MotorCurrentN[MOTOR_CURR_RUN].value = mc.runCurrent;
490  MotorCurrentN[MOTOR_CURR_DEC].value = mc.decCurrent;
491  MotorCurrentN[MOTOR_CURR_HOLD].value = mc.holdCurrent;
492  MotorCurrentNP.s = IPS_OK;
493  IDSetNumber(&MotorCurrentNP, nullptr);
494 
495  // Also update motor hold switch
496  const char *activeSwitchID = motorHoldActive ? "HOLD_ON" : "HOLD_OFF";
497  ISwitch *sp = IUFindSwitch(&MotorHoldSP, activeSwitchID);
498  assert(sp != nullptr && "Motor hold switch not found");
499  if (sp)
500  {
501  IUResetSwitch(&MotorHoldSP);
502  sp->s = ISS_ON;
503  MotorHoldSP.s = motorHoldActive ? IPS_OK : IPS_ALERT;
504  IDSetSwitch(&MotorHoldSP, nullptr);
505  }
506 
507  if (motorHoldActive && mc.holdCurrent == 0)
508  {
509  LOG_WARN("Motor hold current set to 0, motor hold setting will have no effect");
510  }
511 
512  return true;
513 }
514 
515 bool SestoSenso2::applyMotorRates()
516 {
517  if (isSimulation())
518  return true;
519 
520  // Send INDI state to driver
521  MotorRates mr;
522  mr.accRate = static_cast<uint32_t>(MotorRateN[MOTOR_RATE_ACC].value);
523  mr.runSpeed = static_cast<uint32_t>(MotorRateN[MOTOR_RATE_RUN].value);
524  mr.decRate = static_cast<uint32_t>(MotorRateN[MOTOR_RATE_DEC].value);
525 
526  if (!command->setMotorRates(mr))
527  {
528  LOG_ERROR("Failed to apply motor rates");
529  // TODO: Error state?
530  return false;
531  }
532 
533  LOGF_INFO("Motor rates applied: Acc: %u Run: %u Dec: %u", mr.accRate, mr.runSpeed, mr.decRate);
534  return true;
535 }
536 
537 bool SestoSenso2::applyMotorCurrents()
538 {
539  if (isSimulation())
540  return true;
541 
542  // Send INDI state to driver
543  MotorCurrents mc;
544  mc.accCurrent = static_cast<uint32_t>(MotorCurrentN[MOTOR_CURR_ACC].value);
545  mc.runCurrent = static_cast<uint32_t>(MotorCurrentN[MOTOR_CURR_RUN].value);
546  mc.decCurrent = static_cast<uint32_t>(MotorCurrentN[MOTOR_CURR_DEC].value);
547  mc.holdCurrent = static_cast<uint32_t>(MotorCurrentN[MOTOR_CURR_HOLD].value);
548 
549  if (!command->setMotorCurrents(mc))
550  {
551  LOG_ERROR("Failed to apply motor currents");
552  // TODO: Error state?
553  return false;
554  }
555 
556  LOGF_INFO("Motor currents applied: Acc: %u Run: %u Dec: %u Hold: %u", mc.accCurrent, mc.runCurrent, mc.decCurrent,
557  mc.holdCurrent);
558  return true;
559 }
560 
561 bool SestoSenso2::isMotionComplete()
562 {
563  char res[SESTO_LEN] = {0};
564 
565  if (isSimulation())
566  {
567  int32_t nextPos = FocusAbsPosN[0].value;
568  int32_t targPos = static_cast<int32_t>(targetPos);
569 
570  if (targPos > nextPos)
571  nextPos += 250;
572  else if (targPos < nextPos)
573  nextPos -= 250;
574 
575  if (abs(nextPos - targPos) < 250)
576  nextPos = targetPos;
577  else if (nextPos < 0)
578  nextPos = 0;
579  else if (nextPos > FocusAbsPosN[0].max)
580  nextPos = FocusAbsPosN[0].max;
581 
582  snprintf(res, SESTO_LEN, "%d", nextPos);
583  }
584  else
585  {
586  if(command->getCurrentSpeed(res))
587  {
588  try
589  {
590  uint32_t newSpeed = std::stoi(res);
591  SpeedN[0].value = newSpeed;
592  SpeedNP.s = IPS_OK;
593  }
594  catch (...)
595  {
596  LOGF_WARN("Failed to get motor speed response: %s (%d bytes)", res, strlen(res));
597  }
598 
599  if(!strcmp(res, "0"))
600  return true;
601 
602  *res = {0};
603  if(command->getAbsolutePosition(res))
604  {
605  try
606  {
607  uint32_t newPos = std::stoi(res);
608  if(backlashDirection == FOCUS_INWARD)
609  {
610  newPos += backlashTicks;
611  }
612  else
613  {
614  newPos -= backlashTicks;
615  }
616  FocusAbsPosN[0].value = newPos;
617  }
618  catch (...)
619  {
620  LOGF_WARN("Failed to process motion response: %s (%d bytes)", res, strlen(res));
621  }
622  }
623  }
624 
625  }
626 
627  return false;
628 }
629 
630 bool SestoSenso2::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
631 {
632  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
633  {
634  // Calibrate focuser
635  if (!strcmp(name, CalibrationSP.name))
636  {
637  int current_switch = 0;
638 
639  CalibrationSP.s = IPS_BUSY;
640  //IDSetSwitch(&CalibrationSP, nullptr);
641  IUUpdateSwitch(&CalibrationSP, states, names, n);
642 
643  current_switch = IUFindOnSwitchIndex(&CalibrationSP);
644  CalibrationS[current_switch].s = ISS_ON;
645  IDSetSwitch(&CalibrationSP, nullptr);
646 
647  if (current_switch == CALIBRATION_START)
648  {
649  if (cStage == Idle || cStage == Complete )
650  {
651  // Start the calibration process
652  LOG_INFO("Start Calibration");
653  CalibrationSP.s = IPS_BUSY;
654  IDSetSwitch(&CalibrationSP, nullptr);
655 
656  //
657  // Init
658  //
659  if (m_IsSestoSenso2 && command->initCalibration() == false)
660  return false;
661 
662  IUSaveText(&CalibrationMessageT[0], "Set focus in MIN position and then press NEXT.");
663  IDSetText(&CalibrationMessageTP, nullptr);
664 
665  // Motor hold disabled during calibration init, so fetch new hold state
666  fetchMotorSettings();
667 
668  // Set next step
669  cStage = GoToMiddle;
670  }
671  else
672  {
673  LOG_INFO("Already started calibration. Proceed to next step.");
674  IUSaveText(&CalibrationMessageT[0], "Already started. Proceed to NEXT.");
675  IDSetText(&CalibrationMessageTP, nullptr);
676  }
677  }
678  else if (current_switch == CALIBRATION_NEXT)
679  {
680  if (cStage == GoToMiddle)
681  {
682  defineProperty(&FastMoveSP);
683  if (m_IsSestoSenso2)
684  {
685  if (command->storeAsMinPosition() == false)
686  return false;
687 
688  IUSaveText(&CalibrationMessageT[0], "Press MOVE OUT to move focuser out (CAUTION!)");
689  IDSetText(&CalibrationMessageTP, nullptr);
690  cStage = GoMinimum;
691  }
692  // For Esatto, start moving out immediately
693  else
694  {
695  cStage = GoMaximum;
696 
697  ISState fs[1] = { ISS_ON };
698  const char *fn[1] = { FastMoveS[FASTMOVE_OUT].name };
699  ISNewSwitch(getDeviceName(), FastMoveSP.name, fs, const_cast<char **>(fn), 1);
700  }
701  }
702  else if (cStage == GoMinimum)
703  {
704  char res[SESTO_LEN] = {0};
705  if (m_IsSestoSenso2 && command->storeAsMaxPosition(res) == false)
706  return false;
707 
708  IUSaveText(&CalibrationMessageT[0], "Press NEXT to finish.");
709  IDSetText(&CalibrationMessageTP, nullptr);
710  cStage = GoMaximum;
711  }
712  else if (cStage == GoMaximum)
713  {
714  char res[SESTO_LEN] = {0};
715 
716  if (command->getMaxPosition(res) == false)
717  return false;
718 
719  int maxLimit = 0;
720  sscanf(res, "%d", &maxLimit);
721  LOGF_INFO("MAX setting is %d", maxLimit);
722 
723  FocusMaxPosN[0].max = maxLimit;
724  FocusMaxPosN[0].value = maxLimit;
725 
726  FocusAbsPosN[0].min = 0;
727  FocusAbsPosN[0].max = maxLimit;
728  FocusAbsPosN[0].value = maxLimit;
729  FocusAbsPosN[0].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
730 
731  FocusRelPosN[0].min = 0.;
732  FocusRelPosN[0].max = FocusAbsPosN[0].step * 10;
733  FocusRelPosN[0].value = 0;
734  FocusRelPosN[0].step = FocusAbsPosN[0].step;
735 
736  PresetN[0].max = maxLimit;
737  PresetN[0].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
738  PresetN[1].max = maxLimit;
739  PresetN[1].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
740  PresetN[2].max = maxLimit;
741  PresetN[2].step = (FocusAbsPosN[0].max - FocusAbsPosN[0].min) / 50.0;
742 
748 
749  IUSaveText(&CalibrationMessageT[0], "Calibration Completed.");
750  IDSetText(&CalibrationMessageTP, nullptr);
751 
752  deleteProperty(FastMoveSP.name);
753  cStage = Complete;
754 
755  LOG_INFO("Calibration completed");
756  CalibrationSP.s = IPS_OK;
757  IDSetSwitch(&CalibrationSP, nullptr);
758  CalibrationS[current_switch].s = ISS_OFF;
759  IDSetSwitch(&CalibrationSP, nullptr);
760 
761  // Double check motor hold state after calibration
762  fetchMotorSettings();
763  }
764  else
765  {
766  IUSaveText(&CalibrationMessageT[0], "Calibration not in progress.");
767  IDSetText(&CalibrationMessageTP, nullptr);
768  }
769 
770  }
771  return true;
772  }
773  // Set backlash
774  else if (!strcmp(name, BacklashSP.name))
775  {
776  int current_switch = 0;
777 
778  BacklashSP.s = IPS_BUSY;
779  //IDSetSwitch(&BacklashSP, nullptr);
780  IUUpdateSwitch(&BacklashSP, states, names, n);
781 
782  current_switch = IUFindOnSwitchIndex(&BacklashSP);
783  BacklashS[current_switch].s = ISS_ON;
784  IDSetSwitch(&BacklashSP, nullptr);
785 
786  if (current_switch == BACKLASH_START)
787  {
788  if (bStage == BacklashIdle || bStage == BacklashComplete )
789  {
790  // Start the backlash measurement process
791  LOG_INFO("Start Backlash Measure");
792  BacklashSP.s = IPS_BUSY;
793  IDSetSwitch(&BacklashSP, nullptr);
794 
795  //
796  // Init
797  //
798 
799  IUSaveText(&BacklashMessageT[0], "Drive the focuser in any direction until focus changes.");
800  IDSetText(&BacklashMessageTP, nullptr);
801 
802  // Motor hold disabled during calibration init, so fetch new hold state
803  fetchMotorSettings();
804 
805  // Set next step
806  bStage = BacklashMinimum;
807  }
808  else
809  {
810  LOG_INFO("Already started backlash measure. Proceed to next step.");
811  IUSaveText(&BacklashMessageT[0], "Already started. Proceed to NEXT.");
812  IDSetText(&BacklashMessageTP, nullptr);
813  }
814  }
815  else if (current_switch == BACKLASH_NEXT)
816  {
817  if (bStage == BacklashMinimum)
818  {
819  FocusBacklashN[0].value = static_cast<int32_t>(FocusAbsPosN[0].value);
820 
821  IUSaveText(&BacklashMessageT[0], "Drive the focuser in the opposite direction, then press NEXT to finish.");
822  IDSetText(&BacklashMessageTP, nullptr);
823  bStage = BacklashMaximum;
824  }
825  else if (bStage == BacklashMaximum)
826  {
827  FocusBacklashN[0].value -= FocusAbsPosN[0].value;
828  IDSetNumber(&FocusBacklashNP, nullptr);
830 
831  IUSaveText(&BacklashMessageT[0], "Backlash Measure Completed.");
832  IDSetText(&BacklashMessageTP, nullptr);
833 
834  bStage = BacklashComplete;
835 
836  LOG_INFO("Backlash measurement completed");
837  BacklashSP.s = IPS_OK;
838  IDSetSwitch(&BacklashSP, nullptr);
839  BacklashS[current_switch].s = ISS_OFF;
840  IDSetSwitch(&BacklashSP, nullptr);
841  }
842  else
843  {
844  IUSaveText(&BacklashMessageT[0], "Backlash not in progress.");
845  IDSetText(&BacklashMessageTP, nullptr);
846  }
847 
848  }
849  return true;
850  }
851  // Fast motion
852  else if (!strcmp(name, FastMoveSP.name))
853  {
854  IUUpdateSwitch(&FastMoveSP, states, names, n);
855  int current_switch = IUFindOnSwitchIndex(&FastMoveSP);
856  char res[SESTO_LEN] = {0};
857 
858  switch (current_switch)
859  {
860  case FASTMOVE_IN:
861  if (command->fastMoveIn(res) == false)
862  {
863  return false;
864  }
865  break;
866  case FASTMOVE_OUT:
867  if (m_IsSestoSenso2)
868  {
869  if (command->goOutToFindMaxPos() == false)
870  {
871  return false;
872  }
873  IUSaveText(&CalibrationMessageT[0], "Press STOP focuser almost at MAX position.");
874 
875  // GoOutToFindMaxPos should cause motor hold to be reactivated
876  fetchMotorSettings();
877  }
878  else
879  {
880  if (command->fastMoveOut(res))
881  {
882  IUSaveText(&CalibrationMessageT[0], "Focusing out to detect hall sensor.");
883 
884  // if (m_MotionProgressTimerID > 0)
885  // IERmTimer(m_MotionProgressTimerID);
886  // m_MotionProgressTimerID = IEAddTimer(500, &SestoSenso2::checkMotionProgressHelper, this);
887  m_MotionProgressTimer.start(500);
888  }
889  }
890  IDSetText(&CalibrationMessageTP, nullptr);
891  break;
892  case FASTMOVE_STOP:
893  if (command->stop() == false)
894  {
895  return false;
896  }
897  IUSaveText(&CalibrationMessageT[0], "Press NEXT to store max limit.");
898  IDSetText(&CalibrationMessageTP, nullptr);
899  break;
900  default:
901  break;
902  }
903 
904  FastMoveSP.s = IPS_BUSY;
905  IDSetSwitch(&FastMoveSP, nullptr);
906  return true;
907  }
908  // Homing
909  else if (!strcmp(HomeSP.name, name))
910  {
911  char res[SESTO_LEN] = {0};
912  if (command->goHome(res))
913  {
914  HomeS[0].s = ISS_ON;
915  HomeSP.s = IPS_BUSY;
916 
917  // if (m_MotionProgressTimerID > 0)
918  // IERmTimer(m_MotionProgressTimerID);
919  // m_MotionProgressTimerID = IEAddTimer(100, &SestoSenso2::checkMotionProgressHelper, this);
920  m_MotionProgressTimer.start(100);
921  }
922  else
923  {
924  HomeS[0].s = ISS_OFF;
925  HomeSP.s = IPS_ALERT;
926  }
927 
928  IDSetSwitch(&HomeSP, nullptr);
929  return true;
930  }
931  else if (!strcmp(name, MotorHoldSP.name))
932  {
933  IUUpdateSwitch(&MotorHoldSP, states, names, n);
934  ISwitch *sp = IUFindOnSwitch(&MotorHoldSP);
935  assert(sp != nullptr);
936 
937  // NOTE: Default to HOLD_ON as a safety feature
938  if (!strcmp(sp->name, "HOLD_OFF"))
939  {
940  command->setMotorHold(false);
941  MotorHoldSP.s = IPS_ALERT;
942  LOG_INFO("Motor hold OFF. You may now manually adjust the focuser. Remember to enable motor hold once done.");
943  }
944  else
945  {
946  command->setMotorHold(true);
947  MotorHoldSP.s = IPS_OK;
948  LOG_INFO("Motor hold ON. Do NOT attempt to manually adjust the focuser!");
949  if (MotorCurrentN[MOTOR_CURR_HOLD].value < 2.0)
950  {
951  LOGF_WARN("Motor hold current set to %.1f: This may be insufficent to hold focus", MotorCurrentN[MOTOR_CURR_HOLD].value);
952  }
953  }
954 
955  IDSetSwitch(&MotorHoldSP, nullptr);
956  return true;
957  }
958  else if (!strcmp(name, MotorApplyPresetSP.name))
959  {
960  IUUpdateSwitch(&MotorApplyPresetSP, states, names, n);
961  int index = IUFindOnSwitchIndex(&MotorApplyPresetSP);
962  assert(index >= 0 && index < 3);
963 
964  const char* presetName = MOTOR_PRESET_NAMES[index];
965 
966  if (command->applyMotorPreset(presetName))
967  {
968  LOGF_INFO("Loaded motor preset: %s", presetName);
969  MotorApplyPresetSP.s = IPS_IDLE;
970  }
971  else
972  {
973  LOGF_ERROR("Failed to load motor preset: %s", presetName);
974  MotorApplyPresetSP.s = IPS_ALERT;
975  }
976 
977  MotorApplyPresetS[index].s = ISS_OFF;
978  IDSetSwitch(&MotorApplyPresetSP, nullptr);
979 
980  fetchMotorSettings();
981  return true;
982  }
983  else if (!strcmp(name, MotorApplyUserPresetSP.name))
984  {
985  IUUpdateSwitch(&MotorApplyUserPresetSP, states, names, n);
986  int index = IUFindOnSwitchIndex(&MotorApplyUserPresetSP);
987  assert(index >= 0 && index < 3);
988  uint32_t userIndex = index + 1;
989 
990  if (command->applyMotorUserPreset(userIndex))
991  {
992  LOGF_INFO("Loaded motor user preset: %u", userIndex);
993  MotorApplyUserPresetSP.s = IPS_IDLE;
994  }
995  else
996  {
997  LOGF_ERROR("Failed to load motor user preset: %u", userIndex);
998  MotorApplyUserPresetSP.s = IPS_ALERT;
999  }
1000 
1001  MotorApplyUserPresetS[index].s = ISS_OFF;
1002  IDSetSwitch(&MotorApplyUserPresetSP, nullptr);
1003 
1004  fetchMotorSettings();
1005  return true;
1006  }
1007  else if (!strcmp(name, MotorSaveUserPresetSP.name))
1008  {
1009  IUUpdateSwitch(&MotorSaveUserPresetSP, states, names, n);
1010  int index = IUFindOnSwitchIndex(&MotorSaveUserPresetSP);
1011  assert(index >= 0 && index < 3);
1012  uint32_t userIndex = index + 1;
1013 
1014  MotorRates mr;
1015  mr.accRate = static_cast<uint32_t>(MotorRateN[MOTOR_RATE_ACC].value);
1016  mr.runSpeed = static_cast<uint32_t>(MotorRateN[MOTOR_RATE_RUN].value);
1017  mr.decRate = static_cast<uint32_t>(MotorRateN[MOTOR_RATE_DEC].value);
1018 
1019  MotorCurrents mc;
1020  mc.accCurrent = static_cast<uint32_t>(MotorCurrentN[MOTOR_CURR_ACC].value);
1021  mc.runCurrent = static_cast<uint32_t>(MotorCurrentN[MOTOR_CURR_RUN].value);
1022  mc.decCurrent = static_cast<uint32_t>(MotorCurrentN[MOTOR_CURR_DEC].value);
1023  mc.holdCurrent = static_cast<uint32_t>(MotorCurrentN[MOTOR_CURR_HOLD].value);
1024 
1025  if (command->saveMotorUserPreset(userIndex, mr, mc))
1026  {
1027  LOGF_INFO("Saved motor user preset %u to firmware", userIndex);
1028  MotorSaveUserPresetSP.s = IPS_IDLE;
1029  }
1030  else
1031  {
1032  LOGF_ERROR("Failed to save motor user preset %u to firmware", userIndex);
1033  MotorSaveUserPresetSP.s = IPS_ALERT;
1034  }
1035 
1036  MotorSaveUserPresetS[index].s = ISS_OFF;
1037  IDSetSwitch(&MotorSaveUserPresetSP, nullptr);
1038  return true;
1039  }
1040  }
1041  return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
1042 }
1043 
1044 bool SestoSenso2::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
1045 {
1046  if (dev == nullptr || strcmp(dev, getDeviceName()) != 0)
1047  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
1048 
1049  if (!strcmp(name, MotorRateNP.name))
1050  {
1051  IUUpdateNumber(&MotorRateNP, values, names, n);
1052  MotorRateNP.s = IPS_OK;
1053  applyMotorRates();
1054  IDSetNumber(&MotorRateNP, nullptr);
1055  return true;
1056  }
1057  else if (!strcmp(name, MotorCurrentNP.name))
1058  {
1059  IUUpdateNumber(&MotorCurrentNP, values, names, n);
1060  MotorCurrentNP.s = IPS_OK;
1061  applyMotorCurrents();
1062  IDSetNumber(&MotorCurrentNP, nullptr);
1063  return true;
1064  }
1065 
1066  return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
1067 }
1068 
1070 {
1071  targetPos = targetTicks;
1072 
1073  if (isSimulation() == false)
1074  {
1075  backlashDirection = targetTicks < lastPos ? FOCUS_INWARD : FOCUS_OUTWARD;
1076  if(backlashDirection == FOCUS_INWARD)
1077  {
1078  targetPos -= backlashTicks;
1079  }
1080  else
1081  {
1082  targetPos += backlashTicks;
1083  }
1084  char res[SESTO_LEN] = {0};
1085  if (command->go(static_cast<uint32_t>(targetPos), res) == false)
1086  return IPS_ALERT;
1087  }
1088 
1089  // if (m_MotionProgressTimerID > 0)
1090  // IERmTimer(m_MotionProgressTimerID);
1091  // m_MotionProgressTimerID = IEAddTimer(10, &SestoSenso2::checkMotionProgressHelper, this);
1092  m_MotionProgressTimer.start(10);
1093  return IPS_BUSY;
1094 }
1095 
1097 {
1098  int reversed = (IUFindOnSwitchIndex(&FocusReverseSP) == INDI_ENABLED) ? -1 : 1;
1099  int relativeTicks = ((dir == FOCUS_INWARD) ? -ticks : ticks) * reversed;
1100  double newPosition = FocusAbsPosN[0].value + relativeTicks;
1101 
1102  bool rc = MoveAbsFocuser(newPosition);
1103 
1104  return (rc ? IPS_BUSY : IPS_ALERT);
1105 }
1106 
1108 {
1109  // if (m_MotionProgressTimerID > 0)
1110  // {
1111  // IERmTimer(m_MotionProgressTimerID);
1112  // m_MotionProgressTimerID = -1;
1113  // }
1114 
1115  m_MotionProgressTimer.stop();
1116 
1117  if (isSimulation())
1118  return true;
1119 
1120  bool rc = command->abort();
1121 
1122  if (rc && HomeSP.s == IPS_BUSY)
1123  {
1124  HomeS[0].s = ISS_OFF;
1125  HomeSP.s = IPS_IDLE;
1126  IDSetSwitch(&HomeSP, nullptr);
1127  }
1128 
1129  return rc;
1130 }
1131 
1132 //void SestoSenso2::checkMotionProgressHelper(void *context)
1133 //{
1134 // static_cast<SestoSenso2*>(context)->checkMotionProgressCallback();
1135 //}
1136 
1137 //void SestoSenso2::checkHallSensorHelper(void *context)
1138 //{
1139 // static_cast<SestoSenso2*>(context)->checkHallSensorCallback();
1140 //}
1141 
1142 //
1143 // This timer function is initiated when a GT command has been issued
1144 // A timer will call this function on a regular interval during the motion
1145 // Modified the code to exit when motion is complete
1146 //
1147 void SestoSenso2::checkMotionProgressCallback()
1148 {
1149  if (isMotionComplete())
1150  {
1153  SpeedNP.s = IPS_OK;
1154  SpeedN[0].value = 0;
1155  IDSetNumber(&SpeedNP, nullptr);
1156 
1157  IDSetNumber(&FocusRelPosNP, nullptr);
1158  IDSetNumber(&FocusAbsPosNP, nullptr);
1159 
1160 
1161  lastPos = FocusAbsPosN[0].value;
1162 
1163  if (HomeSP.s == IPS_BUSY)
1164  {
1165  LOG_INFO("Focuser at home position.");
1166  HomeS[0].s = ISS_OFF;
1167  HomeSP.s = IPS_OK;
1168  IDSetSwitch(&HomeSP, nullptr);
1169  }
1170  else if (CalibrationSP.s == IPS_BUSY)
1171  {
1172  ISState states[2] = { ISS_OFF, ISS_ON };
1173  const char * names[2] = { CalibrationS[CALIBRATION_START].name, CalibrationS[CALIBRATION_NEXT].name };
1174  ISNewSwitch(getDeviceName(), CalibrationSP.name, states, const_cast<char **>(names), CalibrationSP.nsp);
1175  }
1176  else
1177  LOG_INFO("Focuser reached requested position.");
1178  return;
1179  }
1180  else
1181  {
1182  IDSetNumber(&FocusAbsPosNP, nullptr);
1183  }
1184 
1185  SpeedNP.s = IPS_BUSY;
1186  IDSetNumber(&SpeedNP, nullptr);
1187 
1188  lastPos = FocusAbsPosN[0].value;
1189 
1190  // IERmTimer(m_MotionProgressTimerID);
1191  // m_MotionProgressTimerID = IEAddTimer(500, &SestoSenso2::checkMotionProgressHelper, this);
1192  m_MotionProgressTimer.start(500);
1193 }
1194 
1195 void SestoSenso2::checkHallSensorCallback()
1196 {
1197  // FIXME
1198  // Function not getting call from anywhere?
1199  char res[SESTO_LEN] = {0};
1200  if (command->getHallSensor(res))
1201  {
1202  int detected = 0;
1203  if (sscanf(res, "%d", &detected) == 1)
1204  {
1205  if (detected == 1)
1206  {
1207  ISState states[2] = { ISS_OFF, ISS_ON };
1208  const char * names[2] = { CalibrationS[CALIBRATION_START].name, CalibrationS[CALIBRATION_NEXT].name };
1209  ISNewSwitch(getDeviceName(), CalibrationSP.name, states, const_cast<char **>(names), CalibrationSP.nsp);
1210  return;
1211  }
1212  }
1213  }
1214 
1215  //m_HallSensorTimerID = IEAddTimer(1000, &SestoSenso2::checkHallSensorHelper, this);
1216  m_HallSensorTimer.start();
1217 }
1218 
1220 {
1221  if (!isConnected() || FocusAbsPosNP.s == IPS_BUSY || FocusRelPosNP.s == IPS_BUSY || (m_IsSestoSenso2
1222  && CalibrationSP.s == IPS_BUSY))
1223  {
1225  return;
1226  }
1227 
1228  bool rc = updatePosition();
1229  if (rc)
1230  {
1231  if (fabs(lastPos - FocusAbsPosN[0].value) > 0)
1232  {
1233  IDSetNumber(&FocusAbsPosNP, nullptr);
1234  lastPos = FocusAbsPosN[0].value;
1235  }
1236  }
1237 
1238  if (m_TemperatureCounter++ == SESTO_TEMPERATURE_FREQ)
1239  {
1240  rc = updateTemperature();
1241  if (rc)
1242  {
1243  if (fabs(lastTemperature - TemperatureN[0].value) >= 0.1)
1244  {
1245  IDSetNumber(&TemperatureNP, nullptr);
1246  lastTemperature = TemperatureN[0].value;
1247  }
1248  }
1249 
1250  // Also use temparature poll rate for tracking input voltage
1251  rc = updateVoltageIn();
1252  if (rc)
1253  {
1254  if (fabs(lastVoltageIn - VoltageInN[0].value) >= 0.1)
1255  {
1256  IDSetNumber(&VoltageInNP, nullptr);
1257  lastVoltageIn = VoltageInN[0].value;
1258 
1259  if (VoltageInN[0].value < 11.0)
1260  {
1261  LOG_WARN("Please check 12v DC power supply is connected.");
1262  }
1263  }
1264  }
1265 
1266  m_TemperatureCounter = 0; // Reset the counter
1267  }
1268 
1270 }
1271 
1272 bool SestoSenso2::getStartupValues()
1273 {
1274  bool rc = updatePosition();
1275  if (rc)
1276  {
1277  IDSetNumber(&FocusAbsPosNP, nullptr);
1278  }
1279 
1280  rc &= fetchMotorSettings();
1281 
1282  return (rc);
1283 }
1284 
1285 
1287 {
1288  INDI_UNUSED(enable);
1289  return false;
1290 }
1291 
1292 
1293 bool SestoSenso2::Ack()
1294 {
1295  char res[SESTO_LEN] = {0};
1296 
1297  if (isSimulation())
1298  strncpy(res, "1.0 Simulation", SESTO_LEN);
1299  else
1300  {
1301  if(initCommandSet() == false)
1302  {
1303  LOG_ERROR("Failed setting attributes on serial port and init command sets");
1304  return false;
1305  }
1306  if(command->getSerialNumber(res))
1307  {
1308  LOGF_INFO("Serial number: %s", res);
1309  }
1310  else
1311  {
1312  return false;
1313  }
1314  }
1315 
1316  m_IsSestoSenso2 = !strstr(res, "ESATTO");
1317  IUSaveText(&FirmwareT[FIRMWARE_SN], res);
1318 
1319  if (command->getFirmwareVersion(res))
1320  {
1321  LOGF_INFO("Firmware version: %s", res);
1322  IUSaveText(&FirmwareT[FIRMWARE_VERSION], res);
1323  }
1324  else
1325  {
1326  return false;
1327  }
1328 
1329  return true;
1330 }
1331 
1332 
1333 void SestoSenso2::setConnectionParams()
1334 {
1337 }
1338 
1339 
1340 bool SestoSenso2::initCommandSet()
1341 {
1342  command = new CommandSet(PortFD, getDeviceName());
1343 
1344  struct termios tty_setting;
1345  if (tcgetattr(PortFD, &tty_setting) == -1)
1346  {
1347  LOG_ERROR("setTTYFlags: failed getting tty attributes.");
1348  return false;
1349  }
1350  tty_setting.c_lflag |= ICANON;
1351  if (tcsetattr(PortFD, TCSANOW, &tty_setting))
1352  {
1353  LOG_ERROR("setTTYFlags: failed setting attributes on serial port.");
1354  return false;
1355  }
1356  return true;
1357 }
1358 
1360 {
1361  Focuser::saveConfigItems(fp);
1362 
1363  IUSaveConfigNumber(fp, &MotorRateNP);
1364  IUSaveConfigNumber(fp, &MotorCurrentNP);
1365  return true;
1366 }
1367 
1368 bool CommandSet::send(const std::string &request, std::string &response) const
1369 {
1370  tcflush(CommandSet::PortFD, TCIOFLUSH);
1371  if (write(CommandSet::PortFD, request.c_str(), request.length()) == 0)
1372  {
1373  LOGF_ERROR("Failed to send to device: %s", request.c_str());
1374  return false;
1375  }
1376 
1377  // NOTE: Every request should result in a response from the device
1378  char read_buf[SESTO_LEN] = {0};
1379  if (read(CommandSet::PortFD, &read_buf, sizeof(read_buf)) == 0)
1380  {
1381  LOGF_ERROR("No response from device for request: %s", request.c_str());
1382  return false;
1383  }
1384 
1385  LOGF_DEBUG("Received response: %s", read_buf);
1386 
1387  response = read_buf;
1388  return true;
1389 }
1390 
1391 bool CommandSet::sendCmd(const std::string &cmd, std::string property, char *res) const
1392 {
1393  tcflush(PortFD, TCIOFLUSH);
1394  LOGF_DEBUG("Sending command: %s with property: %s", cmd.c_str(), property.c_str());
1395  std::string response;
1396  if (!send(cmd, response))
1397  return false;
1398 
1399  if (property.empty() || res == nullptr)
1400  return true;
1401 
1402  if (getValueFromResponse(response, property, res) == false)
1403  {
1404  LOGF_ERROR("Communication error: cmd %s property %s response: %s", cmd.c_str(), property.c_str(), res);
1405  return false;
1406  }
1407 
1408  tcflush(PortFD, TCIOFLUSH);
1409 
1410  return true;
1411 }
1412 
1413 bool CommandSet::sendCmd(const std::string &cmd, std::string property, std::string &res) const
1414 {
1415  char response_buff[SESTO_LEN] = {0};
1416  bool success = sendCmd(cmd, property, response_buff);
1417  res = response_buff;
1418  return success;
1419 }
1420 
1421 inline void remove_chars_inplace(std::string &str, char ch)
1422 {
1423  str.erase(std::remove(str.begin(), str.end(), ch), str.end());
1424 }
1425 
1426 bool CommandSet::getValueFromResponse(const std::string &response, const std::string &property, char *value) const
1427 {
1428  // FIXME: This parsing code will only return the first named property,
1429  // if JSON layout changes, this may break things in unexpected ways.
1430 
1431  // Find property
1432  std::size_t property_pos = response.find(property);
1433  if (property_pos == std::string::npos)
1434  {
1435  LOGF_ERROR("Failed to find property: %s", property.c_str());
1436  return false;
1437  }
1438 
1439  // Skip past key name
1440  std::string sub = response.substr(property_pos + property.length());
1441 
1442  // Find end of current JSON element: , or }
1443  std::size_t found = sub.find(",");
1444  if(found != std::string::npos)
1445  {
1446  sub = sub.substr(0, found);
1447  }
1448  else
1449  {
1450  found = sub.find("}");
1451  sub = sub.substr(0, found);
1452  }
1453 
1454  // Strip JSON related formatting
1455  remove_chars_inplace(sub, '\"');
1456  remove_chars_inplace(sub, ',');
1457  remove_chars_inplace(sub, ':');
1458  strcpy(value, sub.c_str());
1459 
1460  return true;
1461 }
1462 
1463 bool CommandSet::parseUIntFromResponse(const std::string &response, const std::string &property, uint32_t &result) const
1464 {
1465  char valueBuff[SESTO_LEN] = { 0 };
1466  if (!getValueFromResponse(response, property, valueBuff))
1467  return false;
1468 
1469  if (sscanf(valueBuff, "%u", &result) != 1)
1470  {
1471  LOGF_ERROR("Failed to parse integer property %s with value: %s", property.c_str(), valueBuff);
1472  return false;
1473  }
1474 
1475  return true;
1476 }
1477 
1479 {
1480  return sendCmd("{\"req\":{\"get\":{\"SN\":\"\"}}}", "SN", res);
1481 }
1482 
1484 {
1485  return sendCmd("{\"req\":{\"get\":{\"SWVERS\":\"\"}}}", "SWAPP", res);
1486 }
1487 
1489 {
1490  return sendCmd("{\"req\":{\"cmd\":{\"MOT1\" :{\"MOT_ABORT\":\"\"}}}}");
1491 }
1492 
1493 bool CommandSet::go(uint32_t targetTicks, char *res)
1494 {
1495  char cmd[SESTO_LEN] = {0};
1496  snprintf(cmd, sizeof(cmd), "{\"req\":{\"cmd\":{\"MOT1\" :{\"GOTO\":%u}}}}", targetTicks);
1497  return sendCmd(cmd, "GOTO", res);
1498 }
1499 
1501 {
1502  return sendCmd("{\"req\":{\"cmd\":{\"MOT1\" :{\"MOT_STOP\":\"\"}}}}");
1503 }
1504 
1505 bool CommandSet::goHome(char *res)
1506 {
1507  return sendCmd("{\"req\":{\"cmd\":{\"MOT1\" :{\"GOHOME\":\"\"}}}}", "GOHOME", res);
1508 }
1509 
1511 {
1512  return sendCmd("{\"req\":{\"cmd\":{\"MOT1\" :{\"F_OUTW\":\"\"}}}}", "F_OUTW", res);
1513 }
1514 
1515 bool CommandSet::fastMoveIn(char *res)
1516 {
1517  return sendCmd("{\"req\":{\"cmd\":{\"MOT1\" :{\"F_INW\":\"\"}}}}", "F_INW", res);
1518 }
1519 
1521 {
1522  return sendCmd("{\"req\":{\"get\":{\"MOT1\":\"\"}}}", "CAL_MAXPOS", res);
1523 }
1524 
1526 {
1527  return sendCmd("{\"req\":{\"get\":{\"MOT1\":\"\"}}}", "HSENDET", res);
1528 }
1529 
1531 {
1532  return sendCmd("{\"req\":{\"cmd\": {\"MOT1\": {\"CAL_FOCUSER\": \"StoreAsMaxPos\"}}}}", res);
1533 }
1534 
1536 {
1537  return sendCmd("{\"req\":{\"cmd\": {\"MOT1\": {\"CAL_FOCUSER\": \"GoOutToFindMaxPos\"}}}}");
1538 }
1539 
1541 {
1542  return sendCmd("{\"req\":{\"cmd\": {\"MOT1\": {\"CAL_FOCUSER\": \"StoreAsMinPos\"}}}}");
1543 }
1544 
1546 {
1547  return sendCmd("{\"req\":{\"cmd\": {\"MOT1\": {\"CAL_FOCUSER\": \"Init\"}}}}");
1548 }
1549 
1551 {
1552  return sendCmd("{\"req\":{\"get\":{\"MOT1\":\"\"}}}", "ABS_POS", res);
1553 }
1554 
1556 {
1557  return sendCmd("{\"req\":{\"get\":{\"MOT1\":\"\"}}}", "SPEED", res);
1558 }
1559 
1561 {
1562  char cmd[SESTO_LEN] = {0};
1563  snprintf(cmd, sizeof(cmd), "{\"req\":{\"cmd\":{\"RUNPRESET\":\"%s\"}}}", name);
1564 
1565  std::string result;
1566  if (!sendCmd(cmd, "RUNPRESET", result))
1567  return false;
1568 
1569  if (result == "done")
1570  return true;
1571 
1572  LOGF_ERROR("Req RUNPRESET %s returned: %s", name, result.c_str());
1573  return false;
1574 }
1575 
1577 {
1578  // WORKAROUND: Due to a bug in the Sesto Senso 2 FW, the RUNPRESET
1579  // command fails when applied to user presets. Therefore here we
1580  // fetch the motor preset and then apply it ourselves.
1581  char request[SESTO_LEN] = {0};
1582  snprintf(request, sizeof(request), "{\"req\":{\"get\":{\"RUNPRESET_%u\":\"\"}}}}", index);
1583 
1584  std::string response;
1585  if (!send(request, response))
1586  return false; // send() call handles failure logging
1587 
1588  MotorRates mr;
1589  MotorCurrents mc;
1590  if (parseUIntFromResponse(response, "M1ACC", mr.accRate)
1591  && parseUIntFromResponse(response, "M1SPD", mr.runSpeed)
1592  && parseUIntFromResponse(response, "M1DEC", mr.decRate)
1593  && parseUIntFromResponse(response, "M1CACC", mc.accCurrent)
1594  && parseUIntFromResponse(response, "M1CSPD", mc.runCurrent)
1595  && parseUIntFromResponse(response, "M1CDEC", mc.decCurrent)
1596  && parseUIntFromResponse(response, "M1HOLD", mc.holdCurrent))
1597  {
1598  return setMotorRates(mr) && setMotorCurrents(mc);
1599  }
1600 
1601  // parseUIntFromResponse() should log failure
1602  return false;
1603 
1604  /* TODO: Replace above code with this once RUNPRESET is verified as fixed:
1605  char cmd[SESTO_LEN] = {0};
1606  snprintf(cmd, sizeof(cmd), "{\"req\":{\"cmd\":{\"RUNPRESET\":%u}}}", index);
1607 
1608  std::string result;
1609  if (!sendCmd(cmd, "RUNPRESET", result))
1610  return false;
1611 
1612  if (result == "done")
1613  return true;
1614 
1615  LOGF_ERROR("Req RUNPRESET %u returned: %s cmd:\n%s", index, result.c_str(), cmd);
1616  return false;
1617  */
1618 }
1619 
1620 constexpr char MOTOR_SAVE_PRESET_CMD[] =
1621  "{\"req\":{\"set\":{\"RUNPRESET_%u\":{"
1622  "\"RP_NAME\":\"User%u\","
1623  "\"M1ACC\":%u,\"M1DEC\":%u,\"M1SPD\":%u,"
1624  "\"M1CACC\":%u,\"M1CDEC\":%u,\"M1CSPD\":%u,\"M1HOLD\":%u"
1625  "}}}}";
1626 
1628 {
1629  char cmd[SESTO_LEN] = {0};
1630  snprintf(cmd, sizeof(cmd), MOTOR_SAVE_PRESET_CMD, index,
1631  index,
1632  mr.accRate, mr.decRate, mr.runSpeed,
1633  mc.accCurrent, mc.decCurrent, mc.runCurrent, mc.holdCurrent);
1634 
1635  std::string result;
1636  if (!sendCmd(cmd, "M1ACC", result))
1637  return false;
1638 
1639  // TODO: Check each parameter's result
1640  if (result == "done")
1641  return true;
1642 
1643  LOGF_ERROR("Set RUNPRESET %u returned: %s", index, result.c_str());
1644  return false;
1645 }
1646 
1648 {
1649  return sendCmd("{\"req\":{\"get\":{\"MOT1\":\"\"}}}", "NTC_T", res);
1650 }
1651 
1653 {
1654  return sendCmd("{\"req\":{\"get\":{\"EXT_T\":\"\"}}}", "EXT_T", res);
1655 }
1656 
1658 {
1659  return sendCmd("{\"req\":{\"get\":{\"VIN_12V\":\"\"}}}", "VIN_12V", res);
1660 }
1661 
1662 bool CommandSet::getMotorSettings(struct MotorRates &mr, struct MotorCurrents &mc, bool &motorHoldActive)
1663 {
1664  std::string response;
1665  if (!send("{\"req\":{\"get\":{\"MOT1\":\"\"}}}", response))
1666  return false; // send() call handles failure logging
1667 
1668  uint32_t holdStatus = 0;
1669  if (parseUIntFromResponse(response, "FnRUN_ACC", mr.accRate)
1670  && parseUIntFromResponse(response, "FnRUN_SPD", mr.runSpeed)
1671  && parseUIntFromResponse(response, "FnRUN_DEC", mr.decRate)
1672  && parseUIntFromResponse(response, "FnRUN_CURR_ACC", mc.accCurrent)
1673  && parseUIntFromResponse(response, "FnRUN_CURR_SPD", mc.runCurrent)
1674  && parseUIntFromResponse(response, "FnRUN_CURR_DEC", mc.decCurrent)
1675  && parseUIntFromResponse(response, "FnRUN_CURR_HOLD", mc.holdCurrent)
1676  && parseUIntFromResponse(response, "HOLDCURR_STATUS", holdStatus))
1677  {
1678  motorHoldActive = holdStatus != 0;
1679  return true;
1680  }
1681 
1682  // parseUIntFromResponse() should log failure
1683  return false;
1684 }
1685 
1686 constexpr char MOTOR_RATES_CMD[] =
1687  "{\"req\":{\"set\":{\"MOT1\":{"
1688  "\"FnRUN_ACC\":%u,"
1689  "\"FnRUN_SPD\":%u,"
1690  "\"FnRUN_DEC\":%u"
1691  "}}}}";
1692 
1694 {
1695  char cmd[SESTO_LEN] = {0};
1696  snprintf(cmd, sizeof(cmd), MOTOR_RATES_CMD, mr.accRate, mr.runSpeed, mr.decRate);
1697 
1698  std::string response;
1699  return send(cmd, response); // TODO: Check response!
1700 }
1701 
1702 constexpr char MOTOR_CURRENTS_CMD[] =
1703  "{\"req\":{\"set\":{\"MOT1\":{"
1704  "\"FnRUN_CURR_ACC\":%u,"
1705  "\"FnRUN_CURR_SPD\":%u,"
1706  "\"FnRUN_CURR_DEC\":%u,"
1707  "\"FnRUN_CURR_HOLD\":%u"
1708  "}}}}";
1709 
1711 {
1712  char cmd[SESTO_LEN] = {0};
1713  snprintf(cmd, sizeof(cmd), MOTOR_CURRENTS_CMD, mc.accCurrent, mc.runCurrent, mc.decCurrent, mc.holdCurrent);
1714 
1715  std::string response;
1716  return send(cmd, response); // TODO: Check response!
1717 }
1718 
1720 {
1721  char cmd[SESTO_LEN] = {0};
1722  snprintf(cmd, sizeof(cmd), "{\"req\":{\"set\":{\"MOT1\":{\"HOLDCURR_STATUS\":%u}}}}", hold ? 1 : 0);
1723 
1724  std::string response;
1725  return send(cmd, response); // TODO: Check response!
1726 }
INDI::FocuserInterface::FOCUSER_CAN_ABS_MOVE
@ FOCUSER_CAN_ABS_MOVE
Definition: indifocuserinterface.h:74
IP_RO
@ IP_RO
Definition: indiapi.h:183
CommandSet::getMotorTemp
bool getMotorTemp(char *res)
Definition: sestosenso2.cpp:1647
INDI::FocuserInterface::FOCUSER_CAN_REL_MOVE
@ FOCUSER_CAN_REL_MOVE
Definition: indifocuserinterface.h:75
MotorCurrents::accCurrent
uint32_t accCurrent
Definition: sestosenso2.cpp:51
MOTOR_PRESET_NAMES
const char * MOTOR_PRESET_NAMES[]
Definition: sestosenso2.cpp:57
CommandSet::stop
bool stop()
Definition: sestosenso2.cpp:1500
INDI::FocuserInterface::FocusAbsPosNP
INumberVectorProperty FocusAbsPosNP
Definition: indifocuserinterface.h:282
CommandSet::getMaxPosition
bool getMaxPosition(char *res)
Definition: sestosenso2.cpp:1520
CommandSet::abort
bool abort()
Definition: sestosenso2.cpp:1488
cmd
__u8 cmd[4]
Definition: pwc-ioctl.h:4
Connection::Serial::setWordSize
void setWordSize(const uint8_t &value)
setWordSize Set word size to be used in the serial connection. Default 8
Definition: connectionserial.h:157
MotorCurrents::holdCurrent
uint32_t holdCurrent
Definition: sestosenso2.cpp:53
INDI::DefaultDevice::addAuxControls
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
Definition: defaultdevice.cpp:665
INDI::FocuserInterface::FocusMaxPosNP
INumberVectorProperty FocusMaxPosNP
Definition: indifocuserinterface.h:290
SestoSenso2::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: sestosenso2.cpp:1044
IUUpdateMinMax
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
Definition: indidriver.c:1850
CommandSet::storeAsMinPosition
bool storeAsMinPosition()
Definition: sestosenso2.cpp:1540
IPState
IPState
Property state.
Definition: indiapi.h:158
INDI::Focuser::PresetNP
INumberVectorProperty PresetNP
Definition: indifocuser.h:105
INDI::FocuserInterface::FocusBacklashN
INumber FocusBacklashN[1]
Definition: indifocuserinterface.h:311
SestoSenso2::Disconnect
virtual bool Disconnect() override
Disconnect from device.
Definition: sestosenso2.cpp:279
LOGF_ERROR
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
IPS_OK
@ IPS_OK
Definition: indiapi.h:161
_INumberVectorProperty::s
IPState s
Definition: indiapi.h:332
ISwitch
One switch descriptor.
sestosenso2.h
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
CommandSet::goOutToFindMaxPos
bool goOutToFindMaxPos()
Definition: sestosenso2.cpp:1535
indicom.h
Implementations for common driver routines.
IDSetText
void IDSetText(const ITextVectorProperty *t, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing text vector property.
CommandSet::getMotorSettings
bool getMotorSettings(struct MotorRates &ms, struct MotorCurrents &mc, bool &motorHoldActive)
Definition: sestosenso2.cpp:1662
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
CommandSet::getSerialNumber
bool getSerialNumber(char *res)
Definition: sestosenso2.cpp:1478
CommandSet::getCurrentSpeed
bool getCurrentSpeed(char *res)
Definition: sestosenso2.cpp:1555
INDI::DefaultDevice::isSimulation
bool isSimulation() const
Definition: defaultdevice.cpp:734
IUFillNumber
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: indidriver.c:348
INDI::DefaultDevice::defineProperty
void defineProperty(INumberVectorProperty *property)
Definition: defaultdevice.cpp:997
MAIN_CONTROL_TAB
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
Definition: defaultdevice.cpp:34
MotorCurrents::decCurrent
uint32_t decCurrent
Definition: sestosenso2.cpp:51
Connection::Serial::B_115200
@ B_115200
Definition: connectionserial.h:82
IUFillTextVector
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.
Definition: indidriver.c:477
CommandSet::setMotorHold
bool setMotorHold(bool hold)
Definition: sestosenso2.cpp:1719
SestoSenso2::MoveAbsFocuser
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
Definition: sestosenso2.cpp:1069
INDI::DefaultDevice::setDefaultPollingPeriod
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
Definition: defaultdevice.cpp:1157
INDI_UNUSED
#define INDI_UNUSED(x)
Definition: indidevapi.h:799
SestoSenso2::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: sestosenso2.cpp:73
SestoSenso2::AbortFocuser
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: sestosenso2.cpp:1107
INDI::DefaultDevice::setVersion
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
Definition: defaultdevice.cpp:1219
MotorRates::runSpeed
uint32_t runSpeed
Definition: sestosenso2.cpp:45
INDI::BaseDevice::getDeviceName
const char * getDeviceName() const
Definition: basedevice.cpp:799
CommandSet::getFirmwareVersion
bool getFirmwareVersion(char *res)
Definition: sestosenso2.cpp:1483
INDI::Focuser::serialConnection
Connection::Serial * serialConnection
Definition: indifocuser.h:113
INDI::FocuserInterface::FOCUSER_CAN_ABORT
@ FOCUSER_CAN_ABORT
Definition: indifocuserinterface.h:76
CommandSet::setMotorRates
bool setMotorRates(struct MotorRates &ms)
Definition: sestosenso2.cpp:1693
IUSaveConfigNumber
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indicom.c:1455
IUFillText
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.
Definition: indidriver.c:369
INDI::FocuserInterface::FocusReverseSP
ISwitchVectorProperty FocusReverseSP
Definition: indifocuserinterface.h:302
MOTOR_SAVE_PRESET_CMD
constexpr char MOTOR_SAVE_PRESET_CMD[]
Definition: sestosenso2.cpp:1620
LOG_INFO
#define LOG_INFO(txt)
Definition: indilogger.h:74
max
double max(void)
SestoSenso2::getDefaultName
const char * getDefaultName() override
Definition: sestosenso2.cpp:295
SestoSenso2::TimerHit
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: sestosenso2.cpp:1219
IUResetSwitch
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indicom.c:1442
CommandSet::getVoltageIn
bool getVoltageIn(char *res)
Definition: sestosenso2.cpp:1657
CommandSet::saveMotorUserPreset
bool saveMotorUserPreset(uint32_t index, struct MotorRates &mr, struct MotorCurrents &mc)
Definition: sestosenso2.cpp:1627
IUFindOnSwitch
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *sp)
Returns the first ON switch it finds in the vector switch property.
Definition: indicom.c:1414
INDI::DefaultDevice::getCurrentPollingPeriod
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
Definition: defaultdevice.cpp:1139
MOTOR_RATES_CMD
constexpr char MOTOR_RATES_CMD[]
Definition: sestosenso2.cpp:1686
INDI::Timer::start
void start()
Starts or restarts the timer with the timeout specified in interval.
Definition: inditimer.cpp:78
MotorRates::decRate
uint32_t decRate
Definition: sestosenso2.cpp:45
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
Connection::Serial::setDefaultBaudRate
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
Definition: connectionserial.cpp:381
CommandSet::getAbsolutePosition
bool getAbsolutePosition(char *res)
Definition: sestosenso2.cpp:1550
SestoSenso2
Definition: sestosenso2.h:89
INDI::DefaultDevice::SetTimer
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
Definition: defaultdevice.cpp:865
INDI::Focuser::PortFD
int PortFD
Definition: indifocuser.h:116
INDI::Timer::setSingleShot
void setSingleShot(bool singleShot)
Set whether the timer is a single-shot timer.
Definition: inditimer.cpp:105
SestoSenso2::ReverseFocuser
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
Definition: sestosenso2.cpp:1286
INDI::Timer::setInterval
void setInterval(int msec)
Set the timeout interval in milliseconds.
Definition: inditimer.cpp:99
SestoSenso2::MoveRelFocuser
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: sestosenso2.cpp:1096
CommandSet::applyMotorUserPreset
bool applyMotorUserPreset(uint32_t index)
Definition: sestosenso2.cpp:1576
_ISwitchVectorProperty::nsp
int nsp
Definition: indiapi.h:386
MotorRates
Definition: sestosenso2.cpp:42
LOGF_WARN
#define LOGF_WARN(fmt,...)
Definition: indilogger.h:81
INDI::FocuserInterface::FOCUS_INWARD
@ FOCUS_INWARD
Definition: indifocuserinterface.h:68
INDI::Focuser::PresetN
INumber PresetN[3]
Definition: indifocuser.h:104
IUFillSwitchVector
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: indidriver.c:412
INDI::BaseDevice::INDI_ENABLED
@ INDI_ENABLED
Definition: basedevice.h:64
IUFillNumberVector
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: indidriver.c:455
IPS_BUSY
@ IPS_BUSY
Definition: indiapi.h:162
ISR_1OFMANY
@ ISR_1OFMANY
Definition: indiapi.h:172
connectionserial.h
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
MotorRates::accRate
uint32_t accRate
Definition: sestosenso2.cpp:45
Connection::CONNECTION_TAB
const char * CONNECTION_TAB
CONNECTION_TAB Where all device connection settings (serial, usb, ethernet) are defined and controlle...
Definition: connectioninterface.cpp:41
INDI::FocuserInterface::FocusRelPosN
INumber FocusRelPosN[1]
Definition: indifocuserinterface.h:287
CommandSet::storeAsMaxPosition
bool storeAsMaxPosition(char *res)
Definition: sestosenso2.cpp:1530
ISR_ATMOST1
@ ISR_ATMOST1
Definition: indiapi.h:173
INDI::Focuser::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: indifocuser.cpp:120
_INumberVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:322
INDI::Timer::callOnTimeout
void callOnTimeout(const std::function< void()> &callback)
Definition: inditimer.cpp:72
MotorCurrents::runCurrent
uint32_t runCurrent
Definition: sestosenso2.cpp:51
remove_chars_inplace
void remove_chars_inplace(std::string &str, char ch)
Definition: sestosenso2.cpp:1421
IUUpdateSwitch
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:171
INDI::FocuserInterface::FocusRelPosNP
INumberVectorProperty FocusRelPosNP
Definition: indifocuserinterface.h:286
_ITextVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:249
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
CommandSet::getExternalTemp
bool getExternalTemp(char *res)
Definition: sestosenso2.cpp:1652
INDI::FocuserInterface::SetFocuserBacklashEnabled
virtual bool SetFocuserBacklashEnabled(bool enabled)
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
Definition: indifocuserinterface.cpp:587
CommandSet::setMotorCurrents
bool setMotorCurrents(struct MotorCurrents &mc)
Definition: sestosenso2.cpp:1710
SestoSenso2::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: sestosenso2.cpp:630
LOGF_INFO
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
CommandSet::getHallSensor
bool getHallSensor(char *res)
Definition: sestosenso2.cpp:1525
LOG_ERROR
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
INDI::FocuserInterface::SetCapability
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
Definition: indifocuserinterface.h:95
CommandSet::applyMotorPreset
bool applyMotorPreset(const char *name)
Definition: sestosenso2.cpp:1560
IUSaveText
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indicom.c:1449
name
const char * name
Definition: indiserver.c:116
MOTOR_CURRENTS_CMD
constexpr char MOTOR_CURRENTS_CMD[]
Definition: sestosenso2.cpp:1702
SestoSenso2::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Saves the Device Port and Focuser Presets in the configuration file
Definition: sestosenso2.cpp:1359
_ISwitchVectorProperty::s
IPState s
Definition: indiapi.h:382
tty_setting
struct termios tty_setting
Definition: stvdriver.c:51
INDI::FocuserInterface::FocusMaxPosN
INumber FocusMaxPosN[1]
Definition: indifocuserinterface.h:291
INDI::Timer::stop
void stop()
Stops the timer.
Definition: inditimer.cpp:93
CommandSet::go
bool go(uint32_t targetTicks, char *res)
Definition: sestosenso2.cpp:1493
CommandSet::fastMoveIn
bool fastMoveIn(char *res)
Definition: sestosenso2.cpp:1515
SestoSenso2::SetFocuserBacklash
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
Definition: sestosenso2.cpp:287
IUUpdateNumber
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:225
INDI::FocuserInterface::FocusDirection
FocusDirection
Definition: indifocuserinterface.h:66
IP_RW
@ IP_RW
Definition: indiapi.h:185
LOG_WARN
#define LOG_WARN(txt)
Definition: indilogger.h:73
SestoSenso2::Handshake
virtual bool Handshake() override
perform handshake with device to check communication
Definition: sestosenso2.cpp:267
CommandSet::PortFD
int PortFD
Definition: sestosenso2.h:35
INDI::FocuserInterface::FOCUSER_HAS_BACKLASH
@ FOCUSER_HAS_BACKLASH
Definition: indifocuserinterface.h:80
ISState
ISState
Switch state.
Definition: indiapi.h:148
ENVIRONMENT_TAB
#define ENVIRONMENT_TAB
Definition: lx200_OnStep.cpp:39
CommandSet::fastMoveOut
bool fastMoveOut(char *res)
Definition: sestosenso2.cpp:1510
CommandSet
Definition: sestosenso2.h:26
IUFindSwitch
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
Definition: indicom.c:1382
IUFindOnSwitchIndex
int IUFindOnSwitchIndex(const ISwitchVectorProperty *sp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indicom.c:1424
SestoSenso2::SestoSenso2
SestoSenso2()
Definition: sestosenso2.cpp:59
INDI::Focuser::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indifocuser.cpp:58
INDI::DefaultDevice::Disconnect
virtual bool Disconnect()
Disconnect from device.
Definition: defaultdevice.cpp:1083
INDI::FocuserInterface::FocusBacklashNP
INumberVectorProperty FocusBacklashNP
Definition: indifocuserinterface.h:310
MotorCurrents
Definition: sestosenso2.cpp:48
INDI::DefaultDevice::deleteProperty
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
Definition: defaultdevice.cpp:965
INDI::Focuser::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: indifocuser.cpp:168
INDI::FocuserInterface::FocusAbsPosN
INumber FocusAbsPosN[1]
Definition: indifocuserinterface.h:283
IDSetNumber
void void void IDSetNumber(const INumberVectorProperty *n, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing number vector property.
IDSetSwitch
void void void void void IDSetSwitch(const ISwitchVectorProperty *s, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing switch vector property.
SestoSenso2::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: sestosenso2.cpp:209
CommandSet::goHome
bool goHome(char *res)
Definition: sestosenso2.cpp:1505
CommandSet::initCalibration
bool initCalibration()
Definition: sestosenso2.cpp:1545
IUFillSwitch
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: indidriver.c:320
INDI::Focuser::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: indifocuser.cpp:145
_ISwitchVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:370
ISS_ON
@ ISS_ON
Definition: indiapi.h:151
INDI::FocuserInterface::FOCUS_OUTWARD
@ FOCUS_OUTWARD
Definition: indifocuserinterface.h:69