serial.cpp
Go to the documentation of this file.
1 
7 
8 #include <fcntl.h>
9 #include <ros/console.h>
10 #include <string.h>
11 
12 namespace toposens_driver
13 {
22 Serial::Serial(std::string port)
23 {
24  _fd = -1;
25  _port = port;
26 
27  // Open serial port to access sensor stream
28  _fd = open(_port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
29 
30  int retry_counter = 5;
31 
32  while (_fd == -1)
33  {
34  std::string msg = "Error opening connection at " + _port + ": " + strerror(errno);
35 
36  if (retry_counter--)
37  {
38  ROS_WARN_STREAM(msg);
39  ros::Duration(0.5).sleep();
40 
41  ROS_INFO_STREAM("Retrying to establish connection at " << _port << " ...");
42  _fd = open(_port.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);
43  }
44  else
45  {
46  throw std::runtime_error(msg);
47  }
48  }
49  ROS_DEBUG("Toposens serial established with fd %d\n", _fd);
50 
51  // Set options of serial data transfer
52  struct termios tty;
53  memset(&tty, 0, sizeof(tty));
54 
55  // Get current attributes of termios structure
56  if (tcgetattr(_fd, &tty) != 0)
57  {
58  ROS_WARN("Error retrieving attributes at %s: %s", _port.c_str(), strerror(errno));
59  return;
60  }
61 
62  // set I/O baud rate
63  cfsetispeed(&tty, kBaud);
64  cfsetospeed(&tty, kBaud);
65 
66  // enable reading, ignore ctrl lines, 8 bits per byte
67  tty.c_cflag |= CREAD | CLOCAL | CS8;
68  // turn off parity, use one stop bit, disable HW flow control
69  tty.c_cflag &= ~(PARENB | CSTOPB | CRTSCTS);
70 
71  // disable canonical mode and echoes
72  tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL);
73 
74  // disable software flow control
75  tty.c_iflag &= ~(IXON | IXOFF | IXANY);
76  // disable break condition handling
77  tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK);
78  // disable special data pre-processing
79  tty.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL);
80 
81  // disable special data post-processing
82  tty.c_oflag &= ~(OPOST | ONLCR);
83 
84  // wait for at least 1 byte to be received
85  // length of a valid empty data frame
86  tty.c_cc[VMIN] = 1;
87  // wait for 0.1s till data received
88  tty.c_cc[VTIME] = 1;
89 
90  // Set attributes of termios structure
91  if (tcsetattr(_fd, TCSANOW, &tty) != 0)
92  {
93  ROS_WARN("Error configuring device at %s: %s", _port.c_str(), strerror(errno));
94  return;
95  }
96 
97  ROS_DEBUG(
98  "Serial settings updated:\n BaudRate = %d \n DataBits = 8 \n "
99  "Parity = disabled",
100  kBaud);
101  tcflush(_fd, TCIFLUSH); // Discard old data in RX buffer
102  ROS_INFO("Device at %s ready for communication", _port.c_str());
103 }
104 
109 {
110  ROS_INFO("Closing serial connection...");
111  tcflush(_fd, TCIOFLUSH);
112 
113  if (close(_fd) == -1)
114  {
115  ROS_ERROR("Error closing serial connection: %s", strerror(errno));
116  }
117  else
118  {
119  ROS_INFO("Serial connection killed");
120  }
121 }
122 
127 void Serial::getFrame(std::stringstream &data)
128 {
129  char buffer[1];
130  int nBytes = 0;
131  ros::Time latest = ros::Time::now();
132 
133  do
134  {
135  memset(&buffer, '\0', sizeof(buffer));
136  nBytes = read(_fd, &buffer, sizeof(buffer));
137 
138  if (nBytes < 1)
139  {
140  ros::Duration(0.01).sleep();
141  continue;
142  }
143 
144  // add only first byte in buffer to data stream to work on armhf
145  // architectures
146  data << buffer[0];
147  latest = ros::Time::now();
148 
149  if (buffer[nBytes - 1] == 'E') break;
150 
151  } while (ros::Time::now() - latest < ros::Duration(1));
152 }
153 
157 void Serial::sendCmd(Command cmd, std::stringstream &data)
158 {
159  char *bytes = cmd.getBytes();
160  data.str("");
161 
162  try
163  {
164  if (_fd == -1)
165  {
166  std::string msg = "Connection at " + _port + " unavailable: " + strerror(errno);
167  throw std::runtime_error(msg);
168  }
169 
170  int tx_length = write(_fd, bytes, strlen(bytes));
171 
172  if (tx_length != -1)
173  {
174  ROS_DEBUG("Bytes transmitted: %s", bytes);
175  if (!waitForAcknowledgement(data)) ROS_WARN_STREAM("Settings update timed out! - Aborting.");
176  }
177  else
178  {
179  ROS_ERROR("Failed to transmit %s: %s", bytes, strerror(errno));
180  }
181  }
182  catch (const std::exception &e)
183  {
184  ROS_ERROR("%s: %s", e.what(), cmd.getBytes());
185  }
186 }
187 
190 bool Serial::isAcknowledgementFrame(std::string frame)
191 {
192  size_t frame_start = frame.find('S');
193 
194  if (frame_start < 0)
195  return false;
196  else
197  return (frame[frame_start + 7] == 'C');
198 }
199 
203 bool Serial::waitForAcknowledgement(std::stringstream &buffer)
204 {
205  std::string data;
206  size_t frame_start = -1;
207 
208  ros::Time latest = ros::Time::now();
209 
210  while (ros::Time::now() - latest <= ros::Duration(2))
211  {
212  buffer.str(std::string());
213  buffer.clear();
214 
215  this->getFrame(buffer);
216 
217  if (isAcknowledgementFrame(buffer.str().c_str())) return true;
218  }
219 
220  // Watchdog timer expired
221  buffer.str("");
222  return false;
223 }
224 
225 } // namespace toposens_driver
bool isAcknowledgementFrame(std::string frame)
Definition: serial.cpp:190
std::string _port
Definition: serial.h:78
const unsigned int kBaud
Definition: serial.h:79
#define ROS_WARN(...)
#define ROS_INFO(...)
#define ROS_WARN_STREAM(args)
Serial(std::string port)
Definition: serial.cpp:22
#define ROS_INFO_STREAM(args)
static Time now()
Generates firmware-compatible commands for tuning performance parameters.
Definition: command.h:60
bool waitForAcknowledgement(std::stringstream &buffer)
Definition: serial.cpp:203
void sendCmd(Command cmd, std::stringstream &data)
Definition: serial.cpp:157
bool sleep() const
#define ROS_ERROR(...)
void getFrame(std::stringstream &data)
Definition: serial.cpp:127
#define ROS_DEBUG(...)


toposens_driver
Author(s): Adi Singh, Sebastian Dengler, Christopher Lang, Roua Mokchah, Nancy Seckel, Georgiana Barbut
autogenerated on Mon Feb 28 2022 23:57:40