Instrument Neutral Distributed Interface INDI  2.0.2
planewave_delta.cpp
Go to the documentation of this file.
1 /*
2  PlaneWave Delta Protocol
3 
4  Copyright (C) 2020 Jasem Mutlaq (mutlaqja@ikarustech.com)
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 
34 static 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  uint16_t bld = res[7] << 8 | res[8];
147 
148  version = std::to_string(res[5]) + "." +
149  std::to_string(res[6]) +
150  " (" + std::to_string(bld) + ")";
151 
152  IUSaveText(&InfoT[0], version.c_str());
153 
154  LOGF_INFO("Detected version %s", version.c_str());
155 
156  return true;
157 }
158 
163 {
164  return "PlaneWave DeltaT";
165 }
166 
170 const char *DeltaT::getHeaterName(int index)
171 {
172  switch (index)
173  {
174  case 0:
175  return "Primary Backplate Heater";
176  case 1:
177  return "Secondary Mirror Heater";
178  case 2:
179  return "Terietary Heater";
180  default:
181  return "Uknkown heater";
182  }
183 }
184 
188 bool DeltaT::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
189 {
190  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
191  {
192  // Reset/Boot
193  if (!strcmp(ForceSP.name, name))
194  {
195  IUUpdateSwitch(&ForceSP, states, names, n);
196  bool rc = false;
197  if (!strcmp(IUFindOnSwitchName(states, names, n), ForceS[FORCE_RESET].name))
198  rc = forceReset();
199  else if (!strcmp(IUFindOnSwitchName(states, names, n), ForceS[FORCE_BOOT].name))
200  rc = forceBoot();
201 
202  IUResetSwitch(&ForceSP);
203  ForceSP.s = rc ? IPS_OK : IPS_ALERT;
204  IDSetSwitch(&ForceSP, nullptr);
205  return true;
206  }
207  // Heater Control
208  else
209  {
210  for (uint8_t i = 0; i < HeaterControlSP.size(); i++)
211  {
212  if (!strcmp(HeaterControlSP[i]->name, name))
213  {
214  IUUpdateSwitch(HeaterControlSP[i].get(), states, names, n);
215 
216  int index = IUFindOnSwitchIndex(HeaterControlSP[i].get());
217  switch (index)
218  {
219  case HEATER_OFF:
220  setHeaterEnabled(i, false);
221  LOGF_INFO("%s is off.", getHeaterName(i));
222  break;
223 
224  case HEATER_ON:
225  setHeaterEnabled(i, true);
226  LOGF_INFO("%s is on.", getHeaterName(i));
227  break;
228 
229  case HEATER_CONTROL:
230  LOGF_INFO("%s automatic control is enabled. Temperature delta will kept at %.2f C", getHeaterName(i),
231  HeaterParamNP[i]->np[PARAM_CONTROL].value);
232  break;
233 
234  case HEATER_THRESHOLD:
235  LOGF_INFO("%s threshold control is enabled. When ambient temperature falls below %.2f C, heater would be turned on at %.f%% power.",
236  getHeaterName(i),
237  HeaterParamNP[i]->np[PARAM_THRESHOLD].value,
238  HeaterParamNP[i]->np[PARAM_DUTY].value);
239  break;
240  }
241 
242  HeaterControlSP[i]->s = IPS_OK;
243  IDSetSwitch(HeaterControlSP[i].get(), nullptr);
244  return true;
245  }
246  }
247  }
248 
249  }
250 
251  return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
252 }
253 
257 bool DeltaT::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
258 {
259  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
260  {
261  for (uint8_t i = 0; i < HeaterParamNP.size(); i++)
262  {
263  if (!strcmp(HeaterParamNP[i]->name, name))
264  {
265  IUUpdateNumber(HeaterParamNP[i].get(), values, names, n);
266  HeaterParamNP[i]->s = IPS_OK;
267  IDSetNumber(HeaterParamNP[i].get(), nullptr);
268 
269  // Update existing on heater
270  if (IUFindOnSwitchIndex(HeaterControlSP[i].get()) == HEATER_ON)
271  setHeaterEnabled(i, true);
272 
273  saveConfig(true, HeaterParamNP[i]->name);
274  return true;
275  }
276  }
277  }
278 
279  return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
280 }
281 
282 
287 {
288  if (!isConnected())
289  return;
290 
291  for (uint8_t i = 0 ; i < HeaterControlSP.size(); i++)
292  {
293  if (readReport(i))
294  {
295  //IDSetSwitch(HeaterControlSP[i].get(), nullptr);
296  IDSetNumber(HeaterMonitorNP[i].get(), nullptr);
297  }
298  }
299 
300  if (readTemperature())
301  {
302  // Send temperature is above threshold
303  bool aboveThreshold = false;
304  for (int i = 0; i < TemperatureNP.nnp; i++)
305  {
306  if (std::fabs(TemperatureN[i].value - m_LastTemperature[i]) > TEMPERATURE_REPORT_THRESHOLD)
307  {
308  aboveThreshold = true;
309  m_LastTemperature[i] = TemperatureN[i].value;
310  }
311  }
312 
313  if (aboveThreshold)
314  IDSetNumber(&TemperatureNP, nullptr);
315  }
316 
317  for (uint8_t i = 0 ; i < HeaterControlSP.size(); i++)
318  {
319  const int controlMode = IUFindOnSwitchIndex(HeaterControlSP[i].get());
320  switch (controlMode)
321  {
322  // Manual controls
323  case HEATER_OFF:
324  case HEATER_ON:
325  break;
326 
327  case HEATER_CONTROL:
328  {
329  // Current Surface Temperature
330  double surfaceTemperature = TemperatureN[i == 0 ? TEMPERATURE_BACKPLATE : TEMPERATURE_SECONDARY].value;
331  // Temperature Temperature
332  double targetTemperature = HeaterParamNP[i]->np[PARAM_CONTROL].value + TemperatureN[TEMPERATURE_AMBIENT].value;
333  // Get target duty
334  double targetDuty = m_Controllers[i]->calculate(targetTemperature, surfaceTemperature);
335  // Limit to 0 - 100
336  double heaterDuty = std::max(0., std::min(100., targetDuty));
337  // Only update if there is a difference
338  if (std::abs(heaterDuty - HeaterMonitorNP[i]->np[MONITOR_DUTY].value) > 0.001 ||
339  std::abs(HeaterParamNP[i]->np[PARAM_PERIOD].value - HeaterMonitorNP[i]->np[MONITOR_PERIOD].value) > 0)
340  setHeaterParam(i, HeaterParamNP[i]->np[PARAM_PERIOD].value, heaterDuty);
341  }
342  break;
343 
344  case HEATER_THRESHOLD:
345  {
346  // If ambient is within threshold, don't do anything
347  // so that we don't over-control
348  if (std::fabs(TemperatureN[TEMPERATURE_AMBIENT].value - HeaterParamNP[i]->np[PARAM_THRESHOLD].value)
349  < TEMPERATURE_CONTROL_THRESHOLD)
350  break;
351 
352  // If heater is off, check if we need to turn it on.
353  if (HeaterMonitorNP[i]->s == IPS_IDLE
354  && (TemperatureN[TEMPERATURE_AMBIENT].value < HeaterParamNP[i]->np[PARAM_THRESHOLD].value))
355  setHeaterEnabled(i, true);
356  else if (HeaterMonitorNP[i]->s == IPS_BUSY
357  && (TemperatureN[TEMPERATURE_AMBIENT].value > HeaterParamNP[i]->np[PARAM_THRESHOLD].value))
358  setHeaterEnabled(i, false);
359  break;
360  }
361  }
362  }
363 
365 }
366 
367 
371 bool DeltaT::saveConfigItems(FILE * fp)
372 {
374  for (uint8_t i = 0; i < HeaterParamNP.size(); i++)
375  IUSaveConfigNumber(fp, HeaterMonitorNP[i].get());
376  return true;
377 }
378 
382 bool DeltaT::readReport(uint8_t index)
383 {
384  char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
385 
386  cmd[0] = DRIVER_SOM;
387  cmd[1] = 0x04;
388  cmd[2] = DEVICE_PC;
389  cmd[3] = DEVICE_DELTA;
390  cmd[4] = COH_REPORT;
391  cmd[5] = index;
392  cmd[6] = calculateCheckSum(cmd, 7);
393 
394  if (!sendCommand(cmd, res, 7, 19))
395  return false;
396 
397  if (static_cast<uint8_t>(res[5]) != 0x80)
398  return false;
399 
400  HeaterReport report;
401 
402  report.StateUB = res[6];
403  report.ModeUB = res[7];
404  report.SetPointUW = res[9] << 8 | res[8];
405  report.TempHtrIdUB = res[10];
406  report.TempHtrUW = res[12] << 8 | res[11];
407  report.TempAmbUW = res[14] << 8 | res[13];
408  report.PeriodUW = res[16] << 8 | res[15];
409  report.DutyCycleUB = res[17];
410 
411  //bool stateChanged = false, paramChanged = false;
412 
413  // bool wasOn = IUFindOnSwitchIndex(HeaterControlSP[index].get()) == HEATER_ON;
414 
415  // IUResetSwitch(HeaterControlSP[index].get());
416  // HeaterControlSP[index]->sp[HEATER_ON].s = report.StateUB == 1 ? ISS_ON : ISS_OFF;
417  // HeaterControlSP[index]->sp[HEATER_OFF].s = report.StateUB == 1 ? ISS_OFF : ISS_ON;
418 
419  // bool isOn = IUFindOnSwitchIndex(HeaterControlSP[index].get()) == HEATER_ON;
420 
421  // stateChanged = wasOn != isOn;
422 
423  double currentPeriod = HeaterMonitorNP[index]->np[MONITOR_PERIOD].value;
424  double currentDuty = HeaterMonitorNP[index]->np[MONITOR_DUTY].value;
425  IPState currentState = HeaterMonitorNP[index]->s;
426 
427  HeaterMonitorNP[index]->np[MONITOR_PERIOD].value = report.PeriodUW / 10.0;
428  HeaterMonitorNP[index]->np[MONITOR_DUTY].value = report.DutyCycleUB;
429  HeaterMonitorNP[index]->s = (report.StateUB == 1) ? IPS_BUSY : IPS_IDLE;
430 
431  bool paramChanged = std::fabs(currentPeriod - HeaterMonitorNP[index]->np[MONITOR_PERIOD].value) > 0.1 ||
432  std::fabs(currentDuty - HeaterMonitorNP[index]->np[MONITOR_DUTY].value) > 0 ||
433  currentState != HeaterMonitorNP[index]->s;
434 
435  // Return true if only something changed.
436  return (paramChanged);
437 }
438 
442 bool DeltaT::initializeHeaters()
443 {
444  char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
445 
446  cmd[0] = DRIVER_SOM;
447  cmd[1] = 0x03;
448  cmd[2] = DEVICE_PC;
449  cmd[3] = DEVICE_DELTA;
450  cmd[4] = COH_NUMHEATERS;
451  cmd[5] = calculateCheckSum(cmd, 6);
452 
453  if (!sendCommand(cmd, res, 6, 7))
454  return false;
455 
456  uint8_t nHeaters = res[5];
457 
458  LOGF_INFO("Detected %d heaters", nHeaters);
459 
460  // Create heater controls
461  for (uint8_t i = 0; i < nHeaters; i++)
462  {
463  // TODO fine tune the params
464  std::unique_ptr<PID> Controller;
465  Controller.reset(new PID(1, 100, 0, 200, 0, 0.75));
466  m_Controllers.push_back(std::move(Controller));
467 
468  std::unique_ptr<ISwitchVectorProperty> ControlSP;
469  ControlSP.reset(new ISwitchVectorProperty);
470  std::unique_ptr<ISwitch[]> ControlS;
471  ControlS.reset(new ISwitch[4]);
472 
473  char switchName[MAXINDINAME] = {0}, groupLabel[MAXINDINAME] = {0};
474  snprintf(switchName, MAXINDINAME, "HEATER_%d", i + 1);
475  snprintf(groupLabel, MAXINDINAME, "%s", getHeaterName(i));
476  IUFillSwitch(&ControlS[HEATER_OFF], "HEATER_OFF", "Off", ISS_ON);
477  IUFillSwitch(&ControlS[HEATER_ON], "HEATER_ON", "On", ISS_OFF);
478  IUFillSwitch(&ControlS[HEATER_CONTROL], "HEATER_CONTROL", "Control", ISS_OFF);
479  IUFillSwitch(&ControlS[HEATER_THRESHOLD], "HEATER_THRESHOLD", "Theshold", ISS_OFF);
480  IUFillSwitchVector(ControlSP.get(), ControlS.get(), 4, getDeviceName(), switchName, "Heater",
481  groupLabel, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
482 
483  HeaterControlSP.push_back(std::move(ControlSP));
484  HeaterControlS.push_back(std::move(ControlS));
485  }
486 
487  // Create heater parameters
488  for (uint8_t i = 0; i < nHeaters; i++)
489  {
490 
491  // Control Parameters
492  std::unique_ptr<INumberVectorProperty> ControlNP;
493  ControlNP.reset(new INumberVectorProperty);
494  std::unique_ptr<INumber[]> ControlN;
495  ControlN.reset(new INumber[4]);
496 
497  char numberName[MAXINDINAME] = {0}, groupLabel[MAXINDINAME] = {0};
498  snprintf(numberName, MAXINDINAME, "PARAM_%d", i + 1);
499  snprintf(groupLabel, MAXINDINAME, "%s", getHeaterName(i));
500  IUFillNumber(&ControlN[PARAM_PERIOD], "PARAM_PERIOD", "Period", "%.1f", 0.1, 60, 1, 1);
501  IUFillNumber(&ControlN[PARAM_DUTY], "PARAM_DUTY", "Duty", "%.f", 1, 100, 5, 1);
502  IUFillNumber(&ControlN[PARAM_CONTROL], "PARAM_CONTROL", "ΔAmbient =", "%.1f", 0, 100, 5, 2.5);
503  IUFillNumber(&ControlN[PARAM_THRESHOLD], "PARAM_THRESHOLD", "Ambient less", "%.1f", -50, 50, 5, 2.5);
504  IUFillNumberVector(ControlNP.get(), ControlN.get(), 4, getDeviceName(), numberName, "Params",
505  groupLabel, IP_RW, 60, IPS_IDLE);
506 
507  HeaterParamNP.push_back(std::move(ControlNP));
508  HeaterParamN.push_back(std::move(ControlN));
509 
510 
511  // Monitoring
512  std::unique_ptr<INumberVectorProperty> MonitorNP;
513  MonitorNP.reset(new INumberVectorProperty);
514  std::unique_ptr<INumber[]> MonitorN;
515  MonitorN.reset(new INumber[2]);
516 
517  snprintf(numberName, MAXINDINAME, "MONITOR_%d", i + 1);
518  snprintf(groupLabel, MAXINDINAME, "%s", getHeaterName(i));
519  IUFillNumber(&MonitorN[MONITOR_PERIOD], "MONITOR_PERIOD", "Period", "%.1f", 0.1, 60, 1, 1);
520  IUFillNumber(&MonitorN[MONITOR_DUTY], "MONITOR_DUTY", "Duty", "%.f", 1, 100, 5, 1);
521  IUFillNumberVector(MonitorNP.get(), MonitorN.get(), 2, getDeviceName(), numberName, "Monitor",
522  groupLabel, IP_RO, 60, IPS_IDLE);
523 
524  HeaterMonitorNP.push_back(std::move(MonitorNP));
525  HeaterMonitorN.push_back(std::move(MonitorN));
526  }
527 
528  return true;
529 }
530 
534 bool DeltaT::setHeaterEnabled(uint8_t index, bool enabled)
535 {
536  if (!enabled)
537  {
538  char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
539 
540  cmd[0] = DRIVER_SOM;
541  cmd[1] = 0x04;
542  cmd[2] = DEVICE_PC;
543  cmd[3] = DEVICE_DELTA;
544  cmd[4] = COH_OFF;
545  cmd[5] = index;
546  cmd[6] = calculateCheckSum(cmd, 7);
547 
548  if (!sendCommand(cmd, res, 7, 7))
549  return false;
550 
551  // 0x80 is OK
552  return (static_cast<uint8_t>(res[5]) == 0x80);
553  }
554  else
555  {
556  return setHeaterParam(index, HeaterParamNP[index]->np[PARAM_PERIOD].value, HeaterParamNP[index]->np[PARAM_DUTY].value);
557  }
558 }
559 
563 bool DeltaT::setHeaterParam(uint8_t index, double period, double duty)
564 {
565  char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
566 
567  uint16_t seconds = period * 10;
568 
569  cmd[0] = DRIVER_SOM;
570  cmd[1] = 0x07;
571  cmd[2] = DEVICE_PC;
572  cmd[3] = DEVICE_DELTA;
573  cmd[4] = COH_ON_MANUAL;
574  cmd[5] = index;
575  // LSB
576  cmd[6] = seconds & 0xFF;
577  // MSB
578  cmd[7] = (seconds >> 8) & 0xFF;
579  cmd[8] = static_cast<uint8_t>(duty);
580  cmd[9] = calculateCheckSum(cmd, 10);
581 
582  if (!sendCommand(cmd, res, 10, 7))
583  return false;
584 
585  // 0x80 is OK
586  return (static_cast<uint8_t>(res[5]) == 0x80);
587 }
588 
592 bool DeltaT::forceBoot()
593 {
594  char cmd[DRIVER_LEN] = {0};
595  cmd[0] = DRIVER_SOM;
596  cmd[1] = 0x03;
597  cmd[2] = DEVICE_PC;
598  cmd[3] = DEVICE_DELTA;
599  cmd[4] = CMD_FORCE_BOOT;
600  cmd[5] = calculateCheckSum(cmd, 6);
601  return sendCommand(cmd, nullptr, 6, 0);
602 }
603 
607 bool DeltaT::forceReset()
608 {
609  char cmd[DRIVER_LEN] = {0};
610  cmd[0] = DRIVER_SOM;
611  cmd[1] = 0x03;
612  cmd[2] = DEVICE_PC;
613  cmd[3] = DEVICE_DELTA;
614  cmd[4] = CMD_FORCE_RESET;
615  cmd[5] = calculateCheckSum(cmd, 6);
616  return sendCommand(cmd, nullptr, 6, 0);
617 }
618 
622 bool DeltaT::readTemperature()
623 {
624  char cmd[DRIVER_LEN] = {0}, res[DRIVER_LEN] = {0};
625 
626  for (uint8_t i = 0; i < 3; i++)
627  {
628  cmd[0] = DRIVER_SOM;
629  cmd[1] = 0x04;
630  cmd[2] = DEVICE_PC;
631  cmd[3] = DEVICE_DELTA;
632  cmd[4] = TEMP_GET;
633  cmd[5] = i + 1;
634  cmd[6] = calculateCheckSum(cmd, 7);
635 
636  if (!sendCommand(cmd, res, 7, 8))
637  return false;
638 
639  double newTemperature = calculateTemperature(res[5], res[6]);
640  // Temperature fluctuates a lot so let's change it only when the delta > 0.1
641  if (std::abs(TemperatureN[i].value - newTemperature) > TEMPERATURE_CONTROL_THRESHOLD)
642  TemperatureN[i].value = newTemperature;
643 
644  }
645 
646  return true;
647 }
648 
652 double DeltaT::calculateTemperature(uint8_t lsb, uint8_t msb)
653 {
654  if (lsb == 0x7F && msb == 0x7F)
655  return -100;
656 
657  int raw_temperature = lsb << 8 | msb;
658  if (raw_temperature & 0x8000)
659  raw_temperature = raw_temperature - 0x10000;
660 
661  return raw_temperature / 16.0;
662 }
663 
667 bool DeltaT::sendCommand(const char * cmd, char * res, uint32_t cmd_len, uint32_t res_len)
668 {
669  int nbytes_written = 0, nbytes_read = 0, rc = 0;
670 
671  for (int j = 0; j < 3; j++)
672  {
673  char hex_cmd[DRIVER_LEN * 3] = {0};
674  hexDump(hex_cmd, cmd, cmd_len);
675  LOGF_DEBUG("CMD <%s>", hex_cmd);
676  rc = tty_write(PortFD, cmd, cmd_len, &nbytes_written);
677 
678  if (rc != TTY_OK)
679  {
680  char errstr[MAXRBUF] = {0};
681  tty_error_msg(rc, errstr, MAXRBUF);
682  LOGF_ERROR("Serial write error: %s.", errstr);
683  return false;
684  }
685 
686  if (res == nullptr || res_len == 0)
687  return true;
688 
689  // Next read the response from DeltaT
690  rc = tty_read(PortFD, res, res_len, DRIVER_TIMEOUT, &nbytes_read);
691 
692  if (rc == TTY_OK)
693  break;
694 
695  usleep(100000);
696  }
697 
698  if (rc != TTY_OK)
699  {
700  char errstr[MAXRBUF] = {0};
701  tty_error_msg(rc, errstr, MAXRBUF);
702  LOGF_ERROR("Serial read error: %s.", errstr);
703  return false;
704  }
705 
706  char hex_res[DRIVER_LEN * 3] = {0};
707  hexDump(hex_res, res, res_len);
708  LOGF_DEBUG("RES <%s>", hex_res);
709 
710  int8_t chk = calculateCheckSum(res, res_len);
711 
712  if (res_len > 0 && chk != res[res_len - 1])
713  {
714  LOG_ERROR("Invalid checksum!");
715  return false;
716  }
717 
718  return true;
719 }
720 
724 void DeltaT::hexDump(char * buf, const char * data, uint32_t size)
725 {
726  for (uint32_t i = 0; i < size; i++)
727  sprintf(buf + 3 * i, "%02X ", static_cast<uint8_t>(data[i]));
728 
729  if (size > 0)
730  buf[3 * size - 1] = '\0';
731 }
732 
736 std::vector<std::string> DeltaT::split(const std::string &input, const std::string &regex)
737 {
738  // passing -1 as the submatch index parameter performs splitting
739  std::regex re(regex);
740  std::sregex_token_iterator
741  first{input.begin(), input.end(), re, -1},
742  last;
743  return {first, last};
744 }
745 
749 template <typename T>
750 std::string DeltaT::to_string(const T a_value, const int n)
751 {
752  std::ostringstream out;
753  out.precision(n);
754  out << std::fixed << a_value;
755  return out.str();
756 }
757 
761 uint8_t DeltaT::calculateCheckSum(const char *cmd, uint32_t len)
762 {
763  int32_t sum = 0;
764  for (uint32_t i = 1; i < len - 1; i++)
765  sum += cmd[i];
766  return (-sum & 0xFF);
767 }
void registerHandshake(std::function< bool()> callback)
registerHandshake Register a handshake function to be called once the intial connection to the device...
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...
@ CMD_GET_VERSION
@ CMD_FORCE_BOOT
@ COH_NUMHEATERS
@ CMD_FORCE_RESET
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
Initilize 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
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
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 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()
Initilize 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.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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.
double max(void)
double min(void)
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
@ ISR_ATMOST1
Definition: indiapi.h:174
#define MAXINDINAME
Definition: indiapi.h:191
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
int tty_read(int fd, char *buf, int nbytes, int timeout, int *nbytes_read)
read buffer from terminal
Definition: indicom.c:482
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
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:272
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
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:291
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:137
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
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:158
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:198
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:180
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:235
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
#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
#define MAXRBUF
Definition: indiserver.cpp:102
auto get(const nlohmann::detail::iteration_proxy_value< IteratorType > &i) -> decltype(i.key())
Definition: json.h:4953
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.h:23613
__u8 cmd[4]
Definition: pwc-ioctl.h:2
#define currentDuty
Definition: robofocus.cpp:40
One number descriptor.
One switch descriptor.
Number vector property descriptor.
Definition: indiapi.h:319
char name[MAXINDINAME]
Definition: indiapi.h:323
Switch vector property descriptor.
Definition: indiapi.h:367
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250