Instrument Neutral Distributed Interface INDI  2.0.2
scopedome_arduino.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  ScopeDome Dome INDI Driver
3 
4  Copyright(c) 2017-2021 Jarno Paananen. All rights reserved.
5 
6  based on:
7 
8  ScopeDome Windows ASCOM driver version 5.1.30
9 
10  This library is free software; you can redistribute it and/or
11  modify it under the terms of the GNU Library General Public
12  License version 2 as published by the Free Software Foundation.
13  .
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  Library General Public License for more details.
18  .
19  You should have received a copy of the GNU Library General Public License
20  along with this library; see the file COPYING.LIB. If not, write to
21  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  Boston, MA 02110-1301, USA.
23 *******************************************************************************/
24 
25 #include "scopedome_arduino.h"
28 #include "indicom.h"
29 
30 #include <termios.h>
31 
32 #include <cstring>
33 
34 #define SCOPEDOME_TIMEOUT 2
35 #define SCOPEDOME_MAX_READS 10
36 
37 static size_t WriteCallback(void *contents, size_t size, size_t nmemb, void *userp)
38 {
39  static_cast<std::string *>(userp)->append((char *)contents, size * nmemb);
40  return size * nmemb;
41 }
42 
44  : parent(driver), interface(iface)
45 {
47  {
48  ethernet = false;
49  PortFD = static_cast<Connection::Serial*>(interface)->getPortFD();
50  }
51  else
52  {
53  ethernet = true;
54  curl_global_init(CURL_GLOBAL_ALL);
55  curl = curl_easy_init();
56  if (curl)
57  {
58  curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L);
59  const INDI::PropertyText &credentials = parent->getCredentials();
60  curl_easy_setopt(curl, CURLOPT_USERNAME, credentials[0].getText());
61  curl_easy_setopt(curl, CURLOPT_PASSWORD, credentials[1].getText());
62 
63  // (Ab)use TCP connection to get host and port (though port is not actually used)
64  auto tcp = static_cast<Connection::TCP*>(interface);
65  hostName = tcp->host();
66  port = tcp->port();
67  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
68  }
69  else
70  {
71  LOG_ERROR("Error initializing curl library");
72  }
73  }
74 }
75 
77 {
78  if (ethernet)
79  {
80  curl_global_cleanup();
81  if (curl)
82  {
83  curl_easy_cleanup(curl);
84  curl = nullptr;
85  }
86  }
87 }
88 
90 {
91  std::string res;
92  if(performCommand("getMode", res))
93  {
94  return (res == "MASTER"); // only master & slave mode is currently supported (not clamshell or rolloff root)
95  }
96  return false;
97 }
98 
99 bool ScopeDomeArduino::performCommand(const std::string &command, std::string &response)
100 {
101  if (ethernet)
102  {
103  if (curl)
104  {
105  std::string readBuffer;
106  char requestURL[MAXRBUF + 1];
107 
108  // Write buffer
109  LOGF_DEBUG("write cmd: %s", command.c_str());
110 
111  snprintf(requestURL, MAXRBUF, "http://%s/?%s", hostName.c_str(), command.c_str());
112 
113  curl_easy_setopt(curl, CURLOPT_URL, requestURL);
114  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
115  curl_easy_perform(curl);
116 
117  if(readBuffer.size() < 3)
118  {
119  LOGF_ERROR("Error reading: %s. Cmd: %s", readBuffer.c_str(), command.c_str());
120  return false;
121  }
122  // Response is in form command|status|response, strip \r\n
123  std::string resp(readBuffer.begin(), readBuffer.end() - 2);
124 
125  auto split = splitString(resp, '|');
126 
127  if (split.size() != 3)
128  {
129  LOGF_ERROR("Invalid response: %s. Cmd: %s", resp.c_str(), command.c_str());
130  return false;
131  }
132 
133  if (split[1] != "OK")
134  {
135  LOGF_ERROR("Error from device: %s. Cmd: %s", resp.c_str(), command.c_str());
136  return false;
137  }
138  response = split[2];
139  LOGF_DEBUG("read response: %s", response.c_str());
140  return true;
141  }
142  else
143  {
144  return false;
145  }
146  }
147  else
148  {
149  int nbytes_written = 0, rc = -1;
150  char errstr[MAXRBUF];
151 
152  if (command.length() == 0)
153  return false;
154 
155  tcflush(PortFD, TCIOFLUSH);
156 
157  // Write buffer
158  LOGF_DEBUG("write cmd: %s", command.c_str());
159 
160  char lineBuf[MAXRBUF + 1];
161  snprintf(lineBuf, MAXRBUF, "%s\r\n", command.c_str());
162 
163  if ((rc = tty_write_string(PortFD, lineBuf, &nbytes_written)) != TTY_OK)
164  {
165  tty_error_msg(rc, errstr, MAXRBUF);
166  LOGF_ERROR("Error writing command: %s. Cmd: %s", errstr, command.c_str());
167  return false;
168  }
169 
170  int nbytes_read = 0;
171 
172  // Read response
173  if ((rc = tty_nread_section(PortFD, lineBuf, MAXRBUF, '\n', SCOPEDOME_TIMEOUT, &nbytes_read)) != TTY_OK)
174  {
175  tty_error_msg(rc, errstr, MAXRBUF);
176  LOGF_ERROR("Error reading: %s. Cmd: %s", errstr, command.c_str());
177  return false;
178  }
179  lineBuf[nbytes_read] = 0;
180  int len = strlen(lineBuf);
181  if(len < 3)
182  {
183  LOGF_ERROR("Error reading: %s. Cmd: %s", errstr, command.c_str());
184  return false;
185  }
186  // Response is in form command|status|response, strip \n
187  std::string resp(lineBuf, strlen(lineBuf) - 1);
188 
189  auto split = splitString(resp, '|');
190 
191  if (split.size() != 3)
192  {
193  LOGF_ERROR("Invalid response: %s. Cmd: %s", lineBuf, command.c_str());
194  return false;
195  }
196 
197  if (split[1] != "OK")
198  {
199  LOGF_ERROR("Error from device: %s. Cmd: %s", lineBuf, command.c_str());
200  return false;
201  }
202  response = split[2];
203  LOGF_DEBUG("read response: %s", response.c_str());
204  return true;
205  }
206 }
207 
208 void ScopeDomeArduino::getFirmwareVersions(double &main, double &rotary)
209 {
210  std::string res;
211  if(performCommand("getFirmwareVersion", res))
212  {
213  main = std::stod(res);
214  }
215 
216  if(performCommand("slave=getFirmwareVersion", res))
217  {
218  rotary = std::stod(res);
219  }
220 }
221 
223 {
224  std::string res;
225  stepsPerRevolution = 0;
226  if(performCommand("getCalibratedRotation", res))
227  {
228  stepsPerRevolution = std::stoi(res);
229  }
230  if(stepsPerRevolution == 0)
231  {
232  LOG_INFO("Step count read as zero, run calibration");
233  stepsPerRevolution = 3240;
234  }
235  return stepsPerRevolution;
236 }
237 
238 
240 {
241  std::string status;
242  if(!performCommand("getStatus", status))
243  {
244  return -1;
245  }
246 
247  // Parse the string
248  // getStatus|OK|0:1:0:1:1:1:1:1;0.0000:1.0000:0.4399;32000;31.29:38.00:999.90:999.90:999.90:999.90:999.90:999.90:999.90;1009.21:999.90;0;5.09;1:1:1:1:1:1;0:0:0:0:0.0000:0.0000:0:0:0;0;126;1#0:1:1:1:1:1:1:1;0.0000:1.0000:0.4291;32000;32.31:39.00:999.90:999.90:999.90:999.90:999.90:999.90:999.90;1009.30:999.90;0;5.09;1:1:1:1:1:1;0:0:0:0:0.0000:0.0000:0:0:0;0;106;1#1:0:0:0
249  /*
250  get master+slave status in format:
251  <master digital inputs>;<master analog
252  inputs>;<master encoders>;<master
253  temperatures>;<master+slave
254  clouds>;<master Vcc>;<master
255  buttons>;<master relays>;<master loop
256  time>;<master fresh flag>#<slave digital
257  inputs>;<slave analog inputs>;<slave
258  encoders>;<slave temperatures>;<slave
259  clouds>;<slave Vcc>;<slave
260  buttons>;<slave relays>;<slave loop
261  time>;<slave fresh flag>#<flags>
262  */
263  auto parts = splitString(status, '#');
264  if(parts.size() == 3)
265  {
266  auto master = splitString(parts[0], ';');
267 
268  if (master.size() == 12)
269  {
270  int i = 0;
271  for(auto &s : master)
272  {
273  LOGF_DEBUG("master %d: %s", i++, s.c_str());
274  }
275  i = 0;
276  auto digitalInputs = splitString(master[0], ':');
277 
278  for(const auto &input : digitalInputs)
279  {
280  inputs[i++] = (input == "0"); // inverted for some reason
281  }
282 
283  i = 0;
284  auto analogInputs = splitString(master[1], ':');
285  for(const auto &input : analogInputs)
286  {
287  sensors[i++] = std::stod(input);
288  }
289  rotaryEncoder = std::stoi(master[2]);
290  auto temperatures = splitString(master[3], ':');
291  for(const auto &temp : temperatures)
292  {
293  sensors[i++] = std::stod(temp);
294  }
295  auto pressure_humidity = splitString(master[4], ':');
296  for(const auto &value : pressure_humidity)
297  {
298  sensors[i++] = std::stod(value);
299  }
300  std::string clouds = master[5];
301  std::string vcc = master[6];
302  std::string buttons = master[7];
303  auto relayPWM = splitString(master[8], ':');
304  i = 0;
305  for(int r = 6; r < 9; r++)
306  {
307  relays[i++] = (relayPWM[r] == "1");
308  }
309  moving = (relayPWM[0] == "1" || relayPWM[1] == "1");
310  if(!moving && rotaryEncoder != previousEncoder) // Check if the dome is still moving from inertia
311  moving = true;
312  previousEncoder = rotaryEncoder;
313 
314  std::string emergency = master[9];
315  std::string loopTime = master[10];
316  std::string freshFlag = master[11];
317 
318  // Voltage 064V needs some adjustment
319  sensors[0] *= 10.0 * (5.0 / 0.1955);
320  }
321  /*
322  MASTER_INPD_DETECT_230LOSS:
323  MASTER_INPD_ENCODERA:
324  MASTER_INPD_HOMESENSOR:
325  MASTER_INPD_FREE1:
326  MASTER_INPD_FREE2:
327  MASTER_INPD_RAINSENSOR:
328  MASTER_INPD_CLOUDSENSOR:
329  MASTER_INPD_TELESCOPE_A_H;
330  MASTER_INPA_VOLTAGE064:
331  MASTER_INPA_T_PT100:
332  MASTER_INPA_T_PCB;
333  MASTER_ENCODER_A;
334  MASTER_THERMOMETER_PCB:
335  MASTER_BAROMETER_TEMPERATURE:
336  MASTER_THERMOMETER_ONEWIRE_MOTOR:
337  MASTER_THERMOMETER_ONEWIRE_OUTSIDE:
338  MASTER_THERMOMETER_ONEWIRE_MIRROR_1:
339  MASTER_THERMOMETER_ONEWIRE_MIRROR_2:
340  MASTER_HIGROMETER_TEMPERATURE:
341  MASTER_PIROMETER_AMBIENT:
342  MASTER_PIROMETER_SENSOR;
343  MASTER_BAROMETER_PRESSURE:
344  MASTER_HIGROMETER_HUMIDITY;
345  MASTER_CLOUDS;
346  MASTER_Vcc;
347  MASTER_BUTTON_CW:
348  MASTER_BUTTON_CCW:
349  MASTER_BUTTON_FREE1:
350  MASTER_BUTTON_FREE2:
351  MASTER_BUTTON_FREE3:
352  MASTER_BUTTON_PAIR;
353  MASTER_REL_CW:
354  MASTER_REL_CCW:
355  MASTER_REL_INBOX:
356  MASTER_REL_MOTOR:
357  MASTER_PWM_1:
358  MASTER_PWM_2:
359  MASTER_REL_FREE1:
360  MASTER_REL_FREE2:
361  MASTER_REL_FREE3;
362  MASTER_EMERGENCY;
363  MASTER_LOOP_TIME;
364  MASTER_FRESH_FLAG
365  */
366  auto slave = splitString(parts[1], ';');
367 
368  if (slave.size() == 12)
369  {
370  int i = 0;
371  for(auto &s : slave)
372  {
373  LOGF_DEBUG("slave %d: %s", i++, s.c_str());
374  }
375 
376  auto digitalInputs = splitString(slave[0], ':');
377  i = 8;
378  for(const auto &input : digitalInputs)
379  {
380  inputs[i++] = (input == "0"); // inverted for some reason
381  }
382  i = 14;
383  auto analogInputs = splitString(slave[1], ':');
384  for(const auto &input : analogInputs)
385  {
386  sensors[i++] = std::stod(input);
387  }
388  shutterEncoder = std::stoi(slave[2]);
389  auto temperatures = splitString(slave[3], ':');
390  for(const auto &temp : temperatures)
391  {
392  sensors[i++] = std::stod(temp);
393  }
394  auto pressure_humidity = splitString(slave[4], ':');
395  for(const auto &value : pressure_humidity)
396  {
397  sensors[i++] = std::stod(value);
398  }
399  std::string clouds = slave[5];
400  std::string vcc = slave[6];
401  std::string buttons = slave[7];
402  auto relayPWM = splitString(slave[8], ':');
403  i = 3;
404  for(int r = 6; r < 9; r++)
405  {
406  relays[i++] = (relayPWM[r] == "1");
407  }
408  std::string emergency = slave[9];
409  std::string loopTime = slave[10];
410  std::string freshFlag = slave[11];
411 
412  // Voltage 064V needs some adjustment
413  sensors[14] *= 10.0 * (5.0 / 0.1955);
414  }
415 
416  /*
417  SLAVE_INPD_DETECT_230LOSS:
418  SLAVE_INPD_ENCODERA:
419  SLAVE_INPD_HOMESENSOR:
420  SLAVE_INPD_OPEN1:
421  SLAVE_INPD_CLOSE1:
422  SLAVE_INPD_RAINSENSOR:
423  SLAVE_INPD_CLOUDSENSOR:
424  SLAVE_INPD_TELESCOPE_A_H;
425  SLAVE_INPA_VOLTAGE064:
426  SLAVE_INPA_T_PT100:
427  SLAVE_INPA_T_PCB;
428  SLAVE_ENCODER_A;
429  SLAVE_THERMOMETER_PCB:
430  SLAVE_BAROMETER_TEMPERATURE:
431  SLAVE_THERMOMETER_ONEWIRE_MOTOR:
432  SLAVE_THERMOMETER_ONEWIRE_OUTSIDE:
433  SLAVE_THERMOMETER_ONEWIRE_MIRROR_1:
434  SLAVE_THERMOMETER_ONEWIRE_MIRROR_2:
435  SLAVE_HIGROMETER_TEMPERATURE:
436  SLAVE_PIROMETER_AMBIENT:
437  SLAVE_PIROMETER_SENSOR;
438  SLAVE_BAROMETER_PRESSURE:
439  SLAVE_HIGROMETER_HUMIDITY;
440  SLAVE_CLOUDS;
441  SLAVE_Vcc;
442  SLAVE_BUTTON_CW:
443  SLAVE_BUTTON_CCW:
444  SLAVE_BUTTON_FREE1:
445  SLAVE_BUTTON_FREE2:
446  SLAVE_BUTTON_FREE3:
447  SLAVE_BUTTON_PAIR;
448  SLAVE_REL_OPEN1:
449  SLAVE_REL_CLOSE1:
450  SLAVE_REL_INBOX:
451  SLAVE_REL_MOTOR:
452  SLAVE_PWM_1:
453  SLAVE_PWM_2:
454  SLAVE_REL_FREE1:
455  SLAVE_REL_FREE2:
456  SLAVE_REL_FREE3;
457  SLAVE_EMERGENCY;
458  SLAVE_LOOP_TIME;
459  SLAVE_FRESH_FLAG
460  */
461 
462  auto flags = splitString(parts[2], ':');
463  if (flags.size() == 4)
464  {
465  int i = 0;
466  for(auto &s : flags)
467  {
468  LOGF_DEBUG("flags %d: %s", i++, s.c_str());
469  }
470  rotaryLink = (flags[0] == "1");
471  homing = (flags[1] == "1");
472  moveShutterOnHome = (flags[2] == "1");
473  calibrating = (flags[3] == "1");
474  }
475  /*
476  IS_SLAVE_ONLINE:
477  IS_COMPLEX_COMMAND_FINDING_HOME:
478  IS_COMPLEX_COMMAND_MOVE_SHUTTER_ON_HOME:
479  IS_COMPLEX_COMMAND_CALIBRATING
480  */
481  }
482  else
483  {
484  LOGF_DEBUG("invalid status response: %s", status.c_str());
485  }
486  return 0;
487 }
488 
490 {
491  uint32_t status = 0;
492  if(homing)
493  {
494  status |= STATUS_HOMING | STATUS_MOVING;
495  }
496  if(calibrating)
497  {
498  status |= STATUS_CALIBRATING | STATUS_MOVING;
499  }
500  if(moving)
501  {
502  status |= STATUS_MOVING;
503  }
504  LOGF_DEBUG("getStatus: %x", status);
505  return status;
506 }
507 
508 // Abstract versions
510 {
511  bool state = false;
512 
513  switch(input)
514  {
515  case HOME:
516  state = inputs[2];
517  break;
518  case OPEN1:
519  state = inputs[11];
520  break;
521  case CLOSED1:
522  state = inputs[12];
523  break;
524  case OPEN2:
525  case CLOSED2:
526  break;
527  case ROTARY_LINK:
528  state = rotaryLink;
529  break;
530  default:
531  LOG_ERROR("invalid input");
532  break;
533  }
534  return state ? ISS_ON : ISS_OFF;
535 }
536 
538 {
539  std::string res;
540  switch(output)
541  {
542  case RESET:
543  // performCommand("resetSoft", res);
544  break;
545  case CW:
546  if(onOff == ISS_ON)
547  {
548  performCommand("moveDome=CW", res);
549  }
550  else
551  {
552  performCommand("stopDome", res);
553  }
554  break;
555  case CCW:
556  if(onOff == ISS_ON)
557  {
558  performCommand("moveDome=CCW", res);
559  }
560  else
561  {
562  performCommand("stopDome", res);
563  }
564  break;
565  default:
566  LOG_ERROR("invalid output");
567  return -1;
568  }
569  return 0;
570 }
571 
573 {
574  // Make rotary encoder value similar to USB Card 2.1
575  return encoderBaseValue - rotaryEncoder;
576 }
577 
579 {
580  // Make relative to home sensor position like with USB Card 2.1
581  return encoderBaseValue - rotaryEncoder;
582 }
583 
585 {
586  return false;
587 }
588 
590 {
591  std::string res;
592  performCommand("stopDome", res);
593  performCommand("stopShutter", res);
594 }
595 
597 {
598  std::string res;
599  performCommand("calibrate", res);
600 }
601 
603 {
604  std::string res;
605  performCommand("findHome", res);
606 }
607 
609 {
610  std::string res;
611  switch(operation)
612  {
613  case OPEN_SHUTTER:
614  performCommand("moveShutter=OPEN", res);
615  break;
616  case CLOSE_SHUTTER:
617  performCommand("moveShutter=CLOSE", res);
618  break;
619  case STOP_SHUTTER:
620  performCommand("stopShutter", res);
621  break;
622  default:
623  LOG_ERROR("Unknown shutter operation");
624  break;
625  }
626 }
627 
629 {
630  // Doesn't seem to be needed as the counter resets by itself when passing home
631  // std::string res;
632  // char cmd[32] = {0};
633  // snprintf(cmd, 31, "setEncoderA=%d", encoderBaseValue);
634  // performCommand(cmd, res);
635 }
636 
637 void ScopeDomeArduino::move(int steps)
638 {
639  std::string res;
640  if(steps < 0 )
641  {
642  char cmd[32] = {0};
643  snprintf(cmd, 31, "moveDome=CCW:%d", -steps);
644  performCommand(cmd, res);
645  }
646  else
647  {
648  char cmd[32] = {0};
649  snprintf(cmd, 31, "moveDome=CW:%d", steps);
650  performCommand(cmd, res);
651  }
652 }
653 
655 {
656  return 28;
657 }
658 
660 {
662  switch(index)
663  {
664  case 0:
665  info.propName = "VOLTAGE064";
666  info.label = "Master 64V";
667  info.format = "%3.2f";
668  info.minValue = 0;
669  info.maxValue = 100;
670  break;
671  case 1:
672  info.propName = "T_PT100";
673  info.label = "T_PT100";
674  info.format = "%3.2f";
675  info.minValue = -100;
676  info.maxValue = 100;
677  break;
678  case 2:
679  info.propName = "T_PCB";
680  info.label = "T_PCB";
681  info.format = "%3.2f";
682  info.minValue = -100;
683  info.maxValue = 100;
684  break;
685  case 3:
686  info.propName = "THERMOMETER_PCB";
687  info.label = "PCB thermometer";
688  info.format = "%3.2f";
689  info.minValue = -100;
690  info.maxValue = 100;
691  break;
692  case 4:
693  info.propName = "BAROMETER_TEMPERATURE";
694  info.label = "Barometer temperature";
695  info.format = "%3.2f";
696  info.minValue = -100;
697  info.maxValue = 100;
698  break;
699  case 5:
700  info.propName = "THERMOMETER_ONEWIRE_MOTOR";
701  info.label = "Motor temperature";
702  info.format = "%3.2f";
703  info.minValue = -100;
704  info.maxValue = 100;
705  break;
706  case 6:
707  info.propName = "THERMOMETER_ONEWIRE_OUTSIDE";
708  info.label = "Outside temperature";
709  info.format = "%3.2f";
710  info.minValue = -100;
711  info.maxValue = 100;
712  break;
713  case 7:
714  info.propName = "THERMOMETER_ONEWIRE_MIRROR_1";
715  info.label = "Mirror 1 temperature";
716  info.format = "%3.2f";
717  info.minValue = -100;
718  info.maxValue = 100;
719  break;
720  case 8:
721  info.propName = "THERMOMETER_ONEWIRE_MIRROR_2";
722  info.label = "Mirror 2 temperature";
723  info.format = "%3.2f";
724  info.minValue = -100;
725  info.maxValue = 100;
726  break;
727  case 9:
728  info.propName = "HIGROMETER_TEMPERATURE";
729  info.label = "Higrometer temperature";
730  info.format = "%3.2f";
731  info.minValue = -100;
732  info.maxValue = 100;
733  break;
734  case 10:
735  info.propName = "PIROMETER_AMBIENT";
736  info.label = "Pirometer ambient temperature";
737  info.format = "%3.2f";
738  info.minValue = -100;
739  info.maxValue = 100;
740  break;
741  case 11:
742  info.propName = "PIROMETER_SENSOR";
743  info.label = "Pirometer sensor temperature";
744  info.format = "%3.2f";
745  info.minValue = -100;
746  info.maxValue = 100;
747  break;
748  case 12:
749  info.propName = "BAROMETER_PRESSURE";
750  info.label = "Barometer pressure";
751  info.format = "%4.2f";
752  info.minValue = 0;
753  info.maxValue = 2000;
754  break;
755  case 13:
756  info.propName = "HIGROMETER_HUMIDITY";
757  info.label = "Higrometer humidity";
758  info.format = "%3.2f";
759  info.minValue = 0;
760  info.maxValue = 100;
761  break;
762  case 14:
763  info.propName = "S_VOLTAGE064";
764  info.label = "Slave 64V";
765  info.format = "%3.2f";
766  info.minValue = 0;
767  info.maxValue = 100;
768  break;
769  case 15:
770  info.propName = "S_T_PT100";
771  info.label = "Slave T_PT100";
772  info.format = "%3.2f";
773  info.minValue = -100;
774  info.maxValue = 100;
775  break;
776  case 16:
777  info.propName = "S_T_PCB";
778  info.label = "Slave T_PCB";
779  info.format = "%3.2f";
780  info.minValue = -100;
781  info.maxValue = 100;
782  break;
783  case 17:
784  info.propName = "S_THERMOMETER_PCB";
785  info.label = "Slave PCB thermometer";
786  info.format = "%3.2f";
787  info.minValue = -100;
788  info.maxValue = 100;
789  break;
790  case 18:
791  info.propName = "S_BAROMETER_TEMPERATURE";
792  info.label = "Slave barometer temperature";
793  info.format = "%3.2f";
794  info.minValue = -100;
795  info.maxValue = 100;
796  break;
797  case 19:
798  info.propName = "S_THERMOMETER_ONEWIRE_MOTOR";
799  info.label = "Slave motor temperature";
800  info.format = "%3.2f";
801  info.minValue = -100;
802  info.maxValue = 100;
803  break;
804  case 20:
805  info.propName = "S_THERMOMETER_ONEWIRE_OUTSIDE";
806  info.label = "Slave outside temperature";
807  info.format = "%3.2f";
808  info.minValue = -100;
809  info.maxValue = 100;
810  break;
811  case 21:
812  info.propName = "S_THERMOMETER_ONEWIRE_MIRROR_1";
813  info.label = "Slave mirror 1 temperature";
814  info.format = "%3.2f";
815  info.minValue = -100;
816  info.maxValue = 100;
817  break;
818  case 22:
819  info.propName = "S_THERMOMETER_ONEWIRE_MIRROR_2";
820  info.label = "Slave mirror 2 temperature";
821  info.format = "%3.2f";
822  info.minValue = -100;
823  info.maxValue = 100;
824  break;
825  case 23:
826  info.propName = "S_HIGROMETER_TEMPERATURE";
827  info.label = "Slave higrometer temperature";
828  info.format = "%3.2f";
829  info.minValue = -100;
830  info.maxValue = 100;
831  break;
832  case 24:
833  info.propName = "S_PIROMETER_AMBIENT";
834  info.label = "Slave pirometer ambient temperature";
835  info.format = "%3.2f";
836  info.minValue = -100;
837  info.maxValue = 100;
838  break;
839  case 25:
840  info.propName = "S_PIROMETER_SENSOR";
841  info.label = "Slave pirometer sensor temperature";
842  info.format = "%3.2f";
843  info.minValue = -100;
844  info.maxValue = 100;
845  break;
846  case 26:
847  info.propName = "S_BAROMETER_PRESSURE";
848  info.label = "Slave barometer pressure";
849  info.format = "%4.2f";
850  info.minValue = 0;
851  info.maxValue = 2000;
852  break;
853  case 27:
854  info.propName = "S_HIGROMETER_HUMIDITY";
855  info.label = "Slave higrometer humidity";
856  info.format = "%3.2f";
857  info.minValue = 0;
858  info.maxValue = 100;
859  break;
860  default:
861  LOG_ERROR("invalid sensor index");
862  break;
863  }
864  return info;
865 }
866 
868 {
869  return sensors[index];
870 }
871 
873 {
874  return 6;
875 }
876 
878 {
880  switch(index)
881  {
882  case 0:
883  info.propName = "RELAY_1";
884  info.label = "Relay 1";
885  break;
886  case 1:
887  info.propName = "RELAY_2";
888  info.label = "Relay 2";
889  break;
890  case 2:
891  info.propName = "RELAY_3";
892  info.label = "Relay 3";
893  break;
894  case 3:
895  info.propName = "S_RELAY_1";
896  info.label = "Slave relay 1";
897  break;
898  case 4:
899  info.propName = "S_RELAY_2";
900  info.label = "Slave relay 2";
901  break;
902  case 5:
903  info.propName = "S_RELAY_3";
904  info.label = "Slave relay 3";
905  break;
906  default:
907  LOG_ERROR("invalid relay index");
908  break;
909  }
910  return info;
911 }
912 
914 {
915  return relays[index] ? ISS_ON : ISS_OFF;
916 }
917 
918 void ScopeDomeArduino::setRelayState(size_t index, ISState state)
919 {
920  std::string res;
921  std::string cmd;
922  if(index >= 3)
923  {
924  cmd = "slave=";
925  index -= 3;
926  }
927  if(state == ISS_ON)
928  {
929  cmd += "switchOnFreeRelay=";
930  }
931  else
932  {
933  cmd += "switchOffFreeRelay=";
934  }
935  cmd += std::to_string(index + 1);
936  performCommand(cmd, res);
937 }
938 
940 {
941  return 16;
942 }
943 
945 {
947  switch(index)
948  {
949  case 0:
950  info.propName = "DETECT_230LOSS";
951  info.label = "Detect 230V loss";
952  break;
953  case 1:
954  info.propName = "ENCODERA";
955  info.label = "Rotary encoder";
956  break;
957  case 2:
958  info.propName = "HOMESENSOR";
959  info.label = "Home sensor";
960  break;
961  case 3:
962  info.propName = "FREE1";
963  info.label = "Free 1";
964  break;
965  case 4:
966  info.propName = "FREE2";
967  info.label = "Free 2";
968  break;
969  case 5:
970  info.propName = "RAINSENSOR";
971  info.label = "Rain sensor";
972  break;
973  case 6:
974  info.propName = "CLOUDSENSOR";
975  info.label = "Cloud sensor";
976  break;
977  case 7:
978  info.propName = "TELESCOPE_A_H";
979  info.label = "Telescope at home";
980  break;
981  case 8:
982  info.propName = "S_DETECT_230LOSS";
983  info.label = "Slave detect 230V loss";
984  break;
985  case 9:
986  info.propName = "S_ENCODERA";
987  info.label = "Shutter encoder";
988  break;
989  case 10:
990  info.propName = "S_HOMESENSOR";
991  info.label = "Slave home sensor";
992  break;
993  case 11:
994  info.propName = "OPEN1";
995  info.label = "Shutter 1 open";
996  break;
997  case 12:
998  info.propName = "CLOSED1";
999  info.label = "Shutter 1 closed";
1000  break;
1001  case 13:
1002  info.propName = "S_RAINSENSOR";
1003  info.label = "Slave rain sensor";
1004  break;
1005  case 14:
1006  info.propName = "S_CLOUDSENSOR";
1007  info.label = "Slave cloud sensor";
1008  break;
1009  case 15:
1010  info.propName = "S_TELESCOPE_A_H";
1011  info.label = "Slave telescope at home";
1012  break;
1013  default:
1014  LOG_ERROR("invalid input index");
1015  break;
1016  }
1017  return info;
1018 }
1019 
1021 {
1022  return inputs[index] ? ISS_ON : ISS_OFF;
1023 }
1024 
1026 {
1027  std::string res;
1028  std::string cmd = "setHomeSignalLow=";
1029 
1030  switch(polarity)
1031  {
1032  case ACTIVE_HIGH:
1033  cmd += "0";
1034  break;
1035  case ACTIVE_LOW:
1036  cmd += "1";
1037  break;
1038  }
1039  performCommand(cmd, res);
1040 }
1041 
1042 std::vector<std::string> ScopeDomeArduino::splitString(const std::string &src, char splitChar)
1043 {
1044  std::vector<std::string> results;
1045 
1046  size_t start = 0;
1047  size_t end;
1048  while( (end = src.find(splitChar, start)) != std::string::npos)
1049  {
1050  results.push_back(src.substr(start, end - start));
1051  start = end + 1;
1052  }
1053  results.push_back(src.substr(start, std::string::npos));
1054  return results;
1055 }
The Interface class is the base class for all INDI connection plugins.
virtual Type type()
type Return connection type
The Serial class manages connection with serial devices including Bluetooth. Serial communication is ...
The TCP class manages connection with devices over the network via TCP/IP. Upon successfull connectio...
Definition: connectiontcp.h:38
virtual SensorInfo getSensorInfo(size_t index) override
virtual void controlShutter(ShutterOperation operation) override
virtual size_t getNumberOfRelays() override
virtual void setHomeSensorPolarity(HomeSensorPolarity polarity) override
virtual ISState getInputValue(size_t index) override
ScopeDomeArduino(ScopeDome *driver, Connection::Interface *interface)
virtual int getRotationCounterExt() override
virtual uint32_t getStatus() override
virtual void resetCounter() override
virtual size_t getNumberOfSensors() override
virtual uint32_t getStepsPerRevolution() override
virtual void getFirmwareVersions(double &main, double &rotary) override
virtual bool isCalibrationNeeded() override
virtual bool detect() override
virtual void abort() override
virtual double getSensorValue(size_t index) override
virtual size_t getNumberOfInputs() override
virtual RelayInfo getRelayInfo(size_t index) override
virtual ISState getInputState(AbstractInput input) override
virtual int getRotationCounter() override
virtual void calibrate() override
virtual ISState getRelayState(size_t index) override
virtual void findHome() override
virtual void move(int steps) override
virtual void setRelayState(size_t index, ISState state) override
virtual int updateState() override
virtual int setOutputState(AbstractOutput output, ISState state) override
virtual InputInfo getInputInfo(size_t index) override
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
int tty_write_string(int fd, const char *buf, int *nbytes_written)
Writes a null terminated string to fd.
Definition: indicom.c:474
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 LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define MAXRBUF
Definition: indiserver.cpp:102
std::vector< std::string > split(const std::string &input, const std::string &regex)
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 SCOPEDOME_TIMEOUT
int main(int, char *[])