Instrument Neutral Distributed Interface INDI 2.2.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
37static std::unique_ptr<SestoSenso2> sesto(new SestoSenso2());
38
39static const char *MOTOR_TAB = "Motor";
40static const char *ENVIRONMENT_TAB = "Environment";
41// Settings names for the default motor settings presets
42const char *MOTOR_PRESET_NAMES[] = { "light", "medium", "slow" };
43
45{
46 setVersion(1, 2);
47
48 // Can move in Absolute & Relative motions, can AbortFocuser motion.
50
51 m_MotionProgressTimer.callOnTimeout(std::bind(&SestoSenso2::checkMotionProgressCallback, this));
52 m_MotionProgressTimer.setSingleShot(true);
53}
54
59{
60
62
63 FocusBacklashNP[0].setMin(0);
64 FocusBacklashNP[0].setMax(10000);
65 FocusBacklashNP[0].setStep(1);
66 FocusBacklashNP[0].setValue(0);
67
68 setConnectionParams();
69
70 // Firmware information
71 FirmwareTP[FIRMWARE_SN].fill("SERIALNUMBER", "Serial Number", "");
72 FirmwareTP[FIRMWARE_VERSION].fill("VERSION", "Version", "");
73 FirmwareTP.fill(getDeviceName(), "FOCUS_FIRMWARE", "Firmware", CONNECTION_TAB, IP_RO, 0, IPS_IDLE);
74
75 // Voltage Information
76 VoltageInNP[0].fill("VOLTAGEIN", "Volts", "%.2f", 0, 100, 0., 0.);
77 VoltageInNP.fill(getDeviceName(), "VOLTAGE_IN", "Voltage in", ENVIRONMENT_TAB, IP_RO, 0, IPS_IDLE);
78
79 // Focuser temperature
80 TemperatureNP[TEMPERATURE_MOTOR].fill("TEMPERATURE", "Motor (c)", "%.2f", -50, 70., 0., 0.);
81 TemperatureNP[TEMPERATURE_EXTERNAL].fill("TEMPERATURE_ETX", "External (c)", "%.2f", -50, 70., 0., 0.);
82 TemperatureNP.fill(getDeviceName(), "FOCUS_TEMPERATURE", "Temperature", ENVIRONMENT_TAB, IP_RO, 0, IPS_IDLE);
83
84 // Focuser calibration
85 CalibrationMessageTP[0].fill("CALIBRATION", "Calibration stage", "Press START to begin the Calibration.");
86 CalibrationMessageTP.fill(getDeviceName(), "CALIBRATION_MESSAGE", "Calibration", MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE);
87
88 // Calibration
89 CalibrationSP[CALIBRATION_START].fill("CALIBRATION_START", "Start", ISS_OFF);
90 CalibrationSP[CALIBRATION_NEXT].fill("CALIBRATION_NEXT", "Next", ISS_OFF);
91 CalibrationSP.fill(getDeviceName(), "FOCUS_CALIBRATION", "Calibration", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
92
93 // Speed Moves
94 FastMoveSP[FASTMOVE_IN].fill("FASTMOVE_IN", "Move In", ISS_OFF);
95 FastMoveSP[FASTMOVE_OUT].fill("FASTMOVE_OUT", "Move out", ISS_OFF);
96 FastMoveSP[FASTMOVE_STOP].fill("FASTMOVE_STOP", "Stop", ISS_OFF);
97 FastMoveSP.fill(getDeviceName(), "FAST_MOVE", "Calibration Move", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
98
99 // Hold state
100 MotorHoldSP[MOTOR_HOLD_ON].fill("HOLD_ON", "Hold On", ISS_OFF);
101 MotorHoldSP[MOTOR_HOLD_OFF].fill("HOLD_OFF", "Hold Off", ISS_OFF);
102 MotorHoldSP.fill(getDeviceName(), "MOTOR_HOLD", "Motor Hold", MAIN_CONTROL_TAB,
104
105 // Override the default Max. Position to make it Read-Only
106 FocusMaxPosNP.fill(getDeviceName(), "FOCUS_MAX", "Max. Position", MAIN_CONTROL_TAB, IP_RO,
107 0, IPS_IDLE);
108
109 // Motor rate
110 MotorRateNP[MOTOR_RATE_ACC].fill("ACC", "Acceleration", "%.f", 1, 10, 1, 1);
111 MotorRateNP[MOTOR_RATE_RUN].fill("RUN", "Run Speed", "%.f", 1, 10, 1, 2);
112 MotorRateNP[MOTOR_RATE_DEC].fill("DEC", "Deceleration", "%.f", 1, 10, 1, 1);
113 MotorRateNP.fill(getDeviceName(), "MOTOR_RATE", "Motor Rate", MOTOR_TAB, IP_RW, 0, IPS_IDLE);
114
115 // Motor current
116 MotorCurrentNP[MOTOR_CURR_ACC].fill("CURR_ACC", "Acceleration", "%.f", 1, 10, 1, 7);
117 MotorCurrentNP[MOTOR_CURR_RUN].fill("CURR_RUN", "Run", "%.f", 1, 10, 1, 7);
118 MotorCurrentNP[MOTOR_CURR_DEC].fill("CURR_DEC", "Deceleration", "%.f", 1, 10, 1, 7);
119 MotorCurrentNP[MOTOR_CURR_HOLD].fill("CURR_HOLD", "Hold", "%.f", 0, 5, 1, 3);
120 MotorCurrentNP.fill(getDeviceName(), "MOTOR_CURRENT", "Current", MOTOR_TAB, IP_RW, 0, IPS_IDLE);
121
122 // Load motor preset
123 MotorApplyPresetSP[MOTOR_APPLY_LIGHT].fill("MOTOR_APPLY_LIGHT", "Light", ISS_OFF);
124 MotorApplyPresetSP[MOTOR_APPLY_MEDIUM].fill("MOTOR_APPLY_MEDIUM", "Medium", ISS_OFF);
125 MotorApplyPresetSP[MOTOR_APPLY_HEAVY].fill("MOTOR_APPLY_HEAVY", "Heavy", ISS_OFF);
126 MotorApplyPresetSP.fill(getDeviceName(), "MOTOR_APPLY_PRESET", "Apply Preset", MOTOR_TAB, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
127
128 // Load user preset
129 MotorApplyUserPresetSP[MOTOR_APPLY_USER1].fill("MOTOR_APPLY_USER1", "User 1", ISS_OFF);
130 MotorApplyUserPresetSP[MOTOR_APPLY_USER2].fill("MOTOR_APPLY_USER2", "User 2", ISS_OFF);
131 MotorApplyUserPresetSP[MOTOR_APPLY_USER3].fill("MOTOR_APPLY_USER3", "User 3", ISS_OFF);
132 MotorApplyUserPresetSP.fill(getDeviceName(), "MOTOR_APPLY_USER_PRESET", "Apply Custom", MOTOR_TAB, IP_RW, ISR_ATMOST1, 0,
133 IPS_IDLE);
134
135 // Save user preset
136 MotorSaveUserPresetSP[MOTOR_SAVE_USER1].fill("MOTOR_SAVE_USER1", "User 1", ISS_OFF);
137 MotorSaveUserPresetSP[MOTOR_SAVE_USER2].fill("MOTOR_SAVE_USER2", "User 2", ISS_OFF);
138 MotorSaveUserPresetSP[MOTOR_SAVE_USER3].fill("MOTOR_SAVE_USER3", "User 3", ISS_OFF);
139 MotorSaveUserPresetSP.fill(getDeviceName(), "MOTOR_SAVE_USER_PRESET", "Save Custom", MOTOR_TAB, IP_RW, ISR_ATMOST1, 0,
140 IPS_IDLE);
141
142 // Relative and absolute movement
143 FocusRelPosNP[0].setMin(0.);
144 FocusRelPosNP[0].setMax(50000.);
145 FocusRelPosNP[0].setValue(0);
146 FocusRelPosNP[0].setStep(1000);
147
148 FocusAbsPosNP[0].setMin(0.);
149 FocusAbsPosNP[0].setMax(200000.);
150 FocusAbsPosNP[0].setValue(0);
151 FocusAbsPosNP[0].setStep(1000);
152
153 FocusMaxPosNP[0].setValue(2097152);
154 PresetNP[0].setMax(FocusMaxPosNP[0].getValue());
155 PresetNP[1].setMax(FocusMaxPosNP[0].getValue());
156 PresetNP[2].setMax(FocusMaxPosNP[0].getValue());
157
159
161
162 return true;
163}
164
169{
170 if (isConnected() && updateMaxLimit() == false)
171 LOGF_WARN("Check you have the latest %s firmware. Focuser requires calibration.", getDeviceName());
172
174
175 if (isConnected())
176 {
177 defineProperty(CalibrationMessageTP);
178 defineProperty(CalibrationSP);
179 defineProperty(MotorRateNP);
180 defineProperty(MotorCurrentNP);
181 defineProperty(MotorHoldSP);
182 defineProperty(MotorApplyPresetSP);
183 defineProperty(MotorApplyUserPresetSP);
184 defineProperty(MotorSaveUserPresetSP);
185
186 defineProperty(FirmwareTP);
187
188 if (updateTemperature())
189 defineProperty(TemperatureNP);
190
191 if (updateVoltageIn())
192 defineProperty(VoltageInNP);
193
194 if (getStartupValues())
195 LOG_INFO("Parameters updated, focuser ready for use.");
196 else
197 LOG_WARN("Failed to inquire parameters. Check logs.");
198 }
199 else
200 {
201 if (TemperatureNP.getState() == IPS_OK)
202 deleteProperty(TemperatureNP);
203 deleteProperty(FirmwareTP);
204 deleteProperty(VoltageInNP);
205 deleteProperty(CalibrationMessageTP);
206 deleteProperty(CalibrationSP);
207 deleteProperty(MotorRateNP);
208 deleteProperty(MotorCurrentNP);
209 deleteProperty(MotorHoldSP);
210 deleteProperty(MotorApplyPresetSP);
211 deleteProperty(MotorApplyUserPresetSP);
212 deleteProperty(MotorSaveUserPresetSP);
213 }
214
215 return true;
216}
217
222{
223 if (Ack())
224 {
225 LOGF_INFO("%s is online. Getting focus parameters...", getDeviceName());
226 return true;
227 }
228
229 LOG_INFO("Error retrieving data from device, please ensure focuser is powered and the port is correct.");
230 return false;
231}
232
237{
238 return m_SestoSenso2->setBacklash(steps);
239}
240
245{
246 return "Sesto Senso 2";
247}
248
252bool SestoSenso2::updateTemperature()
253{
254 double temperature = 0;
255
256 if (isSimulation())
257 temperature = 23.5;
258 else if ( m_SestoSenso2->getMotorTemp(temperature) == false)
259 return false;
260
261 if (temperature > 90)
262 return false;
263
264 TemperatureNP[TEMPERATURE_MOTOR].setValue(temperature);
265 TemperatureNP.setState(IPS_OK);
266
267 // External temperature - Optional
268 if (m_SestoSenso2->getExternalTemp(temperature))
269 {
270 if (temperature < 90)
271 TemperatureNP[TEMPERATURE_EXTERNAL].setValue(temperature);
272 else
273 TemperatureNP[TEMPERATURE_EXTERNAL].setValue(-273.15);
274 }
275
276 return true;
277}
278
282bool SestoSenso2::updateMaxLimit()
283{
284 uint32_t maxLimit = 0;
285
286 if (isSimulation())
287 return true;
288
289 if (m_SestoSenso2->getMaxPosition(maxLimit) == false)
290 return false;
291
292 FocusMaxPosNP[0].setMax(maxLimit);
293 if (FocusMaxPosNP[0].getValue() > maxLimit)
294 FocusMaxPosNP[0].setValue(maxLimit);
295
296 FocusAbsPosNP[0].setMin(0);
297 FocusAbsPosNP[0].setMax(maxLimit);
298 FocusAbsPosNP[0].setValue(0);
299 FocusAbsPosNP[0].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
300
301 FocusRelPosNP[0].setMin(0.);
302 FocusRelPosNP[0].setMax(FocusAbsPosNP[0].getStep() * 10);
303 FocusRelPosNP[0].setValue(0);
304 FocusRelPosNP[0].setStep(FocusAbsPosNP[0].getStep());
305
306 PresetNP[0].setMax(maxLimit);
307 PresetNP[0].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
308 PresetNP[1].setMax(maxLimit);
309 PresetNP[1].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
310 PresetNP[2].setMax(maxLimit);
311 PresetNP[2].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
312
313
315 return true;
316}
317
321bool SestoSenso2::updatePosition()
322{
323 uint32_t steps = 0;
324 if (isSimulation())
325 steps = static_cast<uint32_t>(FocusAbsPosNP[0].getValue());
326 else if (m_SestoSenso2->getAbsolutePosition(steps) == false)
327 return false;
328
329 FocusAbsPosNP[0].setValue(steps);
330 return true;
331}
332
336bool SestoSenso2::updateVoltageIn()
337{
338 double voltageIn = 0;
339
340 if (isSimulation())
341 voltageIn = 12.0;
342 else if (m_SestoSenso2->getVoltage12v(voltageIn) == false)
343 return false;
344
345 if (voltageIn > 24)
346 return false;
347
348 VoltageInNP[0].setValue(voltageIn);
349 VoltageInNP.setState((voltageIn >= 11.0) ? IPS_OK : IPS_ALERT);
350
351 return true;
352}
353
357bool SestoSenso2::fetchMotorSettings()
358{
359 // Fetch driver state and reflect in INDI
362 bool motorHoldActive = false;
363
364 if (isSimulation())
365 {
366 ms.accRate = 1;
367 ms.runSpeed = 2;
368 ms.decRate = 1;
369 mc.accCurrent = 3;
370 mc.runCurrent = 4;
371 mc.decCurrent = 3;
372 mc.holdCurrent = 2;
373 }
374 else
375 {
376 if (!m_SestoSenso2->getMotorSettings(ms, mc, motorHoldActive))
377 {
378 MotorRateNP.setState(IPS_IDLE);
379 MotorCurrentNP.setState(IPS_IDLE);
380 MotorHoldSP.setState(IPS_IDLE);
381 return false;
382 }
383 }
384
385 MotorRateNP[MOTOR_RATE_ACC].setValue(ms.accRate);
386 MotorRateNP[MOTOR_RATE_RUN].setValue(ms.runSpeed);
387 MotorRateNP[MOTOR_RATE_DEC].setValue(ms.decRate);
388 MotorRateNP.setState(IPS_OK);
389 MotorRateNP.apply();
390
391 MotorCurrentNP[MOTOR_CURR_ACC].setValue(mc.accCurrent);
392 MotorCurrentNP[MOTOR_CURR_RUN].setValue(mc.runCurrent);
393 MotorCurrentNP[MOTOR_CURR_DEC].setValue(mc.decCurrent);
394 MotorCurrentNP[MOTOR_CURR_HOLD].setValue(mc.holdCurrent);
395 MotorCurrentNP.setState(IPS_OK);
396 MotorCurrentNP.apply();
397
398 // Also update motor hold switch
399 auto activeSwitchID = motorHoldActive ? "HOLD_ON" : "HOLD_OFF";
400 auto sp = MotorHoldSP.findWidgetByName(activeSwitchID);
401 assert(sp != nullptr && "Motor hold switch not found");
402 if (sp)
403 {
404 MotorHoldSP.reset();
405 sp->setState(ISS_ON);
406 MotorHoldSP.setState(motorHoldActive ? IPS_OK : IPS_ALERT);
407 MotorHoldSP.apply();
408 }
409
410 if (motorHoldActive && mc.holdCurrent == 0)
411 {
412 LOG_WARN("Motor hold current set to 0, motor hold setting will have no effect");
413 }
414
415 return true;
416}
417
421bool SestoSenso2::applyMotorRates()
422{
423 if (isSimulation())
424 return true;
425
426 // Send INDI state to driver
428 mr.accRate = static_cast<uint32_t>(MotorRateNP[MOTOR_RATE_ACC].getValue());
429 mr.runSpeed = static_cast<uint32_t>(MotorRateNP[MOTOR_RATE_RUN].getValue());
430 mr.decRate = static_cast<uint32_t>(MotorRateNP[MOTOR_RATE_DEC].getValue());
431
432 if (!m_SestoSenso2->setMotorRates(mr))
433 {
434 LOG_ERROR("Failed to apply motor rates");
435 return false;
436 }
437
438 LOGF_INFO("Motor rates applied: Acc: %u Run: %u Dec: %u", mr.accRate, mr.runSpeed, mr.decRate);
439 return true;
440}
441
445bool SestoSenso2::applyMotorCurrents()
446{
447 if (isSimulation())
448 return true;
449
450 // Send INDI state to driver
452 mc.accCurrent = static_cast<uint32_t>(MotorCurrentNP[MOTOR_CURR_ACC].getValue());
453 mc.runCurrent = static_cast<uint32_t>(MotorCurrentNP[MOTOR_CURR_RUN].getValue());
454 mc.decCurrent = static_cast<uint32_t>(MotorCurrentNP[MOTOR_CURR_DEC].getValue());
455 mc.holdCurrent = static_cast<uint32_t>(MotorCurrentNP[MOTOR_CURR_HOLD].getValue());
456
457 if (!m_SestoSenso2->setMotorCurrents(mc))
458 {
459 LOG_ERROR("Failed to apply motor currents");
460 return false;
461 }
462
463 LOGF_INFO("Motor currents applied: Acc: %u Run: %u Dec: %u Hold: %u", mc.accCurrent, mc.runCurrent, mc.decCurrent,
464 mc.holdCurrent);
465 return true;
466}
467
471bool SestoSenso2::isMotionComplete()
472{
473 if (isSimulation())
474 {
475 int32_t nextPos = FocusAbsPosNP[0].getValue();
476 int32_t targPos = static_cast<int32_t>(targetPos);
477
478 if (targPos > nextPos)
479 nextPos += 250;
480 else if (targPos < nextPos)
481 nextPos -= 250;
482
483 if (abs(nextPos - targPos) < 250)
484 nextPos = targetPos;
485 else if (nextPos < 0)
486 nextPos = 0;
487 else if (nextPos > FocusAbsPosNP[0].getMax())
488 nextPos = FocusAbsPosNP[0].getMax();
489
490 FocusAbsPosNP[0].setValue(nextPos);
491 return (std::abs(nextPos - static_cast<int32_t>(targetPos)) == 0);
492 }
493
494 return !m_SestoSenso2->isBusy();
495}
496
500bool SestoSenso2::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
501{
502 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
503 {
504 // Calibrate focuser
505 if (CalibrationSP.isNameMatch(name))
506 {
507 CalibrationSP.update(states, names, n);
508 auto current_switch = CalibrationSP.findOnSwitchIndex();
509 CalibrationSP[current_switch].setState(ISS_ON);
510 CalibrationSP.apply();
511
512 if (current_switch == CALIBRATION_START)
513 {
514 if (cStage == Idle || cStage == Complete )
515 {
516 // Start the calibration process
517 LOG_INFO("Start Calibration");
518 CalibrationSP.setState(IPS_BUSY);
519 CalibrationSP.apply();
520
521 // SestoSenso2: Manual Calibration (user moves by hand)
522 if (m_SestoSenso2->initCalibration() == false)
523 return false;
524 CalibrationMessageTP[0].setText("Manual Calibration: Set focus in MIN position and then press NEXT.");
525 CalibrationMessageTP.apply();
526 fetchMotorSettings();
527 cStage = GoToMiddle;
528 }
529 else
530 {
531 LOG_INFO("Already started calibration. Proceed to next step.");
532 CalibrationMessageTP[0].setText("Already started. Proceed to NEXT.");
533 CalibrationMessageTP.apply();
534 }
535 }
536 else if (current_switch == CALIBRATION_NEXT)
537 {
538 if (cStage == GoToMiddle)
539 {
540 defineProperty(FastMoveSP);
541 if (m_SestoSenso2->storeAsMinPosition() == false)
542 return false;
543 CalibrationMessageTP[0].setText("Manual Calibration: Press MOVE OUT to move focuser out (CAUTION!)");
544 CalibrationMessageTP.apply();
545 cStage = GoMinimum;
546 }
547 else if (cStage == GoMinimum)
548 {
549 if (m_SestoSenso2->storeAsMaxPosition() == false)
550 return false;
551 CalibrationMessageTP[0].setText("Manual Calibration: Press NEXT to finish.");
552 CalibrationMessageTP.apply();
553 cStage = GoMaximum;
554 }
555 else if (cStage == GoMaximum)
556 {
557 uint32_t maxLimit = 0;
558
559 if (m_SestoSenso2->getMaxPosition(maxLimit) == false)
560 return false;
561
562 LOGF_INFO("MAX setting is %d", maxLimit);
563
564 FocusMaxPosNP[0].setMax(maxLimit);
565 FocusMaxPosNP[0].setValue(maxLimit);
566
567 FocusAbsPosNP[0].setMin(0);
568 FocusAbsPosNP[0].setMax(maxLimit);
569 FocusAbsPosNP[0].setValue(maxLimit);
570 FocusAbsPosNP[0].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
571
572 FocusRelPosNP[0].setMin(0.);
573 FocusRelPosNP[0].setMax(FocusAbsPosNP[0].getStep() * 10);
574 FocusRelPosNP[0].setValue(0);
575 FocusRelPosNP[0].setStep(FocusAbsPosNP[0].getStep());
576
577 PresetNP[0].setMax(maxLimit);
578 PresetNP[0].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
579 PresetNP[1].setMax(maxLimit);
580 PresetNP[1].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
581 PresetNP[2].setMax(maxLimit);
582 PresetNP[2].setStep((FocusAbsPosNP[0].getMax() - FocusAbsPosNP[0].getMin()) / 50.0);
583
589
590 CalibrationMessageTP[0].setText("Calibration Completed.");
591 CalibrationMessageTP.apply();
592
593 deleteProperty(FastMoveSP);
594
595 cStage = Complete;
596
597 LOG_INFO("Calibration completed");
598 CalibrationSP.setState(IPS_OK);
599 CalibrationSP.apply();
600 CalibrationSP[current_switch].setState(ISS_OFF);
601 CalibrationSP.apply();
602
603 // Double check motor hold state after calibration
604 fetchMotorSettings();
605 }
606 else
607 {
608 CalibrationMessageTP[0].setText("Calibration not in progress.");
609 CalibrationMessageTP.apply();
610 }
611
612 }
613 return true;
614 }
615 // Fast motion
616 else if (FastMoveSP.isNameMatch(name))
617 {
618 FastMoveSP.update(states, names, n);
619 auto current_switch = FastMoveSP.findOnSwitchIndex();
620
621 switch (current_switch)
622 {
623 case FASTMOVE_IN:
624 if (m_SestoSenso2->fastMoveIn() == false)
625 return false;
626 break;
627 case FASTMOVE_OUT:
628 if (m_SestoSenso2->goOutToFindMaxPos() == false)
629 return false;
630 CalibrationMessageTP[0].setText("Manual Calibration: Press STOP focuser almost at MAX position.");
631 CalibrationMessageTP.apply();
632 fetchMotorSettings();
633 break;
634 case FASTMOVE_STOP:
635 if (m_SestoSenso2->stop() == false)
636 return false;
637 CalibrationMessageTP[0].setText("Press NEXT to store max limit.");
638 CalibrationMessageTP.apply();
639 break;
640 default:
641 break;
642 }
643
644 FastMoveSP.setState(IPS_BUSY);
645 FastMoveSP.apply();
646 return true;
647 }
648 // Homing
649 else if (MotorHoldSP.isNameMatch(name))
650 {
651 MotorHoldSP.update(states, names, n);
652 auto sp = MotorHoldSP.findOnSwitch();
653 assert(sp != nullptr);
654
655 // NOTE: Default to HOLD_ON as a safety feature
656 if (!strcmp(sp->name, "HOLD_OFF"))
657 {
658 m_SestoSenso2->setMotorHold(false);
659 MotorHoldSP.setState(IPS_ALERT);
660 LOG_INFO("Motor hold OFF. You may now manually adjust the focuser. Remember to enable motor hold once done.");
661 }
662 else
663 {
664 m_SestoSenso2->setMotorHold(true);
665 MotorHoldSP.setState(IPS_OK);
666 LOG_INFO("Motor hold ON. Do NOT attempt to manually adjust the focuser!");
667 if (MotorCurrentNP[MOTOR_CURR_HOLD].getValue() < 2.0)
668 {
669 LOGF_WARN("Motor hold current set to %.1f: This may be insufficient to hold focus",
670 MotorCurrentNP[MOTOR_CURR_HOLD].getValue());
671 }
672 }
673
674 MotorHoldSP.apply();
675 return true;
676 }
677 else if (MotorApplyPresetSP.isNameMatch(name))
678 {
679 MotorApplyPresetSP.update(states, names, n);
680 auto index = MotorApplyPresetSP.findOnSwitchIndex();
681 assert(index >= 0 && index < 3);
682
683 const char* presetName = MOTOR_PRESET_NAMES[index];
684
685 if (m_SestoSenso2->applyMotorPreset(presetName))
686 {
687 LOGF_INFO("Loaded motor preset: %s", presetName);
688 MotorApplyPresetSP.setState(IPS_IDLE);
689 }
690 else
691 {
692 LOGF_ERROR("Failed to load motor preset: %s", presetName);
693 MotorApplyPresetSP.setState(IPS_ALERT);
694 }
695
696 MotorApplyPresetSP[index].setState(ISS_OFF);
697 MotorApplyPresetSP.apply();
698
699 fetchMotorSettings();
700 return true;
701 }
702 }
703 return INDI::Focuser::ISNewSwitch(dev, name, states, names, n);
704}
705
709bool SestoSenso2::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
710{
711 if (dev == nullptr || strcmp(dev, getDeviceName()) != 0)
712 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
713
714 if (MotorRateNP.isNameMatch(name))
715 {
716 MotorRateNP.update(values, names, n);
717 MotorRateNP.setState(IPS_OK);
718 applyMotorRates();
719 MotorRateNP.apply();
720 return true;
721 }
722 else if (MotorCurrentNP.isNameMatch(name))
723 {
724 MotorCurrentNP.update(values, names, n);
725 MotorCurrentNP.setState(IPS_OK);
726 applyMotorCurrents();
727 MotorCurrentNP.apply();
728 return true;
729 }
730
731 return INDI::Focuser::ISNewNumber(dev, name, values, names, n);
732}
733
738{
739 targetPos = targetTicks;
740
741 if (isSimulation() == false)
742 {
743 backlashDirection = targetTicks < lastPos ? FOCUS_INWARD : FOCUS_OUTWARD;
744 if(backlashDirection == FOCUS_INWARD)
745 {
746 targetPos -= backlashTicks;
747 }
748 else
749 {
750 targetPos += backlashTicks;
751 }
752 if (m_SestoSenso2->goAbsolutePosition(targetPos) == false)
753 return IPS_ALERT;
754 }
755
756 m_MotionProgressTimer.start(10);
757 return IPS_BUSY;
758}
759
764{
765 int reversed = (FocusReverseSP.findOnSwitchIndex() == INDI_ENABLED) ? -1 : 1;
766 int relativeTicks = ((dir == FOCUS_INWARD) ? -ticks : ticks) * reversed;
767 double newPosition = FocusAbsPosNP[0].getValue() + relativeTicks;
768
769 bool rc = MoveAbsFocuser(newPosition);
770
771 return (rc ? IPS_BUSY : IPS_ALERT);
772}
773
778{
779 m_MotionProgressTimer.stop();
780
781 if (isSimulation())
782 return true;
783
784 return m_SestoSenso2->stop();
785}
786
790void SestoSenso2::checkMotionProgressCallback()
791{
793 lastPos = FocusAbsPosNP[0].getValue();
794 if (isMotionComplete())
795 {
797
798 if (CalibrationSP.getState() == IPS_BUSY)
799 {
800 ISState states[2] = { ISS_OFF, ISS_ON };
801 const char * names[2] = { CalibrationSP[CALIBRATION_START].getName(), CalibrationSP[CALIBRATION_NEXT].getName() };
802 ISNewSwitch(getDeviceName(), CalibrationSP.getName(), states, const_cast<char **>(names), CalibrationSP.count());
803 }
804 else
805 LOG_INFO("Focuser reached requested position.");
808 return;
809 }
810
811 m_MotionProgressTimer.start(500);
812}
813
818{
819 if (!isConnected() ||
822 CalibrationSP.getState() == IPS_BUSY)
823 {
825 return;
826 }
827
828 bool rc = updatePosition();
829 if (rc)
830 {
831 if (fabs(lastPos - FocusAbsPosNP[0].getValue()) > 0)
832 {
834 lastPos = FocusAbsPosNP[0].getValue();
835 }
836 }
837
838 if (m_TemperatureCounter++ == SESTO_TEMPERATURE_FREQ)
839 {
840 rc = updateTemperature();
841 if (rc)
842 {
843 if (fabs(lastTemperature - TemperatureNP[0].getValue()) >= 0.1)
844 {
845 TemperatureNP.apply();
846 lastTemperature = TemperatureNP[0].getValue();
847 }
848 }
849
850 // Also use temperature poll rate for tracking input voltage
851 rc = updateVoltageIn();
852 if (rc)
853 {
854 if (fabs(lastVoltageIn - VoltageInNP[0].getValue()) >= 0.1)
855 {
856 VoltageInNP.apply();
857 lastVoltageIn = VoltageInNP[0].getValue();
858
859 if (VoltageInNP[0].getValue() < 11.0)
860 {
861 LOG_WARN("Please check 12v DC power supply is connected.");
862 }
863 }
864 }
865
866 m_TemperatureCounter = 0; // Reset the counter
867 }
868
870}
871
875bool SestoSenso2::getStartupValues()
876{
877 bool rc = updatePosition();
878 if (rc)
879 {
881 }
882
883 rc &= fetchMotorSettings();
884
885 return (rc);
886}
887
892{
893 INDI_UNUSED(enable);
894 return false;
895}
896
900bool SestoSenso2::Ack()
901{
902 std::string response;
903
904 if (isSimulation())
905 response = "1.0 Simulation";
906 else
907 {
908 if(initCommandSet() == false)
909 {
910 LOG_ERROR("Failed setting attributes on serial port and init command sets");
911 return false;
912 }
913 if(m_SestoSenso2->getSerialNumber(response))
914 {
915 LOGF_INFO("Serial number: %s", response.c_str());
916 }
917 else
918 {
919 return false;
920 }
921
922 std::string modelName;
923 if (m_SestoSenso2->getModel(modelName))
924 {
925 LOGF_INFO("Model name: %s", modelName.c_str());
926 }
927 }
928
929 FirmwareTP[FIRMWARE_SN].setText(response.c_str());
930
931 if (m_SestoSenso2->getFirmwareVersion(response))
932 {
933 LOGF_INFO("Firmware version: %s", response.c_str());
934 IUSaveText(&FirmwareTP[FIRMWARE_VERSION], response.c_str());
935 }
936 else
937 {
938 return false;
939 }
940
941 return true;
942}
943
947void SestoSenso2::setConnectionParams()
948{
951}
952
956bool SestoSenso2::initCommandSet()
957{
958 m_SestoSenso2.reset(new PrimalucaLabs::SestoSenso2(getDeviceName(), PortFD));
959
960 struct termios tty_setting;
961 if (tcgetattr(PortFD, &tty_setting) == -1)
962 {
963 LOG_ERROR("setTTYFlags: failed getting tty attributes.");
964 return false;
965 }
966 tty_setting.c_lflag |= ICANON;
967 if (tcsetattr(PortFD, TCSANOW, &tty_setting))
968 {
969 LOG_ERROR("setTTYFlags: failed setting attributes on serial port.");
970 return false;
971 }
972 return true;
973}
974
979{
980 Focuser::saveConfigItems(fp);
981 MotorRateNP.save(fp);
982 MotorCurrentNP.save(fp);
983 return true;
984}
double temperature
void setWordSize(const uint8_t &value)
setWordSize Set word size to be used in the serial connection. Default 8
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
bool isConnected() const
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
INDI::PropertyNumber FocusRelPosNP
INDI::PropertySwitch FocusReverseSP
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusMaxPosNP
INDI::PropertyNumber FocusBacklashNP
INDI::PropertyNumber FocusAbsPosNP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
Connection::Serial * serialConnection
INDI::PropertyNumber PresetNP
void setState(IPState state)
WidgetView< T > * findWidgetByName(const char *name) const
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)
INDI::WidgetViewSwitch * findOnSwitch() const
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
void setSingleShot(bool singleShot)
Set whether the timer is a single-shot timer.
void callOnTimeout(const std::function< void()> &callback)
Definition inditimer.cpp:76
void start()
Starts or restarts the timer with the timeout specified in interval.
Definition inditimer.cpp:82
void stop()
Stops the timer.
Definition inditimer.cpp:97
virtual bool updateProperties() override
Updates the state and definition of properties based on the current connection status....
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Processes incoming new number values from the client for this device. Handles updates to FocuserInter...
virtual bool initProperties() override
Initializes the default properties for the focuser device. Sets up standard INDI properties,...
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
virtual bool saveConfigItems(FILE *fp) override
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Processes incoming new switch states from the client for this device. Handles updates to FocuserInter...
const char * getDefaultName() override
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool Handshake() override
Performs a device-specific handshake after a connection is established. This method should be overrid...
const char * CONNECTION_TAB
CONNECTION_TAB Where all device connection settings (serial, usb, ethernet) are defined and controlle...
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_ATMOST1
Definition indiapi.h:172
Implementations for common driver routines.
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define getDeviceName
#define ENVIRONMENT_TAB
const char * MOTOR_PRESET_NAMES[]
struct termios tty_setting
Definition stvdriver.c:51