Main MRPT website > C++ reference for MRPT 1.9.9
CBoardENoses.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "hwdrivers-precomp.h" // Precompiled headers
11 
12 #include <mrpt/system/os.h>
14 #include <mrpt/utils/CMessage.h>
15 #include <mrpt/math/ops_vectors.h>
16 
17 #include <thread>
18 
19 using namespace mrpt::utils;
20 using namespace mrpt::math;
21 using namespace mrpt::obs;
22 using namespace mrpt::poses;
23 using namespace mrpt::hwdrivers;
24 using namespace std;
25 
27 
28 /*-------------------------------------------------------------
29  CBoardENoses
30 -------------------------------------------------------------*/
32  : m_usbSerialNumber("ENOSE001"), m_COM_port(), m_COM_baud(115200)
33 {
34  m_sensorLabel = "ENOSE";
35  first_reading = true;
36 }
37 
38 /*-------------------------------------------------------------
39  loadConfig_sensorSpecific
40 -------------------------------------------------------------*/
41 void CBoardENoses::loadConfig_sensorSpecific(
42  const mrpt::utils::CConfigFileBase& configSource,
43  const std::string& iniSection)
44 {
46 
47  m_usbSerialNumber =
48  configSource.read_string(iniSection, "USB_serialname", "", false);
49 
50 #ifdef MRPT_OS_WINDOWS
51  m_COM_port =
52  configSource.read_string(iniSection, "COM_port_WIN", m_COM_port);
53 #else
54  m_COM_port =
55  configSource.read_string(iniSection, "COM_port_LIN", m_COM_port);
56 #endif
57  m_COM_baud =
58  configSource.read_uint64_t(iniSection, "COM_baudRate", m_COM_baud);
59 
60  configSource.read_vector(
61  iniSection, "enose_poses_x", vector<float>(0), enose_poses_x, true);
62  configSource.read_vector(
63  iniSection, "enose_poses_y", vector<float>(0), enose_poses_y, true);
64  configSource.read_vector(
65  iniSection, "enose_poses_z", vector<float>(0), enose_poses_z, true);
66 
67  configSource.read_vector(
68  iniSection, "enose_poses_yaw", vector<float>(0), enose_poses_yaw, true);
69  configSource.read_vector(
70  iniSection, "enose_poses_pitch", vector<float>(0), enose_poses_pitch,
71  true);
72  configSource.read_vector(
73  iniSection, "enose_poses_roll", vector<float>(0), enose_poses_roll,
74  true);
75 
76  ASSERT_(enose_poses_x.size() == enose_poses_y.size());
77  ASSERT_(enose_poses_x.size() == enose_poses_z.size());
78  ASSERT_(enose_poses_x.size() == enose_poses_yaw.size());
79  ASSERT_(enose_poses_x.size() == enose_poses_pitch.size());
80  ASSERT_(enose_poses_x.size() == enose_poses_roll.size());
81 
82  // Pass angles to radians:
83  enose_poses_yaw *= M_PIf / 180.0f;
84  enose_poses_pitch *= M_PIf / 180.0f;
85  enose_poses_roll *= M_PIf / 180.0f;
86 
87  MRPT_END
88 }
89 
90 /*-------------------------------------------------------------
91  queryFirmwareVersion
92 -------------------------------------------------------------*/
93 bool CBoardENoses::queryFirmwareVersion(string& out_firmwareVersion)
94 {
95  try
96  {
97  mrpt::utils::CMessage msg, msgRx;
98 
99  // Try to connect to the device:
100  CStream* comms = checkConnectionAndConnect();
101  if (!comms) return false;
102 
103  msg.type = 0x10;
104  comms->sendMessage(msg);
105 
106  if (comms->receiveMessage(msgRx))
107  {
108  msgRx.getContentAsString(out_firmwareVersion);
109  return true;
110  }
111  else
112  return false;
113  }
114  catch (...)
115  {
116  // Close everything and start again:
117  m_stream_SERIAL.reset();
118  m_stream_FTDI.reset();
119  return false;
120  }
121 }
122 
123 /*-------------------------------------------------------------
124  checkConnectionAndConnect
125 -------------------------------------------------------------*/
126 CStream* CBoardENoses::checkConnectionAndConnect()
127 {
128  // Make sure one of the two possible pipes is open:
129  if (!m_stream_FTDI && !m_stream_SERIAL)
130  {
131  if (!m_COM_port.empty())
132  m_stream_SERIAL.reset(new mrpt::comms::CSerialPort);
133  else
134  m_stream_FTDI.reset(new mrpt::comms::CInterfaceFTDI);
135  }
136 
137  if (m_stream_FTDI)
138  { // FTDI pipe ==================
139  if (m_stream_FTDI->isOpen()) return m_stream_FTDI.get();
140  try
141  {
142  m_stream_FTDI->OpenBySerialNumber(m_usbSerialNumber);
143  std::this_thread::sleep_for(10ms);
144  m_stream_FTDI->Purge();
145  std::this_thread::sleep_for(10ms);
146  m_stream_FTDI->SetLatencyTimer(1);
147  m_stream_FTDI->SetTimeouts(10, 100);
148  return m_stream_FTDI.get();
149  }
150  catch (...)
151  { // Error opening device:
152  m_stream_FTDI->Close();
153  return nullptr;
154  }
155  }
156  else
157  { // Serial pipe ==================
158  ASSERT_(m_stream_SERIAL)
159  if (m_stream_SERIAL->isOpen()) return m_stream_SERIAL.get();
160  try
161  {
162  m_stream_SERIAL->open(m_COM_port);
163  m_stream_SERIAL->setConfig(m_COM_baud);
164  std::this_thread::sleep_for(10ms);
165  m_stream_SERIAL->purgeBuffers();
166  std::this_thread::sleep_for(10ms);
167  // m_stream_SERIAL->setTimeouts(25,1,100, 1,20);
168  m_stream_SERIAL->setTimeouts(50, 1, 100, 1, 20);
169  return m_stream_SERIAL.get();
170  }
171  catch (...)
172  { // Error opening device:
173  m_stream_SERIAL->close();
174  return nullptr;
175  }
176  }
177 }
178 
179 /*-------------------------------------------------------------
180  getObservation
181 -------------------------------------------------------------*/
183 {
184  try
185  {
186  // Connected?
187  CStream* comms = checkConnectionAndConnect();
188 
189  if (!comms) return false;
190 
191  utils::CMessage msg;
193 
194  obs.m_readings.clear();
195 
196  //// Send request:
197  // msg.type = 0x11;
198  // msg.content.clear();
199  // comms->sendMessage( msg );
200 
201  //----------------------------MCE-nose
202  // FRAME--------------------------------------------------
203  // Wait for e-nose frame: <0x69><0x91><lenght><body><0x96> "Bytes"
204  // Where <body> = [Numchamber, Activechamber, N sensors*M chambers*2, 2
205  // timestamp] of uint16_t
206  // MCE-nose provides a 136B body lenght which makes 140B total frame
207  // lenght
208 
209  if (!comms->receiveMessage(msg))
210  {
211  return false;
212  }
213 
214  // m_state = ssWorking;
215 
216  // Copy to "uint16_t":
217  ASSERT_((msg.content.size() % 2) == 0);
218 
219  vector<uint16_t> readings(
220  msg.content.size() /
221  2); // divide by 2 to pass from byte to word. 136B/2 = 68 Words
222 
223  if (msg.content.size() > 0)
224  {
225  // Copy to a vector of 16bit integers:
226  memcpy(
227  &readings[0], &msg.content[0],
228  msg.content.size() * sizeof(msg.content[0]));
229 
230  // HEADER Frame [ NÂș of chambers/enoses (16b) , Active Chamber
231  // (16b)]
232  size_t NumberOfChambers = (size_t)readings[0];
233  size_t ActiveChamber = (size_t)readings[1];
234 
235  // Sensors readings info
236  ASSERT_(((readings.size() - 4) % NumberOfChambers) == 0);
237  size_t wordsPereNose = (readings.size() - 4) / NumberOfChambers;
238 
239  // Process each chamber
240  for (size_t i = 0; i < NumberOfChambers; i++)
241  {
242  // ----------------------------------------------------------------------
243  // Each "i" comprises a complete Enose reading: Gas sensors +
244  // temperature
245  // ----------------------------------------------------------------------
246 
247  // Do we have the sensor position?
248  if (i < enose_poses_x.size())
249  {
250  newRead.eNosePoseOnTheRobot = CPose3D(
251  enose_poses_x[i], enose_poses_y[i], enose_poses_z[i],
252  enose_poses_yaw[i], enose_poses_pitch[i],
253  enose_poses_roll[i]);
254  }
255  else
256  newRead.eNosePoseOnTheRobot = CPose3D(0, 0, 0);
257 
258  // Process the sensor codes:
259  newRead.sensorTypes.clear();
260  newRead.readingsVoltage.clear();
261  newRead.hasTemperature = false;
262  newRead.isActive = false;
263 
264  // check if active chamber
265  if (i == (ActiveChamber)) newRead.isActive = true;
266 
267  // process each sensor on this chamber "i"
268  for (size_t idx = 0; idx < wordsPereNose / 2; idx++)
269  {
270  if (readings[i * wordsPereNose + 2 * idx + 2] !=
271  0x0000) // not empty slot
272  {
273  // Is temperature?
274  if (readings[i * wordsPereNose + 2 * idx + 2] == 0xFFFF)
275  {
276  newRead.hasTemperature = true;
277  newRead.temperature =
278  ((int16_t)readings[i * wordsPereNose + 2 * idx +
279  3]) /
280  32.0f;
281  }
282  else // Is a gas sensors
283  {
284  // It is not a null code: There is a valid measure:
285  newRead.sensorTypes.push_back(
286  readings[i * wordsPereNose + 2 * idx + 2]);
287 
288  // Pass from ADC value[12bits] to [0-2.5] volt
289  // range:
290  newRead.readingsVoltage.push_back(
291  (readings[i * wordsPereNose + 2 * idx + 3] *
292  5.0f) /
293  4096.0f);
294  }
295  }
296  } // end for each sensor on this eNose
297 
298  // Add to observations:
299  if (!newRead.sensorTypes.empty())
300  obs.m_readings.push_back(newRead);
301 
302  } // end for each i'th eNose
303 
304  obs.sensorLabel = m_sensorLabel;
305 
306  // Set Timestamp
307  uint16_t* p =
308  (uint16_t*)&readings[readings.size() - 2]; // Get readings time
309  // from frame
310  // (always last 2
311  // words)
312  obs.timestamp =
313  mrpt::system::secondsToTimestamp(((double)*p) / 1000);
314 
315  if (first_reading)
316  {
317  initial_timestamp =
319  first_reading = false;
320  }
321  obs.timestamp = obs.timestamp + initial_timestamp;
322 
323  } // end if message has data
324 
325  // CONTROL
326  bool correct = true;
327 
328  if (obs.m_readings.size() != 4)
329  correct = false;
330  else
331  {
332  for (size_t ch = 0; ch < obs.m_readings.size(); ch++)
333  {
334  if ((obs.m_readings[ch].sensorTypes.size() != 7) ||
335  (obs.m_readings[ch].readingsVoltage.size() != 7))
336  correct = false;
337  else
338  {
339  }
340  }
341  }
342 
343  if (!correct) printf("Error en la observacion"); // For debug
344 
345  return !obs.m_readings.empty(); // Done OK!
346  }
347  catch (exception& e)
348  {
349  cerr << "[CBoardENoses::getObservation] Returning false due to "
350  "exception: "
351  << endl;
352  cerr << e.what() << endl;
353  return false;
354  }
355  catch (...)
356  {
357  return false;
358  }
359 }
360 
361 /*-------------------------------------------------------------
362  doProcess
363 -------------------------------------------------------------*/
364 /** This method should be called periodically (at least at 1Hz to capture ALL
365 * the real-time data)
366 * It is thread safe, i.e. you can call this from one thread, then to other
367 * methods from other threads.
368 */
369 void CBoardENoses::doProcess()
370 {
372  mrpt::make_aligned_shared<CObservationGasSensors>();
373 
374  if (getObservation(*obs))
375  {
376  m_state = ssWorking;
377  appendObservation(obs);
378  }
379  else
380  {
381  m_state = ssError;
382  // THROW_EXCEPTION("No observation received from the USB board!");
383  }
384 }
385 
386 /*-------------------------------------------------------------
387  initialize
388 -------------------------------------------------------------*/
389 /** Tries to open the camera, after setting all the parameters with a call to
390  * loadConfig.
391  * \exception This method must throw an exception with a descriptive message
392  * if some critical error is found.
393  */
394 void CBoardENoses::initialize()
395 {
396  // We'll rather try it in doProcess() since it's quite usual that it fails
397  // on a first try, then works on the next ones.
398  /*
399  if (!checkConnectionAndConnect())
400  THROW_EXCEPTION("Couldn't connect to the eNose board");
401  */
402 }
403 
404 /*-------------------------------------------------------------
405  setActiveChamber
406 -------------------------------------------------------------*/
407 /** Send to the MCE-nose the next Active Chamber */
408 
409 bool CBoardENoses::setActiveChamber(unsigned char chamber)
410 {
411  try
412  {
413  // Try to connect to the device:
414  CStream* comms = checkConnectionAndConnect();
415  if (!comms) return false;
416 
417  // Send a byte to set the Active chamber on device.
418  // by default: Byte_to_send = 10_ _ _ _10
419  unsigned char buf[1];
420  buf[0] = ((chamber & 15) << 2) | 130; // 130= 10 0000 10
421 
422  comms->WriteBuffer(buf, 1); // Exceptions will be raised on errors here
423  return true;
424  }
425  catch (...)
426  {
427  // Close everything and start again:
428  m_stream_SERIAL.reset();
429  m_stream_FTDI.reset();
430  return false;
431  }
432 }
Classes for serialization, sockets, ini-file manipulation, streams, list of properties-values, timewatch, extensions to STL.
unsigned __int16 uint16_t
Definition: rptypes.h:44
A communications serial port built as an implementation of a utils::CStream.
Definition: CSerialPort.h:47
mrpt::system::TTimeStamp getCurrentTime()
Returns the current (UTC) system time.
Definition: datetime.cpp:73
float temperature
Sensed temperature in Celcius (valid if hasTemperature=true only)
void WriteBuffer(const void *Buffer, size_t Count)
Writes a block of bytes to the stream from Buffer.
Definition: CStream.cpp:64
Contains classes for various device interfaces.
vector_int sensorTypes
The kind of sensors in the array (size of "sensorTypes" is the same that the size of "readingsVoltage...
std::string read_string(const std::string &section, const std::string &name, const std::string &defaultValue, bool failIfNotFound=false) const
STL namespace.
This class allows loading and storing values and vectors of different types from a configuration text...
This base class is used to provide a unified interface to files,memory buffers,..Please see the deriv...
Definition: CStream.h:41
This base provides a set of functions for maths stuff.
Definition: CArrayNumeric.h:19
std::shared_ptr< CObservationGasSensors > Ptr
#define M_PIf
math::TPose3D eNosePoseOnTheRobot
The pose of the sensors on the robot.
__int16 int16_t
Definition: rptypes.h:43
#define MRPT_END
void read_vector(const std::string &section, const std::string &name, const VECTOR_TYPE &defaultValue, VECTOR_TYPE &outValues, bool failIfNotFound=false) const
Reads a configuration parameter of type vector, stored in the file as a string: "[v1 v2 v3 ...
A class for interfacing an e-Noses via a FTDI USB link.
Definition: CBoardENoses.h:57
bool hasTemperature
Must be true for "temperature" to contain a valid measurement.
This namespace contains representation of robot actions and observations.
Declares a class derived from "CObservation" that represents a set of readings from gas sensors...
GLsizei const GLchar ** string
Definition: glext.h:4101
#define IMPLEMENTS_GENERIC_SENSOR(class_name, NameSpace)
This must be inserted in all CGenericSensor classes implementation files:
Classes for 2D/3D geometry representation, both of single values and probability density distribution...
Definition: CPoint.h:17
#define MRPT_START
std::string sensorLabel
An arbitrary label that can be used to identify the sensor.
Definition: CObservation.h:60
bool isActive
True if the input to this chamber/enose is poluted air, False if clean air.
void getContentAsString(std::string &str)
Gets the contents of the message as a string.
mrpt::system::TTimeStamp timestamp
The associated UTC time-stamp.
Definition: CObservation.h:58
bool receiveMessage(utils::CMessage &msg)
Tries to receive a message from the device.
Definition: CStream.cpp:686
A definition of a CStream actually representing a USB connection to a FTDI chip.
A class used to store a 3D pose (a 3D translation + a rotation in 3D).
Definition: CPose3D.h:88
std::vector< TObservationENose > m_readings
One entry per e-nose on the robot.
OBSERVATION_T::Ptr getObservation(mrpt::obs::CSensoryFrame::Ptr &observations, mrpt::obs::CObservation::Ptr &observation, bool priority_to_sf=true)
Given an mrpt::obs::CSensoryFrame and a mrpt::obs::CObservation pointer if a OBSERVATION_T type obser...
Definition: obs_utils.h:36
#define ASSERT_(f)
uint64_t read_uint64_t(const std::string &section, const std::string &name, uint64_t defaultValue, bool failIfNotFound=false) const
void sendMessage(const utils::CMessage &msg)
Send a message to the device.
Definition: CStream.cpp:649
std::vector< uint8_t > content
The contents of the message (memory is automatically handled by the std::vector object) ...
Definition: CMessage.h:39
mrpt::system::TTimeStamp secondsToTimestamp(const double nSeconds)
Transform a time interval (in seconds) into TTimeStamp (e.g.
Definition: datetime.cpp:223
std::vector< float > readingsVoltage
The set of readings (in volts) from the array of sensors (size of "sensorTypes" is the same that the ...
GLfloat GLfloat p
Definition: glext.h:6305
A class that contain generic messages, that can be sent and received from a "CClientTCPSocket" object...
Definition: CMessage.h:31
uint32_t type
An identifier of the message type (only the least-sig byte is typically sent)
Definition: CMessage.h:36
void memcpy(void *dest, size_t destSize, const void *src, size_t copyCount) noexcept
An OS and compiler independent version of "memcpy".
Definition: os.cpp:355



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019