45static std::unique_ptr<SVBONYPowerBox> svbonypowerbox(
new SVBONYPowerBox());
48#define CMD_OFFSET_FRAME_HEADER 0
49#define CMD_OFFSET_DATA_LEN 1
50#define CMD_OFFSET_CMD 2
53#define TIMEOUT_MSEC 500
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);
105 setupComplete =
true;
113 setupComplete =
false;
129bool SVBONYPowerBox::Handshake()
131 uint32_t SVB_capabilities =
164 ioctl(PortFD, TIOCMGET, &flags);
165 flags &= ~(TIOCM_RTS | TIOCM_DTR);
166 ioctl(PortFD, TIOCMSET, &flags);
169 bool isResetting =
true;
171 const int maxRetries = 10;
173 while (isResetting && retryCount < maxRetries)
178 std::this_thread::sleep_for(std::chrono::milliseconds(50));
187 LOGF_ERROR(
"Handshake error.(Serial read error: %s)", errorMessage);
190 std::string response(buf);
191 LOGF_DEBUG(
"Handshake response: %s", response.c_str());
192 std::this_thread::sleep_for(std::chrono::milliseconds(50));
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;
224 int enterIdelRetryCount = 10;
225 while (enterIdelRetryCount-- > 0)
227 tcflush(PortFD, TCIOFLUSH);
229 if (sendCommand((
const unsigned char*)
"\x08", 1,
nullptr, 10)) {
232 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
234 if (enterIdelRetryCount<= 0)
267 snprintf(propLabel,
MAXINDILABEL,
"DC %d",
static_cast<int>(i + 1));
278 snprintf(propLabel,
MAXINDILABEL,
"PWM %d",
static_cast<int>(i + 1));
279 snprintf(propDutyLabel,
MAXINDILABEL,
"PWM %d (%%)",
static_cast<int>(i + 1));
306 return "SVBONY PowerBox";
317 if (processButtonSwitch(dev, name, states, names, n))
322 if (strcmp(name,
"DEBUG") == 0)
324 tty_set_debug(strcmp(names[0],
"ENABLE") == 0 ?
true :
false);
362bool SVBONYPowerBox::sendCommand(
const unsigned char*
cmd,
unsigned int cmd_len,
unsigned char* res,
int res_len)
365 int nbytes_written = 0;
367 const char frame_header = 0x24;
370 int full_cmd_len = 2 + cmd_len + 1;
371 int full_res_len = 3 + res_len + 1;
372 unsigned checksum = 0;
373 unsigned char *response = (
unsigned char *)alloca(3 + res_len +
379 for (
unsigned int i = 0; i < cmd_len; i++)
384 for (
unsigned int i = 0; i < 2 + cmd_len; i++)
386 checksum += command[i];
388 command[full_cmd_len - 1] = (
unsigned char)(checksum % 0xFF);
391 switch (full_cmd_len)
394 LOGF_DEBUG(
"CMD <%02X %02X %02X %02X>", command[0], command[1], command[2], command[3]);
397 LOGF_DEBUG(
"CMD <%02X %02X %02X %02X %02X>", command[0], command[1], command[2], command[3], command[4]);
400 LOGF_DEBUG(
"CMD <%02X %02X %02X %02X %02X %02X>", command[0], command[1], command[2], command[3], command[4], command[5]);
408 tcflush(PortFD, TCIOFLUSH);
410 if ( (tty_ret =
tty_write(PortFD, (
const char*)command, full_cmd_len, &nbytes_written)) !=
TTY_OK)
415 LOGF_ERROR(
"Serial write error: %s", errorMessage);
418 if (nbytes_written != full_cmd_len)
421 LOGF_ERROR(
"Serial write error: expected %d bytes, wrote %d bytes", full_cmd_len, nbytes_written);
425 std::this_thread::sleep_for(std::chrono::milliseconds(100));
429 tcflush(PortFD, TCIOFLUSH);
430 std::this_thread::sleep_for(std::chrono::milliseconds(100));
436 LOGF_ERROR(
"Serial read error: %s", errorMessage);
439 if (nbytes_read != full_res_len)
441 LOGF_ERROR(
"Serial read error: expected %d bytes, got %d bytes", res_len, nbytes_read);
447 for (i = 0; i < (nbytes_read - 1); i++)
449 checksum += response[i];
453 if ( (
unsigned char)(checksum % 0xFF) != response[nbytes_read - 1])
455 LOG_ERROR(
"Serial read error: checksum mismatch");
461 for (
int i = 0; i < res_len; i++)
463 res[i] = response[3 + i];
467 return response[2] != 0xaa ? true :
false;
485void SVBONYPowerBox::Get_State()
487 unsigned char cmd = 0;
488 unsigned char res[10] = {0};
490 double shtTemp = 0.0;
491 double shtHumidity = 0.0;
492 bool hasShtTemp =
false;
493 bool hasShtHumidity =
false;
496 if (sendCommand(&
cmd, 1, res, 4))
498 value = convert4BytesToDouble(res, 100);
499 LOGF_DEBUG(
"INA219 Power Value: %lf mW", value);
505 if (sendCommand(&
cmd, 1, res, 4))
507 value = convert4BytesToDouble(res, 100);
508 LOGF_DEBUG(
"INA219 Load Voltage Value: %lf V", value);
514 if (sendCommand(&
cmd, 1, res, 4))
516 value = std::round((convert4BytesToDouble(res, 100) - 255.5) * 100.0) / 100.0;
517 LOGF_DEBUG(
"DS18B20 Temperature Value: %lf C", value);
518 WeatherSVBSensorsNP[SVB_SENSOR_DS18B20_TEMP].setValue(value);
523 if (sendCommand(&
cmd, 1, res, 4))
525 value = std::round((convert4BytesToDouble(res, 100) - 254) * 10.0) / 10.0;
526 LOGF_DEBUG(
"SHT40 Temperature Value: %lf C", value);
527 WeatherSVBSensorsNP[SVB_SENSOR_SHT40_TEMP].setValue(value);
533 if (sendCommand(&
cmd, 1, res, 4))
535 value = std::round((convert4BytesToDouble(res, 100) - 254) * 10.0) / 10.0;
536 LOGF_DEBUG(
"SHT40 Humidity Value: %lf %", value);
537 WeatherSVBSensorsNP[SVB_SENSOR_SHT40_HUMIDITY].setValue(value);
539 hasShtHumidity =
true;
543 if (hasShtTemp && hasShtHumidity)
545 double svp = CalculateSVP(shtTemp);
546 double vp = CalculateVP(shtHumidity, svp);
547 double dewPoint = CalculateDewPointFromVP(vp);
549 WeatherSVBSensorsNP[SVB_SENSOR_DEW_POINT].setValue(
dewPoint);
551 WeatherSVBSensorsNP.
apply();
555 if (sendCommand(&
cmd, 1, res, 4))
557 value = convert4BytesToDouble(res, 100);
558 LOGF_DEBUG(
"INA219 Current Value: %lf mA", value);
566 if (sendCommand(&
cmd, 1, res, 10))
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]);
572 for (
int i = 0; i < 5; i++)
579 for (
int i = 0; i < 2; i++)
586 double voltage = std::round((res[7] * 15.3 / 255.0) * 10.0) / 10.0;
605 for (
int i = 0; i < 2; i++)
607 unsigned char pwmValue = std::round(100.0 * (((
double)res[i + 8]) / 255.0));
628bool SVBONYPowerBox::processButtonSwitch(
const char *dev,
const char *name,
ISState *states,
char *names[],
int n)
646 unsigned char cmd[3];
649 cmd[2] = enabled ? 0xFF : 0x00;
651 return sendCommand(
cmd,
sizeof cmd,
nullptr, 2);
659 unsigned char cmd[3];
662 cmd[2] = enabled ? (
unsigned char)std::round(255.0 * ((
double)dutyCycle / 100.0)) : 0x00;
664 return sendCommand(
cmd,
sizeof cmd,
nullptr, 2);
676 unsigned char cmd[3];
679 double raw = (voltage / 15.3) * 255.0;
680 cmd[2] = enabled ? (
unsigned char)(std::max(0.0, std::min(255.0, raw))) : 0x00;
682 return sendCommand(
cmd,
sizeof cmd,
nullptr, 2);
702 if (sendCommand((
const unsigned char*)
"\xFF\xFF", 2,
nullptr, 2) ==
false)
705 std::this_thread::sleep_for(std::chrono::milliseconds(1000));
707 if (sendCommand((
const unsigned char*)
"\xFE\xFE", 2,
nullptr, 2) ==
false)
718 unsigned char cmd[3];
721 cmd[2] = enabled ? 0xFF : 0x00;
723 return sendCommand(
cmd,
sizeof cmd,
nullptr, 2);
726double SVBONYPowerBox::convert4BytesToDouble(
const unsigned char *data,
double scale)
728 if (data ==
nullptr || scale == 0.0)
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]);
736 return static_cast<double>(raw) / scale;
739double SVBONYPowerBox::CalculateSVP(
double temperature)
745double SVBONYPowerBox::CalculateVP(
double humidity,
double svp)
751double SVBONYPowerBox::CalculateDewPointFromVP(
double vp)
753 double minDifference = 1e6;
754 double minTemp = -100.0;
756 for (
double temp = -100.0; temp <= 100.0; temp += 0.01)
758 double currentSvp = CalculateSVP(temp);
759 double difference = std::fabs(vp - currentSvp);
760 if (difference < minDifference)
762 minDifference = difference;
768 return std::round(minTemp * 100.0) / 100.0;
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...
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.
@ POWER_HAS_VOLTAGE_SENSOR
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.
void tty_set_debug(int debug)
tty_set_debug Enable or disable debug which prints verbose information.
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
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
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
Implementations for common driver routines.
#define LOGF_DEBUG(fmt,...)
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
#define LOGF_ERROR(fmt,...)
Namespace to encapsulate INDI client, drivers, and mediator classes.
#define CMD_OFFSET_FRAME_HEADER
#define CMD_OFFSET_DATA_LEN