Instrument Neutral Distributed Interface INDI  1.9.5
pegasus_upb.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2018 Jasem Mutlaq. All rights reserved.
3 
4  Pegasus Ultimate Power Box 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_upb.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 
38 // We declare an auto pointer to PegasusUPB.
39 static std::unique_ptr<PegasusUPB> upb(new PegasusUPB());
40 
41 PegasusUPB::PegasusUPB() : FI(this), WI(this)
42 {
43  setVersion(1, 6);
44 
45  lastSensorData.reserve(21);
46  lastPowerData.reserve(4);
47  lastStepperData.reserve(4);
48  lastDewAggData.reserve(1);
49 }
50 
52 {
54 
56 
63 
65  WI::initProperties(ENVIRONMENT_TAB, ENVIRONMENT_TAB);
66 
68 
72  // Cycle all power on/off
73  IUFillSwitch(&PowerCycleAllS[POWER_CYCLE_ON], "POWER_CYCLE_ON", "All On", ISS_OFF);
74  IUFillSwitch(&PowerCycleAllS[POWER_CYCLE_OFF], "POWER_CYCLE_OFF", "All Off", ISS_OFF);
75  IUFillSwitchVector(&PowerCycleAllSP, PowerCycleAllS, 2, getDeviceName(), "POWER_CYCLE", "Cycle Power", MAIN_CONTROL_TAB,
76  IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
77 
78  // Reboot
79  IUFillSwitch(&RebootS[0], "REBOOT", "Reboot Device", ISS_OFF);
80  IUFillSwitchVector(&RebootSP, RebootS, 1, getDeviceName(), "REBOOT_DEVICE", "Device", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1,
81  60, IPS_IDLE);
82 
83  // Power Sensors
84  IUFillNumber(&PowerSensorsN[SENSOR_VOLTAGE], "SENSOR_VOLTAGE", "Voltage (V)", "%4.2f", 0, 999, 100, 0);
85  IUFillNumber(&PowerSensorsN[SENSOR_CURRENT], "SENSOR_CURRENT", "Current (A)", "%4.2f", 0, 999, 100, 0);
86  IUFillNumber(&PowerSensorsN[SENSOR_POWER], "SENSOR_POWER", "Power (W)", "%4.2f", 0, 999, 100, 0);
87  IUFillNumberVector(&PowerSensorsNP, PowerSensorsN, 3, getDeviceName(), "POWER_SENSORS", "Sensors", MAIN_CONTROL_TAB, IP_RO,
88  60, IPS_IDLE);
89 
90  // Overall Power Consumption
91  IUFillNumber(&PowerConsumptionN[CONSUMPTION_AVG_AMPS], "CONSUMPTION_AVG_AMPS", "Avg. Amps", "%4.2f", 0, 999, 100, 0);
92  IUFillNumber(&PowerConsumptionN[CONSUMPTION_AMP_HOURS], "CONSUMPTION_AMP_HOURS", "Amp Hours", "%4.2f", 0, 999, 100, 0);
93  IUFillNumber(&PowerConsumptionN[CONSUMPTION_WATT_HOURS], "CONSUMPTION_WATT_HOURS", "Watt Hours", "%4.2f", 0, 999, 100, 0);
94  IUFillNumberVector(&PowerConsumptionNP, PowerConsumptionN, 3, getDeviceName(), "POWER_CONSUMPTION", "Consumption",
96 
100 
101  // Dew Labels. Need to delare them here to use in the Power usage section
102  IUFillText(&DewControlsLabelsT[0], "DEW_LABEL_1", "Dew A", "Dew A");
103  IUFillText(&DewControlsLabelsT[1], "DEW_LABEL_2", "Dew B", "Dew B");
104  IUFillText(&DewControlsLabelsT[2], "DEW_LABEL_3", "Dew C", "Dew C");
105  IUFillTextVector(&DewControlsLabelsTP, DewControlsLabelsT, 3, getDeviceName(), "DEW_CONTROL_LABEL", "Dew Labels",
106  DEW_TAB, IP_WO, 60, IPS_IDLE);
107 
108  char dewLabel[MAXINDILABEL];
109 
110  // Turn on/off power and power boot up
111  memset(dewLabel, 0, MAXINDILABEL);
112  int dewRC = IUGetConfigText(getDeviceName(), DewControlsLabelsTP.name, DewControlsLabelsT[0].name, dewLabel,
113  MAXINDILABEL);
114  IUFillSwitch(&AutoDewV2S[DEW_PWM_A], "DEW_A", dewRC == -1 ? "Dew A" : dewLabel, ISS_OFF);
115  memset(dewLabel, 0, MAXINDILABEL);
116  dewRC = IUGetConfigText(getDeviceName(), DewControlsLabelsTP.name, DewControlsLabelsT[1].name, dewLabel,
117  MAXINDILABEL);
118  IUFillSwitch(&AutoDewV2S[DEW_PWM_B], "DEW_B", dewRC == -1 ? "Dew B" : dewLabel, ISS_OFF);
119  memset(dewLabel, 0, MAXINDILABEL);
120  dewRC = IUGetConfigText(getDeviceName(), DewControlsLabelsTP.name, DewControlsLabelsT[2].name, dewLabel,
121  MAXINDILABEL);
122  IUFillSwitch(&AutoDewV2S[DEW_PWM_C], "DEW_C", dewRC == -1 ? "Dew C" : dewLabel, ISS_OFF);
123  IUFillSwitchVector(&AutoDewV2SP, AutoDewV2S, 3, getDeviceName(), "AUTO_DEW", "Auto Dew", DEW_TAB, IP_RW, ISR_NOFMANY, 60,
124  IPS_IDLE);
125 
126  // Dew Labels with custom labels
127  IUFillText(&DewControlsLabelsT[0], "DEW_LABEL_1", "Dew A", AutoDewV2S[0].label);
128  IUFillText(&DewControlsLabelsT[1], "DEW_LABEL_2", "Dew B", AutoDewV2S[1].label);
129  IUFillText(&DewControlsLabelsT[2], "DEW_LABEL_3", "Dew C", AutoDewV2S[2].label);
130  IUFillTextVector(&DewControlsLabelsTP, DewControlsLabelsT, 3, getDeviceName(), "DEW_CONTROL_LABEL", "DEW Labels",
131  DEW_TAB, IP_WO, 60, IPS_IDLE);
132  // Power Labels
133  IUFillText(&PowerControlsLabelsT[0], "POWER_LABEL_1", "Port 1", "Port 1");
134  IUFillText(&PowerControlsLabelsT[1], "POWER_LABEL_2", "Port 2", "Port 2");
135  IUFillText(&PowerControlsLabelsT[2], "POWER_LABEL_3", "Port 3", "Port 3");
136  IUFillText(&PowerControlsLabelsT[3], "POWER_LABEL_4", "Port 4", "Port 4");
137  IUFillTextVector(&PowerControlsLabelsTP, PowerControlsLabelsT, 4, getDeviceName(), "POWER_CONTROL_LABEL", "Power Labels",
138  POWER_TAB, IP_WO, 60, IPS_IDLE);
139 
140  char portLabel[MAXINDILABEL];
141 
142  // Turn on/off power and power boot up
143  memset(portLabel, 0, MAXINDILABEL);
144  int portRC = IUGetConfigText(getDeviceName(), PowerControlsLabelsTP.name, PowerControlsLabelsT[0].name, portLabel,
145  MAXINDILABEL);
146  IUFillSwitch(&PowerControlS[0], "POWER_CONTROL_1", portRC == -1 ? "Port 1" : portLabel, ISS_OFF);
147 
148  memset(portLabel, 0, MAXINDILABEL);
149  portRC = IUGetConfigText(getDeviceName(), PowerControlsLabelsTP.name, PowerControlsLabelsT[1].name, portLabel,
150  MAXINDILABEL);
151  IUFillSwitch(&PowerControlS[1], "POWER_CONTROL_2", portRC == -1 ? "Port 2" : portLabel, ISS_OFF);
152 
153  memset(portLabel, 0, MAXINDILABEL);
154  portRC = IUGetConfigText(getDeviceName(), PowerControlsLabelsTP.name, PowerControlsLabelsT[2].name, portLabel,
155  MAXINDILABEL);
156  IUFillSwitch(&PowerControlS[2], "POWER_CONTROL_3", portRC == -1 ? "Port 3" : portLabel, ISS_OFF);
157 
158  memset(portLabel, 0, MAXINDILABEL);
159  portRC = IUGetConfigText(getDeviceName(), PowerControlsLabelsTP.name, PowerControlsLabelsT[3].name, portLabel,
160  MAXINDILABEL);
161  IUFillSwitch(&PowerControlS[3], "POWER_CONTROL_4", portRC == -1 ? "Port 4" : portLabel, ISS_OFF);
162 
163  IUFillSwitchVector(&PowerControlSP, PowerControlS, 4, getDeviceName(), "POWER_CONTROL", "Power Control", POWER_TAB, IP_RW,
164  ISR_NOFMANY, 60, IPS_IDLE);
165 
166  // Power Labels
167  IUFillText(&PowerControlsLabelsT[0], "POWER_LABEL_1", "Port 1", PowerControlS[0].label);
168  IUFillText(&PowerControlsLabelsT[1], "POWER_LABEL_2", "Port 2", PowerControlS[1].label);
169  IUFillText(&PowerControlsLabelsT[2], "POWER_LABEL_3", "Port 3", PowerControlS[2].label);
170  IUFillText(&PowerControlsLabelsT[3], "POWER_LABEL_4", "Port 4", PowerControlS[3].label);
171  IUFillTextVector(&PowerControlsLabelsTP, PowerControlsLabelsT, 4, getDeviceName(), "POWER_CONTROL_LABEL", "Power Labels",
172  POWER_TAB, IP_WO, 60, IPS_IDLE);
173 
174  // Current Draw
175  IUFillNumber(&PowerCurrentN[0], "POWER_CURRENT_1", PowerControlS[0].label, "%4.2f A", 0, 1000, 0, 0);
176  IUFillNumber(&PowerCurrentN[1], "POWER_CURRENT_2", PowerControlS[1].label, "%4.2f A", 0, 1000, 0, 0);
177  IUFillNumber(&PowerCurrentN[2], "POWER_CURRENT_3", PowerControlS[2].label, "%4.2f A", 0, 1000, 0, 0);
178  IUFillNumber(&PowerCurrentN[3], "POWER_CURRENT_4", PowerControlS[3].label, "%4.2f A", 0, 1000, 0, 0);
179  IUFillNumberVector(&PowerCurrentNP, PowerCurrentN, 4, getDeviceName(), "POWER_CURRENT", "Current Draw", POWER_TAB, IP_RO,
180  60, IPS_IDLE);
181 
182  // Power on Boot
183  IUFillSwitch(&PowerOnBootS[0], "POWER_PORT_1", PowerControlS[0].label, ISS_ON);
184  IUFillSwitch(&PowerOnBootS[1], "POWER_PORT_2", PowerControlS[1].label, ISS_ON);
185  IUFillSwitch(&PowerOnBootS[2], "POWER_PORT_3", PowerControlS[2].label, ISS_ON);
186  IUFillSwitch(&PowerOnBootS[3], "POWER_PORT_4", PowerControlS[3].label, ISS_ON);
187  IUFillSwitchVector(&PowerOnBootSP, PowerOnBootS, 4, getDeviceName(), "POWER_ON_BOOT", "Power On Boot", POWER_TAB, IP_RW,
188  ISR_NOFMANY, 60, IPS_IDLE);
189 
190  // Over Current
191  IUFillLight(&OverCurrentL[0], "POWER_PORT_1", PowerControlS[0].label, IPS_OK);
192  IUFillLight(&OverCurrentL[1], "POWER_PORT_2", PowerControlS[1].label, IPS_OK);
193  IUFillLight(&OverCurrentL[2], "POWER_PORT_3", PowerControlS[2].label, IPS_OK);
194  IUFillLight(&OverCurrentL[3], "POWER_PORT_4", PowerControlS[3].label, IPS_OK);
195 
196  char tempLabel[MAXINDILABEL + 5];
197  memset(tempLabel, 0, MAXINDILABEL + 5);
198  sprintf(tempLabel, "%s %s", "Dew:", AutoDewV2S[0].label);
199  IUFillLight(&OverCurrentL[4], "DEW_A", tempLabel, IPS_OK);
200  memset(tempLabel, 0, MAXINDILABEL);
201  sprintf(tempLabel, "%s %s", "Dew:", AutoDewV2S[1].label);
202  IUFillLight(&OverCurrentL[5], "DEW_B", tempLabel, IPS_OK);
203  memset(tempLabel, 0, MAXINDILABEL);
204  sprintf(tempLabel, "%s %s", "Dew:", AutoDewV2S[2].label);
205  IUFillLight(&OverCurrentL[6], "DEW_C", tempLabel, IPS_OK);
206  IUFillLightVector(&OverCurrentLP, OverCurrentL, 7, getDeviceName(), "POWER_OVER_CURRENT", "Over Current", POWER_TAB,
207  IPS_IDLE);
208 
209  // Power LED
210  IUFillSwitch(&PowerLEDS[POWER_LED_ON], "POWER_LED_ON", "On", ISS_ON);
211  IUFillSwitch(&PowerLEDS[POWER_LED_OFF], "POWER_LED_OFF", "Off", ISS_OFF);
212  IUFillSwitchVector(&PowerLEDSP, PowerLEDS, 2, getDeviceName(), "POWER_LED", "LED", POWER_TAB, IP_RW, ISR_1OFMANY, 60,
213  IPS_IDLE);
214 
215  IUFillNumber(&AdjustableOutputN[0], "ADJUSTABLE_VOLTAGE_VALUE", "Voltage (V)", "%.f", 3, 12, 1, 12);
216  IUFillNumberVector(&AdjustableOutputNP, AdjustableOutputN, 1, getDeviceName(), "ADJUSTABLE_VOLTAGE", "Adj. Output",
217  POWER_TAB, IP_RW, 60, IPS_IDLE);
218 
219 
223 
224  // Automatic Dew v1
225  IUFillSwitch(&AutoDewS[INDI_ENABLED], "INDI_ENABLED", "Enabled", ISS_OFF);
226  IUFillSwitch(&AutoDewS[INDI_DISABLED], "INDI_DISABLED", "Disabled", ISS_ON);
227  IUFillSwitchVector(&AutoDewSP, AutoDewS, 2, getDeviceName(), "AUTO_DEW", "Auto Dew", DEW_TAB, IP_RW, ISR_1OFMANY, 60,
228  IPS_IDLE);
229 
230  // Automatic Dew Aggressiveness v2
231  IUFillNumber(&AutoDewAggN[AUTO_DEW_AGG], "AUTO_DEW_AGG_VALUE", "Auto Dew Agg (50-250)", "%.2f", 50, 250, 20, 0);
232  IUFillNumberVector(&AutoDewAggNP, AutoDewAggN, 1, getDeviceName(), "AUTO_DEW_AGG", "Auto Dew Agg", DEW_TAB, IP_RW, 60,
233  IPS_IDLE);
234 
235  // Dew PWM
236  IUFillNumber(&DewPWMN[DEW_PWM_A], "DEW_A", AutoDewV2S[0].label, "%.2f %%", 0, 100, 10, 0);
237  IUFillNumber(&DewPWMN[DEW_PWM_B], "DEW_B", AutoDewV2S[1].label, "%.2f %%", 0, 100, 10, 0);
238  IUFillNumber(&DewPWMN[DEW_PWM_C], "DEW_C", AutoDewV2S[2].label, "%.2f %%", 0, 100, 10, 0);
239  IUFillNumberVector(&DewPWMNP, DewPWMN, 3, getDeviceName(), "DEW_PWM", "Dew PWM", DEW_TAB, IP_RW, 60, IPS_IDLE);
240 
241  // Dew current draw
242  IUFillNumber(&DewCurrentDrawN[DEW_PWM_A], "DEW_CURRENT_A", AutoDewV2S[0].label, "%4.2f A", 0, 1000, 10, 0);
243  IUFillNumber(&DewCurrentDrawN[DEW_PWM_B], "DEW_CURRENT_B", AutoDewV2S[1].label, "%4.2f A", 0, 1000, 10, 0);
244  IUFillNumber(&DewCurrentDrawN[DEW_PWM_C], "DEW_CURRENT_C", AutoDewV2S[2].label, "%4.2f A", 0, 1000, 10, 0);
245  IUFillNumberVector(&DewCurrentDrawNP, DewCurrentDrawN, 3, getDeviceName(), "DEW_CURRENT", "Dew Current", DEW_TAB, IP_RO, 60,
246  IPS_IDLE);
247 
251 
252  // USB Hub control v1
253  IUFillSwitch(&USBControlS[INDI_ENABLED], "INDI_ENABLED", "Enabled", ISS_ON);
254  IUFillSwitch(&USBControlS[INDI_DISABLED], "INDI_DISABLED", "Disabled", ISS_OFF);
255  IUFillSwitchVector(&USBControlSP, USBControlS, 2, getDeviceName(), "USB_HUB_CONTROL", "Hub", USB_TAB, IP_RW, ISR_1OFMANY,
256  60, IPS_IDLE);
257 
258  // USB Labels
259  IUFillText(&USBControlsLabelsT[0], "USB_LABEL_1", "USB3 Port1", "USB3 Port1");
260  IUFillText(&USBControlsLabelsT[1], "USB_LABEL_2", "USB3 Port2", "USB3 Port2");
261  IUFillText(&USBControlsLabelsT[2], "USB_LABEL_3", "USB3 Port3", "USB3 Port3");
262  IUFillText(&USBControlsLabelsT[3], "USB_LABEL_4", "USB3 Port4", "USB3 Port4");
263  IUFillText(&USBControlsLabelsT[4], "USB_LABEL_5", "USB2 Port5", "USB2 Port5");
264  IUFillText(&USBControlsLabelsT[5], "USB_LABEL_6", "USB2 Port6", "USB2 Port6");
265 
266  IUFillTextVector(&USBControlsLabelsTP, USBControlsLabelsT, 6, getDeviceName(), "USB_CONTROL_LABEL", "USB Labels",
267  USB_TAB, IP_WO, 60, IPS_IDLE);
268 
269  // USB Hub control v2
270 
271  char USBLabel[MAXINDILABEL];
272 
273  // Turn on/off power and power boot up
274  memset(USBLabel, 0, MAXINDILABEL);
275  int USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[0].name, USBLabel,
276  MAXINDILABEL);
277  IUFillSwitch(&USBControlV2S[0], "PORT_1", USBRC == -1 ? "USB3 Port1" : USBLabel, ISS_ON);
278  memset(USBLabel, 0, MAXINDILABEL);
279  USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[1].name, USBLabel,
280  MAXINDILABEL);
281  IUFillSwitch(&USBControlV2S[1], "PORT_2", USBRC == -1 ? "USB3 Port2" : USBLabel, ISS_ON);
282  memset(USBLabel, 0, MAXINDILABEL);
283  USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[2].name, USBLabel,
284  MAXINDILABEL);
285  IUFillSwitch(&USBControlV2S[2], "PORT_3", USBRC == -1 ? "USB3 Port3" : USBLabel, ISS_ON);
286  memset(USBLabel, 0, MAXINDILABEL);
287  USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[3].name, USBLabel,
288  MAXINDILABEL);
289  IUFillSwitch(&USBControlV2S[3], "PORT_4", USBRC == -1 ? "USB3 Port4" : USBLabel, ISS_ON);
290  memset(USBLabel, 0, MAXINDILABEL);
291  USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[4].name, USBLabel,
292  MAXINDILABEL);
293  IUFillSwitch(&USBControlV2S[4], "PORT_5", USBRC == -1 ? "USB2 Port5" : USBLabel, ISS_ON);
294  memset(USBLabel, 0, MAXINDILABEL);
295  USBRC = IUGetConfigText(getDeviceName(), USBControlsLabelsTP.name, USBControlsLabelsT[5].name, USBLabel,
296  MAXINDILABEL);
297  IUFillSwitch(&USBControlV2S[5], "PORT_6", USBRC == -1 ? "USB2 Port6" : USBLabel, ISS_ON);
298 
299  IUFillSwitchVector(&USBControlV2SP, USBControlV2S, 6, getDeviceName(), "USB_PORT_CONTROL", "Ports", USB_TAB, IP_RW,
300  ISR_NOFMANY, 60, IPS_IDLE);
301 
302  // USB Labels update with custom values
303  IUFillText(&USBControlsLabelsT[0], "USB_LABEL_1", "USB3 Port1", USBControlV2S[0].label);
304  IUFillText(&USBControlsLabelsT[1], "USB_LABEL_2", "USB3 Port2", USBControlV2S[1].label);
305  IUFillText(&USBControlsLabelsT[2], "USB_LABEL_3", "USB3 Port3", USBControlV2S[2].label);
306  IUFillText(&USBControlsLabelsT[3], "USB_LABEL_4", "USB3 Port4", USBControlV2S[3].label);
307  IUFillText(&USBControlsLabelsT[4], "USB_LABEL_5", "USB2 Port5", USBControlV2S[4].label);
308  IUFillText(&USBControlsLabelsT[5], "USB_LABEL_6", "USB2 Port6", USBControlV2S[5].label);
309 
310  IUFillTextVector(&USBControlsLabelsTP, USBControlsLabelsT, 6, getDeviceName(), "USB_CONTROL_LABEL", "USB Labels",
311  USB_TAB, IP_WO, 60, IPS_IDLE);
312  // USB Hub Status
313  IUFillLight(&USBStatusL[0], "PORT_1", USBControlV2S[0].label, IPS_OK);
314  IUFillLight(&USBStatusL[1], "PORT_2", USBControlV2S[1].label, IPS_OK);
315  IUFillLight(&USBStatusL[2], "PORT_3", USBControlV2S[2].label, IPS_OK);
316  IUFillLight(&USBStatusL[3], "PORT_4", USBControlV2S[3].label, IPS_OK);
317  IUFillLight(&USBStatusL[4], "PORT_5", USBControlV2S[4].label, IPS_OK);
318  IUFillLight(&USBStatusL[5], "PORT_6", USBControlV2S[5].label, IPS_OK);
319  IUFillLightVector(&USBStatusLP, USBStatusL, 6, getDeviceName(), "USB_PORT_STATUS", "Status", USB_TAB, IPS_IDLE);
320 
324 
325  // Settings
326  // IUFillNumber(&FocusBacklashN[0], "SETTING_BACKLASH", "Backlash (steps)", "%.f", 0, 999, 100, 0);
327  IUFillNumber(&FocuserSettingsN[SETTING_MAX_SPEED], "SETTING_MAX_SPEED", "Max Speed (%)", "%.f", 0, 900, 100, 400);
328  IUFillNumberVector(&FocuserSettingsNP, FocuserSettingsN, 1, getDeviceName(), "FOCUSER_SETTINGS", "Settings", FOCUS_TAB,
329  IP_RW, 60, IPS_IDLE);
333  IUFillText(&FirmwareT[FIRMWARE_VERSION], "VERSION", "Version", "NA");
334  IUFillText(&FirmwareT[FIRMWARE_UPTIME], "UPTIME", "Uptime (h)", "NA");
335  IUFillTextVector(&FirmwareTP, FirmwareT, 2, getDeviceName(), "FIRMWARE_INFO", "Firmware", FIRMWARE_TAB, IP_RO, 60,
336  IPS_IDLE);
340  addParameter("WEATHER_TEMPERATURE", "Temperature (C)", -15, 35, 15);
341  addParameter("WEATHER_HUMIDITY", "Humidity %", 0, 100, 15);
342  addParameter("WEATHER_DEWPOINT", "Dew Point (C)", 0, 100, 15);
343  setCriticalParameter("WEATHER_TEMPERATURE");
344 
348  serialConnection = new Connection::Serial(this);
349  serialConnection->registerHandshake([&]()
350  {
351  return Handshake();
352  });
353  registerConnection(serialConnection);
354 
355  return true;
356 }
357 
359 {
361 
362  if (isConnected())
363  {
364  // Setup Parameters
365  setupParams();
366 
367  // Main Control
368  defineProperty(&PowerCycleAllSP);
369  defineProperty(&PowerSensorsNP);
370  defineProperty(&PowerConsumptionNP);
371  defineProperty(&RebootSP);
372 
373  // Power
374  defineProperty(&PowerControlSP);
375  defineProperty(&PowerControlsLabelsTP);
376  defineProperty(&PowerCurrentNP);
377  defineProperty(&PowerOnBootSP);
378  OverCurrentLP.nlp = (version == UPB_V1) ? 4 : 7;
379  defineProperty(&OverCurrentLP);
380  if (version == UPB_V1)
381  defineProperty(&PowerLEDSP);
382  if (version == UPB_V2)
383  defineProperty(&AdjustableOutputNP);
384 
385  // Dew
386  if (version == UPB_V1)
387  defineProperty(&AutoDewSP);
388  else
389  defineProperty(&AutoDewV2SP);
390 
391  DewControlsLabelsTP.ntp = (version == UPB_V1) ? 2 : 3;
392  defineProperty(&DewControlsLabelsTP);
393 
394  if (version == UPB_V2)
395  defineProperty(&AutoDewAggNP);
396 
397  DewPWMNP.nnp = (version == UPB_V1) ? 2 : 3;
398  defineProperty(&DewPWMNP);
399 
400  DewCurrentDrawNP.nnp = (version == UPB_V1) ? 2 : 3;
401  defineProperty(&DewCurrentDrawNP);
402 
403  // USB
404  defineProperty(&USBControlSP);
405  if (version == UPB_V2)
406  defineProperty(&USBControlV2SP);
407  if (version == UPB_V1)
408  defineProperty(&USBStatusLP);
409  defineProperty(&USBControlsLabelsTP);
410 
411  // Focuser
413  defineProperty(&FocuserSettingsNP);
414 
416 
417  // Firmware
418  defineProperty(&FirmwareTP);
419 
420  setupComplete = true;
421  }
422  else
423  {
424  // Main Control
425  deleteProperty(PowerCycleAllSP.name);
426  deleteProperty(PowerSensorsNP.name);
427  deleteProperty(PowerConsumptionNP.name);
428  deleteProperty(RebootSP.name);
429 
430  // Power
431  deleteProperty(PowerControlSP.name);
432  deleteProperty(PowerControlsLabelsTP.name);
433  deleteProperty(PowerCurrentNP.name);
434  deleteProperty(PowerOnBootSP.name);
435  deleteProperty(OverCurrentLP.name);
436  if (version == UPB_V1)
437  deleteProperty(PowerLEDSP.name);
438  if (version == UPB_V2)
439  deleteProperty(AdjustableOutputNP.name);
440 
441  // Dew
442  if (version == UPB_V1)
443  deleteProperty(AutoDewSP.name);
444  else
445  {
446  deleteProperty(AutoDewV2SP.name);
447  deleteProperty(DewControlsLabelsTP.name);
448  deleteProperty(AutoDewAggNP.name);
449  }
450 
451  deleteProperty(DewPWMNP.name);
452  deleteProperty(DewCurrentDrawNP.name);
453 
454  // USB
455  deleteProperty(USBControlSP.name);
456  if (version == UPB_V2)
457  deleteProperty(USBControlV2SP.name);
458  if (version == UPB_V1)
459  deleteProperty(USBStatusLP.name);
460  deleteProperty(USBControlsLabelsTP.name);
461 
462  // Focuser
464  deleteProperty(FocuserSettingsNP.name);
465 
467 
468  deleteProperty(FirmwareTP.name);
469 
470  setupComplete = false;
471  }
472 
473  return true;
474 }
475 
477 {
478  return "Pegasus UPB";
479 }
480 
484 bool PegasusUPB::Handshake()
485 {
486  char response[PEGASUS_LEN] = {0};
487  int nbytes_read = 0;
488  PortFD = serialConnection->getPortFD();
489 
490  LOG_DEBUG("CMD <P#>");
491 
492  if (isSimulation())
493  {
494  snprintf(response, PEGASUS_LEN, "UPB2_OK");
495  nbytes_read = 8;
496  }
497  else
498  {
499  int tty_rc = 0, nbytes_written = 0;
500  char command[PEGASUS_LEN] = {0};
501  tcflush(PortFD, TCIOFLUSH);
502  strncpy(command, "P#\n", PEGASUS_LEN);
503  if ( (tty_rc = tty_write_string(PortFD, command, &nbytes_written)) != TTY_OK)
504  {
505  char errorMessage[MAXRBUF];
506  tty_error_msg(tty_rc, errorMessage, MAXRBUF);
507  LOGF_ERROR("Serial write error: %s", errorMessage);
508  return false;
509  }
510 
511  // Try first with stopChar as the stop character
512  if ( (tty_rc = tty_nread_section(PortFD, response, PEGASUS_LEN, stopChar, 1, &nbytes_read)) != TTY_OK)
513  {
514  // Try 0xA as the stop character
515  if (tty_rc == TTY_OVERFLOW || tty_rc == TTY_TIME_OUT)
516  {
517  tcflush(PortFD, TCIOFLUSH);
518  tty_write_string(PortFD, command, &nbytes_written);
519  stopChar = 0xA;
520  tty_rc = tty_nread_section(PortFD, response, PEGASUS_LEN, stopChar, 1, &nbytes_read);
521  }
522 
523  if (tty_rc != TTY_OK)
524  {
525  char errorMessage[MAXRBUF];
526  tty_error_msg(tty_rc, errorMessage, MAXRBUF);
527  LOGF_ERROR("Serial read error: %s", errorMessage);
528  return false;
529  }
530  }
531 
532  cleanupResponse(response);
533  tcflush(PortFD, TCIOFLUSH);
534  }
535 
536 
537  LOGF_DEBUG("RES <%s>", response);
538 
539  setupComplete = false;
540 
541  version = strstr(response, "UPB2_OK") ? UPB_V2 : UPB_V1;
542 
543  return true;
544 }
545 
549 bool PegasusUPB::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
550 {
551  if (dev && !strcmp(dev, getDeviceName()))
552  {
553  // Cycle all power on or off
554  if (!strcmp(name, PowerCycleAllSP.name))
555  {
556  IUUpdateSwitch(&PowerCycleAllSP, states, names, n);
557 
558  PowerCycleAllSP.s = IPS_ALERT;
559  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
560  snprintf(cmd, PEGASUS_LEN, "PZ:%d", IUFindOnSwitchIndex(&PowerCycleAllSP));
561  if (sendCommand(cmd, res))
562  {
563  PowerCycleAllSP.s = !strcmp(cmd, res) ? IPS_OK : IPS_ALERT;
564  }
565 
566  IUResetSwitch(&PowerCycleAllSP);
567  IDSetSwitch(&PowerCycleAllSP, nullptr);
568  return true;
569  }
570 
571  // Reboot
572  if (!strcmp(name, RebootSP.name))
573  {
574  RebootSP.s = reboot() ? IPS_OK : IPS_ALERT;
575  IDSetSwitch(&RebootSP, nullptr);
576  LOG_INFO("Rebooting device...");
577  return true;
578  }
579 
580  // Control Power per port
581  if (!strcmp(name, PowerControlSP.name))
582  {
583  bool failed = false;
584  for (int i = 0; i < n; i++)
585  {
586  if (!strcmp(names[i], PowerControlS[i].name) && states[i] != PowerControlS[i].s)
587  {
588  if (setPowerEnabled(i + 1, states[i] == ISS_ON) == false)
589  {
590  failed = true;
591  break;
592  }
593  }
594  }
595 
596  if (failed)
597  PowerControlSP.s = IPS_ALERT;
598  else
599  {
600  PowerControlSP.s = IPS_OK;
601  IUUpdateSwitch(&PowerControlSP, states, names, n);
602  }
603 
604  IDSetSwitch(&PowerControlSP, nullptr);
605  return true;
606  }
607 
608  // Power on boot
609  if (!strcmp(name, PowerOnBootSP.name))
610  {
611  IUUpdateSwitch(&PowerOnBootSP, states, names, n);
612  PowerOnBootSP.s = setPowerOnBoot() ? IPS_OK : IPS_ALERT;
613  IDSetSwitch(&PowerOnBootSP, nullptr);
614  saveConfig(true, PowerOnBootSP.name);
615  return true;
616  }
617 
618  // Auto Dew v1.
619  if ((!strcmp(name, AutoDewSP.name)) && (version == UPB_V1))
620  {
621  int prevIndex = IUFindOnSwitchIndex(&AutoDewSP);
622  IUUpdateSwitch(&AutoDewSP, states, names, n);
623  if (setAutoDewEnabled(AutoDewS[INDI_ENABLED].s == ISS_ON))
624  {
625  AutoDewSP.s = IPS_OK;
626  }
627  else
628  {
629  IUResetSwitch(&AutoDewSP);
630  AutoDewS[prevIndex].s = ISS_ON;
631  AutoDewSP.s = IPS_ALERT;
632  }
633 
634  IDSetSwitch(&AutoDewSP, nullptr);
635  return true;
636  }
637 
638  // Auto Dew v2.
639  if ((!strcmp(name, AutoDewV2SP.name)) && (version == UPB_V2))
640  {
641  ISState Dew1 = AutoDewV2S[DEW_PWM_A].s;
642  ISState Dew2 = AutoDewV2S[DEW_PWM_B].s;
643  ISState Dew3 = AutoDewV2S[DEW_PWM_C].s;
644  IUUpdateSwitch(&AutoDewV2SP, states, names, n);
645  if (toggleAutoDewV2())
646  {
647  Dew1 = AutoDewV2S[DEW_PWM_A].s;
648  Dew2 = AutoDewV2S[DEW_PWM_B].s;
649  Dew3 = AutoDewV2S[DEW_PWM_C].s;
650  if (Dew1 == ISS_OFF && Dew2 == ISS_OFF && Dew3 == ISS_OFF)
651  AutoDewV2SP.s = IPS_IDLE;
652  else
653  AutoDewV2SP.s = IPS_OK;
654  }
655  else
656  {
657  IUResetSwitch(&AutoDewV2SP);
658  AutoDewV2S[DEW_PWM_A].s = Dew1;
659  AutoDewV2S[DEW_PWM_B].s = Dew2;
660  AutoDewV2S[DEW_PWM_C].s = Dew3;
661  AutoDewV2SP.s = IPS_ALERT;
662  }
663 
664  IDSetSwitch(&AutoDewV2SP, nullptr);
665  return true;
666  }
667 
668  // USB Hub Control v1
669  if (!strcmp(name, USBControlSP.name))
670  {
671  int prevIndex = IUFindOnSwitchIndex(&USBControlSP);
672  IUUpdateSwitch(&USBControlSP, states, names, n);
673  if (setUSBHubEnabled(USBControlS[0].s == ISS_ON))
674  {
675  USBControlSP.s = IPS_OK;
676  }
677  else
678  {
679  IUResetSwitch(&USBControlSP);
680  USBControlS[prevIndex].s = ISS_ON;
681  USBControlSP.s = IPS_ALERT;
682  }
683 
684  IDSetSwitch(&USBControlSP, nullptr);
685  return true;
686  }
687 
688  // USB Hub Control v2
689  if (!strcmp(name, USBControlV2SP.name))
690  {
691  bool rc[6] = {false};
692  std::fill_n(rc, 6, true);
693  ISState ports[6] = {ISS_ON};
694 
695  for (int i = 0; i < USBControlV2SP.nsp; i++)
696  ports[i] = USBControlV2S[i].s;
697 
698  IUUpdateSwitch(&USBControlV2SP, states, names, n);
699  for (int i = 0; i < USBControlV2SP.nsp; i++)
700  {
701  if (ports[i] != USBControlV2S[i].s)
702  rc[i] = setUSBPortEnabled(i, USBControlV2S[i].s == ISS_ON);
703  }
704 
705  // All is OK
706  if (rc[0] && rc[1] && rc[2] && rc[3] && rc[4] && rc[5])
707  {
708  USBControlSP.s = IPS_OK;
709  }
710  else
711  {
712  IUResetSwitch(&USBControlV2SP);
713  for (int i = 0; i < USBControlV2SP.nsp; i++)
714  USBControlV2S[i].s = ports[i];
715  USBControlV2SP.s = IPS_ALERT;
716  }
717 
718  IDSetSwitch(&USBControlV2SP, nullptr);
719 
720  return true;
721  }
722 
723  // Focuser backlash
724  // if (!strcmp(name, FocusBacklashSP.name))
725  // {
726  // int prevIndex = IUFindOnSwitchIndex(&FocusBacklashSP);
727  // IUUpdateSwitch(&FocusBacklashSP, states, names, n);
728  // if (setFocuserBacklashEnabled(FocusBacklashS[0].s == ISS_ON))
729  // {
730  // FocusBacklashSP.s = IPS_OK;
731  // }
732  // else
733  // {
734  // IUResetSwitch(&FocusBacklashSP);
735  // FocusBacklashS[prevIndex].s = ISS_ON;
736  // FocusBacklashSP.s = IPS_ALERT;
737  // }
738 
739  // IDSetSwitch(&FocusBacklashSP, nullptr);
740  // return true;
741  // }
742 
743  // Power LED
744  if (!strcmp(name, PowerLEDSP.name) && (version == UPB_V1))
745  {
746  int prevIndex = IUFindOnSwitchIndex(&PowerLEDSP);
747  IUUpdateSwitch(&PowerLEDSP, states, names, n);
748  if (setPowerLEDEnabled(PowerLEDS[0].s == ISS_ON))
749  {
750  PowerLEDSP.s = IPS_OK;
751  }
752  else
753  {
754  IUResetSwitch(&PowerLEDSP);
755  PowerLEDS[prevIndex].s = ISS_ON;
756  PowerLEDSP.s = IPS_ALERT;
757  }
758 
759  IDSetSwitch(&PowerLEDSP, nullptr);
760  return true;
761  }
762 
763  if (strstr(name, "FOCUS"))
764  return FI::processSwitch(dev, name, states, names, n);
765  }
766 
767  return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
768 }
769 
773 bool PegasusUPB::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
774 {
775  if (dev && !strcmp(dev, getDeviceName()))
776  {
777  // Adjustable output
778  if (!strcmp(name, AdjustableOutputNP.name))
779  {
780  if (setAdjustableOutput(static_cast<uint8_t>(values[0])))
781  {
782  IUUpdateNumber(&AdjustableOutputNP, values, names, n);
783  AdjustableOutputNP.s = IPS_OK;
784  }
785  else
786  AdjustableOutputNP.s = IPS_ALERT;
787 
788  IDSetNumber(&AdjustableOutputNP, nullptr);
789  return true;
790  }
791 
792  // Dew PWM
793  if (!strcmp(name, DewPWMNP.name))
794  {
795  bool rc1 = false, rc2 = false, rc3 = false;
796  for (int i = 0; i < n; i++)
797  {
798  if (!strcmp(names[i], DewPWMN[DEW_PWM_A].name))
799  rc1 = setDewPWM(5, static_cast<uint8_t>(values[i] / 100.0 * 255.0));
800  else if (!strcmp(names[i], DewPWMN[DEW_PWM_B].name))
801  rc2 = setDewPWM(6, static_cast<uint8_t>(values[i] / 100.0 * 255.0));
802  else if (!strcmp(names[i], DewPWMN[DEW_PWM_C].name))
803  rc3 = setDewPWM(7, static_cast<uint8_t>(values[i] / 100.0 * 255.0));
804  }
805 
806  DewPWMNP.s = (rc1 && rc2 && rc3) ? IPS_OK : IPS_ALERT;
807  if (DewPWMNP.s == IPS_OK)
808  IUUpdateNumber(&DewPWMNP, values, names, n);
809  IDSetNumber(&DewPWMNP, nullptr);
810  return true;
811  }
812 
813  // Auto Dew Aggressiveness
814  if (!strcmp(name, AutoDewAggNP.name))
815  {
816  if (setAutoDewAgg(values[0]))
817  {
818  AutoDewAggN[0].value = values[0];
819  AutoDewAggNP.s = IPS_OK;
820  }
821  else
822  {
823  AutoDewAggNP.s = IPS_ALERT;
824  }
825 
826  IDSetNumber(&AutoDewAggNP, nullptr);
827  return true;
828  }
829 
830  // Focuser Settings
831  if (!strcmp(name, FocuserSettingsNP.name))
832  {
833  if (setFocuserMaxSpeed(values[0]))
834  {
835  FocuserSettingsN[0].value = values[0];
836  FocuserSettingsNP.s = IPS_OK;
837  }
838  else
839  {
840  FocuserSettingsNP.s = IPS_ALERT;
841  }
842 
843  IDSetNumber(&FocuserSettingsNP, nullptr);
844  return true;
845  }
846 
847  if (strstr(name, "FOCUS_"))
848  return FI::processNumber(dev, name, values, names, n);
849 
850  if (strstr(name, "WEATHER_"))
851  return WI::processNumber(dev, name, values, names, n);
852  }
853  return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
854 }
855 
859 bool PegasusUPB::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
860 {
861  if (dev && !strcmp(dev, getDeviceName()))
862  {
863  // Power Labels
864  if (!strcmp(name, PowerControlsLabelsTP.name))
865  {
866  IUUpdateText(&PowerControlsLabelsTP, texts, names, n);
867  PowerControlsLabelsTP.s = IPS_OK;
868  LOG_INFO("Power port labels saved. Driver must be restarted for the labels to take effect.");
869  saveConfig();
870  IDSetText(&PowerControlsLabelsTP, nullptr);
871  return true;
872  }
873  // Dew Labels
874  if (!strcmp(name, DewControlsLabelsTP.name))
875  {
876  IUUpdateText(&DewControlsLabelsTP, texts, names, n);
877  DewControlsLabelsTP.s = IPS_OK;
878  LOG_INFO("Dew labels saved. Driver must be restarted for the labels to take effect.");
879  saveConfig();
880  IDSetText(&DewControlsLabelsTP, nullptr);
881  return true;
882  }
883  // USB Labels
884  if (!strcmp(name, USBControlsLabelsTP.name))
885  {
886  IUUpdateText(&USBControlsLabelsTP, texts, names, n);
887  USBControlsLabelsTP.s = IPS_OK;
888  LOG_INFO("USB labels saved. Driver must be restarted for the labels to take effect.");
889  saveConfig();
890  IDSetText(&USBControlsLabelsTP, nullptr);
891  return true;
892  }
893  }
894 
895  return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
896 }
897 
901 bool PegasusUPB::sendCommand(const char * cmd, char * res)
902 {
903  int nbytes_read = 0, nbytes_written = 0, tty_rc = 0;
904  LOGF_DEBUG("CMD <%s>", cmd);
905 
906  if (isSimulation())
907  {
908  if (!strcmp(cmd, "PS"))
909  {
910  strncpy(res, "PS:1111:12", PEGASUS_LEN);
911  }
912  else if (!strcmp(cmd, "PA"))
913  {
914  strncpy(res, "UPB2:12.0:0.9:10:24.8:37:9.1:1111:111111:153:153:0:0:0:0:0:70:0:0:0000000:0", PEGASUS_LEN);
915  }
916  else if (!strcmp(cmd, "PC"))
917  {
918  strncpy(res, "0.40:0.00:0.03:26969", PEGASUS_LEN);
919  }
920  else if (!strcmp(cmd, "SA"))
921  {
922  strncpy(res, "3000:0:0:10", PEGASUS_LEN);
923  }
924  else if (!strcmp(cmd, "SS"))
925  {
926  strncpy(res, "999", PEGASUS_LEN);
927  }
928  else if (!strcmp(cmd, "PD"))
929  {
930  strncpy(res, "210", PEGASUS_LEN);
931  }
932  else if (!strcmp(cmd, "PV"))
933  {
934  strncpy(res, "Sim v1.0", PEGASUS_LEN);
935  }
936  else if (res)
937  {
938  strncpy(res, cmd, PEGASUS_LEN);
939  }
940 
941  return true;
942  }
943 
944  for (int i = 0; i < 2; i++)
945  {
946  char command[PEGASUS_LEN] = {0};
947  tcflush(PortFD, TCIOFLUSH);
948  snprintf(command, PEGASUS_LEN, "%s\n", cmd);
949  if ( (tty_rc = tty_write_string(PortFD, command, &nbytes_written)) != TTY_OK)
950  continue;
951 
952  if (!res)
953  {
954  tcflush(PortFD, TCIOFLUSH);
955  return true;
956  }
957 
958  if ( (tty_rc = tty_nread_section(PortFD, res, PEGASUS_LEN, stopChar, PEGASUS_TIMEOUT, &nbytes_read)) != TTY_OK
959  || nbytes_read == 1)
960  continue;
961 
962  tcflush(PortFD, TCIOFLUSH);
963 
964  cleanupResponse(res);
965  LOGF_DEBUG("RES <%s>", res);
966  return true;
967  }
968 
969  if (tty_rc != TTY_OK)
970  {
971  char errorMessage[MAXRBUF];
972  tty_error_msg(tty_rc, errorMessage, MAXRBUF);
973  LOGF_ERROR("Serial error: %s", errorMessage);
974  }
975 
976  return false;
977 }
978 
982 IPState PegasusUPB::MoveAbsFocuser(uint32_t targetTicks)
983 {
984  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
985  snprintf(cmd, PEGASUS_LEN, "SM:%u", targetTicks);
986  if (sendCommand(cmd, res))
987  {
988  return (!strcmp(res, cmd) ? IPS_BUSY : IPS_ALERT);
989  }
990 
991  return IPS_ALERT;
992 }
993 
998 {
999  return MoveAbsFocuser(dir == FOCUS_INWARD ? FocusAbsPosN[0].value - ticks : FocusAbsPosN[0].value + ticks);
1000 }
1001 
1006 {
1007  char res[PEGASUS_LEN] = {0};
1008  if (sendCommand("SH", res))
1009  {
1010  return (!strcmp(res, "SH"));
1011  }
1012 
1013  return false;
1014 }
1015 
1019 bool PegasusUPB::ReverseFocuser(bool enabled)
1020 {
1021  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1022  snprintf(cmd, PEGASUS_LEN, "SR:%d", enabled ? 1 : 0);
1023  if (sendCommand(cmd, res))
1024  {
1025  return (!strcmp(res, cmd));
1026  }
1027 
1028  return false;
1029 }
1030 
1034 bool PegasusUPB::SyncFocuser(uint32_t ticks)
1035 {
1036  char cmd[PEGASUS_LEN] = {0};
1037  snprintf(cmd, PEGASUS_LEN, "SC:%u", ticks);
1038  return sendCommand(cmd, nullptr);
1039 }
1040 
1045 {
1046  char cmd[PEGASUS_LEN] = {0};
1047  snprintf(cmd, PEGASUS_LEN, "SB:%d", steps);
1048  return sendCommand(cmd, nullptr);
1049 }
1050 
1054 bool PegasusUPB::setFocuserMaxSpeed(uint16_t maxSpeed)
1055 {
1056  char cmd[PEGASUS_LEN] = {0};
1057  snprintf(cmd, PEGASUS_LEN, "SS:%d", maxSpeed);
1058  return sendCommand(cmd, nullptr);
1059 }
1060 
1065 {
1066  char cmd[PEGASUS_LEN] = {0};
1067  snprintf(cmd, PEGASUS_LEN, "SB:%d", enabled ? 1 : 0);
1068  return sendCommand(cmd, nullptr);
1069 }
1070 
1074 bool PegasusUPB::setPowerEnabled(uint8_t port, bool enabled)
1075 {
1076  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1077  snprintf(cmd, PEGASUS_LEN, "P%d:%d", port, enabled ? 1 : 0);
1078  if (sendCommand(cmd, res))
1079  {
1080  return (!strcmp(res, cmd));
1081  }
1082 
1083  return false;
1084 }
1085 
1089 bool PegasusUPB::setPowerLEDEnabled(bool enabled)
1090 {
1091  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1092  snprintf(cmd, PEGASUS_LEN, "PL:%d", enabled ? 1 : 0);
1093  if (sendCommand(cmd, res))
1094  {
1095  return (!strcmp(res, cmd));
1096  }
1097 
1098  return false;
1099 }
1100 
1104 bool PegasusUPB::setAutoDewEnabled(bool enabled)
1105 {
1106  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1107  snprintf(cmd, PEGASUS_LEN, "PD:%d", enabled ? 1 : 0);
1108  if (sendCommand(cmd, res))
1109  {
1110  return (!strcmp(res, cmd));
1111  }
1112 
1113  return false;
1114 }
1115 
1119 bool PegasusUPB::setAutoDewAgg(uint8_t value)
1120 {
1121  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0};
1122  snprintf(cmd, PEGASUS_LEN, "PD:%03d", value);
1123  snprintf(expected, PEGASUS_LEN, "PD:%d", value);
1124  if (sendCommand(cmd, res))
1125  {
1126  return (!strcmp(res, expected));
1127  }
1128 
1129  return false;
1130 }
1134 bool PegasusUPB::setAdjustableOutput(uint8_t voltage)
1135 {
1136  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1137  snprintf(cmd, PEGASUS_LEN, "P8:%d", voltage);
1138  if (sendCommand(cmd, res))
1139  {
1140  return (!strcmp(res, cmd));
1141  }
1142 
1143  return false;
1144 }
1145 
1149 bool PegasusUPB::setPowerOnBoot()
1150 {
1151  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1152  snprintf(cmd, PEGASUS_LEN, "PE:%d%d%d%d", PowerOnBootS[0].s == ISS_ON ? 1 : 0,
1153  PowerOnBootS[1].s == ISS_ON ? 1 : 0,
1154  PowerOnBootS[2].s == ISS_ON ? 1 : 0,
1155  PowerOnBootS[3].s == ISS_ON ? 1 : 0);
1156  if (sendCommand(cmd, res))
1157  {
1158  return (!strcmp(res, "PE:1"));
1159  }
1160 
1161  return false;
1162 }
1163 
1167 bool PegasusUPB::getPowerOnBoot()
1168 {
1169  char res[PEGASUS_LEN] = {0};
1170  if (sendCommand("PS", res))
1171  {
1172  std::vector<std::string> result = split(res, ":");
1173  if (result.size() != 3)
1174  {
1175  LOGF_WARN("Received wrong number (%i) of power on boot data (%s). Retrying...", result.size(), res);
1176  return false;
1177  }
1178 
1179  const char *status = result[1].c_str();
1180  PowerOnBootS[0].s = (status[0] == '1') ? ISS_ON : ISS_OFF;
1181  PowerOnBootS[1].s = (status[1] == '1') ? ISS_ON : ISS_OFF;
1182  PowerOnBootS[2].s = (status[2] == '1') ? ISS_ON : ISS_OFF;
1183  PowerOnBootS[3].s = (status[3] == '1') ? ISS_ON : ISS_OFF;
1184 
1185  AdjustableOutputN[0].value = std::stod(result[2]);
1186  AdjustableOutputNP.s = IPS_OK;
1187 
1188  return true;
1189  }
1190 
1191  return false;
1192 }
1193 
1197 bool PegasusUPB::setDewPWM(uint8_t id, uint8_t value)
1198 {
1199  char cmd[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0};
1200  snprintf(cmd, PEGASUS_LEN, "P%d:%03d", id, value);
1201  snprintf(expected, PEGASUS_LEN, "P%d:%d", id, value);
1202  if (sendCommand(cmd, res))
1203  {
1204  return (!strcmp(res, expected));
1205  }
1206 
1207  return false;
1208 }
1209 
1213 bool PegasusUPB::setUSBHubEnabled(bool enabled)
1214 {
1215  char cmd[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1216  snprintf(cmd, PEGASUS_LEN, "PU:%d", enabled ? 1 : 0);
1217  snprintf(expected, PEGASUS_LEN, "PU:%d", enabled ? 0 : 1);
1218  if (sendCommand(cmd, res))
1219  {
1220  return (!strcmp(res, expected));
1221  }
1222 
1223  return false;
1224 }
1225 
1229 bool PegasusUPB::setUSBPortEnabled(uint8_t port, bool enabled)
1230 {
1231  char cmd[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1232  snprintf(cmd, PEGASUS_LEN, "U%d:%d", port + 1, enabled ? 1 : 0);
1233  snprintf(expected, PEGASUS_LEN, "U%d:%d", port + 1, enabled ? 1 : 0);
1234  if (sendCommand(cmd, res))
1235  {
1236  return (!strcmp(res, expected));
1237  }
1238 
1239  return false;
1240 }
1241 
1245 bool PegasusUPB::toggleAutoDewV2()
1246 {
1247  char cmd[PEGASUS_LEN] = {0}, expected[PEGASUS_LEN] = {0}, res[PEGASUS_LEN] = {0};
1248 
1249  uint8_t value = 0;
1250 
1251  if (IUFindOnSwitchIndex(&AutoDewV2SP) == -1)
1252  value = 0;
1253  else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON && AutoDewV2S[DEW_PWM_B].s == ISS_ON && AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1254  value = 1;
1255  else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON && AutoDewV2S[DEW_PWM_B].s == ISS_ON)
1256  value = 5;
1257  else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON && AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1258  value = 6;
1259  else if (AutoDewV2S[DEW_PWM_B].s == ISS_ON && AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1260  value = 7;
1261  else if (AutoDewV2S[DEW_PWM_A].s == ISS_ON)
1262  value = 2;
1263  else if (AutoDewV2S[DEW_PWM_B].s == ISS_ON)
1264  value = 3;
1265  else if (AutoDewV2S[DEW_PWM_C].s == ISS_ON)
1266  value = 4;
1267 
1268  snprintf(cmd, PEGASUS_LEN, "PD:%d", value);
1269  snprintf(expected, PEGASUS_LEN, "PD:%d", value);
1270  if (sendCommand(cmd, res))
1271  {
1272  return (!strcmp(res, expected));
1273  }
1274 
1275  return false;
1276 }
1277 
1282 {
1283  // Save CCD Config
1285  FI::saveConfigItems(fp);
1286  WI::saveConfigItems(fp);
1287 
1288  IUSaveConfigSwitch(fp, &PowerLEDSP);
1289  IUSaveConfigSwitch(fp, &AutoDewSP);
1290  if (version == UPB_V2)
1291  IUSaveConfigNumber(fp, &AutoDewAggNP);
1292  IUSaveConfigNumber(fp, &FocuserSettingsNP);
1293  IUSaveConfigText(fp, &PowerControlsLabelsTP);
1294  IUSaveConfigText(fp, &DewControlsLabelsTP);
1295  IUSaveConfigText(fp, &USBControlsLabelsTP);
1296  return true;
1297 }
1298 
1303 {
1304  if (!isConnected() || setupComplete == false)
1305  {
1307  return;
1308  }
1309 
1310  if (getSensorData())
1311  {
1312  getPowerData();
1313  getStepperData();
1314 
1315  if (version == UPB_V2)
1316  getDewAggData();
1317  }
1318 
1320 }
1321 
1325 bool PegasusUPB::sendFirmware()
1326 {
1327  char res[PEGASUS_LEN] = {0};
1328  if (sendCommand("PV", res))
1329  {
1330  LOGF_INFO("Detected firmware %s", res);
1331  IUSaveText(&FirmwareT[FIRMWARE_VERSION], res);
1332  IDSetText(&FirmwareTP, nullptr);
1333  return true;
1334  }
1335 
1336  return false;
1337 }
1338 
1342 bool PegasusUPB::sensorUpdated(const std::vector<std::string> &result, uint8_t start, uint8_t end)
1343 {
1344  for (uint8_t index = start; index <= end; index++)
1345  if (result[index] != lastSensorData[index])
1346  return true;
1347 
1348  return false;
1349 }
1350 
1354 bool PegasusUPB::getSensorData()
1355 {
1356  char res[PEGASUS_LEN] = {0};
1357  if (sendCommand("PA", res))
1358  {
1359  std::vector<std::string> result = split(res, ":");
1360  if ( (version == UPB_V1 && result.size() != 19) ||
1361  (version == UPB_V2 && result.size() != 21))
1362  {
1363  LOGF_WARN("Received wrong number (%i) of detailed sensor data (%s). Retrying...", result.size(), res);
1364  return false;
1365  }
1366 
1367  if (result == lastSensorData)
1368  return true;
1369 
1370  // Power Sensors
1371  PowerSensorsN[SENSOR_VOLTAGE].value = std::stod(result[1]);
1372  PowerSensorsN[SENSOR_CURRENT].value = std::stod(result[2]);
1373  PowerSensorsN[SENSOR_POWER].value = std::stod(result[3]);
1374  PowerSensorsNP.s = IPS_OK;
1375  //if (lastSensorData[0] != result[0] || lastSensorData[1] != result[1] || lastSensorData[2] != result[2])
1376  if (sensorUpdated(result, 0, 2))
1377  IDSetNumber(&PowerSensorsNP, nullptr);
1378 
1379  // Environment Sensors
1380  setParameterValue("WEATHER_TEMPERATURE", std::stod(result[4]));
1381  setParameterValue("WEATHER_HUMIDITY", std::stod(result[5]));
1382  setParameterValue("WEATHER_DEWPOINT", std::stod(result[6]));
1383  //if (lastSensorData[4] != result[4] || lastSensorData[5] != result[5] || lastSensorData[6] != result[6])
1384  if (sensorUpdated(result, 4, 6))
1385  {
1387  IDSetLight(&critialParametersLP, nullptr);
1388  ParametersNP.s = IPS_OK;
1389  IDSetNumber(&ParametersNP, nullptr);
1390  }
1391 
1392  // Port Status
1393  const char * portStatus = result[7].c_str();
1394  PowerControlS[0].s = (portStatus[0] == '1') ? ISS_ON : ISS_OFF;
1395  PowerControlS[1].s = (portStatus[1] == '1') ? ISS_ON : ISS_OFF;
1396  PowerControlS[2].s = (portStatus[2] == '1') ? ISS_ON : ISS_OFF;
1397  PowerControlS[3].s = (portStatus[3] == '1') ? ISS_ON : ISS_OFF;
1398  //if (lastSensorData[7] != result[7])
1399  if (sensorUpdated(result, 7, 7))
1400  IDSetSwitch(&PowerControlSP, nullptr);
1401 
1402  // Hub Status
1403  const char * usb_status = result[8].c_str();
1404  if (version == UPB_V1)
1405  {
1406  USBControlS[0].s = (usb_status[0] == '0') ? ISS_ON : ISS_OFF;
1407  USBControlS[1].s = (usb_status[0] == '0') ? ISS_OFF : ISS_ON;
1408  USBStatusL[0].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1409  USBStatusL[1].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1410  USBStatusL[2].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1411  USBStatusL[3].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1412  USBStatusL[4].s = (USBControlS[0].s == ISS_ON) ? IPS_OK : IPS_IDLE;
1413  //if (lastSensorData[8] != result[8])
1414  if (sensorUpdated(result, 8, 8))
1415  {
1416  USBControlSP.s = (IUFindOnSwitchIndex(&USBControlSP) == 0) ? IPS_OK : IPS_IDLE;
1417  IDSetSwitch(&USBControlSP, nullptr);
1418  IDSetLight(&USBStatusLP, nullptr);
1419  }
1420  }
1421  else
1422  {
1423  USBControlV2S[0].s = (usb_status[0] == '1') ? ISS_ON : ISS_OFF;
1424  USBControlV2S[1].s = (usb_status[1] == '1') ? ISS_ON : ISS_OFF;
1425  USBControlV2S[2].s = (usb_status[2] == '1') ? ISS_ON : ISS_OFF;
1426  USBControlV2S[3].s = (usb_status[3] == '1') ? ISS_ON : ISS_OFF;
1427  USBControlV2S[4].s = (usb_status[4] == '1') ? ISS_ON : ISS_OFF;
1428  USBControlV2S[5].s = (usb_status[5] == '1') ? ISS_ON : ISS_OFF;
1429  USBControlV2SP.s = IPS_OK;
1430  //if (lastSensorData[8] != result[8])
1431  if (sensorUpdated(result, 8, 8))
1432  {
1433  IDSetSwitch(&USBControlV2SP, nullptr);
1434  }
1435  }
1436 
1437  // From here, we get differences between v1 and v2 readings
1438  int index = 9;
1439  // Dew PWM
1440  DewPWMN[DEW_PWM_A].value = std::stod(result[index]) / 255.0 * 100.0;
1441  DewPWMN[DEW_PWM_B].value = std::stod(result[index + 1]) / 255.0 * 100.0;
1442  if (version == UPB_V2)
1443  DewPWMN[DEW_PWM_C].value = std::stod(result[index + 2]) / 255.0 * 100.0;
1444  // if (lastSensorData[index] != result[index] ||
1445  // lastSensorData[index + 1] != result[index + 1] ||
1446  // (version == UPB_V2 && lastSensorData[index +2] != result[index + 2]))
1447  if (sensorUpdated(result, index, version == UPB_V1 ? index + 1 : index + 2))
1448  IDSetNumber(&DewPWMNP, nullptr);
1449 
1450  index = (version == UPB_V1) ? 11 : 12;
1451 
1452  const double ampDivision = (version == UPB_V1) ? 400.0 : 480.0;
1453 
1454  // Current draw
1455  PowerCurrentN[0].value = std::stod(result[index]) / ampDivision;
1456  PowerCurrentN[1].value = std::stod(result[index + 1]) / ampDivision;
1457  PowerCurrentN[2].value = std::stod(result[index + 2]) / ampDivision;
1458  PowerCurrentN[3].value = std::stod(result[index + 3]) / ampDivision;
1459  // if (lastSensorData[index] != result[index] ||
1460  // lastSensorData[index + 1] != result[index + 1] ||
1461  // lastSensorData[index + 2] != result[index + 2] ||
1462  // lastSensorData[index + 3] != result[index + 3])
1463  if (sensorUpdated(result, index, index + 3))
1464  IDSetNumber(&PowerCurrentNP, nullptr);
1465 
1466  index = (version == UPB_V1) ? 15 : 16;
1467 
1468  DewCurrentDrawN[DEW_PWM_A].value = std::stod(result[index]) / ampDivision;
1469  DewCurrentDrawN[DEW_PWM_B].value = std::stod(result[index + 1]) / ampDivision;
1470  if (version == UPB_V2)
1471  DewCurrentDrawN[DEW_PWM_C].value = std::stod(result[index + 2]) / 700;
1472  // if (lastSensorData[index] != result[index] ||
1473  // lastSensorData[index + 1] != result[index + 1] ||
1474  // (version == UPB_V2 && lastSensorData[index + 2] != result[index + 2]))
1475  if (sensorUpdated(result, index, version == UPB_V1 ? index + 1 : index + 2))
1476  IDSetNumber(&DewCurrentDrawNP, nullptr);
1477 
1478  index = (version == UPB_V1) ? 17 : 19;
1479 
1480  // Over Current
1481  //if (lastSensorData[index] != result[index])
1482  if (sensorUpdated(result, index, index))
1483  {
1484  const char * over_curent = result[index].c_str();
1485  OverCurrentL[0].s = (over_curent[0] == '0') ? IPS_OK : IPS_ALERT;
1486  OverCurrentL[1].s = (over_curent[1] == '0') ? IPS_OK : IPS_ALERT;
1487  OverCurrentL[2].s = (over_curent[2] == '0') ? IPS_OK : IPS_ALERT;
1488  OverCurrentL[3].s = (over_curent[3] == '0') ? IPS_OK : IPS_ALERT;
1489  if (version == UPB_V2)
1490  {
1491  OverCurrentL[4].s = (over_curent[4] == '0') ? IPS_OK : IPS_ALERT;
1492  OverCurrentL[5].s = (over_curent[5] == '0') ? IPS_OK : IPS_ALERT;
1493  OverCurrentL[6].s = (over_curent[6] == '0') ? IPS_OK : IPS_ALERT;
1494  }
1495 
1496  IDSetLight(&OverCurrentLP, nullptr);
1497  }
1498 
1499  index = (version == UPB_V1) ? 18 : 20;
1500 
1501  // Auto Dew
1502  if (version == UPB_V1)
1503  {
1504  //if (lastSensorData[index] != result[index])
1505  if (sensorUpdated(result, index, index))
1506  {
1507  AutoDewS[INDI_ENABLED].s = (std::stoi(result[index]) == 1) ? ISS_ON : ISS_OFF;
1508  AutoDewS[INDI_DISABLED].s = (std::stoi(result[index]) == 1) ? ISS_OFF : ISS_ON;
1509  IDSetSwitch(&AutoDewSP, nullptr);
1510  }
1511  }
1512  else
1513  {
1514  //if (lastSensorData[index] != result[index])
1515  if (sensorUpdated(result, index, index))
1516  {
1517  int value = std::stoi(result[index]);
1518  IUResetSwitch(&AutoDewV2SP);
1519  switch (value)
1520  {
1521  case 1:
1522  AutoDewV2S[DEW_PWM_A].s = AutoDewV2S[DEW_PWM_B].s = AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1523  break;
1524 
1525  case 2:
1526  AutoDewV2S[DEW_PWM_A].s = ISS_ON;
1527  break;
1528 
1529  case 3:
1530  AutoDewV2S[DEW_PWM_B].s = ISS_ON;
1531  break;
1532 
1533  case 4:
1534  AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1535  break;
1536 
1537  case 5:
1538  AutoDewV2S[DEW_PWM_A].s = ISS_ON;
1539  AutoDewV2S[DEW_PWM_B].s = ISS_ON;
1540  break;
1541 
1542  case 6:
1543  AutoDewV2S[DEW_PWM_A].s = ISS_ON;
1544  AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1545  break;
1546 
1547  case 7:
1548  AutoDewV2S[DEW_PWM_B].s = ISS_ON;
1549  AutoDewV2S[DEW_PWM_C].s = ISS_ON;
1550  break;
1551  default:
1552  break;
1553  }
1554  IDSetSwitch(&AutoDewV2SP, nullptr);
1555  }
1556  }
1557 
1558  lastSensorData = result;
1559  return true;
1560  }
1561 
1562  return false;
1563 }
1564 
1568 bool PegasusUPB::getPowerData()
1569 {
1570  char res[PEGASUS_LEN] = {0};
1571  if (sendCommand("PC", res))
1572  {
1573  std::vector<std::string> result = split(res, ":");
1574  if ( (version == UPB_V1 && result.size() != 3) ||
1575  (version == UPB_V2 && result.size() != 4))
1576  {
1577  LOGF_WARN("Received wrong number (%i) of power sensor data (%s). Retrying...", result.size(), res);
1578  return false;
1579  }
1580 
1581  if (result == lastPowerData)
1582  return true;
1583 
1584  PowerConsumptionN[CONSUMPTION_AVG_AMPS].value = std::stod(result[0]);
1585  PowerConsumptionN[CONSUMPTION_AMP_HOURS].value = std::stod(result[1]);
1586  PowerConsumptionN[CONSUMPTION_WATT_HOURS].value = std::stod(result[2]);
1587  PowerConsumptionNP.s = IPS_OK;
1588  IDSetNumber(&PowerConsumptionNP, nullptr);
1589 
1590  if (version == UPB_V2)
1591  {
1592  try
1593  {
1594  std::chrono::milliseconds uptime(std::stol(result[3]));
1595  using dhours = std::chrono::duration<double, std::ratio<3600>>;
1596  std::stringstream ss;
1597  ss << std::fixed << std::setprecision(3) << dhours(uptime).count();
1598  IUSaveText(&FirmwareT[FIRMWARE_UPTIME], ss.str().c_str());
1599  }
1600  catch(...)
1601  {
1602  IUSaveText(&FirmwareT[FIRMWARE_UPTIME], "NA");
1603  LOGF_WARN("Failed to process uptime: %s", result[3].c_str());
1604  return false;
1605  }
1606  IDSetText(&FirmwareTP, nullptr);
1607  }
1608 
1609  lastPowerData = result;
1610  return true;
1611  }
1612 
1613  return false;
1614 }
1615 
1619 bool PegasusUPB::getStepperData()
1620 {
1621  char res[PEGASUS_LEN] = {0};
1622  if (sendCommand("SA", res))
1623  {
1624  std::vector<std::string> result = split(res, ":");
1625  if (result.size() != 4)
1626  {
1627  LOGF_WARN("Received wrong number (%i) of stepper sensor data (%s). Retrying...", result.size(), res);
1628  return false;
1629  }
1630 
1631  if (result == lastStepperData)
1632  return true;
1633 
1634  FocusAbsPosN[0].value = std::stoi(result[0]);
1635  focusMotorRunning = (std::stoi(result[1]) == 1);
1636 
1637  if (FocusAbsPosNP.s == IPS_BUSY && focusMotorRunning == false)
1638  {
1641  IDSetNumber(&FocusAbsPosNP, nullptr);
1642  IDSetNumber(&FocusRelPosNP, nullptr);
1643  }
1644  else if (result[0] != lastStepperData[0])
1645  IDSetNumber(&FocusAbsPosNP, nullptr);
1646 
1647  FocusReverseS[INDI_ENABLED].s = (std::stoi(result[2]) == 1) ? ISS_ON : ISS_OFF;
1648  FocusReverseS[INDI_DISABLED].s = (std::stoi(result[2]) == 1) ? ISS_OFF : ISS_ON;
1649 
1650  if (result[2] != lastStepperData[2])
1651  IDSetSwitch(&FocusReverseSP, nullptr);
1652 
1653  uint16_t backlash = std::stoi(result[3]);
1654  if (backlash == 0)
1655  {
1656  FocusBacklashN[0].value = backlash;
1659  if (result[3] != lastStepperData[3])
1660  {
1661  IDSetSwitch(&FocusBacklashSP, nullptr);
1662  IDSetNumber(&FocuserSettingsNP, nullptr);
1663  }
1664  }
1665  else
1666  {
1669  FocusBacklashN[0].value = backlash;
1670  if (result[3] != lastStepperData[3])
1671  {
1672  IDSetSwitch(&FocusBacklashSP, nullptr);
1673  IDSetNumber(&FocuserSettingsNP, nullptr);
1674  }
1675  }
1676 
1677  lastStepperData = result;
1678  return true;
1679  }
1680 
1681  return false;
1682 }
1683 bool PegasusUPB::getDewAggData()
1684 {
1685  char res[PEGASUS_LEN] = {0};
1686  if (sendCommand("DA", res))
1687  {
1688  std::vector<std::string> result = split(res, ":");
1689  if (result.size() != 2)
1690  {
1691  LOGF_WARN("Received wrong number (%i) of dew aggresiveness data (%s). Retrying...", result.size(), res);
1692  return false;
1693  }
1694 
1695  if (result == lastDewAggData)
1696  return true;
1697 
1698  AutoDewAggN[0].value = std::stod(result[1]);
1699  AutoDewAggNP.s = IPS_OK;
1700  IDSetNumber(&AutoDewAggNP, nullptr);
1701 
1702  lastDewAggData = result;
1703  return true;
1704  }
1705  return false;
1706 }
1710 bool PegasusUPB::reboot()
1711 {
1712  return sendCommand("PF", nullptr);
1713 }
1714 
1718 std::vector<std::string> PegasusUPB::split(const std::string &input, const std::string &regex)
1719 {
1720  // passing -1 as the submatch index parameter performs splitting
1721  std::regex re(regex);
1722  std::sregex_token_iterator
1723  first{input.begin(), input.end(), re, -1},
1724  last;
1725  return {first, last};
1726 }
1727 
1731 bool PegasusUPB::setupParams()
1732 {
1733  if (version == UPB_V2)
1734  getPowerOnBoot();
1735 
1736  sendFirmware();
1737 
1738  // Get Max Focuser Speed
1739  char res[PEGASUS_LEN] = {0};
1740  if (sendCommand("SS", res))
1741  {
1742  try
1743  {
1744  uint32_t value = std::stol(res);
1745  if (value == UINT16_MAX)
1746  {
1747  LOGF_WARN("Invalid maximum speed detected: %u. Please set maximum speed appropiate for your motor focus type (0-900)",
1748  value);
1749  FocuserSettingsNP.s = IPS_ALERT;
1750  }
1751  else
1752  {
1753  FocuserSettingsN[SETTING_MAX_SPEED].value = value;
1754  FocuserSettingsNP.s = IPS_OK;
1755  }
1756  }
1757  catch(...)
1758  {
1759  LOGF_WARN("Failed to process focuser max speed: %s", res);
1760  FocuserSettingsNP.s = IPS_ALERT;
1761  }
1762  }
1763 
1764  return false;
1765 }
1766 
1770 void PegasusUPB::cleanupResponse(char *response)
1771 {
1772  std::string s(response);
1773  s.erase(std::remove_if(s.begin(), s.end(),
1774  [](unsigned char x)
1775  {
1776  return std::isspace(x);
1777  }), s.end());
1778  strncpy(response, s.c_str(), PEGASUS_LEN);
1779 }
INDI::FocuserInterface::FOCUSER_CAN_ABS_MOVE
@ FOCUSER_CAN_ABS_MOVE
Definition: indifocuserinterface.h:74
pegasus_upb.h
IP_RO
@ IP_RO
Definition: indiapi.h:183
INDI::FocuserInterface::FOCUSER_CAN_SYNC
@ FOCUSER_CAN_SYNC
Definition: indifocuserinterface.h:78
INDI::FocuserInterface::FOCUSER_CAN_REL_MOVE
@ FOCUSER_CAN_REL_MOVE
Definition: indifocuserinterface.h:75
INDI::FocuserInterface::FocusAbsPosNP
INumberVectorProperty FocusAbsPosNP
Definition: indifocuserinterface.h:282
INDI::WeatherInterface::initProperties
void initProperties(const char *statusGroup, const char *paramsGroup)
Initilize focuser properties. It is recommended to call this function within initProperties() of your...
Definition: indiweatherinterface.cpp:48
TTY_TIME_OUT
@ TTY_TIME_OUT
Definition: indicom.h:98
cmd
__u8 cmd[4]
Definition: pwc-ioctl.h:4
PegasusUPB::saveConfigItems
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
Definition: pegasus_upb.cpp:1281
INDI::WeatherInterface::setCriticalParameter
bool setCriticalParameter(std::string param)
setCriticalParameter Set parameter that is considered critical to the operation of the observatory....
Definition: indiweatherinterface.cpp:167
IP_WO
@ IP_WO
Definition: indiapi.h:184
INDI::DefaultDevice::addAuxControls
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
Definition: defaultdevice.cpp:665
IPState
IPState
Property state.
Definition: indiapi.h:158
tty_nread_section
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:657
PegasusUPB::MoveRelFocuser
virtual IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
Definition: pegasus_upb.cpp:997
INDI::FocuserInterface::FocusBacklashN
INumber FocusBacklashN[1]
Definition: indifocuserinterface.h:311
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
ISS_OFF
@ ISS_OFF
Definition: indiapi.h:150
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.
IPS_ALERT
@ IPS_ALERT
Definition: indiapi.h:163
Connection::Serial
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
Definition: connectionserial.h:56
_INumberVectorProperty::nnp
int nnp
Definition: indiapi.h:336
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
ISR_NOFMANY
@ ISR_NOFMANY
Definition: indiapi.h:174
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
PegasusUPB::updateProperties
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: pegasus_upb.cpp:358
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
INDI::FocuserInterface
Provides interface to implement focuser functionality.
Definition: indifocuserinterface.h:63
PegasusUPB::getDefaultName
const char * getDefaultName() override
Definition: pegasus_upb.cpp:476
INDI::WeatherInterface::syncCriticalParameters
bool syncCriticalParameters()
updateWeatherState Send update weather state to client
Definition: indiweatherinterface.cpp:230
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
_ITextVectorProperty::ntp
int ntp
Definition: indiapi.h:263
INDI::BaseDevice::getDeviceName
const char * getDeviceName() const
Definition: basedevice.cpp:799
PegasusUPB::ISNewText
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition: pegasus_upb.cpp:859
FOCUS_TAB
const char * FOCUS_TAB
FOCUS_TAB Where all the properties for focuser are located.
Definition: defaultdevice.cpp:41
TTY_OVERFLOW
@ TTY_OVERFLOW
Definition: indicom.h:102
IUFillLight
void IUFillLight(ILight *lp, const char *name, const char *label, IPState s)
Assign attributes for a light property. The light's auxiliary elements will be set to NULL.
Definition: indidriver.c:334
INDI::FocuserInterface::FOCUSER_CAN_ABORT
@ FOCUSER_CAN_ABORT
Definition: indifocuserinterface.h:76
IUSaveConfigNumber
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indicom.c:1455
_ILightVectorProperty::nlp
int nlp
Definition: indiapi.h:430
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
IUUpdateText
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:259
LOG_INFO
#define LOG_INFO(txt)
Definition: indilogger.h:74
MAXRBUF
#define MAXRBUF
Definition: indidriver.c:52
INDI::WeatherInterface::ParametersNP
INumberVectorProperty ParametersNP
Definition: indiweatherinterface.h:150
INDI::DefaultDevice::initProperties
virtual bool initProperties()
Initilize properties initial state and value. The child class must implement this function.
Definition: defaultdevice.cpp:917
IUResetSwitch
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indicom.c:1442
_ILightVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:420
tty_error_msg
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1156
INDI::DefaultDevice::getCurrentPollingPeriod
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
Definition: defaultdevice.cpp:1139
INDI::FocuserInterface::FocusBacklashSP
ISwitchVectorProperty FocusBacklashSP
Definition: indifocuserinterface.h:306
PegasusUPB::TimerHit
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
Definition: pegasus_upb.cpp:1302
LOGF_DEBUG
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
IUGetConfigText
int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
IUGetConfigText Opens configuration file and reads single text property.
Definition: indidriver.c:1448
INDI::DefaultDevice::SetTimer
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
Definition: defaultdevice.cpp:865
PegasusUPB::AbortFocuser
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
Definition: pegasus_upb.cpp:1005
INDI::FocuserInterface::saveConfigItems
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
Definition: indifocuserinterface.cpp:596
PegasusUPB::UPB_V1
@ UPB_V1
Definition: pegasus_upb.h:44
IUSaveConfigText
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indicom.c:1460
_ISwitchVectorProperty::nsp
int nsp
Definition: indiapi.h:386
IDSetLight
void void void void void void void IDSetLight(const ILightVectorProperty *l, const char *msg,...) ATTRIBUTE_FORMAT_PRINTF(2
Tell client to update an existing light vector property.
PegasusUPB
Definition: pegasus_upb.h:37
LOGF_WARN
#define LOGF_WARN(fmt,...)
Definition: indilogger.h:81
INDI::FocuserInterface::FOCUS_INWARD
@ FOCUS_INWARD
Definition: indifocuserinterface.h:68
INDI::BaseDevice::FOCUSER_INTERFACE
@ FOCUSER_INTERFACE
Definition: basedevice.h:75
INDI::DefaultDevice::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
Definition: defaultdevice.cpp:593
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
INDI::WeatherInterface::critialParametersLP
ILightVectorProperty critialParametersLP
Definition: indiweatherinterface.h:158
connectionserial.h
IPS_IDLE
@ IPS_IDLE
Definition: indiapi.h:160
INDI::FocuserInterface::processNumber
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process focus number properties.
Definition: indifocuserinterface.cpp:168
INDI::DefaultDevice::registerConnection
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
Definition: defaultdevice.cpp:1107
PegasusUPB::PegasusUPB
PegasusUPB()
Definition: pegasus_upb.cpp:41
INDI::FocuserInterface::FOCUSER_CAN_REVERSE
@ FOCUSER_CAN_REVERSE
Definition: indifocuserinterface.h:77
ISR_ATMOST1
@ ISR_ATMOST1
Definition: indiapi.h:173
_INumberVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:322
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
end
JsonIterator end(JsonValue)
Definition: gason.h:108
INDI::BaseDevice::isConnected
bool isConnected() const
Definition: basedevice.cpp:518
_ITextVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:249
ISNewSwitch
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Update the value of an existing switch vector property.
Definition: defaultdevice.cpp:60
LOG_DEBUG
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
LOGF_INFO
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
INDI::WeatherInterface::saveConfigItems
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save parameters ranges in the config file.
Definition: indiweatherinterface.cpp:312
INDI::FocuserInterface::FocusBacklashS
ISwitch FocusBacklashS[2]
Definition: indifocuserinterface.h:307
INDI::WeatherInterface::processNumber
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process focus number properties.
Definition: indiweatherinterface.cpp:96
PegasusUPB::SyncFocuser
virtual bool SyncFocuser(uint32_t ticks) override
SyncFocuser Set current position to ticks without moving the focuser.
Definition: pegasus_upb.cpp:1034
INDI::FocuserInterface::SetCapability
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
Definition: indifocuserinterface.h:95
INDI::WeatherInterface::addParameter
void addParameter(std::string name, std::string label, double numMinOk, double numMaxOk, double percWarning)
addParameter Add a physical weather measurable parameter to the weather driver. The weather value has...
Definition: indiweatherinterface.cpp:131
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
INDI::FocuserInterface::updateProperties
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
Definition: indifocuserinterface.cpp:101
INDI::FocuserInterface::initProperties
void initProperties(const char *groupName)
Initilize focuser properties. It is recommended to call this function within initProperties() of your...
Definition: indifocuserinterface.cpp:35
_ISwitchVectorProperty::s
IPState s
Definition: indiapi.h:382
PegasusUPB::MoveAbsFocuser
virtual IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute position.
Definition: pegasus_upb.cpp:982
INDI::FocuserInterface::FocusReverseS
ISwitch FocusReverseS[2]
Definition: indifocuserinterface.h:303
_ITextVectorProperty::s
IPState s
Definition: indiapi.h:259
INDI::DefaultDevice::saveConfig
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
Definition: defaultdevice.cpp:221
Connection::Interface::registerHandshake
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the intial connection to the device...
Definition: connectioninterface.cpp:108
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::processSwitch
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process focus switch properties.
Definition: indifocuserinterface.cpp:414
INDI::DefaultDevice::saveConfigItems
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
Definition: defaultdevice.cpp:175
INDI::FocuserInterface::FocusDirection
FocusDirection
Definition: indifocuserinterface.h:66
IP_RW
@ IP_RW
Definition: indiapi.h:185
PegasusUPB::ISNewNumber
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: pegasus_upb.cpp:773
MAXINDILABEL
#define MAXINDILABEL
Definition: indiapi.h:191
PegasusUPB::initProperties
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: pegasus_upb.cpp:51
INDI::FocuserInterface::FOCUSER_HAS_BACKLASH
@ FOCUSER_HAS_BACKLASH
Definition: indifocuserinterface.h:80
ISState
ISState
Switch state.
Definition: indiapi.h:148
Connection::Serial::getPortFD
int getPortFD() const
Definition: connectionserial.h:136
INDI::WeatherInterface
Provides interface to implement weather reporting functionality.
Definition: indiweatherinterface.h:55
IUFindOnSwitchIndex
int IUFindOnSwitchIndex(const ISwitchVectorProperty *sp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indicom.c:1424
PegasusUPB::ReverseFocuser
virtual bool ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
Definition: pegasus_upb.cpp:1019
INDI::BaseDevice::AUX_INTERFACE
@ AUX_INTERFACE
Definition: basedevice.h:87
PegasusUPB::SetFocuserBacklashEnabled
virtual bool SetFocuserBacklashEnabled(bool enabled) override
SetFocuserBacklashEnabled Enables or disables the focuser backlash compensation.
Definition: pegasus_upb.cpp:1064
IUSaveConfigSwitch
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indicom.c:1465
tty_write_string
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:465
TTY_OK
@ TTY_OK
Definition: indicom.h:94
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
IUFillLightVector
void IUFillLightVector(ILightVectorProperty *lvp, ILight *lp, int nlp, const char *dev, const char *name, const char *label, const char *group, IPState s)
Assign attributes for a light vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidriver.c:435
PegasusUPB::ISNewSwitch
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: pegasus_upb.cpp:549
INDI::FocuserInterface::FocusAbsPosN
INumber FocusAbsPosN[1]
Definition: indifocuserinterface.h:283
INDI::DefaultDevice::ISNewText
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
Definition: defaultdevice.cpp:614
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.
INDI::DefaultDevice::setDriverInterface
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
Definition: defaultdevice.cpp:902
INDI::WeatherInterface::updateProperties
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
Definition: indiweatherinterface.cpp:61
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::BaseDevice::WEATHER_INTERFACE
@ WEATHER_INTERFACE
Definition: basedevice.h:79
INDI::DefaultDevice::updateProperties
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: defaultdevice.cpp:890
PegasusUPB::SetFocuserBacklash
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
Definition: pegasus_upb.cpp:1044
INDI::BaseDevice::INDI_DISABLED
@ INDI_DISABLED
Definition: basedevice.h:65
PegasusUPB::UPB_V2
@ UPB_V2
Definition: pegasus_upb.h:45
_ISwitchVectorProperty::name
char name[MAXINDINAME]
Definition: indiapi.h:370
INDI::WeatherInterface::setParameterValue
void setParameterValue(std::string name, double value)
setParameterValue Update weather parameter value
Definition: indiweatherinterface.cpp:155
ISS_ON
@ ISS_ON
Definition: indiapi.h:151