Instrument Neutral Distributed Interface INDI 2.2.5
pegasus_upb3.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2026 Jasem Mutlaq. All rights reserved.
3
4 Pegasus Ultimate Power Box V3 Driver.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2 of the License, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20
21 The full GNU General Public License is included in this distribution in the
22 file called LICENSE.
23*******************************************************************************/
24
25#include "pegasus_upb3.h"
26#include "indicom.h"
28
29#include <memory>
30#include <regex>
31#include <termios.h>
32#include <cstring>
33#include <sys/ioctl.h>
34#include <chrono>
35#include <math.h>
36#include <iomanip>
37
38static constexpr const int PEGASUS_LEN = 128;
39
40// We declare an auto pointer to PegasusUPB3.
41static std::unique_ptr<PegasusUPB3> upb3(new PegasusUPB3());
42
43PegasusUPB3::PegasusUPB3() : INDI::DefaultDevice(),
44 INDI::FocuserInterface(this),
45 INDI::WeatherInterface(this),
46 INDI::PowerInterface(this),
48{
49 setVersion(1, 0);
50
51 lastSensorData.reserve(25);
52 lastPowerData.reserve(5);
53 lastStepperData.reserve(6);
54 lastAutoDewData.reserve(4);
55}
56
58{
59 return "Pegasus UPB3";
60}
61
65bool PegasusUPB3::Handshake()
66{
67 char response[PEGASUS_LEN] = {0};
68 int nbytes_read = 0;
69 PortFD = serialConnection->getPortFD();
70
71 LOG_DEBUG("CMD <P#>");
72
73 if (isSimulation())
74 {
75 snprintf(response, PEGASUS_LEN, "UPBv3:1.0");
76 nbytes_read = 10;
77 }
78 else
79 {
80 int tty_rc = 0, nbytes_written = 0;
81 char command[PEGASUS_LEN] = {0};
82 tcflush(PortFD, TCIOFLUSH);
83 strncpy(command, "P#\n", PEGASUS_LEN);
84 if ( (tty_rc = tty_write_string(PortFD, command, &nbytes_written)) != TTY_OK)
85 {
86 char errorMessage[MAXRBUF];
87 tty_error_msg(tty_rc, errorMessage, MAXRBUF);
88 LOGF_ERROR("Serial write error: %s", errorMessage);
89 return false;
90 }
91
92 // Try first with stopChar as the stop character
93 if ( (tty_rc = tty_nread_section(PortFD, response, PEGASUS_LEN, stopChar, 1, &nbytes_read)) != TTY_OK)
94 {
95 // Try 0xA as the stop character
96 if (tty_rc == TTY_OVERFLOW || tty_rc == TTY_TIME_OUT)
97 {
98 tcflush(PortFD, TCIOFLUSH);
99 tty_write_string(PortFD, command, &nbytes_written);
100 stopChar = 0xA;
101 tty_rc = tty_nread_section(PortFD, response, PEGASUS_LEN, stopChar, 1, &nbytes_read);
102 }
103
104 if (tty_rc != TTY_OK)
105 {
106 char errorMessage[MAXRBUF];
107 tty_error_msg(tty_rc, errorMessage, MAXRBUF);
108 LOGF_ERROR("Serial read error: %s", errorMessage);
109 return false;
110 }
111 }
112
113 cleanupResponse(response);
114 tcflush(PortFD, TCIOFLUSH);
115 }
116
117 LOGF_DEBUG("RES <%s>", response);
118
119 setupComplete = false;
120
121 // Check for UPBv3 identification
122 if (strstr(response, "UPBv3") == nullptr)
123 {
124 LOG_ERROR("Device not recognized as UPBv3. Please check connection.");
125 return false;
126 }
127
128 return true;
129}
130
134bool PegasusUPB3::setPowerEnabled(uint8_t port, uint8_t value)
135{
136 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
137 snprintf(cmd, PEGASUS_LEN, "P%d:%d", port, value);
138 if (sendCommand(cmd, res))
139 {
140 return (!strcmp(res, cmd));
141 }
142 return false;
143}
144
145bool PegasusUPB3::setPowerLEDEnabled(bool enabled)
146{
147 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
148 snprintf(cmd, PEGASUS_LEN, "PL:%d", enabled ? 1 : 0);
149 if (sendCommand(cmd, res))
150 {
151 return (!strcmp(res, cmd));
152 }
153 return false;
154}
155
156bool PegasusUPB3::setBuckVoltage(uint8_t voltage)
157{
158 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
159 snprintf(cmd, PEGASUS_LEN, "PJ:%d", voltage);
160 if (sendCommand(cmd, res))
161 {
162 return (!strcmp(res, cmd));
163 }
164 return false;
165}
166
167bool PegasusUPB3::setBoostVoltage(uint8_t voltage)
168{
169 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
170 snprintf(cmd, PEGASUS_LEN, "PB:%d", voltage);
171 if (sendCommand(cmd, res))
172 {
173 return (!strcmp(res, cmd));
174 }
175 return false;
176}
177
178bool PegasusUPB3::setBuckEnabled(bool enabled)
179{
180 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
181 snprintf(cmd, PEGASUS_LEN, "PJ:%d", enabled ? 1 : 0);
182 if (sendCommand(cmd, res))
183 {
184 return (!strcmp(res, cmd));
185 }
186 return false;
187}
188
189bool PegasusUPB3::setBoostEnabled(bool enabled)
190{
191 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
192 snprintf(cmd, PEGASUS_LEN, "PB:%d", enabled ? 1 : 0);
193 if (sendCommand(cmd, res))
194 {
195 return (!strcmp(res, cmd));
196 }
197 return false;
198}
199
200bool PegasusUPB3::setAutoDewEnabled(uint8_t port, bool enabled)
201{
202 // Per-port autodew: ADW command takes 3 boolean values
203 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
204
205 // Get current states
206 bool dew1 = (PI::AutoDewSP.size() > 0 && PI::AutoDewSP[0].getState() == ISS_ON);
207 bool dew2 = (PI::AutoDewSP.size() > 1 && PI::AutoDewSP[1].getState() == ISS_ON);
208 bool dew3 = (PI::AutoDewSP.size() > 2 && PI::AutoDewSP[2].getState() == ISS_ON);
209
210 // Update the specific port
211 if (port == 1) dew1 = enabled;
212 else if (port == 2) dew2 = enabled;
213 else if (port == 3) dew3 = enabled;
214
215 snprintf(cmd, PEGASUS_LEN, "ADW:%d:%d:%d", dew1 ? 1 : 0, dew2 ? 1 : 0, dew3 ? 1 : 0);
216 if (sendCommand(cmd, res))
217 {
218 return (!strcmp(res, cmd));
219 }
220 return false;
221}
222
223bool PegasusUPB3::setAutoDewAgg(uint8_t value)
224{
225 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
226 snprintf(cmd, PEGASUS_LEN, "DA:%d", value);
227 if (sendCommand(cmd, res))
228 {
229 return (!strcmp(res, cmd));
230 }
231 return false;
232}
233
234bool PegasusUPB3::setDewPWM(uint8_t id, uint8_t value)
235{
236 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
237 snprintf(cmd, PEGASUS_LEN, "D%d:%d", id, value);
238 if (sendCommand(cmd, res))
239 {
240 return (!strcmp(res, cmd));
241 }
242 return false;
243}
244
245bool PegasusUPB3::setUSBPortEnabled(uint8_t port, bool enabled)
246{
247 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
248 snprintf(cmd, PEGASUS_LEN, "U%d:%d", port, enabled ? 1 : 0);
249 if (sendCommand(cmd, res))
250 {
251 return (!strcmp(res, cmd));
252 }
253 return false;
254}
255
256bool PegasusUPB3::setRelay(bool enabled)
257{
258 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
259 snprintf(cmd, PEGASUS_LEN, "RL:%d", enabled ? 1 : 0);
260 if (sendCommand(cmd, res))
261 {
262 return (!strcmp(res, cmd));
263 }
264 return false;
265}
266
267bool PegasusUPB3::setFocuserMaxSpeed(uint16_t maxSpeed)
268{
269 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
270 snprintf(cmd, PEGASUS_LEN, "SS:%d", maxSpeed);
271 if (sendCommand(cmd, res))
272 {
273 return (!strcmp(res, cmd));
274 }
275 return false;
276}
277
278bool PegasusUPB3::setFocuserMicrostepping(uint8_t value)
279{
280 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
281 snprintf(cmd, PEGASUS_LEN, "MSTEP:%d", value);
282 if (sendCommand(cmd, res))
283 {
284 return (!strcmp(res, cmd));
285 }
286 return false;
287}
288
289bool PegasusUPB3::setFocuserCurrentLimit(uint16_t value)
290{
291 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
292 snprintf(cmd, PEGASUS_LEN, "MCUR:%d", value);
293 if (sendCommand(cmd, res))
294 {
295 return (!strcmp(res, cmd));
296 }
297 return false;
298}
299
300bool PegasusUPB3::setFocuserHoldTorque(bool enabled)
301{
302 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
303 snprintf(cmd, PEGASUS_LEN, "MHLD:%d", enabled ? 1 : 0);
304 if (sendCommand(cmd, res))
305 {
306 return (!strcmp(res, cmd));
307 }
308 return false;
309}
310
311bool PegasusUPB3::setPowerOnBoot()
312{
313 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
314 snprintf(cmd, PEGASUS_LEN, "PS"); // Store current power port states
315 if (sendCommand(cmd, res))
316 {
317 return true;
318 }
319 return false;
320}
321
322bool PegasusUPB3::getPowerOnBoot()
323{
324 // UPBv3 stores power states, we'll read them during getSensorData
325 return true;
326}
327
331bool PegasusUPB3::setupParams()
332{
333 sendFirmware();
334 return true;
335}
336
337bool PegasusUPB3::sendFirmware()
338{
339 char res[PEGASUS_LEN] = {0};
340 if (sendCommand("PV", res))
341 {
342 LOGF_INFO("Detected firmware %s", res);
343 FirmwareTP[FIRMWARE_VERSION].setText(res);
344 FirmwareTP.apply();
345 return true;
346 }
347 return false;
348}
349
350bool PegasusUPB3::getSensorData()
351{
352 char res[PEGASUS_LEN] = {0};
353 if (sendCommand("PA", res))
354 {
355 std::vector<std::string> result = split(res, ":");
356 // PA response format: PA:P1:P2:P3:P4:P5:P6:D1:D2:D3:Buck:Boost:Relay
357 // Total: 13 fields (PA + 12 values)
358 if (result.size() < 13)
359 {
360 LOGF_WARN("Received wrong number (%i) of sensor data (%s). Expected 13.", result.size(), res);
361 return false;
362 }
363
364 if (result == lastSensorData)
365 return true;
366
367 // Parse PA response (Power Aggregate Report)
368 // Fields: [0]=PA, [1-6]=Power ports PWM, [7-9]=Dew PWM, [10]=Buck, [11]=Boost, [12]=Relay
369
370 // Update Power ports (P1-P6) - UPBv3 has fixed 6 power ports
371 PI::PowerChannelsSP[0].setState((std::stoi(result[1]) > 0) ? ISS_ON : ISS_OFF);
372 PI::PowerChannelsSP[1].setState((std::stoi(result[2]) > 0) ? ISS_ON : ISS_OFF);
373 PI::PowerChannelsSP[2].setState((std::stoi(result[3]) > 0) ? ISS_ON : ISS_OFF);
374 PI::PowerChannelsSP[3].setState((std::stoi(result[4]) > 0) ? ISS_ON : ISS_OFF);
375 PI::PowerChannelsSP[4].setState((std::stoi(result[5]) > 0) ? ISS_ON : ISS_OFF);
376 PI::PowerChannelsSP[5].setState((std::stoi(result[6]) > 0) ? ISS_ON : ISS_OFF);
377 if (sensorUpdated(result, 1, 6))
379
380 // Update Dew heaters (D1-D3) - UPBv3 has fixed 3 dew heaters, PWM values 0-100
381 PI::DewChannelDutyCycleNP[0].setValue(std::stod(result[7]));
382 PI::DewChannelDutyCycleNP[1].setValue(std::stod(result[8]));
383 PI::DewChannelDutyCycleNP[2].setValue(std::stod(result[9]));
384 if (sensorUpdated(result, 7, 9))
386
387 // Update Buck/Boost status (indices 10, 11)
388 // Update Relay status (index 12)
389 m_RelayState = (std::stoi(result[12]) == 1);
390
391 lastSensorData = result;
392 return true;
393 }
394 return false;
395}
396
397bool PegasusUPB3::getPowerData()
398{
399 char res[PEGASUS_LEN] = {0};
400 if (sendCommand("PC", res))
401 {
402 std::vector<std::string> result = split(res, ":");
403 if (result.size() < 3)
404 {
405 LOGF_WARN("Received wrong number (%i) of power data (%s). Retrying...", result.size(), res);
406 return false;
407 }
408
409 if (result == lastPowerData)
410 return true;
411
412 PowerConsumptionNP[CONSUMPTION_AVG_AMPS].setValue(std::stod(result[1]));
413 PowerConsumptionNP[CONSUMPTION_AMP_HOURS].setValue(std::stod(result[2]));
414 PowerConsumptionNP[CONSUMPTION_WATT_HOURS].setValue(std::stod(result[3]));
415 PowerConsumptionNP.setState(IPS_OK);
416 PowerConsumptionNP.apply();
417
418 if (result.size() >= 5)
419 {
420 FirmwareTP[FIRMWARE_UPTIME].setText(result[4].c_str());
421 FirmwareTP.apply();
422 }
423
424 lastPowerData = result;
425 return true;
426 }
427 return false;
428}
429
430bool PegasusUPB3::getStepperData()
431{
432 char res[PEGASUS_LEN] = {0};
433 if (sendCommand("SA", res))
434 {
435 std::vector<std::string> result = split(res, ":");
436 if (result.size() < 5)
437 {
438 LOGF_WARN("Received wrong number (%i) of stepper data (%s). Retrying...", result.size(), res);
439 return false;
440 }
441
442 if (result == lastStepperData)
443 return true;
444
445 FocusAbsPosNP[0].setValue(std::stoi(result[1]));
446 focusMotorRunning = (std::stoi(result[2]) == 1);
447
448 if (FocusAbsPosNP.getState() == IPS_BUSY && focusMotorRunning == false)
449 {
454 }
455 else if (stepperUpdated(result, 1))
457
458 FocusReverseSP[INDI_ENABLED].setState((std::stoi(result[3]) == 1) ? ISS_ON : ISS_OFF);
459 FocusReverseSP[INDI_DISABLED].setState((std::stoi(result[3]) == 1) ? ISS_OFF : ISS_ON);
460
461 if (stepperUpdated(result, 3))
463
464 lastStepperData = result;
465 return true;
466 }
467 return false;
468}
469
470bool PegasusUPB3::getAutoDewData()
471{
472 char res[PEGASUS_LEN] = {0};
473 if (sendCommand("PD", res))
474 {
475 std::vector<std::string> result = split(res, ":");
476 if (result.size() < 2)
477 {
478 return true; // AutoDew data may not always be available
479 }
480
481 if (result == lastAutoDewData)
482 return true;
483
484 lastAutoDewData = result;
485 return true;
486 }
487 return false;
488}
489
490bool PegasusUPB3::getUSBStatus()
491{
492 char res[PEGASUS_LEN] = {0};
493 if (sendCommand("UA", res))
494 {
495 std::vector<std::string> result = split(res, ":");
496 // UA response format: UA:USB1:USB2:USB3:USB4:USB5:USB6:USB7:USB8
497 // Total: 9 fields (UA + 8 USB port statuses)
498 if (result.size() < 9)
499 {
500 return true; // USB data may not always be available
501 }
502
503 // Update USB port statuses (0 = off, 1 = on)
504 bool changed = false;
505 for (size_t i = 0; i < 8 && i < PI::USBPortSP.size(); i++)
506 {
507 auto newState = (std::stoi(result[i + 1]) == 1) ? ISS_ON : ISS_OFF;
508 if (PI::USBPortSP[i].getState() != newState)
509 {
510 PI::USBPortSP[i].setState(newState);
511 changed = true;
512 }
513 }
514
515 if (changed)
517
518 return true;
519 }
520 return false;
521}
522
523bool PegasusUPB3::getInputVoltageCurrentData()
524{
525 char res[PEGASUS_LEN] = {0};
526 if (sendCommand("VR", res))
527 {
528 std::vector<std::string> result = split(res, ":");
529 // VR response format: VR:<voltage>:<current>
530 if (result.size() < 3)
531 {
532 return true; // VR data may not always be available
533 }
534
535 // Check if data changed
536 if (result == lastVRData)
537 return true;
538
539 // Update PowerSensorsNP from PowerInterface
540 double voltage = std::stod(result[1]);
541 double current = std::stod(result[2]);
542 double power = voltage * current;
543
544 PI::PowerSensorsNP[PI::SENSOR_VOLTAGE].setValue(voltage);
545 PI::PowerSensorsNP[PI::SENSOR_CURRENT].setValue(current);
546 PI::PowerSensorsNP[PI::SENSOR_POWER].setValue(power);
549
550 lastVRData = result;
551 return true;
552 }
553 return false;
554}
555
556bool PegasusUPB3::getEnvironmentalData()
557{
558 char res[PEGASUS_LEN] = {0};
559 if (sendCommand("ES", res))
560 {
561 std::vector<std::string> result = split(res, ":");
562 // ES response format: ES:<temp>:<humidity>:<dewpoint>:<flag>
563 if (result.size() < 4)
564 {
565 return true; // ES data may not always be available
566 }
567
568 // Check if data changed
569 if (result == lastESData)
570 return true;
571
572 // Update Weather parameters
573 setParameterValue("WEATHER_TEMPERATURE", std::stod(result[1]));
574 setParameterValue("WEATHER_HUMIDITY", std::stod(result[2]));
575 setParameterValue("WEATHER_DEWPOINT", std::stod(result[3]));
576
579
582
583 lastESData = result;
584 return true;
585 }
586 return false;
587}
588
592bool PegasusUPB3::SetPowerPort(size_t port, bool enabled)
593{
594 // Port numbers are 0-based in interface, device expects 1-based (P1-P6)
595 // Set to 100% PWM if enabled, 0% if disabled
596 return setPowerEnabled(port + 1, enabled ? 100 : 0);
597}
598
599bool PegasusUPB3::SetDewPort(size_t port, bool enabled, double dutyCycle)
600{
601 // Dew ports are D1-D3, convert duty cycle (0-100%) to 0-255 PWM
602 auto pwmValue = static_cast<uint8_t>(dutyCycle / 100.0 * 255.0);
603 return setDewPWM(port + 1, enabled ? pwmValue : 0);
604}
605
606bool PegasusUPB3::SetVariablePort(size_t port, bool enabled, double voltage)
607{
608 // Port 0 = Buck (PJ), Port 1 = Boost (PB)
609 if (port == 0)
610 {
611 if (enabled)
612 return setBuckVoltage(static_cast<uint8_t>(voltage));
613 else
614 return setBuckEnabled(false);
615 }
616 else if (port == 1)
617 {
618 if (enabled)
619 return setBoostVoltage(static_cast<uint8_t>(voltage));
620 else
621 return setBoostEnabled(false);
622 }
623 return false;
624}
625
627{
628 return setPowerLEDEnabled(enabled);
629}
630
631bool PegasusUPB3::SetAutoDewEnabled(size_t port, bool enabled)
632{
633 return setAutoDewEnabled(port + 1, enabled);
634}
635
637{
638 // Turn all power ports off then on
639 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
640
641 // Turn all off
642 for (int i = 1; i <= 6; i++)
643 {
644 snprintf(cmd, PEGASUS_LEN, "P%d:0", i);
645 sendCommand(cmd, res);
646 }
647
648 // Short delay
649 usleep(100000); // 100ms
650
651 // Turn all on
652 for (int i = 1; i <= 6; i++)
653 {
654 snprintf(cmd, PEGASUS_LEN, "P%d:100", i);
655 sendCommand(cmd, res);
656 }
657
658 return true;
659}
660
661bool PegasusUPB3::SetUSBPort(size_t port, bool enabled)
662{
663 return setUSBPortEnabled(port + 1, enabled);
664}
665
670{
671 // Update relay status from device state
672 for (size_t i = 0; i < DigitalOutputsSP.size(); i++)
673 {
674 auto oldState = DigitalOutputsSP[i].findOnSwitchIndex();
675 auto newState = m_RelayState ? 1 : 0;
676
677 if (oldState != newState)
678 {
679 DigitalOutputsSP[i].reset();
680 DigitalOutputsSP[i][newState].setState(ISS_ON);
681 DigitalOutputsSP[i].setState(IPS_OK);
682 DigitalOutputsSP[i].apply();
683 }
684 }
685
686 return true;
687}
688
689bool PegasusUPB3::CommandOutput(uint32_t index, OutputState command)
690{
691 if (index >= 1) // We only have 1 relay (index 0)
692 return false;
693
694 bool enabled = (command == OutputState::On);
695
696 if (setRelay(enabled))
697 {
698 m_RelayState = enabled;
699 return true;
700 }
701
702 return false;
703}
704
709{
710 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
711 snprintf(cmd, PEGASUS_LEN, "SM:%u", targetTicks);
712 if (sendCommand(cmd, res))
713 {
714 return (!strcmp(res, cmd) ? IPS_BUSY : IPS_ALERT);
715 }
716 return IPS_ALERT;
717}
718
720{
721 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
722 snprintf(cmd, PEGASUS_LEN, "SG:%c%u", dir == FOCUS_INWARD ? '-' : '+', ticks);
723 if (sendCommand(cmd, res))
724 {
725 return (!strcmp(res, cmd) ? IPS_BUSY : IPS_ALERT);
726 }
727 return IPS_ALERT;
728}
729
731{
732 char res[PEGASUS_LEN] = {0};
733 if (sendCommand("SH", res))
734 {
735 return strstr(res, "SH:1") != nullptr;
736 }
737 return false;
738}
739
741{
742 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
743 snprintf(cmd, PEGASUS_LEN, "SR:%d", enabled ? 1 : 0);
744 if (sendCommand(cmd, res))
745 {
746 return (!strcmp(res, cmd));
747 }
748 return false;
749}
750
751bool PegasusUPB3::SyncFocuser(uint32_t ticks)
752{
753 char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
754 snprintf(cmd, PEGASUS_LEN, "SC:%u", ticks);
755 if (sendCommand(cmd, res))
756 {
757 return (!strcmp(res, cmd));
758 }
759 return false;
760}
761
763{
764 // UPBv3 doesn't have explicit backlash command in documentation
765 // This would need to be handled in software
766 INDI_UNUSED(steps);
767 return true;
768}
769
771{
772 // UPBv3 doesn't have explicit backlash command in documentation
773 INDI_UNUSED(enabled);
774 return true;
775}
776
780bool PegasusUPB3::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
781{
782 if (dev && !strcmp(dev, getDeviceName()))
783 {
784 // Reboot
785 if (RebootSP.isNameMatch(name))
786 {
787 RebootSP.setState(reboot() ? IPS_OK : IPS_ALERT);
788 RebootSP.apply();
789 LOG_INFO("Rebooting device...");
790 return true;
791 }
792
793 // Power on boot
794 if (PowerOnBootSP.isNameMatch(name))
795 {
796 PowerOnBootSP.update(states, names, n);
797 PowerOnBootSP.setState(setPowerOnBoot() ? IPS_OK : IPS_ALERT);
798 PowerOnBootSP.apply();
799 saveConfig(PowerOnBootSP);
800 return true;
801 }
802
803 // Focuser Interface
804 if (FI::processSwitch(dev, name, states, names, n))
805 return true;
806
807 // Power Interface
808 if (PI::processSwitch(dev, name, states, names, n))
809 return true;
810
811 // Output Interface (Relay)
812 if (INDI::OutputInterface::processSwitch(dev, name, states, names, n))
813 return true;
814 }
815
816 return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
817}
818
822bool PegasusUPB3::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
823{
824 if (dev && !strcmp(dev, getDeviceName()))
825 {
826 // Auto Dew Aggressiveness (Global)
827 if (AutoDewAggNP.isNameMatch(name))
828 {
829 if (setAutoDewAgg(static_cast<uint8_t>(values[0])))
830 {
831 AutoDewAggNP[0].setValue(values[0]);
832 AutoDewAggNP.setState(IPS_OK);
833 }
834 else
835 {
836 AutoDewAggNP.setState(IPS_ALERT);
837 }
838 AutoDewAggNP.apply();
839 return true;
840 }
841
842 // Auto Dew Aggressiveness per Port
843 if (AutoDewAggPerPortNP.isNameMatch(name))
844 {
845 AutoDewAggPerPortNP.update(values, names, n);
846 AutoDewAggPerPortNP.setState(IPS_OK);
847 AutoDewAggPerPortNP.apply();
848 saveConfig(AutoDewAggPerPortNP);
849 return true;
850 }
851
852 // Focuser Settings
853 if (FocuserSettingsNP.isNameMatch(name))
854 {
855 FocuserSettingsNP.update(values, names, n);
856
857 setFocuserMaxSpeed(static_cast<uint16_t>(FocuserSettingsNP[SETTING_MAX_SPEED].getValue()));
858 setFocuserMicrostepping(static_cast<uint8_t>(FocuserSettingsNP[SETTING_MICROSTEPPING].getValue()));
859 setFocuserCurrentLimit(static_cast<uint16_t>(FocuserSettingsNP[SETTING_CURRENT_LIMIT].getValue()));
860 setFocuserHoldTorque(static_cast<bool>(FocuserSettingsNP[SETTING_HOLD_TORQUE].getValue()));
861
862 FocuserSettingsNP.setState(IPS_OK);
863 FocuserSettingsNP.apply();
864 saveConfig(FocuserSettingsNP);
865 return true;
866 }
867
868 // Focuser
869 if (FI::processNumber(dev, name, values, names, n))
870 return true;
871
872 // Weather
873 if (WI::processNumber(dev, name, values, names, n))
874 return true;
875
876 // Power Interface
877 if (PI::processNumber(dev, name, values, names, n))
878 return true;
879
880 // Output Interface (Relay)
881 if (INDI::OutputInterface::processNumber(dev, name, values, names, n))
882 return true;
883 }
884 return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
885}
886
890bool PegasusUPB3::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
891{
892 if (dev && !strcmp(dev, getDeviceName()))
893 {
894 // Power Interface (USB/Power labels)
895 if (PI::processText(dev, name, texts, names, n))
896 return true;
897
898 // Output Interface (Relay labels)
899 if (INDI::OutputInterface::processText(dev, name, texts, names, n))
900 return true;
901 }
902
903 return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
904}
905
910{
916
917 AutoDewAggNP.save(fp);
918 AutoDewAggPerPortNP.save(fp);
919 FocuserSettingsNP.save(fp);
920 PowerOnBootSP.save(fp);
921
922 return true;
923}
924
929{
930 if (!isConnected() || setupComplete == false)
931 {
933 return;
934 }
935
936 if (getSensorData())
937 {
938 getPowerData();
939 getStepperData();
940 getAutoDewData();
941 getUSBStatus();
942 getInputVoltageCurrentData();
943 getEnvironmentalData();
945 }
946
948}
949
953bool PegasusUPB3::sendCommand(const char * cmd, char * res)
954{
955 int nbytes_read = 0, nbytes_written = 0, tty_rc = 0;
956 LOGF_DEBUG("CMD <%s>", cmd);
957
958 if (isSimulation())
959 {
960 // Simulation responses will be added incrementally
961 if (!strcmp(cmd, "PV"))
962 {
963 strncpy(res, "PV:1.0", PEGASUS_LEN);
964 }
965 else if (res)
966 {
967 snprintf(res, PEGASUS_LEN, "%s", cmd);
968 }
969 return true;
970 }
971
972 for (int i = 0; i < 2; i++)
973 {
974 char command[PEGASUS_LEN] = {0};
975 tcflush(PortFD, TCIOFLUSH);
976 snprintf(command, PEGASUS_LEN, "%s\n", cmd);
977 if ( (tty_rc = tty_write_string(PortFD, command, &nbytes_written)) != TTY_OK)
978 continue;
979
980 if (!res)
981 {
982 tcflush(PortFD, TCIOFLUSH);
983 return true;
984 }
985
986 if ( (tty_rc = tty_nread_section(PortFD, res, PEGASUS_LEN, stopChar, PEGASUS_TIMEOUT, &nbytes_read)) != TTY_OK
987 || nbytes_read == 1)
988 continue;
989
990 tcflush(PortFD, TCIOFLUSH);
991
992 cleanupResponse(res);
993 LOGF_DEBUG("RES <%s>", res);
994 return true;
995 }
996
997 if (tty_rc != TTY_OK)
998 {
999 char errorMessage[MAXRBUF];
1000 tty_error_msg(tty_rc, errorMessage, MAXRBUF);
1001 LOGF_ERROR("Serial error: %s", errorMessage);
1002 }
1003
1004 return false;
1005}
1006
1010void PegasusUPB3::cleanupResponse(char *response)
1011{
1012 std::string s(response);
1013 s.erase(std::remove_if(s.begin(), s.end(),
1014 [](unsigned char x)
1015 {
1016 return std::isspace(x);
1017 }), s.end());
1018 snprintf(response, PEGASUS_LEN, "%s", s.c_str());
1019}
1020
1024std::vector<std::string> PegasusUPB3::split(const std::string &input, const std::string &regex)
1025{
1026 // passing -1 as the submatch index parameter performs splitting
1027 std::regex re(regex);
1028 std::sregex_token_iterator
1029 first{input.begin(), input.end(), re, -1},
1030 last;
1031 return {first, last};
1032}
1033
1037bool PegasusUPB3::sensorUpdated(const std::vector<std::string> &result, uint8_t start, uint8_t end)
1038{
1039 if (lastSensorData.empty())
1040 return true;
1041
1042 for (uint8_t index = start; index <= end; index++)
1043 {
1044 if (index >= lastSensorData.size() || result[index] != lastSensorData[index])
1045 return true;
1046 }
1047
1048 return false;
1049}
1050
1054bool PegasusUPB3::stepperUpdated(const std::vector<std::string> &result, uint8_t index)
1055{
1056 if (lastStepperData.empty())
1057 return true;
1058
1059 return index >= lastStepperData.size() || result[index] != lastStepperData[index];
1060}
1061
1065bool PegasusUPB3::reboot()
1066{
1067 return sendCommand("PF", nullptr);
1068}
1069
1074{
1076
1078
1079 // Focuser Interface
1085
1087
1088 // Weather Interface
1089 WI::initProperties(ENVIRONMENT_TAB, ENVIRONMENT_TAB);
1090 addParameter("WEATHER_TEMPERATURE", "Temperature (C)", -15, 35, 15);
1091 addParameter("WEATHER_HUMIDITY", "Humidity %", 0, 100, 15);
1092 addParameter("WEATHER_DEWPOINT", "Dew Point (C)", 0, 100, 15);
1093 setCriticalParameter("WEATHER_TEMPERATURE");
1094
1095 // Power Interface
1099 // 6 DC ports, 3 DEW ports, 2 Variable ports (Buck+Boost), 3 Auto Dew ports (per-port), 8 USB ports
1100 PI::initProperties(POWER_TAB, 6, 3, 2, 3, 8);
1101
1102 // Set Variable port labels to Buck and Boost
1105
1106 // Set Variable port voltage ranges
1107 // Port 0 = Buck (3-12V), Port 1 = Boost (12-24V)
1108 PI::VariableChannelVoltsNP[0].setMinMax(3, 12);
1109 PI::VariableChannelVoltsNP[0].setValue(12);
1110 PI::VariableChannelVoltsNP[1].setMinMax(12, 24);
1111 PI::VariableChannelVoltsNP[1].setStep(3);
1112 PI::VariableChannelVoltsNP[1].setValue(12);
1113
1114 // Populate PI::USBPortLabelsTP with custom labels
1115 if (PI::USBPortLabelsTP.size() >= 8)
1116 {
1117 PI::USBPortLabelsTP[0].setLabel("USB Port 1");
1118 PI::USBPortLabelsTP[1].setLabel("USB Port 2");
1119 PI::USBPortLabelsTP[2].setLabel("USB Port 3");
1120 PI::USBPortLabelsTP[3].setLabel("USB Port 4");
1121 PI::USBPortLabelsTP[4].setLabel("USB Port 5");
1122 PI::USBPortLabelsTP[5].setLabel("USB Port 6");
1123 PI::USBPortLabelsTP[6].setLabel("USB Port 7");
1124 PI::USBPortLabelsTP[7].setLabel("USB Port 8");
1125 }
1126
1127 // Output Interface for Relay
1128 INDI::OutputInterface::initProperties(RELAY_TAB, 1, "Relay");
1129
1131
1132 // Reboot
1133 RebootSP[0].fill("REBOOT", "Reboot Device", ISS_OFF);
1134 RebootSP.fill(getDeviceName(), "REBOOT_DEVICE", "Device", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
1135
1136 // Power Consumption
1137 PowerConsumptionNP[CONSUMPTION_AVG_AMPS].fill("CONSUMPTION_AVG_AMPS", "Avg. Amps", "%4.2f", 0, 999, 100, 0);
1138 PowerConsumptionNP[CONSUMPTION_AMP_HOURS].fill("CONSUMPTION_AMP_HOURS", "Amp Hours", "%4.2f", 0, 999, 100, 0);
1139 PowerConsumptionNP[CONSUMPTION_WATT_HOURS].fill("CONSUMPTION_WATT_HOURS", "Watt Hours", "%4.2f", 0, 999, 100, 0);
1140 PowerConsumptionNP.fill(getDeviceName(), "POWER_CONSUMPTION", "Consumption", MAIN_CONTROL_TAB, IP_RO, 60, IPS_IDLE);
1141
1142 // Power on Boot
1143 PowerOnBootSP[POWER_PORT_1].fill("POWER_PORT_1", "Power Port 1", ISS_ON);
1144 PowerOnBootSP[POWER_PORT_2].fill("POWER_PORT_2", "Power Port 2", ISS_ON);
1145 PowerOnBootSP[POWER_PORT_3].fill("POWER_PORT_3", "Power Port 3", ISS_ON);
1146 PowerOnBootSP[POWER_PORT_4].fill("POWER_PORT_4", "Power Port 4", ISS_ON);
1147 PowerOnBootSP[POWER_PORT_5].fill("POWER_PORT_5", "Power Port 5", ISS_ON);
1148 PowerOnBootSP[POWER_PORT_6].fill("POWER_PORT_6", "Power Port 6", ISS_ON);
1149 PowerOnBootSP.fill(getDeviceName(), "POWER_ON_BOOT", "Power On Boot", POWER_TAB, IP_RW, ISR_NOFMANY, 60, IPS_IDLE);
1150
1151 // Overcurrent Protection
1152 OverCurrentLP[OC_POWER_1].fill("OC_POWER_1", "Power Port 1", IPS_OK);
1153 OverCurrentLP[OC_POWER_2].fill("OC_POWER_2", "Power Port 2", IPS_OK);
1154 OverCurrentLP[OC_POWER_3].fill("OC_POWER_3", "Power Port 3", IPS_OK);
1155 OverCurrentLP[OC_POWER_4].fill("OC_POWER_4", "Power Port 4", IPS_OK);
1156 OverCurrentLP[OC_POWER_5].fill("OC_POWER_5", "Power Port 5", IPS_OK);
1157 OverCurrentLP[OC_POWER_6].fill("OC_POWER_6", "Power Port 6", IPS_OK);
1158 OverCurrentLP[OC_DEW_1].fill("OC_DEW_1", "Dew A", IPS_OK);
1159 OverCurrentLP[OC_DEW_2].fill("OC_DEW_2", "Dew B", IPS_OK);
1160 OverCurrentLP[OC_DEW_3].fill("OC_DEW_3", "Dew C", IPS_OK);
1161 OverCurrentLP.fill(getDeviceName(), "OVER_CURRENT", "Overcurrent", POWER_TAB, IPS_IDLE);
1162
1163 // Auto Dew Aggressiveness (Global)
1164 AutoDewAggNP[AUTO_DEW_AGG].fill("AUTO_DEW_AGG_VALUE", "Global (0-10)", "%.f", 0, 10, 1, 5);
1165 AutoDewAggNP.fill(getDeviceName(), "AUTO_DEW_AGG", "Auto Dew Agg", DEW_TAB, IP_RW, 60, IPS_IDLE);
1166
1167 // Auto Dew Aggressiveness per Port
1168 AutoDewAggPerPortNP[AUTO_DEW_AGG_1].fill("AUTO_DEW_AGG_1", "Port 1 (1-10)", "%.f", 1, 10, 1, 5);
1169 AutoDewAggPerPortNP[AUTO_DEW_AGG_2].fill("AUTO_DEW_AGG_2", "Port 2 (1-10)", "%.f", 1, 10, 1, 5);
1170 AutoDewAggPerPortNP[AUTO_DEW_AGG_3].fill("AUTO_DEW_AGG_3", "Port 3 (1-10)", "%.f", 1, 10, 1, 5);
1171 AutoDewAggPerPortNP.fill(getDeviceName(), "AUTO_DEW_AGG_PER_PORT", "Per-Port Agg", DEW_TAB, IP_RW, 60, IPS_IDLE);
1172
1173 // Focuser Settings
1174 FocuserSettingsNP[SETTING_MAX_SPEED].fill("SETTING_MAX_SPEED", "Max Speed", "%.f", 0, 1000, 100, 400);
1175 FocuserSettingsNP[SETTING_MICROSTEPPING].fill("SETTING_MICROSTEPPING", "Microstepping", "%.f", 1, 32, 1, 2);
1176 FocuserSettingsNP[SETTING_CURRENT_LIMIT].fill("SETTING_CURRENT_LIMIT", "Current Limit (mA)", "%.f", 0, 3000, 100, 1000);
1177 FocuserSettingsNP[SETTING_HOLD_TORQUE].fill("SETTING_HOLD_TORQUE", "Hold Torque (0/1)", "%.f", 0, 1, 1, 0);
1178 FocuserSettingsNP.fill(getDeviceName(), "FOCUSER_SETTINGS", "Settings", FOCUS_TAB, IP_RW, 60, IPS_IDLE);
1179
1180 // Firmware
1181 FirmwareTP[FIRMWARE_VERSION].fill("VERSION", "Version", "NA");
1182 FirmwareTP[FIRMWARE_UPTIME].fill("UPTIME", "Uptime (s)", "NA");
1183 FirmwareTP.fill(getDeviceName(), "FIRMWARE_INFO", "Firmware", FIRMWARE_TAB, IP_RO, 60, IPS_IDLE);
1184
1185 // Serial Connection
1186 serialConnection = new Connection::Serial(this);
1187 serialConnection->registerHandshake([&]()
1188 {
1189 return Handshake();
1190 });
1191 registerConnection(serialConnection);
1192
1193 return true;
1194}
1195
1200{
1202
1203 if (isConnected())
1204 {
1205 // Setup Parameters
1206 setupParams();
1207
1208 // Main Control
1209 defineProperty(PowerConsumptionNP);
1210 defineProperty(RebootSP);
1211
1212 // Power
1213 defineProperty(PowerOnBootSP);
1214 defineProperty(OverCurrentLP);
1215
1216 // Dew
1217 defineProperty(AutoDewAggNP);
1218 defineProperty(AutoDewAggPerPortNP);
1219
1220 // Focuser
1222 defineProperty(FocuserSettingsNP);
1223
1224 // Weather
1226
1227 // Power Interface
1229
1230 // Output Interface (Relay)
1232
1233 // Firmware
1234 defineProperty(FirmwareTP);
1235
1236 setupComplete = true;
1237 }
1238 else
1239 {
1240 // Main Control
1241 deleteProperty(PowerConsumptionNP);
1242 deleteProperty(RebootSP);
1243
1244 // Power
1245 deleteProperty(PowerOnBootSP);
1246 deleteProperty(OverCurrentLP);
1247
1248 // Dew
1249 deleteProperty(AutoDewAggNP);
1250 deleteProperty(AutoDewAggPerPortNP);
1251
1252 // Focuser
1254 deleteProperty(FocuserSettingsNP);
1255
1256 // Weather
1258
1259 // Power Interface
1261
1262 // Output Interface (Relay)
1264
1265 // Firmware
1266 deleteProperty(FirmwareTP);
1267
1268 setupComplete = false;
1269 }
1270
1271 return true;
1272}
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the initial connection to the devic...
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
bool isConnected() const
const char * getDeviceName() const
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
INDI::PropertyNumber FocusRelPosNP
INDI::PropertySwitch FocusReverseSP
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
void initProperties(const char *groupName)
Initialize focuser properties. It is recommended to call this function within initProperties() of you...
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process focus number properties.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process focus switch properties.
INDI::PropertyNumber FocusAbsPosNP
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process number properties.
void initProperties(const char *groupName, uint8_t outputs, const std::string &prefix="Output")
Initialize filter wheel properties. It is recommended to call this function within initProperties() o...
bool processSwitch(const char *dev, const char *name, ISState states[], char *names[], int n)
Process switch properties.
bool saveConfigItems(FILE *fp)
saveConfigItems save Filter Names in config file
std::vector< INDI::PropertySwitch > DigitalOutputsSP
bool processText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process text properties.
bool updateProperties()
updateProperties Defines or Delete properties based on default device connection status
bool saveConfigItems(FILE *fp)
saveConfigItems Save power properties in config file
INDI::PropertySwitch AutoDewSP
INDI::PropertyNumber VariableChannelVoltsNP
void SetCapability(uint32_t cap)
SetPowerCapability sets the Power capabilities. All capabilities must be initialized.
INDI::PropertyNumber PowerSensorsNP
INDI::PropertyText VariableChannelLabelsTP
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process Power number properties.
void initProperties(const char *groupName, size_t nPowerPorts=0, size_t nDewPorts=0, size_t nVariablePorts=0, size_t nAutoDewPorts=0, size_t nUSBPorts=0)
Initialize Power properties. It is recommended to call this function within initProperties() of your ...
bool processText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process Power text properties.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process Power switch properties.
INDI::PropertySwitch PowerChannelsSP
INDI::PropertySwitch USBPortSP
INDI::PropertyNumber DewChannelDutyCycleNP
INDI::PropertyText USBPortLabelsTP
bool updateProperties()
updateProperties Define or Delete Power properties based on the connection status of the base device
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void save(FILE *f) const
save Save property to configuration file.
void setLabel(const char *label)
bool isNameMatch(const char *otherName) const
void fill(const char *device, const char *name, const char *label, const char *group, IPState state)
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
void addParameter(std::string name, std::string label, double numMinOk, double numMaxOk, double percWarning, bool flipWarning=false)
Add a physical weather measurable parameter to the weather driver.
bool setCriticalParameter(std::string name)
setCriticalParameter Set parameter that is considered critical to the operation of the observatory....
bool syncCriticalParameters()
Synchronize and update the state of all critical parameters.
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process weather number properties.
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save parameters ranges in the config file.
INDI::PropertyNumber ParametersNP
void initProperties(const char *statusGroup, const char *paramsGroup)
Initialize focuser properties. It is recommended to call this function within initProperties() of you...
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
INDI::PropertyLight critialParametersLP
Provides interface to implement Digital Boolean Output (On/Off) functionality.
virtual bool SetLEDEnabled(bool enabled) override
SetLEDEnabled Enable/disable power LEDs.
virtual bool SetDewPort(size_t port, bool enabled, double dutyCycle) override
SetDewPort Set DEW/Dew port duty cycle.
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool initProperties() override
Init Properties.
virtual bool SetUSBPort(size_t port, bool enabled) override
SetUSBPort Set USB port on/off.
virtual void TimerHit() override
Timer Hit.
const char * getDefaultName() override
virtual bool SetFocuserBacklashEnabled(bool enabled) override
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
virtual bool SetVariablePort(size_t port, bool enabled, double voltage) override
SetVariablePort Set variable voltage port.
virtual bool UpdateDigitalOutputs() override
OutputInterface Implementation (Relay)
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
virtual bool updateProperties() override
Update Properties.
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual bool SetPowerPort(size_t port, bool enabled) override
PowerInterface Implementation.
virtual bool CommandOutput(uint32_t index, OutputState command) override
Send command to output.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
ISNewSwitch.
virtual bool saveConfigItems(FILE *fp) override
Save Config Items.
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
FocuserInterface Implementation.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
ISNewText.
virtual bool SetAutoDewEnabled(size_t port, bool enabled) override
SetAutoDewEnabled Enable/disable automatic dew control.
virtual bool CyclePower() override
CyclePower Cycle power to all ports.
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
ISNewNumber.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * FOCUS_TAB
FOCUS_TAB Where all the properties for focuser 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_NOFMANY
Definition indiapi.h:173
@ ISR_ATMOST1
Definition indiapi.h:172
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition indicom.c:473
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:759
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
@ TTY_TIME_OUT
Definition indicom.h:58
@ TTY_OVERFLOW
Definition indicom.h:62
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#define MAXRBUF
Definition indidriver.c:52
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#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
Namespace to encapsulate INDI client, drivers, and mediator classes.
uint8_t cmd[4]
Definition pwc-ioctl.h:2