Instrument Neutral Distributed Interface INDI 2.2.5
planewave_delta.cpp
Go to the documentation of this file.
1/*
2 PlaneWave Delta Protocol
3
4 Copyright (C) 2020 Jasem Mutlaq ([email protected])
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
21
22#include "planewave_delta.h"
23#include "indicom.h"
25
26#include <cmath>
27#include <memory>
28#include <cstring>
29#include <termios.h>
30#include <unistd.h>
31#include <regex>
32#include <sys/ioctl.h>
33
34static std::unique_ptr<DeltaT> deltat(new DeltaT());
35
40{
41 setVersion(1, 1);
42}
43
45{
47
48 // Version
49 IUFillText(&InfoT[INFO_VERSION], "INFO_VERSION", "Version", "NA");
50 IUFillTextVector(&InfoTP, InfoT, 1, getDeviceName(), "INFO", "Info", MAIN_CONTROL_TAB, IP_RO, 60, IPS_IDLE);
51
52 // Reset
53 IUFillSwitch(&ForceS[FORCE_RESET], "FORCE_RESET", "Reset", ISS_OFF);
54 IUFillSwitch(&ForceS[FORCE_BOOT], "FORCE_BOOT", "Boot", ISS_OFF);
55 IUFillSwitchVector(&ForceSP, ForceS, 2, getDeviceName(), "FORCE_CONTROL", "Force", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 60,
56 IPS_IDLE);
57
58 // Temperature
59 IUFillNumber(&TemperatureN[TEMPERATURE_AMBIENT], "TEMPERATURE_AMBIENT", "Ambient (c)", "%.2f", -50, 70., 0., 0.);
60 IUFillNumber(&TemperatureN[TEMPERATURE_SECONDARY], "TEMPERATURE_SECONDARY", "Secondary (c)", "%.2f", -50, 70., 0., 0.);
61 IUFillNumber(&TemperatureN[TEMPERATURE_BACKPLATE], "TEMPERATURE_BACKPLATE", "Backplate (c)", "%.2f", -50, 70., 0., 0.);
62 IUFillNumberVector(&TemperatureNP, TemperatureN, 3, getDeviceName(), "DELTA_TEMPERATURE", "Temperature",
64
65 serialConnection = new Connection::Serial(this);
66 serialConnection->registerHandshake([&]()
67 {
68 return Handshake();
69 });
71 registerConnection(serialConnection);
72
74
77
78 return true;
79}
80
85{
87
88 if (isConnected())
89 {
90 initializeHeaters();
91
92 defineProperty(&InfoTP);
93 defineProperty(&TemperatureNP);
94 defineProperty(&ForceSP);
95
96 for (auto &oneSP : HeaterControlSP)
97 defineProperty(oneSP.get());
98
99 for (auto &oneNP : HeaterParamNP)
100 defineProperty(oneNP.get());
101
102 for (auto &oneNP : HeaterMonitorNP)
103 defineProperty(oneNP.get());
104
105 }
106 else
107 {
108 deleteProperty(InfoTP.name);
109 deleteProperty(TemperatureNP.name);
110 deleteProperty(ForceSP.name);
111
112 for (auto &oneSP : HeaterControlSP)
113 deleteProperty(oneSP->name);
114
115 for (auto &oneNP : HeaterParamNP)
116 deleteProperty(oneNP->name);
117
118 for (auto &oneNP : HeaterMonitorNP)
119 deleteProperty(oneNP->name);
120 }
121
122 return true;
123}
124
129{
130 std::string version;
131
132 PortFD = serialConnection->getPortFD();
133
134 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
135
136 cmd[0] = DRIVER_SOM;
137 cmd[1] = 0x03;
138 cmd[2] = DEVICE_PC;
139 cmd[3] = DEVICE_DELTA;
140 cmd[4] = CMD_GET_VERSION;
141 cmd[5] = calculateCheckSum(cmd, 6);
142
143 if (!sendCommand(cmd, res, 6, 10))
144 return false;
145
146 const uint8_t *ures = reinterpret_cast<const uint8_t *>(res);
147 uint16_t bld = ures[7] << 8 | ures[8];
148
149 version = std::to_string(ures[5]) + "." +
150 std::to_string(ures[6]) +
151 " (" + std::to_string(bld) + ")";
152
153 IUSaveText(&InfoT[0], version.c_str());
154
155 LOGF_INFO("Detected version %s", version.c_str());
156
157 return true;
158}
159
164{
165 return "PlaneWave DeltaT";
166}
167
171const char *DeltaT::getHeaterName(int index)
172{
173 switch (index)
174 {
175 case 0:
176 return "Primary Backplate Heater";
177 case 1:
178 return "Secondary Mirror Heater";
179 case 2:
180 return "Terietary Heater";
181 default:
182 return "Uknkown heater";
183 }
184}
185
189bool DeltaT::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
190{
191 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
192 {
193 // Reset/Boot
194 if (!strcmp(ForceSP.name, name))
195 {
196 IUUpdateSwitch(&ForceSP, states, names, n);
197 bool rc = false;
198 if (!strcmp(IUFindOnSwitchName(states, names, n), ForceS[FORCE_RESET].name))
199 rc = forceReset();
200 else if (!strcmp(IUFindOnSwitchName(states, names, n), ForceS[FORCE_BOOT].name))
201 rc = forceBoot();
202
203 IUResetSwitch(&ForceSP);
204 ForceSP.s = rc ? IPS_OK : IPS_ALERT;
205 IDSetSwitch(&ForceSP, nullptr);
206 return true;
207 }
208 // Heater Control
209 else
210 {
211 for (uint8_t i = 0; i < HeaterControlSP.size(); i++)
212 {
213 if (!strcmp(HeaterControlSP[i]->name, name))
214 {
215 IUUpdateSwitch(HeaterControlSP[i].get(), states, names, n);
216
217 int index = IUFindOnSwitchIndex(HeaterControlSP[i].get());
218 switch (index)
219 {
220 case HEATER_OFF:
221 setHeaterEnabled(i, false);
222 LOGF_INFO("%s is off.", getHeaterName(i));
223 break;
224
225 case HEATER_ON:
226 setHeaterEnabled(i, true);
227 LOGF_INFO("%s is on.", getHeaterName(i));
228 break;
229
230 case HEATER_CONTROL:
231 LOGF_INFO("%s automatic control is enabled. Temperature delta will kept at %.2f C", getHeaterName(i),
232 HeaterParamNP[i]->np[PARAM_CONTROL].value);
233 break;
234
235 case HEATER_THRESHOLD:
236 LOGF_INFO("%s threshold control is enabled. When ambient temperature falls below %.2f C, heater would be turned on at %.f%% power.",
237 getHeaterName(i),
238 HeaterParamNP[i]->np[PARAM_THRESHOLD].value,
239 HeaterParamNP[i]->np[PARAM_DUTY].value);
240 break;
241 }
242
243 HeaterControlSP[i]->s = IPS_OK;
244 IDSetSwitch(HeaterControlSP[i].get(), nullptr);
245 return true;
246 }
247 }
248 }
249
250 }
251
252 return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
253}
254
258bool DeltaT::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
259{
260 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
261 {
262 for (uint8_t i = 0; i < HeaterParamNP.size(); i++)
263 {
264 if (!strcmp(HeaterParamNP[i]->name, name))
265 {
266 IUUpdateNumber(HeaterParamNP[i].get(), values, names, n);
267 HeaterParamNP[i]->s = IPS_OK;
268 IDSetNumber(HeaterParamNP[i].get(), nullptr);
269
270 // Update existing on heater
271 if (IUFindOnSwitchIndex(HeaterControlSP[i].get()) == HEATER_ON)
272 setHeaterEnabled(i, true);
273
274 saveConfig(true, HeaterParamNP[i]->name);
275 return true;
276 }
277 }
278 }
279
280 return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
281}
282
283
288{
289 if (!isConnected())
290 return;
291
292 for (uint8_t i = 0 ; i < HeaterControlSP.size(); i++)
293 {
294 if (readReport(i))
295 {
296 //IDSetSwitch(HeaterControlSP[i].get(), nullptr);
297 IDSetNumber(HeaterMonitorNP[i].get(), nullptr);
298 }
299 }
300
301 if (readTemperature())
302 {
303 // Send temperature is above threshold
304 bool aboveThreshold = false;
305 for (int i = 0; i < TemperatureNP.nnp; i++)
306 {
307 if (std::fabs(TemperatureN[i].value - m_LastTemperature[i]) > TEMPERATURE_REPORT_THRESHOLD)
308 {
309 aboveThreshold = true;
310 m_LastTemperature[i] = TemperatureN[i].value;
311 }
312 }
313
314 if (aboveThreshold)
315 IDSetNumber(&TemperatureNP, nullptr);
316 }
317
318 for (uint8_t i = 0 ; i < HeaterControlSP.size(); i++)
319 {
320 const int controlMode = IUFindOnSwitchIndex(HeaterControlSP[i].get());
321 switch (controlMode)
322 {
323 // Manual controls
324 case HEATER_OFF:
325 case HEATER_ON:
326 break;
327
328 case HEATER_CONTROL:
329 {
330 // Current Surface Temperature
331 double surfaceTemperature = TemperatureN[i == 0 ? TEMPERATURE_BACKPLATE : TEMPERATURE_SECONDARY].value;
332 // Temperature Temperature
333 double targetTemperature = HeaterParamNP[i]->np[PARAM_CONTROL].value + TemperatureN[TEMPERATURE_AMBIENT].value;
334 // Get target duty
335 double targetDuty = m_Controllers[i]->calculate(targetTemperature, surfaceTemperature);
336 // Limit to 0 - 100
337 double heaterDuty = std::max(0., std::min(100., targetDuty));
338 // Only update if there is a difference
339 if (std::abs(heaterDuty - HeaterMonitorNP[i]->np[MONITOR_DUTY].value) > 0.001 ||
340 std::abs(HeaterParamNP[i]->np[PARAM_PERIOD].value - HeaterMonitorNP[i]->np[MONITOR_PERIOD].value) > 0)
341 setHeaterParam(i, HeaterParamNP[i]->np[PARAM_PERIOD].value, heaterDuty);
342 }
343 break;
344
345 case HEATER_THRESHOLD:
346 {
347 // If ambient is within threshold, don't do anything
348 // so that we don't over-control
349 if (std::fabs(TemperatureN[TEMPERATURE_AMBIENT].value - HeaterParamNP[i]->np[PARAM_THRESHOLD].value)
350 < TEMPERATURE_CONTROL_THRESHOLD)
351 break;
352
353 // If heater is off, check if we need to turn it on.
354 if (HeaterMonitorNP[i]->s == IPS_IDLE
355 && (TemperatureN[TEMPERATURE_AMBIENT].value < HeaterParamNP[i]->np[PARAM_THRESHOLD].value))
356 setHeaterEnabled(i, true);
357 else if (HeaterMonitorNP[i]->s == IPS_BUSY
358 && (TemperatureN[TEMPERATURE_AMBIENT].value > HeaterParamNP[i]->np[PARAM_THRESHOLD].value))
359 setHeaterEnabled(i, false);
360 break;
361 }
362 }
363 }
364
366}
367
368
373{
375 for (uint8_t i = 0; i < HeaterParamNP.size(); i++)
376 IUSaveConfigNumber(fp, HeaterMonitorNP[i].get());
377 return true;
378}
379
383bool DeltaT::readReport(uint8_t index)
384{
385 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
386
387 cmd[0] = DRIVER_SOM;
388 cmd[1] = 0x04;
389 cmd[2] = DEVICE_PC;
390 cmd[3] = DEVICE_DELTA;
391 cmd[4] = COH_REPORT;
392 cmd[5] = index;
393 cmd[6] = calculateCheckSum(cmd, 7);
394
395 if (!sendCommand(cmd, res, 7, 19))
396 return false;
397
398 if (static_cast<uint8_t>(res[5]) != 0x80)
399 return false;
400
401 const uint8_t *ures = reinterpret_cast<const uint8_t *>(res);
402 HeaterReport report;
403
404 report.StateUB = ures[6];
405 report.ModeUB = ures[7];
406 report.SetPointUW = ures[9] << 8 | ures[8];
407 report.TempHtrIdUB = ures[10];
408 report.TempHtrUW = ures[12] << 8 | ures[11];
409 report.TempAmbUW = ures[14] << 8 | ures[13];
410 report.PeriodUW = ures[16] << 8 | ures[15];
411 report.DutyCycleUB = ures[17];
412
413 //bool stateChanged = false, paramChanged = false;
414
415 // bool wasOn = IUFindOnSwitchIndex(HeaterControlSP[index].get()) == HEATER_ON;
416
417 // IUResetSwitch(HeaterControlSP[index].get());
418 // HeaterControlSP[index]->sp[HEATER_ON].s = report.StateUB == 1 ? ISS_ON : ISS_OFF;
419 // HeaterControlSP[index]->sp[HEATER_OFF].s = report.StateUB == 1 ? ISS_OFF : ISS_ON;
420
421 // bool isOn = IUFindOnSwitchIndex(HeaterControlSP[index].get()) == HEATER_ON;
422
423 // stateChanged = wasOn != isOn;
424
425 double currentPeriod = HeaterMonitorNP[index]->np[MONITOR_PERIOD].value;
426 double currentDuty = HeaterMonitorNP[index]->np[MONITOR_DUTY].value;
427 IPState currentState = HeaterMonitorNP[index]->s;
428
429 HeaterMonitorNP[index]->np[MONITOR_PERIOD].value = report.PeriodUW / 10.0;
430 HeaterMonitorNP[index]->np[MONITOR_DUTY].value = report.DutyCycleUB;
431 HeaterMonitorNP[index]->s = (report.StateUB == 1) ? IPS_BUSY : IPS_IDLE;
432
433 bool paramChanged = std::fabs(currentPeriod - HeaterMonitorNP[index]->np[MONITOR_PERIOD].value) > 0.1 ||
434 std::fabs(currentDuty - HeaterMonitorNP[index]->np[MONITOR_DUTY].value) > 0 ||
435 currentState != HeaterMonitorNP[index]->s;
436
437 // Return true if only something changed.
438 return (paramChanged);
439}
440
444bool DeltaT::initializeHeaters()
445{
446 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
447
448 cmd[0] = DRIVER_SOM;
449 cmd[1] = 0x03;
450 cmd[2] = DEVICE_PC;
451 cmd[3] = DEVICE_DELTA;
452 cmd[4] = COH_NUMHEATERS;
453 cmd[5] = calculateCheckSum(cmd, 6);
454
455 if (!sendCommand(cmd, res, 6, 7))
456 return false;
457
458 uint8_t nHeaters = res[5];
459
460 LOGF_INFO("Detected %d heaters", nHeaters);
461
462 // Create heater controls
463 for (uint8_t i = 0; i < nHeaters; i++)
464 {
465 // TODO fine tune the params
466 std::unique_ptr<PID> Controller;
467 Controller.reset(new PID(1, 100, 0, 200, 0, 0.75));
468 m_Controllers.push_back(std::move(Controller));
469
470 std::unique_ptr<ISwitchVectorProperty> ControlSP;
471 ControlSP.reset(new ISwitchVectorProperty);
472 std::unique_ptr<ISwitch[]> ControlS;
473 ControlS.reset(new ISwitch[4]);
474
475 char switchName[MAXINDINAME] = {0}, groupLabel[MAXINDINAME] = {0};
476 snprintf(switchName, MAXINDINAME, "HEATER_%d", i + 1);
477 snprintf(groupLabel, MAXINDINAME, "%s", getHeaterName(i));
478 IUFillSwitch(&ControlS[HEATER_OFF], "HEATER_OFF", "Off", ISS_ON);
479 IUFillSwitch(&ControlS[HEATER_ON], "HEATER_ON", "On", ISS_OFF);
480 IUFillSwitch(&ControlS[HEATER_CONTROL], "HEATER_CONTROL", "Control", ISS_OFF);
481 IUFillSwitch(&ControlS[HEATER_THRESHOLD], "HEATER_THRESHOLD", "Theshold", ISS_OFF);
482 IUFillSwitchVector(ControlSP.get(), ControlS.get(), 4, getDeviceName(), switchName, "Heater",
483 groupLabel, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
484
485 HeaterControlSP.push_back(std::move(ControlSP));
486 HeaterControlS.push_back(std::move(ControlS));
487 }
488
489 // Create heater parameters
490 for (uint8_t i = 0; i < nHeaters; i++)
491 {
492
493 // Control Parameters
494 std::unique_ptr<INumberVectorProperty> ControlNP;
495 ControlNP.reset(new INumberVectorProperty);
496 std::unique_ptr<INumber[]> ControlN;
497 ControlN.reset(new INumber[4]);
498
499 char numberName[MAXINDINAME] = {0}, groupLabel[MAXINDINAME] = {0};
500 snprintf(numberName, MAXINDINAME, "PARAM_%d", i + 1);
501 snprintf(groupLabel, MAXINDINAME, "%s", getHeaterName(i));
502 IUFillNumber(&ControlN[PARAM_PERIOD], "PARAM_PERIOD", "Period", "%.1f", 0.1, 60, 1, 1);
503 IUFillNumber(&ControlN[PARAM_DUTY], "PARAM_DUTY", "Duty", "%.f", 1, 100, 5, 1);
504 IUFillNumber(&ControlN[PARAM_CONTROL], "PARAM_CONTROL", "ΔAmbient =", "%.1f", 0, 100, 5, 2.5);
505 IUFillNumber(&ControlN[PARAM_THRESHOLD], "PARAM_THRESHOLD", "Ambient less", "%.1f", -50, 50, 5, 2.5);
506 IUFillNumberVector(ControlNP.get(), ControlN.get(), 4, getDeviceName(), numberName, "Params",
507 groupLabel, IP_RW, 60, IPS_IDLE);
508
509 HeaterParamNP.push_back(std::move(ControlNP));
510 HeaterParamN.push_back(std::move(ControlN));
511
512
513 // Monitoring
514 std::unique_ptr<INumberVectorProperty> MonitorNP;
515 MonitorNP.reset(new INumberVectorProperty);
516 std::unique_ptr<INumber[]> MonitorN;
517 MonitorN.reset(new INumber[2]);
518
519 snprintf(numberName, MAXINDINAME, "MONITOR_%d", i + 1);
520 snprintf(groupLabel, MAXINDINAME, "%s", getHeaterName(i));
521 IUFillNumber(&MonitorN[MONITOR_PERIOD], "MONITOR_PERIOD", "Period", "%.1f", 0.1, 60, 1, 1);
522 IUFillNumber(&MonitorN[MONITOR_DUTY], "MONITOR_DUTY", "Duty", "%.f", 1, 100, 5, 1);
523 IUFillNumberVector(MonitorNP.get(), MonitorN.get(), 2, getDeviceName(), numberName, "Monitor",
524 groupLabel, IP_RO, 60, IPS_IDLE);
525
526 HeaterMonitorNP.push_back(std::move(MonitorNP));
527 HeaterMonitorN.push_back(std::move(MonitorN));
528 }
529
530 return true;
531}
532
536bool DeltaT::setHeaterEnabled(uint8_t index, bool enabled)
537{
538 if (!enabled)
539 {
540 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
541
542 cmd[0] = DRIVER_SOM;
543 cmd[1] = 0x04;
544 cmd[2] = DEVICE_PC;
545 cmd[3] = DEVICE_DELTA;
546 cmd[4] = COH_OFF;
547 cmd[5] = index;
548 cmd[6] = calculateCheckSum(cmd, 7);
549
550 if (!sendCommand(cmd, res, 7, 7))
551 return false;
552
553 // 0x80 is OK
554 return (static_cast<uint8_t>(res[5]) == 0x80);
555 }
556 else
557 {
558 return setHeaterParam(index, HeaterParamNP[index]->np[PARAM_PERIOD].value, HeaterParamNP[index]->np[PARAM_DUTY].value);
559 }
560}
561
565bool DeltaT::setHeaterParam(uint8_t index, double period, double duty)
566{
567 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
568
569 uint16_t seconds = period * 10;
570
571 cmd[0] = DRIVER_SOM;
572 cmd[1] = 0x07;
573 cmd[2] = DEVICE_PC;
574 cmd[3] = DEVICE_DELTA;
575 cmd[4] = COH_ON_MANUAL;
576 cmd[5] = index;
577 // LSB
578 cmd[6] = seconds & 0xFF;
579 // MSB
580 cmd[7] = (seconds >> 8) & 0xFF;
581 cmd[8] = static_cast<uint8_t>(duty);
582 cmd[9] = calculateCheckSum(cmd, 10);
583
584 if (!sendCommand(cmd, res, 10, 7))
585 return false;
586
587 // 0x80 is OK
588 return (static_cast<uint8_t>(res[5]) == 0x80);
589}
590
594bool DeltaT::forceBoot()
595{
596 char cmd[DRIVER_LEN] = {0};
597 cmd[0] = DRIVER_SOM;
598 cmd[1] = 0x03;
599 cmd[2] = DEVICE_PC;
600 cmd[3] = DEVICE_DELTA;
601 cmd[4] = CMD_FORCE_BOOT;
602 cmd[5] = calculateCheckSum(cmd, 6);
603 return sendCommand(cmd, nullptr, 6, 0);
604}
605
609bool DeltaT::forceReset()
610{
611 char cmd[DRIVER_LEN] = {0};
612 cmd[0] = DRIVER_SOM;
613 cmd[1] = 0x03;
614 cmd[2] = DEVICE_PC;
615 cmd[3] = DEVICE_DELTA;
616 cmd[4] = CMD_FORCE_RESET;
617 cmd[5] = calculateCheckSum(cmd, 6);
618 return sendCommand(cmd, nullptr, 6, 0);
619}
620
624bool DeltaT::readTemperature()
625{
626 char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
627
628 for (uint8_t i = 0; i < 3; i++)
629 {
630 cmd[0] = DRIVER_SOM;
631 cmd[1] = 0x04;
632 cmd[2] = DEVICE_PC;
633 cmd[3] = DEVICE_DELTA;
634 cmd[4] = TEMP_GET;
635 cmd[5] = i + 1;
636 cmd[6] = calculateCheckSum(cmd, 7);
637
638 if (!sendCommand(cmd, res, 7, 8))
639 return false;
640
641 double newTemperature = calculateTemperature(res[5], res[6]);
642 // Temperature fluctuates a lot so let's change it only when the delta > 0.1
643 if (std::abs(TemperatureN[i].value - newTemperature) > TEMPERATURE_CONTROL_THRESHOLD)
644 TemperatureN[i].value = newTemperature;
645
646 }
647
648 return true;
649}
650
654double DeltaT::calculateTemperature(uint8_t lsb, uint8_t msb)
655{
656 if (lsb == 0x7F && msb == 0x7F)
657 return -100;
658
659 int raw_temperature = lsb << 8 | msb;
660 if (raw_temperature & 0x8000)
661 raw_temperature = raw_temperature - 0x10000;
662
663 return raw_temperature / 16.0;
664}
665
669bool DeltaT::sendCommand(const char * cmd, char * res, uint32_t cmd_len, uint32_t res_len)
670{
671 int nbytes_written = 0, nbytes_read = 0, rc = 0;
672
673 for (int j = 0; j < 3; j++)
674 {
675 char hex_cmd[DRIVER_LEN * 3] = {0};
676 hexDump(hex_cmd, cmd, cmd_len);
677 LOGF_DEBUG("CMD <%s>", hex_cmd);
678 rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
679
680 if (rc != TTY_OK)
681 {
682 char errstr[MAXRBUF] = {0};
683 tty_error_msg(rc, errstr, MAXRBUF);
684 LOGF_ERROR("Serial write error: %s.", errstr);
685 return false;
686 }
687
688 if (res == nullptr || res_len == 0)
689 return true;
690
691 // Next read the response from DeltaT
692 rc = tty_read(PortFD, res, res_len, DRIVER_TIMEOUT, &nbytes_read);
693
694 if (rc == TTY_OK)
695 break;
696
697 usleep(100000);
698 }
699
700 if (rc != TTY_OK)
701 {
702 char errstr[MAXRBUF] = {0};
703 tty_error_msg(rc, errstr, MAXRBUF);
704 LOGF_ERROR("Serial read error: %s.", errstr);
705 return false;
706 }
707
708 char hex_res[DRIVER_LEN * 3] = {0};
709 hexDump(hex_res, res, res_len);
710 LOGF_DEBUG("RES <%s>", hex_res);
711
712 // Compare as unsigned bytes: plain char is signed on x86 but unsigned on ARM
713 if (res_len > 0 && calculateCheckSum(res, res_len) != static_cast<uint8_t>(res[res_len - 1]))
714 {
715 LOG_ERROR("Invalid checksum!");
716 return false;
717 }
718
719 return true;
720}
721
725void DeltaT::hexDump(char * buf, const char * data, uint32_t size)
726{
727 for (uint32_t i = 0; i < size; i++)
728 sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
729
730 if (size > 0)
731 buf[3 * size - 1] = '\0';
732}
733
737std::vector<std::string> DeltaT::split(const std::string &input, const std::string &regex)
738{
739 // passing -1 as the submatch index parameter performs splitting
740 std::regex re(regex);
741 std::sregex_token_iterator
742 first{input.begin(), input.end(), re, -1},
743 last;
744 return {first, last};
745}
746
750template <typename T>
751std::string DeltaT::to_string(const T a_value, const int n)
752{
753 std::ostringstream out;
754 out.precision(n);
755 out << std::fixed << a_value;
756 return out.str();
757}
758
762uint8_t DeltaT::calculateCheckSum(const char *cmd, uint32_t len)
763{
764 int32_t sum = 0;
765 for (uint32_t i = 1; i < len - 1; i++)
766 sum += cmd[i];
767 return (-sum & 0xFF);
768}
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...
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool Handshake()
const char * getDefaultName() override
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
bool isConnected() const
const char * getDeviceName() const
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
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...
void setDefaultPollingPeriod(uint32_t msec)
setDefaultPollingPeriod Change the default polling period to call TimerHit() function in the driver.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
virtual bool initProperties()
Initialize properties initial state and value. The child class must implement this function.
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.
Definition pid.h:29
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_ATMOST1
Definition indiapi.h:172
#define MAXINDINAME
Definition indiapi.h:189
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition indicom.c:548
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
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 indidevapi.c:490
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition indidevapi.c:337
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
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 indidevapi.c:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition indidevapi.c:23
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 indidevapi.c:376
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 indidevapi.c:416
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 indidevapi.c:398
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 indidevapi.c:453
const char * IUFindOnSwitchName(ISState *states, char *names[], int n)
Returns the name of the first ON switch it finds in the supplied arguments.
Definition indidevapi.c:355
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
#define MAXRBUF
Definition indidriver.c:52
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#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
uint8_t cmd[4]
Definition pwc-ioctl.h:2
#define currentDuty
Definition robofocus.cpp:36
One number descriptor.
One switch descriptor.
Number vector property descriptor.
Definition indiapi.h:317
char name[MAXINDINAME]
Definition indiapi.h:321
Switch vector property descriptor.
Definition indiapi.h:365
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248