Instrument Neutral Distributed Interface INDI  2.0.2
connectionserial.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2017 Jasem Mutlaq. All rights reserved.
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License version 2 as published by the Free Software Foundation.
7 
8  This library is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  Library General Public License for more details.
12 
13  You should have received a copy of the GNU Library General Public License
14  along with this library; see the file COPYING.LIB. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17 *******************************************************************************/
18 
19 #include "connectionserial.h"
20 #include "indistandardproperty.h"
21 #include "indicom.h"
22 #include "indilogger.h"
23 
24 #include <dirent.h>
25 #include <cerrno>
26 #include <cstring>
27 #include <algorithm>
28 #include <thread>
29 #include <chrono>
30 #include <regex>
31 #include <random>
32 
33 namespace Connection
34 {
35 extern const char *CONNECTION_TAB;
36 
37 Serial::Serial(INDI::DefaultDevice *dev) : Interface(dev, CONNECTION_SERIAL)
38 {
39  char configPort[256] = {0};
40  // Try to load the port from the config file. If that fails, use default port.
41  if (IUGetConfigText(dev->getDeviceName(), INDI::SP::DEVICE_PORT, "PORT", configPort, 256) == 0)
42  {
43  m_ConfigPort = configPort;
44  IUFillText(&PortT[0], "PORT", "Port", configPort);
45  }
46  else
47  {
48 #ifdef __APPLE__
49  IUFillText(&PortT[0], "PORT", "Port", "/dev/cu.usbserial");
50 #else
51  IUFillText(&PortT[0], "PORT", "Port", "/dev/ttyUSB0");
52 #endif
53  }
55  IPS_IDLE);
56 
57  int autoSearchIndex = 0;
58  // Try to load the port from the config file. If that fails, use default port.
60  IUFillSwitch(&AutoSearchS[INDI::DefaultDevice::INDI_ENABLED], "INDI_ENABLED", "Enabled",
61  autoSearchIndex == 0 ? ISS_ON : ISS_OFF);
62  IUFillSwitch(&AutoSearchS[INDI::DefaultDevice::INDI_DISABLED], "INDI_DISABLED", "Disabled",
63  autoSearchIndex == 0 ? ISS_OFF : ISS_ON);
66 
67  IUFillSwitch(&RefreshS[0], "Scan Ports", "Scan Ports", ISS_OFF);
68  IUFillSwitchVector(&RefreshSP, RefreshS, 1, dev->getDeviceName(), "DEVICE_PORT_SCAN", "Refresh", CONNECTION_TAB,
69  IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
70 
71  IUFillSwitch(&BaudRateS[0], "9600", "", ISS_ON);
72  IUFillSwitch(&BaudRateS[1], "19200", "", ISS_OFF);
73  IUFillSwitch(&BaudRateS[2], "38400", "", ISS_OFF);
74  IUFillSwitch(&BaudRateS[3], "57600", "", ISS_OFF);
75  IUFillSwitch(&BaudRateS[4], "115200", "", ISS_OFF);
76  IUFillSwitch(&BaudRateS[5], "230400", "", ISS_OFF);
78  IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
79 
80  // Try to load the port from the config file. If that fails, use default port.
82  // If we have a valid config entry, se it.
83  if (m_ConfigBaudRate >= 0)
84  {
87  }
88 }
89 
91 {
92  delete[] SystemPortS;
93 }
94 
95 bool Serial::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
96 {
97  if (!strcmp(dev, m_Device->getDeviceName()))
98  {
99  // Serial Port
100  if (!strcmp(name, PortTP.name))
101  {
102  IUUpdateText(&PortTP, texts, names, n);
103  PortTP.s = IPS_OK;
104  IDSetText(&PortTP, nullptr);
105 
106  auto pos = std::find_if(m_SystemPorts.begin(), m_SystemPorts.end(), [&](const std::string onePort)
107  {
108  return !strcmp(PortT[0].text, onePort.c_str());
109  });
110  if (pos != m_SystemPorts.end())
111  {
112  LOGF_DEBUG("Auto search is disabled because %s is not a system port.", PortT[0].text);
113  AutoSearchS[0].s = ISS_OFF;
114  AutoSearchS[1].s = ISS_ON;
115  IDSetSwitch(&AutoSearchSP, nullptr);
116  }
117  }
118  return true;
119  }
120 
121  return false;
122 }
123 
124 bool Serial::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
125 {
126  if (!strcmp(dev, m_Device->getDeviceName()))
127  {
128  // Change Baud Rate
129  if (!strcmp(name, BaudRateSP.name))
130  {
131  IUUpdateSwitch(&BaudRateSP, states, names, n);
132  BaudRateSP.s = IPS_OK;
133  IDSetSwitch(&BaudRateSP, nullptr);
134  return true;
135  }
136 
137  // Auto Search Devices on connection failure
138  if (!strcmp(name, AutoSearchSP.name))
139  {
140  bool wasEnabled = (AutoSearchS[0].s == ISS_ON);
141 
142  IUUpdateSwitch(&AutoSearchSP, states, names, n);
144 
145  // Only display message if there is an actual change
146  if (wasEnabled == false && AutoSearchS[0].s == ISS_ON)
147  LOG_INFO("Auto search is enabled. When connecting, the driver shall attempt to "
148  "communicate with all available system ports until a connection is "
149  "established.");
150  else if (wasEnabled && AutoSearchS[1].s == ISS_ON)
151  LOG_INFO("Auto search is disabled.");
152  IDSetSwitch(&AutoSearchSP, nullptr);
153 
154  return true;
155  }
156 
157  // Refresh Serial Devices
158  if (!strcmp(name, RefreshSP.name))
159  {
161  IDSetSwitch(&RefreshSP, nullptr);
162  return true;
163  }
164 
165  // Check if a system port is selected.
166  if (!strcmp(name, SystemPortSP.name))
167  {
168  IUUpdateSwitch(&SystemPortSP, states, names, n);
169 
170  int index = IUFindOnSwitchIndex(&SystemPortSP);
171  if (index >= 0)
172  {
173  IUSaveText(&PortT[0], m_SystemPorts[index].c_str());
174  IDSetText(&PortTP, nullptr);
175  }
176 
178  IDSetSwitch(&SystemPortSP, nullptr);
179  return true;
180  }
181  }
182 
183  return false;
184 }
185 
187 {
188  uint32_t baud = atoi(IUFindOnSwitch(&BaudRateSP)->name);
189  if (Connect(PortT[0].text, baud) && processHandshake())
190  return true;
191 
192  // Important, disconnect from port immediately
193  // to release the lock, otherwise another driver will find it busy.
195 
196  // Start auto-search if option was selected and IF we have system ports to try connecting to
197  if (AutoSearchS[0].s == ISS_ON && SystemPortS != nullptr && SystemPortSP.nsp > 1)
198  {
199  LOGF_WARN("Communication with %s @ %d failed. Starting Auto Search...", PortT[0].text,
200  baud);
201 
202  std::this_thread::sleep_for(std::chrono::milliseconds(500 + (rand() % 1000)));
203 
204  // Try to connect "randomly" so that competing devices don't all try to connect to the same
205  // ports at the same time.
206  std::vector<std::string> systemPorts;
207  for (int i = 0; i < SystemPortSP.nsp; i++)
208  {
209  // Only try the same port last again.
210  if (!strcmp(m_SystemPorts[i].c_str(), PortT[0].text))
211  continue;
212 
213  systemPorts.push_back(m_SystemPorts[i].c_str());
214  }
215 
216  std::random_device rd;
217  std::minstd_rand g(rd());
218  std::shuffle(systemPorts.begin(), systemPorts.end(), g);
219 
220  std::vector<std::string> doubleSearch = systemPorts;
221 
222  // Try the current port as LAST port again
223  systemPorts.push_back(PortT[0].text);
224 
225  // Double search just in case some items were BUSY in the first pass
226  systemPorts.insert(systemPorts.end(), doubleSearch.begin(), doubleSearch.end());
227 
228  for (const auto &port : systemPorts)
229  {
230  LOGF_INFO("Trying connecting to %s @ %d ...", port.c_str(), baud);
231  if (Connect(port.c_str(), baud) && processHandshake())
232  {
233  IUSaveText(&PortT[0], port.c_str());
234  IDSetText(&PortTP, nullptr);
235 
236 #ifdef __linux__
237  bool saveConfig = false;
238  // Disable auto-search on Linux if not disabled already
240  {
241  saveConfig = true;
244  IDSetSwitch(&AutoSearchSP, nullptr);
245  }
246  // Only save config if different from default port
247  if (m_ConfigPort != std::string(PortT[0].text))
248  {
249  saveConfig = true;
250  }
251 
252  if (saveConfig)
253  m_Device->saveConfig(true);
254 #else
255  // Do not overwrite custom ports because it can be actually cause
256  // temporary failure. For users who use mapped named ports (e.g. /dev/mount), it's not good to override their choice.
257  // So only write to config if the port was a system port.
258  if (std::find(m_SystemPorts.begin(), m_SystemPorts.end(), PortT[0].text) != m_SystemPorts.end())
259  m_Device->saveConfig(true, PortTP.name);
260 #endif
261  return true;
262  }
263 
265  // sleep randomly anytime between 0.5s and ~1.5s
266  // This enables different competing devices to connect
267  std::this_thread::sleep_for(std::chrono::milliseconds(500 + (rand() % 1000)));
268  }
269  }
270 
271  return false;
272 }
273 
275 {
276  LOG_DEBUG("Connection successful, attempting handshake...");
277  bool rc = Handshake();
278  if (rc)
279  {
280  LOGF_INFO("%s is online.", getDeviceName());
281  if (std::string(PortT[0].text) != m_ConfigPort || IUFindOnSwitchIndex(&BaudRateSP) != m_ConfigBaudRate)
282  {
285  }
286  }
287  else
288  LOG_DEBUG("Handshake failed.");
289 
290  return rc;
291 }
292 
293 bool Serial::Connect(const char *port, uint32_t baud)
294 {
295  if (m_Device->isSimulation())
296  return true;
297 
298  int connectrc = 0;
299  char errorMsg[MAXRBUF];
300 
301  LOGF_DEBUG("Connecting to %s @ %d", port, baud);
302 
303  if ((connectrc = tty_connect(port, baud, wordSize, parity, stopBits, &PortFD)) != TTY_OK)
304  {
305  if (connectrc == TTY_PORT_BUSY)
306  {
307  LOGF_WARN("Port %s is already used by another driver or process.", port);
308  return false;
309  }
310 
311  tty_error_msg(connectrc, errorMsg, MAXRBUF);
312  LOGF_ERROR("Failed to connect to port (%s). Error: %s", port, errorMsg);
313  return false;
314  }
315 
316  LOGF_DEBUG("Port FD %d", PortFD);
317 
318  return true;
319 }
320 
322 {
323  if (PortFD > 0)
324  {
326  PortFD = -1;
327  }
328  return true;
329 }
330 
332 {
333  Refresh(true);
338 }
339 
341 {
343  delete[] SystemPortS;
344  SystemPortS = nullptr;
349 }
350 
352 {
353  IUSaveConfigText(fp, &PortTP);
356 
357  return true;
358 }
359 
360 void Serial::setDefaultPort(const char *port)
361 {
362  // JM 2021.09.19: Only set default port if configuration port was not loaded already.
363  if (m_ConfigPort.empty())
364  IUSaveText(&PortT[0], port);
366  IDSetText(&PortTP, nullptr);
367 }
368 
370 {
371  // JM 2021.09.19: Only set default baud rate if configuration baud rate was not loaded already.
372  if (m_ConfigBaudRate == -1)
373  {
375  BaudRateS[newRate].s = ISS_ON;
376  }
377 
379  IDSetSwitch(&BaudRateSP, nullptr);
380 }
381 
382 uint32_t Serial::baud()
383 {
384  return atoi(IUFindOnSwitch(&BaudRateSP)->name);
385 }
386 
387 int serial_dev_file_select(const dirent *entry)
388 {
389 #if defined(__APPLE__)
390  static const char *filter_names[] = { "cu.", nullptr };
391 #else
392  static const char *filter_names[] = { "ttyUSB", "ttyACM", nullptr };
393 #endif
394  const char **filter;
395 
396  for (filter = filter_names; *filter; ++filter)
397  {
398  if (strstr(entry->d_name, *filter) != nullptr)
399  {
400  return (true);
401  }
402  }
403  return (false);
404 }
405 
406 int usb_dev_file_select(const dirent *entry)
407 {
408  static const char *filter_names[] = { "usb-", nullptr };
409  const char **filter;
410 
411  for (filter = filter_names; *filter; ++filter)
412  {
413  if (strstr(entry->d_name, *filter) != nullptr)
414  {
415  return (true);
416  }
417  }
418  return (false);
419 }
420 
421 int bluetooth_dev_file_select(const dirent *entry)
422 {
423  static const char *filter_names[] = {"rfcomm", nullptr };
424  const char **filter;
425 
426  for (filter = filter_names; *filter; ++filter)
427  {
428  if (strstr(entry->d_name, *filter) != nullptr)
429  {
430  return (true);
431  }
432  }
433  return (false);
434 }
435 
436 bool Serial::Refresh(bool silent)
437 {
438  std::vector<std::string> m_Ports;
439 
440  // 0 Serial Only, 1 By USB-ID, 2 Bluetooth
441  auto searchPath = [&](std::string prefix, uint8_t searchType)
442  {
443  struct dirent **namelist;
444  std::vector<std::string> detectedDevices;
445  int devCount = 0;
446  if (searchType == SERIAL_DEV)
447  devCount = scandir(prefix.c_str(), &namelist, serial_dev_file_select, alphasort);
448  else if (searchType == USB_ID_DEV)
449  devCount = scandir(prefix.c_str(), &namelist, usb_dev_file_select, alphasort);
450  else
451  devCount = scandir(prefix.c_str(), &namelist, bluetooth_dev_file_select, alphasort);
452  if (devCount > 0)
453  {
454  while (devCount--)
455  {
456  if (detectedDevices.size() < 10)
457  {
458  std::string s(namelist[devCount]->d_name);
459  s.erase(s.find_last_not_of(" \n\r\t") + 1);
460  detectedDevices.push_back(prefix + s);
461  }
462  else
463  {
464  LOGF_DEBUG("Ignoring devices over %d : %s", detectedDevices.size(),
465  namelist[devCount]->d_name);
466  }
467  free(namelist[devCount]);
468  }
469  free(namelist);
470  }
471 
472  return detectedDevices;
473  };
474 
475 #ifdef __linux__
476  // Search for serial, usb, and bluetooth devices.
477  const std::vector<std::string> serialDevices = searchPath("/dev/", SERIAL_DEV);
478  const std::vector<std::string> usbIDDevices = searchPath("/dev/serial/by-id/", USB_ID_DEV);
479  const std::vector<std::string> btDevices = searchPath("/dev/", BLUETOOTH_DEV);
480  m_Ports.insert(m_Ports.end(), btDevices.begin(), btDevices.end());
481  // Linux Kernel does not add identical VID:PID adapter to serial/by-id
482  // Therefore, we check if there is a 1:1 correspondence between serial/by-id and /dev/ttyUSBX nodes
483  // In case we have more by-id devices then /dev/ttyUSBX, we use them since these symlinks are more reusable in subsequence
484  // sessions
485  if (usbIDDevices.size() >= serialDevices.size())
486  m_Ports.insert(m_Ports.end(), usbIDDevices.begin(), usbIDDevices.end());
487  else
488  m_Ports.insert(m_Ports.end(), serialDevices.begin(), serialDevices.end());
489 #else
490  const std::vector<std::string> serialDevices = searchPath("/dev/", SERIAL_DEV);
491  m_Ports.insert(m_Ports.end(), serialDevices.begin(), serialDevices.end());
492 #endif
493 
494  const int pCount = m_Ports.size();
495 
496  if (pCount == 0)
497  {
498  if (!silent)
499  LOG_WARN("No candidate ports found on the system.");
500  return false;
501  }
502  else
503  {
504  if (!silent)
505  LOGF_INFO("Scan complete. Found %d port(s).", pCount);
506  }
507 
508  // Check if anything changed and the property is already defined then we return.
509  if (m_Ports == m_SystemPorts && SystemPortS)
510  {
512  return true;
513  }
514 
515  m_SystemPorts = m_Ports;
516 
517  if (SystemPortS)
519 
520  delete[] SystemPortS;
521 
522  SystemPortS = new ISwitch[pCount];
523  ISwitch *sp = SystemPortS;
524 
525  for (const auto &onePort : m_Ports)
526  {
527  // Simplify label by removing directory prefix
528  std::string name = onePort.substr(onePort.find_last_of("/\\") + 1);
529  std::string label = name;
530 
531  // Remove Linux extra stuff to simplify string
532 #ifdef __linux__
533  std::regex re("usb-(.[^-]+)");
534  std::smatch match;
535  if (std::regex_search(onePort, match, re))
536  {
537  name = label = match.str(1);
538  // Simplify further by removing non-unique strings
539  std::regex target("FTDI_|UART_|USB_|Bridge_Controller_|to_");
540  label = std::regex_replace(label, target, "");
541  // Protect against too-short of a label
542  if (label.length() <= 2)
543  label = match.str(1);
544  }
545 #endif
546  IUFillSwitch(sp++, name.c_str(), label.c_str(), ISS_OFF);
547  }
548 
549  IUFillSwitchVector(&SystemPortSP, SystemPortS, pCount, m_Device->getDeviceName(), "SYSTEM_PORTS", "System Ports",
551 
553 
554  // If we have one physical port, set the current device port to this physical port
555  // in case the default config port does not exist.
556  if (pCount == 1 && m_ConfigPort.empty())
557  IUSaveText(&PortT[0], m_Ports[0].c_str());
558  return true;
559 }
560 }
The Interface class is the base class for all INDI connection plugins.
std::function< bool()> Handshake
INDI::DefaultDevice * m_Device
ISwitchVectorProperty RefreshSP
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
virtual std::string name() override
BaudRate
Supported baud rates.
std::string m_ConfigPort
ITextVectorProperty PortTP
std::vector< std::string > m_SystemPorts
virtual void Activated() override
Activated Function called by the framework when the plugin is activated (i.e. selected by the user)....
bool Refresh(bool silent=false)
ISwitchVectorProperty BaudRateSP
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
virtual void Deactivated() override
Deactivated Function called by the framework when the plugin is deactivated. It is usually used to de...
ISwitchVectorProperty SystemPortSP
ISwitchVectorProperty AutoSearchSP
Serial(INDI::DefaultDevice *dev)
virtual bool Disconnect() override
Disconnect Disconnect from device.
virtual bool saveConfigItems(FILE *fp) override
void setDefaultPort(const char *port)
setDefaultPort Set default port. Call this function in initProperties() of your driver if you want to...
virtual bool Connect() override
Connect Connect to device via the implemented communication medium. Do not perform any handshakes.
virtual uint32_t baud()
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
virtual bool processHandshake()
virtual std::string label() override
virtual const char * port()
const char * getDeviceName() const
Definition: basedevice.cpp:821
Class to provide extended functionality for devices in addition to the functionality provided by INDI...
bool isInitializationComplete() const
isInitializationComplete Check if driver initialization is complete.
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a 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)
bool isSimulation() const
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
@ 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
int tty_connect(const char *device, int bit_rate, int word_size, int parity, int stop_bits, int *fd)
Establishes a tty connection to a terminal device.
Definition: indicom.c:946
int tty_disconnect(int fd)
Closes a tty connection and flushes the bus.
Definition: indicom.c:1148
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
@ TTY_PORT_BUSY
Definition: indicom.h:159
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
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
ISwitch * IUFindOnSwitch(const ISwitchVectorProperty *svp)
Returns the first ON switch it finds in the vector switch property.
Definition: indidevapi.c:108
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
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 IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indidevapi.c:20
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 IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:1396
int IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
IUGetConfigText Opens configuration file and reads single text property.
Definition: indidriver.c:889
int IUGetConfigOnSwitchIndex(const char *dev, const char *property, int *index)
IUGetConfigOnSwitchIndex Opens configuration file and reads single switch property to find ON switch ...
Definition: indidriver.c:711
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOGF_WARN(fmt,...)
Definition: indilogger.h:81
#define LOG_WARN(txt)
Definition: indilogger.h:73
#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
Combines all INDI Connection Plugins. Each INDI connection plugin is responsible of managing communic...
Definition: arduino_st4.h:34
const char * CONNECTION_TAB
int usb_dev_file_select(const dirent *entry)
int bluetooth_dev_file_select(const dirent *entry)
int serial_dev_file_select(const dirent *entry)
const char * DEVICE_AUTO_SEARCH
Toggle device auto search. If enabled and on connection failure with the default port,...
const char * DEVICE_PORT
Device serial (or bluetooth) connection port. The default value on Linux is /dev/ttyUSB0 while on Mac...
const char * DEVICE_BAUD_RATE
Set device baud rate.
One switch descriptor.
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250