Instrument Neutral Distributed Interface INDI 2.2.5
svbony_powerbox.cpp
Go to the documentation of this file.
1/*******************************************************************************
2 Copyright(c) 2025 Tetsuya Kakura. All rights reserved.
3
4 SVBONYPowerBox
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 "config.h"
26#include "svbony_powerbox.h"
27
28#include "indicom.h"
30#include "inditimer.h"
31
32#include <cerrno>
33#include <cstring>
34#include <chrono>
35#include <thread>
36#include <memory>
37#include <cmath>
38#include <termios.h>
39#include <unistd.h>
40#include <inttypes.h>
41#include <sys/ioctl.h>
42#include <algorithm>
43
44// Declaration for an auto pointer to SVBONYPowerBox
45static std::unique_ptr<SVBONYPowerBox> svbonypowerbox(new SVBONYPowerBox());
46
47#define CMD_MAX_LEN 6
48#define CMD_OFFSET_FRAME_HEADER 0
49#define CMD_OFFSET_DATA_LEN 1
50#define CMD_OFFSET_CMD 2
51
52#define TIMEOUT_SEC 0
53#define TIMEOUT_MSEC 500
54
55SVBONYPowerBox::SVBONYPowerBox() : INDI::PowerInterface(this)
56{
57 setVersion(1, 0);
58}
59
61{
65
66 // Initialize Weather Sensor Properties
67 WeatherSVBSensorsNP[SVB_SENSOR_DS18B20_TEMP].fill("DS18B20_TEMP", "Lens Temperature(C)", "%.1f", -100, 200, 0.1, 0);
68 WeatherSVBSensorsNP[SVB_SENSOR_SHT40_TEMP].fill("SHT40_TEMP", "Temperature(C)", "%.1f", -100, 200, 0.1, 0);
69 WeatherSVBSensorsNP[SVB_SENSOR_SHT40_HUMIDITY].fill("SHT40_HUMI", "Humidity %", "%.1f", 0, 100, 0.1, 0);
70 WeatherSVBSensorsNP[SVB_SENSOR_DEW_POINT].fill("DEW_POINT", "Dew Point(C)", "%.1f", -100, 200, 0.1, 0);
71 WeatherSVBSensorsNP.fill(getDeviceName(), "WEATHER_SV_SENSORS", "Weather Sensors", DEW_TAB, IP_RO, 0, IPS_IDLE);
72
73 // Serial Connection
74 serialConnection = new Connection::Serial(this);
75 serialConnection->registerHandshake([&]()
76 {
77 return Handshake();
78 });
80 registerConnection(serialConnection);
81
82 // Trigger connection by default
83 INDI::Timer::singleShot(500, [this]()
84 {
85 if (Connect())
86 {
87 setConnected(true);
89 }
90 });
91
92 return true;
93}
94
96{
98
99 if (isConnected())
100 {
101 // Define Weather sensor properties
102 defineProperty(WeatherSVBSensorsNP);
103
105 setupComplete = true;
106 }
107 else
108 {
109 // Delete Weather sensor properties
110 deleteProperty(WeatherSVBSensorsNP);
111
113 setupComplete = false;
114 }
115
116 return true;
117}
118
120{
123 return true;
124}
125
126/*
127 Handshake with the device and initialize PowerInterface properties
128*/
129bool SVBONYPowerBox::Handshake()
130{
131 uint32_t SVB_capabilities =
136 /* POWER_HAS_OVERALL_CURRENT | */ // Not supported
137 /* POWER_HAS_PER_PORT_CURRENT | */ // Not supported
138 /* POWER_HAS_LED_TOGGLE | */ // Not supported
139 /* POWER_HAS_AUTO_DEW | */ // Not supported
142 /* POWER_HAS_OVER_VOLTAGE_PROTECTION | */ // Not supported
143 /* POWER_OFF_ON_DISCONNECT; */ // Not supported
144
145 if (isSimulation()) // If in simulation mode, skip actual handshake
146 {
147 // Set capabilities and initialize PI properties
148 PI::SetCapability(SVB_capabilities);
150 POWER_TAB,
151 5, // DC Ports
152 2, // Dew Ports
153 1, // Variable Ports
154 0, // Auto Dew Ports
155 2 // USB Ports
156 );
157 return true;
158 }
159 /*
160 Device Identification
161 */
162 PortFD = serialConnection->getPortFD();
163 int flags;
164 ioctl(PortFD, TIOCMGET, &flags);
165 flags &= ~(TIOCM_RTS | TIOCM_DTR); // Clear RTS and DTR to reset device
166 ioctl(PortFD, TIOCMSET, &flags);
167
168
169 bool isResetting = true;
170 int retryCount = 0;
171 const int maxRetries = 10;
172
173 while (isResetting && retryCount < maxRetries)
174 {
175 char buf[512] = {0};
176 int nbytes_read = 0;
177
178 std::this_thread::sleep_for(std::chrono::milliseconds(50)); // wait 50 ms
179
180 int tty_ret = tty_nread_section_expanded(PortFD,
181 buf, (sizeof buf) - 1, '\n', TIMEOUT_SEC, TIMEOUT_MSEC, &nbytes_read);
182 if (tty_ret != TTY_OK)
183 {
184 char errorMessage[MAXRBUF];
185 tty_error_msg(tty_ret, errorMessage, MAXRBUF);
186
187 LOGF_ERROR("Handshake error.(Serial read error: %s)", errorMessage);
188 return false;
189 }
190 std::string response(buf);
191 LOGF_DEBUG("Handshake response: %s", response.c_str());
192 std::this_thread::sleep_for(std::chrono::milliseconds(50)); // wait 50 ms
193
194 /*
195 Verify that it matches the following string returned when the device is opened
196 However, it does not need to be an exact match.
197 ```
198 ets Jun 8 2016 00:22:57\r\n
199 \r\n
200 rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)\r\n
201 configsip: 188777542, SPIWP:0xee\r\n
202 clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00\r\n
203 mode:DIO, clock div:1\r\n
204 load:0x3fff0030,len:1344\r\n
205 load:0x40078000,len:13964\r\n
206 load:0x40080400,len:3600\r\n
207 entry 0x400805f0\r\n
208 ```
209 */
210 isResetting =
211 response.find("\n") != std::string::npos ||
212 response.find("POW") != std::string::npos ||
213 response.find("0x00") != std::string::npos ||
214 response.find("rst") != std::string::npos ||
215 response.find("loa") != std::string::npos ||
216 response.find("len") != std::string::npos ||
217 response.find("ts") != std::string::npos;
218
219 ++retryCount;
220 }
221 /*
222 After resetting the device, issue a status request command and verify that the device enters the idle state.
223 */
224 int enterIdelRetryCount = 10;
225 while (enterIdelRetryCount-- > 0)
226 {
227 tcflush(PortFD, TCIOFLUSH); // Flush both input and output buffers
228 // Send commands to the SV241 Pro and verify the response
229 if (sendCommand((const unsigned char*)"\x08", 1, nullptr, 10)) {
230 break; // Handshake successful
231 }
232 std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // wait 1,000 ms
233 }
234 if (enterIdelRetryCount<= 0)
235 {
236 LOG_ERROR("Handshake failed.");
237 return false;
238 };
239
240 LOG_INFO("Handshake successful.");
241
242 // Set capabilities and initialize PI properties
243 PI::SetCapability(SVB_capabilities);
245 POWER_TAB,
246 5, // DC Ports
247 2, // Dew Ports
248 1, // Variable Ports
249 0, // Auto Dew Ports
250 2 // USB Ports
251 );
252
253 /*
254 Set the variable voltage channel to 0V to 15.3V
255 */
256 VariableChannelVoltsNP[0].setMinMax(0.0, 15.3); // Range for variable output
257 VariableChannelVoltsNP[0].setStep(1); // Step size
259
260 /*
261 Change property labels to match the device
262 */
263 // DC Channels
264 for (size_t i = 0; i < PowerChannelsSP.size(); i++)
265 {
266 char propLabel[MAXINDILABEL];
267 snprintf(propLabel, MAXINDILABEL, "DC %d", static_cast<int>(i + 1));
268 PowerChannelsSP[i].setLabel(propLabel);
269 PowerChannelLabelsTP[i].setLabel(propLabel);
270 }
273 //Dew Channels
274 for (size_t i = 0; i < DewChannelsSP.size(); i++)
275 {
276 char propLabel[MAXINDILABEL];
277 char propDutyLabel[MAXINDILABEL];
278 snprintf(propLabel, MAXINDILABEL, "PWM %d", static_cast<int>(i + 1));
279 snprintf(propDutyLabel, MAXINDILABEL, "PWM %d (%%)", static_cast<int>(i + 1));
280 DewChannelsSP[i].setLabel(propLabel);
281 DewChannelLabelsTP[i].setLabel(propLabel);
282 DewChannelDutyCycleNP[i].setLabel(propDutyLabel);
283 }
286 // USB Channels
287 USBPortSP[0].setLabel("USB C,1,2");
288 USBPortLabelsTP[0].setLabel("USB C,1,2");
289 USBPortSP[1].setLabel("USB 3,4,5");
290 USBPortLabelsTP[1].setLabel("USB 3,4,5");
293 //Variable Channel
294 VariableChannelsSP[0].setLabel("REGULATED");
295 VariableChannelVoltsNP[0].setLabel("REGULATED (V)");
296 VariableChannelLabelsTP[0].setLabel("REGULATED");
299
300 return true;
301}
302
304{
305 LOG_INFO("GET Name");
306 return "SVBONY PowerBox";
307}
308
309bool SVBONYPowerBox::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
310{
311 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
312 {
313 // Let PowerInterface handle its switches first
314 if (PI::processSwitch(dev, name, states, names, n))
315 return true;
316
317 if (processButtonSwitch(dev, name, states, names, n))
318 return true;
319 }
320#if 0 // May output a large amount of logs; disable
321 // Handle Debug switch
322 if (strcmp(name, "DEBUG") == 0)
323 {
324 tty_set_debug(strcmp(names[0], "ENABLE") == 0 ? true : false);
325 }
326#endif
327 return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
328}
329
330bool SVBONYPowerBox::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
331{
332 if (dev && !strcmp(dev, getDeviceName()))
333 {
334 // Let PowerInterface handle its text properties
335 if (PI::processText(dev, name, texts, names, n))
336 return true;
337 }
338 return INDI::DefaultDevice::ISNewText(dev, name, texts, names, n);
339}
340
341bool SVBONYPowerBox::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
342{
343 if (dev && !strcmp(dev, getDeviceName()))
344 {
345 // Let PowerInterface handle its number properties
346 if (PI::processNumber(dev, name, values, names, n))
347 return true;
348 }
349 return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
350}
351
352/*
353 Send command to the device and read response
354
355 input:
356 cmd: command bytes to send
357 cmd_len: length of command bytes
358 res_len: expected length of response bytes
359 output:
360 res: buffer to store response bytes. Should be at least res_len bytes long. Can be nullptr if response is not needed.
361*/
362bool SVBONYPowerBox::sendCommand(const unsigned char* cmd, unsigned int cmd_len, unsigned char* res, int res_len)
363{
364 int tty_ret = 0; // tty return code
365 int nbytes_written = 0; // number of bytes written
366 int nbytes_read = 0; // number of bytes read
367 const char frame_header = 0x24; // '$'
368
369 unsigned char command[CMD_MAX_LEN] = {0}; // frame_header + data_len + cmd + checksum
370 int full_cmd_len = 2 + cmd_len + 1; // 2:(frame_header + data_len) + cmd + 1:checksum
371 int full_res_len = 3 + res_len + 1; // 3:(frame_header + data_len + cmd) + res + 1:checksum
372 unsigned checksum = 0; // checksum
373 unsigned char *response = (unsigned char *)alloca(3 + res_len +
374 1); // Allocate response buffer. frame_header + data_len + cmd + res + checksum
375 command[CMD_OFFSET_FRAME_HEADER] = frame_header; // Set frame header
376 command[CMD_OFFSET_DATA_LEN] = full_cmd_len; // Set frame_header + data_len + cmd + checksum
377
378 // set command bytes
379 for (unsigned int i = 0; i < cmd_len; i++)
380 {
381 command[CMD_OFFSET_CMD + i] = cmd[i];
382 }
383 // calculate checksum
384 for (unsigned int i = 0; i < 2 + cmd_len; i++)
385 {
386 checksum += command[i];
387 }
388 command[full_cmd_len - 1] = (unsigned char)(checksum % 0xFF);
389
390 // Debug logging
391 switch (full_cmd_len)
392 {
393 case 4:
394 LOGF_DEBUG("CMD <%02X %02X %02X %02X>", command[0], command[1], command[2], command[3]);
395 break;
396 case 5:
397 LOGF_DEBUG("CMD <%02X %02X %02X %02X %02X>", command[0], command[1], command[2], command[3], command[4]);
398 break;
399 case 6:
400 LOGF_DEBUG("CMD <%02X %02X %02X %02X %02X %02X>", command[0], command[1], command[2], command[3], command[4], command[5]);
401 break;
402 default:
403 break;
404 }
405
406 // Send command
407 PortFD = serialConnection->getPortFD(); // Get port file descriptor
408 tcflush(PortFD, TCIOFLUSH); // Flush both input and output buffers
409
410 if ( (tty_ret = tty_write(PortFD, (const char*)command, full_cmd_len, &nbytes_written)) != TTY_OK)
411 {
412 // Write error
413 char errorMessage[MAXRBUF];
414 tty_error_msg(tty_ret, errorMessage, MAXRBUF);
415 LOGF_ERROR("Serial write error: %s", errorMessage);
416 return false;
417 }
418 if (nbytes_written != full_cmd_len)
419 {
420 // The number of bytes requested for writing differs from the actual number of bytes written.
421 LOGF_ERROR("Serial write error: expected %d bytes, wrote %d bytes", full_cmd_len, nbytes_written);
422 return false;
423 }
424 tcdrain(PortFD); // Wait until all output written to the object referred to by fd has been transmitted
425 std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait 100 ms
426
427 // read response
428 tty_ret = tty_read_expanded(PortFD, (char*)response, full_res_len, TIMEOUT_SEC, TIMEOUT_MSEC, &nbytes_read);
429 tcflush(PortFD, TCIOFLUSH); // Flush both input and output buffers
430 std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait 100 ms
431
432 if (tty_ret != TTY_OK)
433 {
434 char errorMessage[MAXRBUF];
435 tty_error_msg(tty_ret, errorMessage, MAXRBUF);
436 LOGF_ERROR("Serial read error: %s", errorMessage);
437 return false;
438 }
439 if (nbytes_read != full_res_len)
440 {
441 LOGF_ERROR("Serial read error: expected %d bytes, got %d bytes", res_len, nbytes_read);
442 return false;
443 }
444
445 checksum = 0;
446 int i;
447 for (i = 0; i < (nbytes_read - 1); i++)
448 {
449 checksum += response[i];
450 LOGF_DEBUG("RES <%02X>", response[i]);
451 }
452 LOGF_DEBUG("RES <%02X>", response[i]);
453 if ( (unsigned char)(checksum % 0xFF) != response[nbytes_read - 1])
454 {
455 LOG_ERROR("Serial read error: checksum mismatch");
456 return false;
457 }
458
459 if (res)
460 {
461 for (int i = 0; i < res_len; i++)
462 {
463 res[i] = response[3 + i];
464 }
465 }
466
467 return response[2] != 0xaa ? true : false; // return true if command was successful. 0xaa indicates failure.
468}
469
471{
472 if (!isConnected() || setupComplete == false)
473 {
474 SetTimer(100);
475 return;
476 }
477
478 Get_State();
479 SetTimer(100);
480}
481
482/*
483 Retrieve each parameter of the SV241 Pro and reflect them in the INDI properties.
484*/
485void SVBONYPowerBox::Get_State()
486{
487 unsigned char cmd = 0;
488 unsigned char res[10] = {0};
489 double value = 0;
490 double shtTemp = 0.0;
491 double shtHumidity = 0.0;
492 bool hasShtTemp = false;
493 bool hasShtHumidity = false;
494 // Read power
495 cmd = 0x02;
496 if (sendCommand(&cmd, 1, res, 4))
497 {
498 value = convert4BytesToDouble(res, 100);
499 LOGF_DEBUG("INA219 Power Value: %lf mW", value);
500 PI::PowerSensorsNP[PI::SENSOR_POWER].setValue(value / 1000.0);
502 }
503 // Read load voltage
504 cmd = 0x03;
505 if (sendCommand(&cmd, 1, res, 4))
506 {
507 value = convert4BytesToDouble(res, 100);
508 LOGF_DEBUG("INA219 Load Voltage Value: %lf V", value);
509 PI::PowerSensorsNP[PI::SENSOR_VOLTAGE].setValue(value);
511 }
512 // Read DS18B20 Temperature
513 cmd = 0x04;
514 if (sendCommand(&cmd, 1, res, 4))
515 {
516 value = std::round((convert4BytesToDouble(res, 100) - 255.5) * 100.0) / 100.0; // Adjust for offset
517 LOGF_DEBUG("DS18B20 Temperature Value: %lf C", value);
518 WeatherSVBSensorsNP[SVB_SENSOR_DS18B20_TEMP].setValue(value);
519 }
520
521 // Read SHT40 Temperature
522 cmd = 0x05;
523 if (sendCommand(&cmd, 1, res, 4))
524 {
525 value = std::round((convert4BytesToDouble(res, 100) - 254) * 10.0) / 10.0; // Adjust for offset
526 LOGF_DEBUG("SHT40 Temperature Value: %lf C", value);
527 WeatherSVBSensorsNP[SVB_SENSOR_SHT40_TEMP].setValue(value);
528 shtTemp = value;
529 hasShtTemp = true;
530 }
531 // Read SHT40 Humidity
532 cmd = 0x06;
533 if (sendCommand(&cmd, 1, res, 4))
534 {
535 value = std::round((convert4BytesToDouble(res, 100) - 254) * 10.0) / 10.0; // Adjust for offset
536 LOGF_DEBUG("SHT40 Humidity Value: %lf %", value);
537 WeatherSVBSensorsNP[SVB_SENSOR_SHT40_HUMIDITY].setValue(value);
538 shtHumidity = value;
539 hasShtHumidity = true;
540 }
541
542 // Calculate dew point from SHT40 temperature and humidity
543 if (hasShtTemp && hasShtHumidity)
544 {
545 double svp = CalculateSVP(shtTemp);
546 double vp = CalculateVP(shtHumidity, svp);
547 double dewPoint = CalculateDewPointFromVP(vp);
548 LOGF_DEBUG("Dew Point Value: %lf C", dewPoint);
549 WeatherSVBSensorsNP[SVB_SENSOR_DEW_POINT].setValue(dewPoint);
550 }
551 WeatherSVBSensorsNP.apply(); // Apply all parameter changes
552
553 // Read load Current
554 cmd = 0x07;
555 if (sendCommand(&cmd, 1, res, 4))
556 {
557 value = convert4BytesToDouble(res, 100);
558 LOGF_DEBUG("INA219 Current Value: %lf mA", value);
559 PI::PowerSensorsNP[PI::SENSOR_CURRENT].setValue(value / 1000.0);
561 }
562 /*
563 USB and Power Channels State Update
564 */
565 cmd = 0x08;
566 if (sendCommand(&cmd, 1, res, 10))
567 {
568 LOGF_DEBUG("Status: GPIO1:%02X GPIO2:%02X GPIO3:%02X GPIO4:%02X GPIO5:%02X GPIO6:%02X GPIO7:%02X pwmA:%02X pwmB:%02X pwmC:%02X",
569 res[0], res[1], res[2], res[3], res[4], res[5], res[6], res[7], res[8], res[9]);
570
571 // Power Channels State Update
572 for (int i = 0; i < 5; i++)
573 {
575 }
577
578 // USB Ports State Update
579 for (int i = 0; i < 2; i++)
580 {
581 PI::USBPortSP[i].setState(res[i + 5] ? ISS_ON : ISS_OFF);
582 }
584
585 // Regulated Output Voltage Update
586 double voltage = std::round((res[7] * 15.3 / 255.0) * 10.0) / 10.0;
587
588 if (voltage <= 0.0)
589 {
590 // When the voltage is 0V, set the channel to off.
591 // Keep the voltage of the control panel channel at the set value.
593 }
594 else
595 {
596 // When the voltage is greater than 0V, set the channel to on.
597 // Reflect the acquired voltage on the control panel.
599 VariableChannelVoltsNP[0].setValue(voltage);
600 }
603
604 // Dew State Update
605 for (int i = 0; i < 2; i++)
606 {
607 unsigned char pwmValue = std::round(100.0 * (((double)res[i + 8]) / 255.0));
608
609 if (pwmValue == 0)
610 {
611 // When the duty cycle is 0%, set the channel to off.
612 // Keep the duty cycle of the control panel channel at the set value.
614 }
615 else
616 {
617 // When the duty cycle is greater than 0%, set the channel to on.
618 // Reflect the acquired duty cycle on the control panel.
620 DewChannelDutyCycleNP[i].setValue(std::round(100.0 * (res[i + 8] / 255.0)));
621 }
622 }
625 }
626}
627
628bool SVBONYPowerBox::processButtonSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
629{
630 INDI_UNUSED(dev);
631 INDI_UNUSED(name);
632 INDI_UNUSED(states);
633 INDI_UNUSED(names);
634 INDI_UNUSED(n);
635
636 // Auto heater handling would go here if needed
637 return false;
638}
639
640// Implement PowerInterface virtual methods
641bool SVBONYPowerBox::SetPowerPort(size_t port, bool enabled)
642{
643 if (port >= 5)
644 return false;
645
646 unsigned char cmd[3];
647 cmd[0] = 0x01;
648 cmd[1] = port;
649 cmd[2] = enabled ? 0xFF : 0x00;
650
651 return sendCommand(cmd, sizeof cmd, nullptr, 2);
652}
653
654bool SVBONYPowerBox::SetDewPort(size_t port, bool enabled, double dutyCycle)
655{
656 if (port >= 2)
657 return false;
658
659 unsigned char cmd[3];
660 cmd[0] = 0x01;
661 cmd[1] = port + 8; // pwmA, pwmB are Dew ports
662 cmd[2] = enabled ? (unsigned char)std::round(255.0 * ((double)dutyCycle / 100.0)) : 0x00;
663
664 return sendCommand(cmd, sizeof cmd, nullptr, 2);
665}
666
667/*
668 enabled: true to turn on the variable port, false to turn off
669 voltage: voltage to set when enabled is true (0.0V to 15.3V)
670*/
671bool SVBONYPowerBox::SetVariablePort(size_t port, bool enabled, double voltage)
672{
673 if (port >= 1)
674 return false;
675
676 unsigned char cmd[3];
677 cmd[0] = 0x01;
678 cmd[1] = 7; // Regulated output port
679 double raw = (voltage / 15.3) * 255.0;
680 cmd[2] = enabled ? (unsigned char)(std::max(0.0, std::min(255.0, raw))) : 0x00;
681
682 return sendCommand(cmd, sizeof cmd, nullptr, 2);
683}
684
686{
687 INDI_UNUSED(enabled);
688 // Not implemented on this device
689 return false;
690}
691
692bool SVBONYPowerBox::SetAutoDewEnabled(size_t port, bool enabled)
693{
694 INDI_UNUSED(port);
695 INDI_UNUSED(enabled);
696 // No auto dew on this device
697 return false;
698}
699
701{
702 if (sendCommand((const unsigned char*)"\xFF\xFF", 2, nullptr, 2) == false)
703 return false;
704
705 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
706
707 if (sendCommand((const unsigned char*)"\xFE\xFE", 2, nullptr, 2) == false)
708 return false;
709
710 return true;
711}
712
713bool SVBONYPowerBox::SetUSBPort(size_t port, bool enabled)
714{
715 if (port > 1)
716 return false;
717
718 unsigned char cmd[3];
719 cmd[0] = 0x01;
720 cmd[1] = port + 5; // GPIO5 and GPIO6 are USB ports
721 cmd[2] = enabled ? 0xFF : 0x00;
722
723 return sendCommand(cmd, sizeof cmd, nullptr, 2);
724}
725
726double SVBONYPowerBox::convert4BytesToDouble(const unsigned char *data, double scale)
727{
728 if (data == nullptr || scale == 0.0)
729 return 0.0;
730
731 uint32_t raw = (static_cast<uint32_t>(data[0]) << 24) |
732 (static_cast<uint32_t>(data[1]) << 16) |
733 (static_cast<uint32_t>(data[2]) << 8) |
734 static_cast<uint32_t>(data[3]);
735
736 return static_cast<double>(raw) / scale;
737}
738
739double SVBONYPowerBox::CalculateSVP(double temperature)
740{
741 // 6.11 × 10^(7.5 × T / (237.7 + T))
742 return 6.11 * std::pow(10.0, 7.5 * temperature / (237.7 + temperature));
743}
744
745double SVBONYPowerBox::CalculateVP(double humidity, double svp)
746{
747 // VP = RH × SVP / 100
748 return humidity * svp / 100.0;
749}
750
751double SVBONYPowerBox::CalculateDewPointFromVP(double vp)
752{
753 double minDifference = 1e6;
754 double minTemp = -100.0;
755
756 for (double temp = -100.0; temp <= 100.0; temp += 0.01)
757 {
758 double currentSvp = CalculateSVP(temp);
759 double difference = std::fabs(vp - currentSvp);
760 if (difference < minDifference)
761 {
762 minDifference = difference;
763 minTemp = temp;
764 }
765 }
766
767 // Round to 2 decimal places
768 return std::round(minTemp * 100.0) / 100.0;
769}
double humidity
double dewPoint
double temperature
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the initial connection to the devic...
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
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 ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
void registerConnection(Connection::Interface *newConnection)
registerConnection Add new connection plugin to the existing connection pool. The connection type sha...
virtual void setConnected(bool status, IPState state=IPS_OK, const char *msg=nullptr)
Set connection switch status in the client.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
bool isSimulation() const
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 Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
INDI::PropertySwitch DewChannelsSP
bool saveConfigItems(FILE *fp)
saveConfigItems Save power properties in config file
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 ...
INDI::PropertyText PowerChannelLabelsTP
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
INDI::PropertySwitch VariableChannelsSP
INDI::PropertyText DewChannelLabelsTP
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void setLabel(const char *label)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
static void singleShot(int msec, const std::function< void()> &callback)
This static function calls a the given function after a given time interval.
virtual bool SetAutoDewEnabled(size_t port, bool enabled) override
SetAutoDewEnabled Enable/disable automatic dew control.
virtual bool SetLEDEnabled(bool enabled) override
SetLEDEnabled Enable/disable power LEDs.
virtual bool SetUSBPort(size_t port, bool enabled) override
SetUSBPort Set USB port on/off.
virtual bool SetDewPort(size_t port, bool enabled, double dutyCycle) override
SetDewPort Set DEW/Dew port duty cycle.
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual const char * getDefaultName() override
virtual bool SetVariablePort(size_t port, bool enabled, double voltage) override
SetVariablePort Set variable voltage port.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool CyclePower() override
CyclePower Cycle power to all ports.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool SetPowerPort(size_t port, bool enabled) override
SetPowerPort Set power port on/off.
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RO
Definition indiapi.h:182
@ IPS_IDLE
Definition indiapi.h:159
#define MAXINDILABEL
Definition indiapi.h:190
void tty_set_debug(int debug)
tty_set_debug Enable or disable debug which prints verbose information.
Definition indicom.c:349
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
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_read_expanded(int fd, char *buf, int nbytes, long timeout_seconds, long timeout_microseconds, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:553
int tty_nread_section_expanded(int fd, char *buf, int nsize, char stop_char, long timeout_seconds, long timeout_microseconds, int *nbytes_read)
read buffer from terminal with a delimiter
Definition indicom.c:764
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
#define INDI_UNUSED(x)
Definition indidevapi.h:132
#define MAXRBUF
Definition indidriver.c:52
#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
#define CMD_OFFSET_FRAME_HEADER
#define CMD_OFFSET_CMD
#define CMD_MAX_LEN
#define TIMEOUT_MSEC
#define TIMEOUT_SEC
#define CMD_OFFSET_DATA_LEN