Instrument Neutral Distributed Interface INDI  2.0.2
myDewControllerPro.cpp
Go to the documentation of this file.
1 /*
2  myDewControllerPro Driver
3  Copyright (C) 2017-2023 Chemistorge
4 
5  This library is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Lesser General Public
7  License as published by the Free Software Foundation; either
8  version 2.1 of the License, or (at your option) any later version.
9 
10  This library is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  Lesser General Public License for more details.
14 
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, write to the Free Software
17  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 
19 */
20 
21 #include "myDewControllerPro.h"
23 #include "indicom.h"
24 
25 #include <termios.h>
26 
27 #define MYDEWHEATERPRO_TIMEOUT 3
28 #define BOARD_FAN_TAB "Board Fan"
29 #define TEMPERATURE_OFFSETS_TAB "Temperature/Tracking Offsets"
30 #define LCD_DISPLAY_TAB "LCD Display"
31 
32 std::unique_ptr<myDewControllerPro> mydewcontrollerpro(new myDewControllerPro());
33 
34 
36 {
37  setVersion(1, 0);
38 }
39 
41 {
42  DefaultDevice::initProperties();
43 
44  /* Channel duty cycles */
45  OutputsNP[DEW_STRAP_ONE_POWER].fill("CHANNEL1", "Strap 1", "%4.0f %%", 0., 100., 1., 0.);
46  OutputsNP[DEW_STRAP_TWO_POWER].fill("CHANNEL2", "Strap 2", "%4.0f %%", 0., 100., 1., 0.);
47  OutputsNP[DEW_STRAP_THREE_POWER].fill("CHANNEL3", "Strap 3", "%4.0f %%", 0., 100., 1., 0.);
48  OutputsNP.fill(getDeviceName(), "OUTPUT", "Outputs", MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE);
49 
50  FanSpeedNP[0].fill("Fan Power", "Fan Speed", "%4.0f %%", 0., 100., 1., 0.);
51  FanSpeedNP.fill(getDeviceName(), "FanSpeed", "Board Fan", BOARD_FAN_TAB, IP_RW, 0, IPS_IDLE);
52 
53  FanModeSP[BOARD_TEMP].fill("Board Temp", "Board Temp Sensor", ISS_OFF);
54  FanModeSP[MANUAL_FAN].fill("Manual", "Manual", ISS_ON);
55  FanModeSP.fill(getDeviceName(), "Fan_Mode", "Fan Mode", BOARD_FAN_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
56 
57  EEPROMSP[RESET_EEPROM].fill("Reset EEPROM", "Reset EEPROM to Defaults", ISS_OFF);
58  EEPROMSP[SAVE_TO_EEPROM].fill("Save to EEPROM", "Save to EEPROM", ISS_OFF);
59  EEPROMSP.fill(getDeviceName(), "EEPROM", "EEPROM", OPTIONS_TAB, IP_WO, ISR_ATMOST1, 0, IPS_IDLE);
60 
61  FanTempTriggerNP[FANTEMPOFF].fill("Board_Temp_Off", "Board Fan Temp Off", "%4.0f \u2103", 0., 100., 1., 0.);
62  FanTempTriggerNP[FANTEMPON].fill("Board_Temp_On", "Board Fan Temp On", "%4.0f \u2103", 0., 100., 1., 0.);
63  FanTempTriggerNP.fill(getDeviceName(), "Fan Trigger Temps", "Fan Trigger", BOARD_FAN_TAB, IP_RW, 0, IPS_IDLE);
64 
65  LCDPageRefreshNP[0].fill("Page Refresh Rate", "Page Refresh Rate", "%4.0f ms", 500., 5000., 500., 0.);
66  LCDPageRefreshNP.fill(getDeviceName(), "LCD Page", "LCD Page", LCD_DISPLAY_TAB, IP_RW, 0, IPS_IDLE);
67 
68  LCDDisplayTempUnitsSP[CELCIUS].fill("Celsius", "Celsius", ISS_ON);
69  LCDDisplayTempUnitsSP[FAHRENHEIT].fill("Fahrenheit", "Fahrenheit", ISS_OFF);
70  LCDDisplayTempUnitsSP.fill(getDeviceName(), "Temp Units", "Temp Units", LCD_DISPLAY_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
71 
72  EnableLCDDisplaySP[DISABLE_LCD].fill("Disabled", "Disabled", ISS_ON);
73  EnableLCDDisplaySP[ENABLE_LCD].fill("Enabled", "Enabled", ISS_OFF);
74  EnableLCDDisplaySP.fill(getDeviceName(), "LCD Status", "LCD Status", LCD_DISPLAY_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
75 
76 
77 
78  /* Channel Manual and Boost */
79  CH1CH2BoostSP[CH1_BOOST_100].fill("BOOST_CH1", "Strap 1 Boost 100%", ISS_OFF);
80  CH1CH2BoostSP[CH2_BOOST_100].fill("BOOST_CH2", "Strap 2 Boost 100%", ISS_OFF);
81  CH1CH2BoostSP.fill(getDeviceName(), "CHANNEL_BOOST", "Heat Boost", MAIN_CONTROL_TAB, IP_RW, ISR_NOFMANY, 0, IPS_IDLE);
82 
83  CH3_ModeSP[DISABLED_STRAP].fill("STRAP_DISABLED", "Strap Disabled", ISS_ON);
84  CH3_ModeSP[DEWSTRAP_ONE].fill("SHADOW STRAP 1", "Shadow Strap 1", ISS_OFF);
85  CH3_ModeSP[DEWSTRAP_TWO].fill("SHADOW STRAP 2", "Shadow Strap 2", ISS_OFF);
86  CH3_ModeSP[MANUAL_STRAP].fill("Manual", "Manual", ISS_OFF);
87  CH3_ModeSP[TEMP_PROBE_THREE].fill("TEMP_PROBE", "Temp Probe", ISS_OFF);
88  CH3_ModeSP.fill(getDeviceName(), "CHANEL 3 SHAWDOW", "Strap 3 Mode", MAIN_CONTROL_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
89 
90  CH3_Manual_PowerNP[0].fill("MANUAL_POWER", "Strap 3 Manual Power", "%4.0f %%", 0., 100., 1., 0.);
91  CH3_Manual_PowerNP.fill(getDeviceName(), "CH3_POWER", "Strap 3 Power", MAIN_CONTROL_TAB, IP_RW, 0, IPS_IDLE);
92 
93 
94 
95  /* Temperatures */
96  TemperaturesNP[PROBE_1].fill("CHANNEL1", "Strap 1", "%3.2f \u2103", -50., 70., 0., 0.);
97  TemperaturesNP[PROBE_2].fill("CHANNEL2", "Strap 2", "%3.2f \u2103", -50., 70., 0., 0.);
98  TemperaturesNP[PROBE_3].fill("CHANNEL3", "Strap 3", "%3.2f \u2103", -50., 70., 0., 0.);
99  TemperaturesNP[AMBIENT_PROBE].fill("AMBIENT", "Ambient", "%3.2f \u2103", -50., 70., 0., 0.);
100  TemperaturesNP[BOARD_PROBE].fill("BOARD Temp", "Board", "%3.2f \u2103", -50., 100., 0., 0.);
101  TemperaturesNP.fill(getDeviceName(), "TEMPERATURES", "Temperatures", MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE);
102 
103  /* Humidity */
104  HumidityNP[0].fill("HUMIDITY", "Humidity", "%3.2f %%", 0., 100., 0., 0.);
105  HumidityNP.fill(getDeviceName(), "HUMIDITY", "Humidity", MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE);
106 
107  /* Dew point */
108  DewpointNP[0].fill("DEWPOINT", "Dew point", "%3.2f \u2103", -50., 70., 0., 0.);
109  DewpointNP.fill(getDeviceName(), "DEWPOINT", "Dew point", MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE);
110 
111  /* Temperature calibration values */
112  TemperatureOffsetsNP[TEMP_PROBE_ONE_OFFSET].fill("CHANNEL1", "Strap 1", "%1.0f \u2103", -10., 10., 1., 0.);
113  TemperatureOffsetsNP[TEMP_PROBE_TWO_OFFSET].fill("CHANNEL2", "Strap 2", "%1.0f \u2103", -10., 10., 1., 0.);
114  TemperatureOffsetsNP[TEMP_PROBE_THREE_OFFSET].fill("CHANNEL3", "Strap 3", "%1.0f \u2103", -10., 10., 1., 0.);
115  TemperatureOffsetsNP[AMBIENT_TEMP_PROBE_OFFSET].fill("AMBIENT", "Ambient", "%4.0f \u2103", -4, 3, 1, 0);
116  TemperatureOffsetsNP.fill(getDeviceName(), "TEMP_CALIBRATIONS", "Temp Offsets", TEMPERATURE_OFFSETS_TAB, IP_RW, 0, IPS_IDLE);
117 
118  ZeroTempOffsetsSP[0].fill("Zero_Temp", "Zero Temperature Offsets", ISS_OFF);
119  ZeroTempOffsetsSP.fill(getDeviceName(), "Zero Offsets", "Zero Offsets", TEMPERATURE_OFFSETS_TAB, IP_RW, ISR_ATMOST1, 0, IPS_IDLE);
120 
121  /* Tracking Mode Options */
122 
123  TrackingModeSP[AMBIENT].fill("AMBIENT", "Ambient", ISS_OFF);
124  TrackingModeSP[DEWPOINT].fill("DEWPOINT", "Dew Point", ISS_ON);
125  TrackingModeSP[MIDPOINT].fill("MIDPOINT", "Mid Point", ISS_OFF);
126  TrackingModeSP.fill(getDeviceName(), "Tracking Mode", "Tracking Mode", TEMPERATURE_OFFSETS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
127 
128  TrackingModeOffsetNP[0].fill("Offset", "Offset", "%4.0f \u2103", -4, 3, 1, 0);
129  TrackingModeOffsetNP.fill(getDeviceName(), "Tracking Offset", "Tracking Offset", TEMPERATURE_OFFSETS_TAB, IP_RW, 0, IPS_IDLE);
130  /* Firmware version */
131  FWVersionNP[0].fill("FIRMWARE", "Firmware Version", "%4.0f", 0., 65535., 1., 0.);
132  FWVersionNP.fill(getDeviceName(), "FW_VERSION", "Firmware", OPTIONS_TAB, IP_RO, 0, IPS_IDLE);
133 
135 
136  addDebugControl();
140 
141  // No simulation control for now
142 
143 
144  serialConnection = new Connection::Serial(this);
145 
146  serialConnection->setDefaultBaudRate(serialConnection->B_57600);
147  serialConnection->registerHandshake([&]()
148  {
149  return Handshake();
150  });
151  registerConnection(serialConnection);
152 
153  return true;
154 }
155 
156 bool myDewControllerPro::cancelOutputBoost()
157 {
158  if (sendCommand(MDCP_CANCEL_BOOST, nullptr)) {
159  return true;
160  } else {
161  LOG_INFO("Failed to cancel Boost");
163  return false;
164  }
165 
166 }
167 
169 {
170  DefaultDevice::updateProperties();
171 
172  if (isConnected())
173  {
174  defineProperty(OutputsNP);
175  defineProperty(CH1CH2BoostSP);
176  defineProperty(CH3_ModeSP);
177  defineProperty(CH3_Manual_PowerNP);
178  defineProperty(TemperaturesNP);
179  defineProperty(HumidityNP);
180  defineProperty(DewpointNP);
181  defineProperty(FanSpeedNP);
182  defineProperty(FanModeSP);
183  defineProperty(TemperatureOffsetsNP);
184  defineProperty(ZeroTempOffsetsSP);
185  defineProperty(TrackingModeSP);
186  defineProperty(TrackingModeOffsetNP);
187  defineProperty(FanTempTriggerNP);
188  defineProperty(EnableLCDDisplaySP);
189  defineProperty(LCDDisplayTempUnitsSP);
190  defineProperty(LCDPageRefreshNP);
191  defineProperty(EEPROMSP);
192  defineProperty(FWVersionNP);
193 
194 
195  cancelOutputBoost();
196 
197  loadConfig(true);
198  if (!readMainValues()) {
199  LOG_INFO("Reading Main Values Error");
200  }
201  if (!readLCDDisplayValues()) {
202  LOG_INFO("Reading LCD Display Values Error");
203  }
204  if (!readBoardFanValues()) {
205  LOG_INFO("Reading Board Fan Values Error");
206  }
207  if (!readOffsetValues()) {
208  LOG_INFO("Reading Offset Values Error");
209  }
210  LOG_INFO("myDewControllerPro parameters updated, device ready for use.");
212  }
213  else
214  {
215  deleteProperty(OutputsNP);
216  deleteProperty(CH1CH2BoostSP);
217  deleteProperty(CH3_ModeSP);
218  deleteProperty(CH3_Manual_PowerNP);
219  deleteProperty(TemperaturesNP);
220  deleteProperty(HumidityNP);
221  deleteProperty(DewpointNP);
222  deleteProperty(FanSpeedNP);
223  deleteProperty(FanModeSP);
224  deleteProperty(TemperatureOffsetsNP);
225  deleteProperty(ZeroTempOffsetsSP);
226  deleteProperty(TrackingModeSP);
227  deleteProperty(TrackingModeOffsetNP);
228  deleteProperty(FanTempTriggerNP);
229  deleteProperty(EnableLCDDisplaySP);
230  deleteProperty(LCDDisplayTempUnitsSP);
231  deleteProperty(LCDPageRefreshNP);
232  deleteProperty(EEPROMSP);
233  deleteProperty(FWVersionNP);
234  }
235 
236  return true;
237 }
238 
240 {
241  return "myDewContollerPro";
242 }
243 
244 bool myDewControllerPro::sendCommand(const char *cmd, char *resp)
245 {
246  int nbytes_written = 0, nbytes_read = 0, rc = -1;
247  char errstr[MAXRBUF];
248  LOGF_DEBUG("CMD: %s.", cmd);
249 
250  tcflush(PortFD, TCIOFLUSH);
251  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
252  {
253  tty_error_msg(rc, errstr, MAXRBUF);
254  LOGF_ERROR("Error writing command %s: %s.", cmd, errstr);
255  return false;
256  }
257 
258  if (resp)
259  {
260  if ((rc = tty_nread_section(PortFD, resp, MDCP_RES_LEN, '$', MYDEWHEATERPRO_TIMEOUT, &nbytes_read)) != TTY_OK)
261  {
262  tty_error_msg(rc, errstr, MAXRBUF);
263  LOGF_ERROR("Error reading response for command %s: %s.", cmd, errstr);
264  return false;
265  }
266  }
267  return true;
268 }
269 
270 
271 
272 bool myDewControllerPro::Handshake()
273 {
274  PortFD = serialConnection->getPortFD();
275 
276  int tries = 3;
277  do
278  {
279  if (Ack())
280  {
281  LOG_INFO("myDewControllerPro is online. Getting device parameters...");
282  return true;
283  }
284  LOG_INFO("Error retrieving data from myDewControllerPro, trying resync...");
285  }
286  while (--tries > 0);
287 
288  //LOG_INFO("Error retrieving data from myDewControllerPro, please ensure controller "
289  // "is powered and the port is correct.");
290  return false;
291 }
292 
293 bool myDewControllerPro::Ack()
294 {
295  char resp[MDCP_RES_LEN] = {};
296  tcflush(PortFD, TCIOFLUSH);
297 
298  if (!sendCommand(MDCP_GET_VERSION, resp))
299  return false;
300 
301  int firmware = -1;
302  int ok = sscanf(resp, MDCP_IDENTIFY_RESPONSE, &firmware);
303  snprintf(resp, 40, "Firmware Version: %d", firmware);
304  LOG_INFO(resp);
305 
306  if (ok != 1)
307  {
308  LOGF_ERROR("myDewControllerPro not properly identified! Answer was: %s.", resp);
309  return false;
310  }
311  if (firmware < 340) {
312  LOG_INFO("Please update myDewControllerPro firmware");
313  LOG_INFO("https://sourceforge.net/projects/arduinonanodewcontrollerpro/files/myDewControllerPro%20v300%203channel/CODE%20ARDUINO/");
314  return false;
315  }
316  int numberProbes = 0;
317  if (!sendCommand(MDCP_GET_NUMBER_OF_PROBES, resp))
318  return false;
319  sscanf(resp, "g%u$", &numberProbes);
320  snprintf(resp, 40, "The number of Temperature Probes are: %d", numberProbes);
321  LOG_INFO(resp);
322  if (numberProbes < 1) {
323  LOG_INFO("Warning no temperature probes detected");
324  }
325  FWVersionNP.setState(IPS_BUSY);
326  FWVersionNP[0].setValue(firmware);
327  FWVersionNP.setState(IPS_OK);
328  FWVersionNP.apply();
329 
330  return true;
331 
332 }
333 
334 bool myDewControllerPro::setOutputBoost(unsigned int channel)
335 {
336 
337 
338 
339  if (channel == 0) {
340  return sendCommand(MDCP_BOOST_CH1, nullptr);
341  } else if (channel == 1) {
342  return sendCommand(MDCP_BOOST_CH2, nullptr);
343  } else {
344  LOG_INFO("No Channel Set");
345  return false;
346  }
347 
348 }
349 
350 bool myDewControllerPro::setInt(int mode, const char *mask, const char *errMessage)
351 {
352  char cmd[MDCP_CMD_LEN + 1];
353 
354  snprintf(cmd, MDCP_CMD_LEN + 1, mask, mode);
355  if (!sendCommand(cmd, nullptr)) {
356  LOG_INFO(errMessage);
357  LOG_INFO(cmd);
358  return false;
359  }
360  return true;
361 
362 }
363 
364 bool myDewControllerPro::setChoice(int testInt, const char *positiveChoice, const char *negativeChoice, const char *errMessage)
365 {
366  const char* mask = testInt == 1 ? positiveChoice : negativeChoice;
367  if (!sendCommand(mask, nullptr)) {
368  LOG_INFO(errMessage);
369 
370  return false;
371  }
372  return true;
373 }
374 
375 
376 
377 bool myDewControllerPro::setTempCalibrations(float ch1, float ch2, float ch3, int ambient)
378 {
379  char cmd[MDCP_CMD_LEN + 1];
380 
381 
382  snprintf(cmd, MDCP_CMD_LEN + 1, MDCP_SET_TEMP_CH1_OFFSET, ch1);
383  if (!sendCommand(cmd, nullptr)) {
384  LOG_INFO("Failed to set CH1 offset");
385  LOG_INFO(cmd);
386  return false;
387  }
388  snprintf(cmd, MDCP_CMD_LEN + 1, MDCP_SET_TEMP_CH2_OFFSET, ch2);
389  if (!sendCommand(cmd, nullptr)) {
390  LOG_INFO("Failed to set CH2 offset");
391  LOG_INFO(cmd);
392  return false;
393  }
394  snprintf(cmd, MDCP_CMD_LEN + 1, MDCP_SET_TEMP_CH3_OFFSET, ch3);
395  if (!sendCommand(cmd, nullptr)) {
396  LOG_INFO("Failed to set CH3 offset");
397  LOG_INFO(cmd);
398  return false;
399  }
400 
401  snprintf(cmd, MDCP_CMD_LEN + 1, MDCP_SET_AMB_TEMP_OFFSET, ambient);
402  if (!sendCommand(cmd, nullptr)) {
403  LOG_INFO("Failed to set CH3 offset");
404  LOG_INFO(cmd);
405  return false;
406  }
407  return true;
408 
409 
410 
411 }
412 
413 bool myDewControllerPro::setFanTempTrigger(int tempOn, int tempOff)
414 {
415  char cmd[MDCP_CMD_LEN + 1];
416 
417 
418  snprintf(cmd, MDCP_CMD_LEN + 1, MDCP_SET_FAN_ON_TEMP, tempOn);
419  if (!sendCommand(cmd, nullptr)) {
420  LOG_INFO("Failed to set temp on");
421  LOG_INFO(cmd);
422  return false;
423  }
424  snprintf(cmd, MDCP_CMD_LEN + 1, MDCP_SET_FAN_OFF_TEMP, tempOff);
425  if (!sendCommand(cmd, nullptr)) {
426  LOG_INFO("Failed to set CH2 offset");
427  LOG_INFO(cmd);
428  return false;
429  }
430 
431  return true;
432 
433 
434 
435 }
436 
437 bool myDewControllerPro::zeroTempCalibrations() {
438 
439  if (!sendCommand(MDCP_CLEAR_TEMP_OFFSETS, nullptr)) {
440  LOG_INFO("Failed to zero temp offset");
441 
442  return false;
443  }
444  if (!sendCommand("e0#", nullptr)) {
445  LOG_INFO("Failed to zero ambtemp offset");
446 
447  return false;
448  }
449  return true;
450 
451 
452 }
453 
454 
455 
456 
457 
458 
459 
460 
461 bool myDewControllerPro::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
462 {
463  if (!dev || strcmp(dev, getDeviceName()))
464  return false;
465 
466  if (CH1CH2BoostSP.isNameMatch(name))
467  {
468  CH1CH2BoostSP.update( states, names, n);
469  CH1CH2BoostSP.setState(IPS_BUSY);
470  cancelOutputBoost();
471  if (CH1CH2BoostSP[CH1_BOOST_100].getState() == ISS_ON) {
472  setOutputBoost(CH1_BOOST_100);
473  }
474  if (CH1CH2BoostSP[CH2_BOOST_100].getState() == ISS_ON) {
475  setOutputBoost(CH2_BOOST_100);
476  }
477  CH1CH2BoostSP.setState(IPS_OK);
478  CH1CH2BoostSP.apply();
479  readMainValues();
480  return true;
481 
482  }
483 
484  if (CH3_ModeSP.isNameMatch(name))
485  {
486  CH3_ModeSP.update(states, names, n);
487  CH3_ModeSP.setState(IPS_BUSY);
488  int mode = CH3_ModeSP.findOnSwitchIndex();
489  setInt(mode, MDCP_SET_CH3_SETTINGS, "Failed to set CH3 mode");
490  CH3_ModeSP.setState(IPS_OK);
491  CH3_ModeSP.apply();
492  readMainValues();
493  return true;
494 
495  }
496 
497  if (ZeroTempOffsetsSP.isNameMatch(name))
498  {
499  ZeroTempOffsetsSP.update(states, names, n);
500  ZeroTempOffsetsSP.setState(IPS_BUSY);
501  zeroTempCalibrations();
502  ZeroTempOffsetsSP.setState(IPS_OK);
503  ZeroTempOffsetsSP[0].setState(ISS_OFF);
504  ZeroTempOffsetsSP.apply();
505  readOffsetValues();
506  return true;
507  }
508 
509  if (TrackingModeSP.isNameMatch(name))
510  {
511  TrackingModeSP.update(states, names, n);
512  TrackingModeSP.setState(IPS_BUSY);
513  int mode = TrackingModeSP.findOnSwitchIndex();
514  setInt(mode, MDCP_SET_TRACKING_MODE, "Failed to set Tracking Mode");
515  TrackingModeSP.setState(IPS_OK);
516  TrackingModeSP.apply();
517  readOffsetValues();
518  return true;
519  }
520 
521  if (FanModeSP.isNameMatch(name))
522  {
523  FanModeSP.update(states, names, n);
524  FanModeSP.setState(IPS_BUSY);
525  int mode = FanModeSP.findOnSwitchIndex();
526  setInt(mode, MDCP_SET_FAN_MODE, "Failed to set Fan Mode");
527  FanModeSP.setState(IPS_OK);
528  FanModeSP.apply();
529  readBoardFanValues();
530  return true;
531  }
532 
533  if (EnableLCDDisplaySP.isNameMatch(name))
534  {
535  EnableLCDDisplaySP.update(states, names, n);
536  EnableLCDDisplaySP.setState(IPS_BUSY);
537  int mode = EnableLCDDisplaySP.findOnSwitchIndex();
538  setChoice(mode, MDCP_LCD_ENABLE, MDCP_LCD_DISABLE, "Failed to set LCD enable");
539  EnableLCDDisplaySP.setState(IPS_OK);
540  EnableLCDDisplaySP.apply();
541  readLCDDisplayValues();
542  return true;
543 
544  }
545 
546  if (LCDDisplayTempUnitsSP.isNameMatch(name))
547  {
548  LCDDisplayTempUnitsSP.update(states, names, n);
549  LCDDisplayTempUnitsSP.setState(IPS_BUSY);
550  int mode = LCDDisplayTempUnitsSP.findOnSwitchIndex();
551  setChoice(mode, MDCP_LCD_DISPLAY_FAHRENHEIT, MDCP_LCD_DISPLAY_CELSIUS, "Failed to set temp display mode");
552  LCDDisplayTempUnitsSP.setState(IPS_OK);
553  LCDDisplayTempUnitsSP.apply();
554  readLCDDisplayValues();
555  return true;
556 
557  }
558 
559  if (EEPROMSP.isNameMatch(name))
560  {
561  EEPROMSP.update(states, names, n);
562  EEPROMSP.setState(IPS_BUSY);
563  int mode = EEPROMSP.findOnSwitchIndex();
564  if (setChoice(mode, MDCP_SAVE_TO_EEPROM, MDCP_RESET_EEPROM_TO_DEFAULT, "Failed to Save/reset EEPROM"))
565  {
566  const char* message = mode == 1 ? "Saved to EEPPROM Successfully" : "Reset EEPROM to Default";
567  LOG_INFO(message);
568  }
569  readMainValues();
570  readOffsetValues();
571  readBoardFanValues();
572  readLCDDisplayValues();
573  EEPROMSP.setState(IPS_OK);
574  EEPROMSP.apply();
575  return true;
576 
577  }
578  return INDI::DefaultDevice::ISNewSwitch(dev, name, states, names, n);
579 }
580 
581 bool myDewControllerPro::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
582 {
583  if (!dev || strcmp(dev, getDeviceName()))
584  return false;
585 
586  if (CH3_Manual_PowerNP.isNameMatch(name))
587  {
588  if (CH3_ModeSP.findOnSwitchIndex() == 3) {
589  CH3_Manual_PowerNP.update(values, names, n);
590  CH3_Manual_PowerNP.setState(IPS_BUSY);
591  int power = CH3_Manual_PowerNP[0].getValue();
592  setInt(power, MDCP_SET_CH3_MANUAL_POWER, "Failed to set CH3 Power");
593  CH3_Manual_PowerNP.setState(IPS_OK);
594  CH3_Manual_PowerNP.apply();
595  } else {
596  LOG_INFO("Power can only be manually adjusted in Strap 3 manual mode");
597  }
598  readMainValues();
599  return true;
600  }
601 
602  if (TemperatureOffsetsNP.isNameMatch(name))
603  {
604  TemperatureOffsetsNP.update(values, names, n);
605  TemperatureOffsetsNP.setState(IPS_BUSY);
606  int ch1 = TemperatureOffsetsNP[TEMP_PROBE_ONE_OFFSET].getValue();
607  int ch2 = TemperatureOffsetsNP[TEMP_PROBE_TWO_OFFSET].getValue();
608  int ch3 = TemperatureOffsetsNP[TEMP_PROBE_THREE_OFFSET].getValue();
609  int ambient = TemperatureOffsetsNP[AMBIENT_TEMP_PROBE_OFFSET].getValue();
610  setTempCalibrations(ch1, ch2, ch3, ambient);
611  TemperatureOffsetsNP.setState(IPS_OK);
612  TemperatureOffsetsNP.apply();
613  readOffsetValues();
614  return true;
615 
616  }
617 
618  if (TrackingModeOffsetNP.isNameMatch(name))
619  {
620  TrackingModeOffsetNP.update(values, names, n);
621  TrackingModeOffsetNP.setState(IPS_BUSY);
622  int offset = TrackingModeOffsetNP[0].getValue();
623  setInt(offset, MDCP_SET_TRACKING_MODE_OFFSET, "Failed to set Tracking Mode offsets");
624  TrackingModeOffsetNP.setState(IPS_OK);
625  TrackingModeOffsetNP.apply();
626  readOffsetValues();
627  return true;
628  }
629 
630  if (FanTempTriggerNP.isNameMatch(name))
631  {
632  FanTempTriggerNP.update(values, names, n);
633  FanTempTriggerNP.setState(IPS_BUSY);
634  int tempOn = FanTempTriggerNP[FANTEMPON].getValue();
635  int tempOff = FanTempTriggerNP[FANTEMPOFF].getValue();
636  setFanTempTrigger(tempOn, tempOff);
637  FanTempTriggerNP.setState(IPS_OK);
638  FanTempTriggerNP.apply();
639  readBoardFanValues();
640  return true;
641 
642  }
643  if (FanSpeedNP.isNameMatch(name))
644  {
645  FanSpeedNP.update(values, names, n);
646  FanSpeedNP.setState(IPS_BUSY);
647  int speed = FanSpeedNP[0].getValue();
648  setInt(speed, MDCP_SET_FAN_SPEED, "Failed to set Fan Speed");
649  FanSpeedNP.setState(IPS_OK);
650  FanSpeedNP.apply();
651  readBoardFanValues();
652  return true;
653 
654  }
655 
656  if (LCDPageRefreshNP.isNameMatch(name)) {
657  LCDPageRefreshNP.update(values, names, n);
658  LCDPageRefreshNP.setState(IPS_BUSY);
659  int time = LCDPageRefreshNP[0].getValue();
660  setInt(time, MDCP_SET_LCD_DISPLAY_TIME, "Failed to set LCD Page refressh");
661  LCDPageRefreshNP.setState(IPS_OK);
662  LCDPageRefreshNP.apply();
663  readLCDDisplayValues();
664  return true;
665 
666  }
667 
668  return INDI::DefaultDevice::ISNewNumber(dev, name, values, names, n);
669 }
670 
671 bool myDewControllerPro::readMainValues()
672 {
673 
674  char resp[MDCP_RES_LEN];
675  float temp1, temp2, temp3, temp_ambient, dewpoint, humidity;
676 
677 
678  if (!sendCommand(MDCP_GET_PROBE_TEMPS, resp)) {
679  LOG_INFO(resp);
680  return false;
681  }
682 
683  if (sscanf(resp, MDCP_GET_TEMP_RESPONSE, &temp1, &temp2, &temp3) == 3) {
684  TemperaturesNP[PROBE_1].setValue(temp1);
685  TemperaturesNP[PROBE_2].setValue(temp2);
686  TemperaturesNP[PROBE_3].setValue(temp3);
687  TemperaturesNP.setState(IPS_OK);
688  TemperaturesNP.apply();
689  }
690 
691  if (!sendCommand(MDCP_GET_AMB_TEMP, resp)) {
692  LOG_INFO(resp);
693  return false;
694  }
695 
696  if (sscanf(resp, MDCP_GET_AMB_TEMP_REPSONSE, &temp_ambient) == 1) {
697  TemperaturesNP[AMBIENT_PROBE].setValue(temp_ambient);
698  TemperaturesNP.setState(IPS_OK);
699  TemperaturesNP.apply();
700  }
701 
702  if (!sendCommand(MDCP_GET_BOARD_TEMP, resp)) {
703  LOG_INFO(resp);
704  return false;
705  }
706 
707  if (sscanf(resp, MDCP_GET_BOARD_TEMP_RESPONSE, &temp_ambient) == 1) {
708  TemperaturesNP[BOARD_PROBE].setValue(temp_ambient);
709  TemperaturesNP.setState(IPS_OK);
710  TemperaturesNP.apply();
711  }
712 
713  if (!sendCommand(MDCP_GET_REL_HUMIDITY, resp)) {
714  LOG_INFO(resp);
715  return false;
716  }
717 
718  if (sscanf(resp, MDCP_GET_REL_HUMIDITY_REPSONSE, &humidity) == 1) {
719 
720  HumidityNP[0].setValue(humidity);
721  HumidityNP.setState(IPS_OK);
722  HumidityNP.apply();
723  } else {
724  LOG_INFO(resp);
725  }
726 
727  if (!sendCommand(MDCP_GET_DEW_POINT, resp)) {
728  LOG_INFO(resp);
729  return false;
730  }
731 
732  if (sscanf(resp, MDCP_GET_DEW_POINT_RESPONSE, &dewpoint) == 1) {
733  DewpointNP[0].setValue(dewpoint);
734  DewpointNP.setState(IPS_OK);
735  DewpointNP.apply();
736  }
737 
738  int power1, power2, power3;
739 
740  if (!sendCommand(MDCP_GET_CHANNEL_POWER, resp)) {
741  LOG_INFO(resp);
742  return false;
743  }
744 
745  if (sscanf(resp, MDCP_GET_CHANNEL_POWER_RESPONSE, &power1, &power2, &power3) == 3) {
746  OutputsNP[DEW_STRAP_ONE_POWER].setValue(power1);
747  OutputsNP[DEW_STRAP_TWO_POWER].setValue(power2);
748  OutputsNP[DEW_STRAP_THREE_POWER].setValue(power3);
749  OutputsNP.setState(IPS_OK);
750  OutputsNP.apply();
751  CH3_Manual_PowerNP[0].setValue(power3);
752  CH3_Manual_PowerNP.apply();
753  } else {
754  LOG_INFO(resp);
755  }
756 
757  int mode;
758 
759  if (!sendCommand(MDCP_GET_CH3_SETTINGS, resp)) {
760  LOG_INFO(resp);
761  return false;
762  }
763 
764  if (sscanf(resp, MDCP_GET_CH3_SETTINGS_RESPONSE, &mode) == 1) {
765  CH3_ModeSP.reset();
766  CH3_ModeSP[mode].setState(ISS_ON);
767  CH3_ModeSP.setState(IPS_OK);
768  CH3_ModeSP.apply();
769  } else {
770  LOG_INFO(resp);
771  }
772 
773  if (!sendCommand(MDCP_GET_FAN_SPEED, resp)) {
774  LOG_INFO(resp);
775  return false;
776  }
777  int fanSpeed;
778 
779  if (sscanf(resp, "F%d$", &fanSpeed) == 1) {
780  FanSpeedNP[0].setValue(fanSpeed);
781  FanSpeedNP.setState(IPS_OK);
782  FanSpeedNP.apply();
783  }
784 
785  return true;
786 }
787 
788 bool myDewControllerPro::readOffsetValues()
789 {
790  char resp[MDCP_RES_LEN];
791  float temp1, temp2, temp3;
792 
793 
794  if (!sendCommand(MDCP_GET_TEMP_OFFSETS, resp)) {
795  LOG_INFO(resp);
796  return false;
797  }
798 
799  if (sscanf(resp, MDCP_GET_TEMP_OFFSETS_RESPONSE, &temp1, &temp2, &temp3) == 3) {
800  TemperatureOffsetsNP[TEMP_PROBE_ONE_OFFSET].setValue(temp1);
801  TemperatureOffsetsNP[TEMP_PROBE_TWO_OFFSET].setValue(temp2);
802  TemperatureOffsetsNP[TEMP_PROBE_THREE_OFFSET].setValue(temp3);
803  TemperatureOffsetsNP.setState(IPS_OK);
804  TemperatureOffsetsNP.apply();
805  }
806 
807  if (!sendCommand(MDCP_GET_AMB_TEMP_OFFSET, resp)) {
808  LOG_INFO(resp);
809  return false;
810  }
811  int atBias = 0;
812  if (sscanf(resp, MDCP_GET_AMB_TEMP_OFFSET_RESPONSE, &atBias) == 1) {
813  TemperatureOffsetsNP[AMBIENT_TEMP_PROBE_OFFSET].setValue(atBias);
814  TemperatureOffsetsNP.setState(IPS_OK);
815  TemperatureOffsetsNP.apply();
816  }
817  if (!sendCommand(MDCP_GET_TRACKING_MODE, resp)) {
818  LOG_INFO(resp);
819  return false;
820  }
821  int mode;
822 
823  if (sscanf(resp, MDCP_GET_TRACKING_MODE_RESPONSE, &mode) == 1) {
824  TrackingModeSP.reset();
825  TrackingModeSP[mode].setState(ISS_ON);
826  TrackingModeSP.setState(IPS_OK);
827  TrackingModeSP.apply();
828  }
829 
830  if (!sendCommand(MDCP_GET_TRACKING_MODE_OFFSET, resp)) {
831  LOG_INFO(resp);
832  return false;
833  }
834  int toffset = 0;
835 
836  if (sscanf(resp, "y%d$", &toffset) == 1) {
837  TrackingModeOffsetNP[0].setValue(toffset);
838  TrackingModeOffsetNP.setState(IPS_OK);
839  TrackingModeOffsetNP.apply();
840  }
841  return true;
842 }
843 
844 
845 bool myDewControllerPro::readBoardFanValues()
846 {
847  char resp[MDCP_RES_LEN];
848 
849  if (!sendCommand(MDCP_GET_FAN_SPEED, resp)) {
850  LOG_INFO(resp);
851  return false;
852  }
853  int fanSpeed;
854 
855  if (sscanf(resp, "F%d$", &fanSpeed) == 1) {
856  FanSpeedNP[0].setValue(fanSpeed);
857  FanSpeedNP.setState(IPS_OK);
858  FanSpeedNP.apply();
859  }
860 
861  if (!sendCommand(MDCP_GET_FAN_MODE, resp)) {
862  LOG_INFO(resp);
863  return false;
864  }
865  int mode;
866  if (sscanf(resp, MDCP_GET_FAN_MODE_RESPONSE, &mode) == 1) {
867  FanModeSP.reset();
868  FanModeSP[mode].setState(ISS_ON);
869  FanModeSP.setState(IPS_OK);
870  FanModeSP.apply();
871  }
872 
873  if (!sendCommand(MDCP_GET_FAN_ON_TEMP, resp)) {
874  LOG_INFO(resp);
875  return false;
876  }
877 
878  int fanTemp;
879 
880  if (sscanf(resp, MDCP_GET_FAN_ON_TEMP_RESPONSE, &fanTemp) == 1) {
881  FanTempTriggerNP[FANTEMPON].setValue(fanTemp);
882  FanTempTriggerNP.setState(IPS_OK);
883  FanTempTriggerNP.apply();
884  }
885 
886  if (!sendCommand(MDCP_GET_FAN_OFF_TEMP, resp)) {
887  LOG_INFO(resp);
888  return false;
889  }
890 
891  if (sscanf(resp, MDCP_GET_FAN_OFF_TEMP_RESPONSE, &fanTemp) == 1) {
892  FanTempTriggerNP[FANTEMPOFF].setValue(fanTemp);
893  FanTempTriggerNP.setState(IPS_OK);
894  FanTempTriggerNP.apply();
895  }
896 
897  return true;
898 }
899 
900 bool myDewControllerPro::readLCDDisplayValues()
901 {
902  char resp[MDCP_RES_LEN];
903  int value;
904 
905  if (!sendCommand(MDCP_GET_LCD_DISPLAY_TIME, resp)) {
906  LOG_INFO(resp);
907  return false;
908  }
909  if (sscanf(resp, MDCP_GET_LCD_DISPLAY_TIME_RESPONSE, &value) == 1) {
910  LCDPageRefreshNP[0].setValue(value);
911  LCDPageRefreshNP.setState(IPS_OK);
912  LCDPageRefreshNP.apply();
913  }
914 
915  if (!sendCommand(MDCP_GET_LCD_STATE, resp)) {
916  LOG_INFO(resp);
917  return false;
918  }
919 
920  if (sscanf(resp, MDCP_GET_LCD_STATE_RESPONSE, &value) == 1) {
921  EnableLCDDisplaySP.reset();
922  EnableLCDDisplaySP[value].setState(ISS_ON);
923  EnableLCDDisplaySP.setState(IPS_OK);
924  EnableLCDDisplaySP.apply();
925  }
926 
927  if (!sendCommand(MDCP_GET_TEMP_DISPLAY, resp)) {
928  LOG_INFO(resp);
929  return false;
930  }
931 
932  if (sscanf(resp, MDCP_GET_TEMP_DISPLAY_RESPONSE, &value) == 1) {
933  LCDDisplayTempUnitsSP.reset();
934  LCDDisplayTempUnitsSP[value-1].setState(ISS_ON);
935  LCDDisplayTempUnitsSP.setState(IPS_OK);
936  LCDDisplayTempUnitsSP.apply();
937  }
938  return true;
939 }
940 
942 {
943  if (!isConnected())
944  {
945  return;
946  }
947 
948  // Get temperatures etc.
949  readMainValues();
951 }
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...
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
void addPollPeriodControl()
Add Polling period control to the driver.
void addConfigurationControl()
Add Configuration control to 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...
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 loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
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.
void addDebugControl()
Add Debug control to the driver.
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool isNameMatch(const char *otherName) const
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
virtual 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 void TimerHit() override
Callback function to be called once SetTimer duration elapses.
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.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
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
@ IP_WO
Definition: indiapi.h:185
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
@ ISR_NOFMANY
Definition: indiapi.h:175
@ ISR_ATMOST1
Definition: indiapi.h:174
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
int tty_nread_section(int fd, char *buf, int nsize, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:666
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define MAXRBUF
Definition: indiserver.cpp:102
#define MYDEWHEATERPRO_TIMEOUT
std::unique_ptr< myDewControllerPro > mydewcontrollerpro(new myDewControllerPro())
#define LCD_DISPLAY_TAB
#define BOARD_FAN_TAB
#define TEMPERATURE_OFFSETS_TAB
#define MDCP_GET_TEMP_DISPLAY_RESPONSE
#define MDCP_GET_PROBE_TEMPS
#define MDCP_GET_AMB_TEMP_OFFSET
#define MDCP_GET_FAN_ON_TEMP_RESPONSE
#define MDCP_SET_TRACKING_MODE
#define MDCP_SET_TRACKING_MODE_OFFSET
#define MDCP_SAVE_TO_EEPROM
#define MDCP_GET_TRACKING_MODE
#define MDCP_GET_CH3_SETTINGS_RESPONSE
#define MDCP_GET_TRACKING_MODE_OFFSET
#define MDCP_GET_BOARD_TEMP_RESPONSE
#define MDCP_GET_CHANNEL_POWER_RESPONSE
#define MDCP_GET_FAN_OFF_TEMP_RESPONSE
#define MDCP_GET_AMB_TEMP
#define MDCP_GET_FAN_OFF_TEMP
#define MDCP_GET_VERSION
#define MDCP_CLEAR_TEMP_OFFSETS
#define MDCP_GET_TEMP_DISPLAY
#define MDCP_GET_REL_HUMIDITY_REPSONSE
#define MDCP_LCD_DISABLE
#define MDCP_GET_REL_HUMIDITY
#define MDCP_LCD_ENABLE
#define MDCP_GET_TEMP_OFFSETS
#define MDCP_GET_FAN_MODE_RESPONSE
#define MDCP_CANCEL_BOOST
#define MDCP_GET_NUMBER_OF_PROBES
#define MDCP_LCD_DISPLAY_CELSIUS
#define MDCP_SET_LCD_DISPLAY_TIME
#define MDCP_LCD_DISPLAY_FAHRENHEIT
#define MDCP_SET_CH3_SETTINGS
#define MDCP_GET_LCD_STATE
#define MDCP_SET_CH3_MANUAL_POWER
#define MDCP_GET_FAN_SPEED
#define MDCP_GET_AMB_TEMP_REPSONSE
#define MDCP_CMD_LEN
#define MDCP_GET_DEW_POINT_RESPONSE
#define MDCP_BOOST_CH1
#define MDCP_GET_AMB_TEMP_OFFSET_RESPONSE
#define MDCP_SET_TEMP_CH1_OFFSET
#define MDCP_IDENTIFY_RESPONSE
#define MDCP_GET_LCD_DISPLAY_TIME
#define MDCP_SET_FAN_SPEED
#define MDCP_SET_TEMP_CH2_OFFSET
#define MDCP_GET_CHANNEL_POWER
#define MDCP_GET_LCD_STATE_RESPONSE
#define MDCP_GET_FAN_ON_TEMP
#define MDCP_GET_FAN_MODE
#define MDCP_GET_TEMP_RESPONSE
#define MDCP_SET_FAN_MODE
#define MDCP_GET_LCD_DISPLAY_TIME_RESPONSE
#define MDCP_RES_LEN
#define MDCP_GET_BOARD_TEMP
#define MDCP_GET_TRACKING_MODE_RESPONSE
#define MDCP_SET_FAN_ON_TEMP
#define MDCP_GET_CH3_SETTINGS
#define MDCP_BOOST_CH2
#define MDCP_GET_DEW_POINT
#define MDCP_SET_TEMP_CH3_OFFSET
#define MDCP_SET_FAN_OFF_TEMP
#define MDCP_RESET_EEPROM_TO_DEFAULT
#define MDCP_GET_TEMP_OFFSETS_RESPONSE
#define MDCP_SET_AMB_TEMP_OFFSET
@ value
the parser finished reading a JSON value
__u8 cmd[4]
Definition: pwc-ioctl.h:2