YARP
Yet Another Robot Platform
SerialDeviceDriver.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: 2006-2021 Istituto Italiano di Tecnologia (IIT)
3  * SPDX-FileCopyrightText: 2007 Alexandre Bernardino
4  * SPDX-FileCopyrightText: 2007 Carlos Beltran-Gonzalez
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include "SerialDeviceDriver.h"
9 
10 #include <yarp/os/Log.h>
11 #include <yarp/os/LogComponent.h>
12 
13 #include <cstdio>
14 #include <cstdlib>
15 
16 using namespace yarp::os;
17 using namespace yarp::dev;
18 
19 #define MAX_FLUSHED_BYTES 10000
20 
21 //inline SerialHandler& RES(void *res) { return *(SerialHandler *)res; }
22 
23 namespace {
24 YARP_LOG_COMPONENT(SERIALPORT, "yarp.device.serialport")
25 }
26 
28  //system_resources = (SerialHandler*) new SerialHandler();
29  verbose=false;
30  line_terminator_char1 = '\r';
31  line_terminator_char2 = '\n';
32 }
33 
35  close();
36 }
37 
39 {
40  //if(RES(system_resources).initialize(config.CommChannel, config.SerialParams) < 0)
41  // return false;
42  //RES(system_resources).setCommandSender(this);
43  //yCTrace(SERIALPORT, "SerialHandler::initialize");
44  yCInfo(SERIALPORT, "Starting Serial Port in %s \n", config.CommChannel);
45 
46  // Initialize serial port
47  if(_serialConnector.connect(_serial_dev, ACE_DEV_Addr(config.CommChannel)) == -1)
48  {
49  yCError(SERIALPORT, "Invalid communications port in %s: %s\n", config.CommChannel, strerror(errno));
50  return false;
51  }
52 
53 
54  // Set TTY_IO parameter into the ACE_TTY_IO device(_serial_dev)
55  if (_serial_dev.control (ACE_TTY_IO::SETPARAMS, &config.SerialParams) == -1)
56  {
57  yCError(SERIALPORT, "Can not control communications port %s \n", config.CommChannel);
58  return false;
59  }
60 
61  return true;
62 }
63 
64 
67  strcpy(config2.CommChannel, config.check("comport",Value("COM3"),"name of the serial channel").asString().c_str());
68  this->verbose = (config.check("verbose",Value(1),"Specifies if the device is in verbose mode (0/1).").asInt32())>0;
69  config2.SerialParams.baudrate = config.check("baudrate",Value(9600),"Specifies the baudrate at which the communication port operates.").asInt32();
70  config2.SerialParams.xonlim = config.check("xonlim",Value(0),"Specifies the minimum number of bytes in input buffer before XON char is sent. Negative value indicates that default value should be used (Win32)").asInt32();
71  config2.SerialParams.xofflim = config.check("xofflim",Value(0),"Specifies the maximum number of bytes in input buffer before XOFF char is sent. Negative value indicates that default value should be used (Win32). ").asInt32();
72  //RANDAZ: as far as I undesrood, the exit condition for recv() function is NOT readmincharacters || readtimeoutmsec. It is readmincharacters && readtimeoutmsec.
73  //On Linux. if readmincharacters params is set !=0, recv() may still block even if readtimeoutmsec is expired.
74  //On Win32, for unknown reason, readmincharacters seems to be ignored, so recv () returns after readtimeoutmsec. Maybe readmincharacters is used if readtimeoutmsec is set to -1?
75  config2.SerialParams.readmincharacters = config.check("readmincharacters",Value(1),"Specifies the minimum number of characters for non-canonical read (POSIX).").asInt32();
76  config2.SerialParams.readtimeoutmsec = config.check("readtimeoutmsec",Value(100),"Specifies the time to wait before returning from read. Negative value means infinite timeout.").asInt32();
77  // config2.SerialParams.parityenb = config.check("parityenb",Value(0),"Enable/disable parity checking.").asInt32();
78  std::string temp = config.check("paritymode",Value("EVEN"),"Specifies the parity mode (EVEN, ODD, NONE). POSIX supports even and odd parity. Additionally Win32 supports mark and space parity modes.").asString();
79  config2.SerialParams.paritymode = temp.c_str();
80  config2.SerialParams.ctsenb = config.check("ctsenb",Value(0),"Enable & set CTS mode. Note that RTS & CTS are enabled/disabled together on some systems (RTS/CTS is enabled if either <code>ctsenb</code> or <code>rtsenb</code> is set).").asInt32();
81  config2.SerialParams.rtsenb = config.check("rtsenb",Value(0),"Enable & set RTS mode. Note that RTS & CTS are enabled/disabled together on some systems (RTS/CTS is enabled if either <code>ctsenb</code> or <code>rtsenb</code> is set).\n- 0 = Disable RTS.\n- 1 = Enable RTS.\n- 2 = Enable RTS flow-control handshaking (Win32).\n- 3 = Specifies that RTS line will be high if bytes are available for transmission.\nAfter transmission RTS will be low (Win32).").asInt32();
82  config2.SerialParams.xinenb = config.check("xinenb",Value(0),"Enable/disable software flow control on input.").asInt32();
83  config2.SerialParams.xoutenb = config.check("xoutenb",Value(0),"Enable/disable software flow control on output.").asInt32();
84  config2.SerialParams.modem = config.check("modem",Value(0),"Specifies if device is a modem (POSIX). If not set modem status lines are ignored. ").asInt32();
85  config2.SerialParams.rcvenb = config.check("rcvenb",Value(0),"Enable/disable receiver (POSIX).").asInt32();
86  config2.SerialParams.dsrenb = config.check("dsrenb",Value(0),"Controls whether DSR is disabled or enabled (Win32).").asInt32();
87  config2.SerialParams.dtrdisable = config.check("dtrdisable",Value(0),"Controls whether DTR is disabled or enabled.").asInt32();
88  config2.SerialParams.databits = config.check("databits",Value(7),"Data bits. Valid values 5, 6, 7 and 8 data bits. Additionally Win32 supports 4 data bits.").asInt32();
89  config2.SerialParams.stopbits = config.check("stopbits",Value(1),"Stop bits. Valid values are 1 and 2.").asInt32();
90 
91  if (config.check("line_terminator_char1", "line terminator character for receiveLine(), default '\r'")) {
92  line_terminator_char1 = config.find("line_terminator_char1").asInt32();
93  }
94 
95  if (config.check("line_terminator_char2", "line terminator character for receiveLine(), default '\n'")) {
96  line_terminator_char2 = config.find("line_terminator_char2").asInt32();
97  }
98 
99  return open(config2);
100 }
101 
103  _serial_dev.close();
104  return true;
105 }
106 
107 bool SerialDeviceDriver::setDTR(bool value) {
108  ACE_TTY_IO::Serial_Params arg;
109  int ret = _serial_dev.control(_serial_dev.GETPARAMS, &arg);
110  if (ret == -1) {
111  return false;
112  }
113  arg.dtrdisable = !value;
114  ret = _serial_dev.control(_serial_dev.SETPARAMS, &arg);
115  if (ret == -1) {
116  return false;
117  }
118  return true;
119 }
120 
122 {
123  if (msg.size() > 0)
124  {
125  int message_size = msg.get(0).asString().length();
126 
127  if (message_size > 0)
128  {
129  if (verbose)
130  {
131  yCDebug(SERIALPORT, "Sending string: %s", msg.get(0).asString().c_str());
132  }
133 
134  // Write message to the serial device
135  ssize_t bytes_written = _serial_dev.send_n((void *) msg.get(0).asString().c_str(), message_size);
136 
137  if (bytes_written == -1)
138  {
139  yCError(SERIALPORT, "Unable to write to serial port");
140  return false;
141  }
142  }
143  else
144  {
145  if (verbose) {
146  yCDebug(SERIALPORT, "The input command bottle contains an empty string.");
147  }
148  return false;
149  }
150  }
151  else
152  {
153  if (verbose) {
154  yCDebug(SERIALPORT, "The input command bottle is empty. \n");
155  }
156  return false;
157  }
158 
159  return true;
160 }
161 
162 bool SerialDeviceDriver::send(char *msg, size_t size)
163 {
164  if (size > 0)
165  {
166  if (verbose)
167  {
168  yCDebug(SERIALPORT, "Sending string: %s", msg);
169  }
170 
171  // Write message in the serial device
172  ssize_t bytes_written = _serial_dev.send_n((void *)msg, size);
173 
174  if (bytes_written == -1)
175  {
176  yCError(SERIALPORT, "Unable to write to serial port");
177  return false;
178  }
179  }
180  else
181  {
182  if (verbose) {
183  yCDebug(SERIALPORT, "The input message is empty. \n");
184  }
185  return false;
186  }
187 
188  return true;
189 }
190 
192 {
193  char chr;
194 
195  //this function call blocks
196  ssize_t bytes_read = _serial_dev.recv ((void *) &chr, 1);
197 
198  if (bytes_read == -1)
199  {
200  yCError(SERIALPORT, "Error in SerialDeviceDriver::receive()");
201  return 0;
202  }
203 
204  if (bytes_read == 0)
205  {
206  return 0;
207  }
208 
209  c=chr;
210  return 1;
211 }
212 
214 {
215  char chr [100];
216  int count=0;
217  ssize_t bytes_read=0;
218  do
219  {
220  bytes_read = _serial_dev.recv((void *) &chr, 100);
221  count+=bytes_read;
222  if (count > MAX_FLUSHED_BYTES) {
223  break; //to prevent endless loop
224  }
225  }
226  while (bytes_read>0);
227  return count;
228 }
229 
230 int SerialDeviceDriver::receiveBytes(unsigned char* bytes, const int size)
231 {
232 #if 1
233  //this function call blocks
234  return _serial_dev.recv((void *)bytes, size);
235 #else
236  int i;
237  for (i = 0; i < size ; ++i)
238  {
239  char recv_ch;
240  int n = receiveChar(recv_ch);
241  if (n <= 0)
242  {
243  return i;
244  }
245  bytes[i] = recv_ch;
246  }
247  return i;
248 #endif
249 }
250 
251 int SerialDeviceDriver::receiveLine(char* buffer, const int MaxLineLength)
252 {
253  int i;
254  for (i = 0; i < MaxLineLength -1; ++i)
255  {
256  char recv_ch;
257  int n = receiveChar(recv_ch);
258  if (n <= 0)
259  {
260  //this invalidates the whole line, because no line terminator \n was found
261  return 0;
262 
263  //use this commented code here if you do NOT want to invalidate the line
264  //buffer[i] = '\0';
265  //return i;
266  }
267  if ((recv_ch == line_terminator_char1) || (recv_ch == line_terminator_char2))
268  {
269  buffer[i] = recv_ch;
270  i++;
271  break;
272  }
273  buffer[i] = recv_ch;
274  }
275  buffer[i] = '\0';
276  return i;
277 }
278 
280 {
281  const int msgSize = 1001;
282  char message[1001];
283 
284  //this function call blocks
285  ssize_t bytes_read = _serial_dev.recv ((void *) message, msgSize - 1);
286 
287  if (bytes_read == -1)
288  {
289  yCError(SERIALPORT, "Error in SerialDeviceDriver::receive()");
290  return false;
291  }
292 
293  if (bytes_read == 0) { //nothing there
294  return true;
295  }
296 
297  message[bytes_read] = 0;
298 
299  if (verbose)
300  {
301  yCDebug(SERIALPORT, "Data received from serial device: %s", message);
302  }
303 
304 
305  // Put message in the bottle
306  msg.addString(message);
307 
308  return true;
309 }
bool ret
#define MAX_FLUSHED_BYTES
ACE_TTY_IO::Serial_Params SerialParams
bool send(const Bottle &msg) override
Sends a string of chars to the serial communications channel.
bool close() override
Close the DeviceDriver.
bool open(yarp::os::Searchable &config) override
Open the DeviceDriver.
int receiveBytes(unsigned char *bytes, const int size) override
Gets an array of bytes (unsigned char) with size <= 'size' parameter.
int receiveLine(char *line, const int MaxLineLength) override
Gets one line (a sequence of chars with a ending '\n' or '\r') from the receive queue.
bool receive(Bottle &msg) override
Gets the existing chars in the receive queue.
int flush() override
Flushes the internal buffer.
bool setDTR(bool value) override
Enable/Disable DTR protocol.
int receiveChar(char &chr) override
Gets one single char from the receive queue.
A simple collection of objects that can be described and transmitted in a portable way.
Definition: Bottle.h:74
size_type size() const
Gets the number of elements in the bottle.
Definition: Bottle.cpp:251
Value & get(size_type index) const
Reads a Value v from a certain part of the list.
Definition: Bottle.cpp:246
void addString(const char *str)
Places a string in the bottle, at the end of the list.
Definition: Bottle.cpp:170
A base class for nested structures that can be searched.
Definition: Searchable.h:66
virtual Value & find(const std::string &key) const =0
Gets a value corresponding to a given keyword.
virtual bool check(const std::string &key) const =0
Check if there exists a property of the given name.
A single value (typically within a Bottle).
Definition: Value.h:45
virtual std::int32_t asInt32() const
Get 32-bit integer value.
Definition: Value.cpp:204
virtual std::string asString() const
Get string value.
Definition: Value.cpp:234
#define yCInfo(component,...)
Definition: LogComponent.h:132
#define yCError(component,...)
Definition: LogComponent.h:154
#define yCDebug(component,...)
Definition: LogComponent.h:109
#define YARP_LOG_COMPONENT(name,...)
Definition: LogComponent.h:77
::ssize_t ssize_t
Definition: numeric.h:86
An interface for the device drivers.
An interface to the operating system, including Port based communication.