vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
vrpn_Streaming_Arduino.C
Go to the documentation of this file.
1// Copyright 2015 by Russ Taylor, working for ReliaSolve.
2// Based on the vrpn_Tng3.C source file.
3// License: Standard VRPN.
4//
5// See the vrpn_streaming_arduino directory for a program that should be
6// loaded onto the Arduino and be running for this device to connect to.
7
8#include <math.h> // for floor
9#include <iostream>
10#include <sstream>
11
13
14#define VRPN_TIMESTAMP_MEMBER m_timestamp // Configuration required for vrpn_MessageMacros in this class.
15#include "vrpn_MessageMacros.h" // for VRPN_MSG_INFO, VRPN_MSG_WARNING, VRPN_MSG_ERROR
16
17#undef VERBOSE
18
19#define MAX_TCHANNELS 8
20
21// Defines the modes in which the box can find itself.
22#define STATUS_RESETTING (-1) // Resetting the box
23#define STATUS_SYNCING (0) // Looking for the first character of report
24#define STATUS_READING (1) // Looking for the rest of the report
25#define MAX_TIME_INTERVAL (2000000) // max time between reports (usec)
26
27
28// This creates the device and sets it to reset mode. It opens
29// the serial device using the code in the vrpn_Serial_Analog constructor.
30
33 std::string port,
34 int numchannels,
35 int baud)
36 :
37 vrpn_Serial_Analog(name.c_str(), c, port.c_str(), baud),
38 vrpn_Button_Filter(name.c_str(), c),
39 m_numchannels(numchannels)
40{
41 // Verify the validity of the parameters
43 std::cerr << "vrpn_Streaming_Arduino: Can only support "
44 << MAX_TCHANNELS << " analog channels, not "
45 << m_numchannels << std::endl;
47 }
48
49 // Set the parameters in the parent classes
51
52 // Set the status of the buttons, analogs and encoders to 0 to start
55
56 // Set the mode to reset
58}
59
61{
62 int i;
63
64 for (i = 0; i < m_numchannels; i++) {
66 }
67}
68
69// This routine will reset the device, asking it to send the requested number
70// of analogs.
71// It verifies that it can communicate with the device.
72
74{
75 //-----------------------------------------------------------------------
76 // Set the values back to zero for all analogs
78
79 //-----------------------------------------------------------------------
80 // Opening the port should cause the board to reset. (We can also do this
81 // by pulling DTR lines to low and then back high again.)
82 // @todo Lower and raise DTR lines. Or close and re-open the serial
83 // port.
84
85 // Wait for two seconds to let the device reset itself and then
86 // clear the input buffer, both the hardware input buffer and
87 // the local input buffer.
88 vrpn_SleepMsecs(2100);
90 m_buffer.clear();
91
92 // Send a command telling how many ports we want. Then make sure we
93 // get a response within a reasonable amount of time.
94 std::ostringstream msg;
95 msg << m_numchannels;
96 msg << "\n";
98 static_cast<const unsigned char *>(
99 static_cast<const void*>(msg.str().c_str())), msg.str().size());
100 struct timeval timeout;
101 timeout.tv_sec = 0;
102 timeout.tv_usec = 10000;
103 unsigned char buffer;
104 int ret = vrpn_read_available_characters(serial_fd, &buffer, 1, &timeout);
105 if (ret != 1) {
106 std::cout << "vrpn_Streaming_Arduino: Could not reset" << std::endl;
107 return -1;
108 }
109 m_buffer += buffer;
110
111 // We already got the first byte of the record, so we drop directly
112 // into reading.
114 vrpn_gettimeofday(&m_timestamp, NULL); // Set watchdog now
115 return 0;
116}
117
118// This function will read characters until it has a full report, then
119// put that report into the time, analog amd button fields and call
120// the report methods on these. The time stored is that of
121// the first character received as part of the report. Each time
122// through, it gets however many characters are available.
123//
124// If we get a report that is not valid, we reset.
125//
126// The routine that calls this one
127// makes sure we get a full reading often enough (ie, it is responsible
128// for doing the watchdog timing to make sure the device hasn't simply
129// stopped sending characters).
130
132{
133 unsigned int buttonBits = 0;
134
135 // Zero timeout, poll for any available characters
136 struct timeval timeout = {0, 0};
137
138 // When we get the first byte and we're syncing, record the time
139 // it arrived.
140 if (status == STATUS_SYNCING) {
141 unsigned char buffer;
142 if (1 == vrpn_read_available_characters(serial_fd, &buffer, 1, &timeout)) {
143 m_buffer += buffer;
146 }
147 }
148
149 // If we're not reading, then we have nothing to do
150 if (STATUS_READING != status) {
151 return 0;
152 }
153
154 // Find out the time of the read.
155 struct timeval read_time;
156 vrpn_gettimeofday(&read_time, NULL);
157
158 // We're reading now. Look for a large number of characters and see
159 // what we get.
160 unsigned char buffer[1024];
161 timeout.tv_sec = 0;
162 timeout.tv_usec = 0;
164 buffer, sizeof(buffer)-1, &timeout);
165 if (result < 0) {
166 VRPN_MSG_WARNING("Bad read, resetting");
167 reset();
168 return 0;
169 }
170 // Copy the resulting bytes into the buffer.
171 buffer[result] = '\0';
172 m_buffer += std::string(static_cast<char*>(static_cast<void*>(buffer)));
173
174 // See if there is a carriage return in the buffer. If so, we can parse
175 // the report ahead of it and then remove it from the buffer.
176 size_t cr = m_buffer.find('\n');
177 while (cr != std::string::npos) {
178
179 // Parse the report.
180 std::istringstream s(m_buffer.substr(0, cr));
181 for (size_t i = 0; i < m_numchannels; i++) {
182 int val = 0;
183 char comma;
184 s >> val;
185 if (s.fail()) {
186 std::cerr << "vrpn_Streaming_Arduino: Can't parse "
187 << s.str() << " for value " << i << std::endl;
188 std::cerr << " Report was: " << m_buffer << std::endl;
189 VRPN_MSG_WARNING("Bad value, resetting");
190 reset();
191 return 0;
192 }
193 if (i < m_numchannels - 1) { s >> comma; }
194 if (s.fail()) {
195 std::cerr << "vrpn_Streaming_Arduino: Can't read comma "
196 "for value " << i << " (value parsed was " << val << ")" << std::endl;
197 std::cerr << " Report was: " << m_buffer << std::endl;
198 VRPN_MSG_WARNING("Bad value, resetting");
199 reset();
200 return 0;
201 }
203 vrpn_Analog::channel[i] = val;
204 }
205
206 // @todo parse any markers and put them into button reports.
207
208 // Back-date the reports based on the transmission rate of the
209 // characters from the Arduino -- the number of characters in
210 // the buffer while we're parsing tells how many characters
211 // arrived since that reading was taken.
212 // The bit rate is 115200, with 10 bits/character making our
213 // byte rate 11520 per second, resulting in around 87 micro-
214 // seconds per character (a little under a tenth of a milli-
215 // second). So we get about 1ms of delay for every 10
216 // characters.
217 int offset_usec = static_cast<int>(87 * m_buffer.size());
218 struct timeval offset = { 0 , offset_usec };
219 m_timestamp = vrpn_TimevalDiff(read_time, offset);
220
221 // Gobble up the first report and see if there is another.
222 m_buffer.erase(0, cr + 1);
223 cr = m_buffer.find('\n');
224
225 // Report any changes.
227 }
228
229 vrpn_gettimeofday(&m_timestamp, NULL); // Set watchdog now
230
232 return 1;
233}
234
235void vrpn_Streaming_Arduino::report_changes(vrpn_uint32 class_of_service)
236{
239
240 vrpn_Analog::report_changes(class_of_service);
242}
243
244void vrpn_Streaming_Arduino::report(vrpn_uint32 class_of_service)
245{
248
249 vrpn_Analog::report(class_of_service);
251}
252
253// This routine is called each time through the server's main loop. It will
254// take a course of action depending on the current status of the TNG3,
255// either trying to reset it or trying to get a reading from it.
257{
259
260 switch(status) {
261 case STATUS_RESETTING:
262 reset();
263 break;
264
265 case STATUS_SYNCING:
266 case STATUS_READING:
267 {
268 // It turns out to be important to get the report before checking
269 // to see if it has been too long since the last report. This is
270 // because there is the possibility that some other device running
271 // in the same server may have taken a long time on its last pass
272 // through mainloop(). Trackers that are resetting do this. When
273 // this happens, you can get an infinite loop -- where one tracker
274 // resets and causes the other to timeout, and then it returns the
275 // favor. By checking for the report here, we reset the timestamp
276 // if there is a report ready (ie, if THIS device is still operating).
277 while (get_report()) {}; // Keep getting reports as long as they come
278 struct timeval current_time;
279 vrpn_gettimeofday(&current_time, NULL);
281 std::cerr << "vrpn_Streaming_Arduino failed to read... current_time="
282 << current_time.tv_sec << ":" << current_time.tv_usec
283 << ", timestamp="
284 << m_timestamp.tv_sec << ":" << m_timestamp.tv_usec
285 << " (resetting)" << std::endl;
286 send_text_message("Too long since last report, resetting", current_time, vrpn_TEXT_ERROR);
288 }
289 }
290 break;
291
292 default:
293 fprintf(stderr,"vrpn_Streaming_Arduino: Unknown mode (internal error)\n");
294 break;
295 }
296}
vrpn_float64 last[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:39
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:38
struct timeval timestamp
Definition: vrpn_Analog.h:41
vrpn_int32 num_channel
Definition: vrpn_Analog.h:40
virtual void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report whether something has changed or not (for servers) Optionally, tell what time to stamp ...
Definition: vrpn_Analog.C:94
virtual void report_changes(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY, const struct timeval time=vrpn_ANALOG_NOW)
Send a report only if something has changed (for servers) Optionally, tell what time to stamp the val...
Definition: vrpn_Analog.C:71
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
int send_text_message(const char *msg, struct timeval timestamp, vrpn_TEXT_SEVERITY type=vrpn_TEXT_NORMAL, vrpn_uint32 level=0)
Sends a NULL-terminated text message from the device d_sender_id.
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition: vrpn_Button.h:66
virtual void report_changes(void)
Definition: vrpn_Button.C:383
struct timeval timestamp
Definition: vrpn_Button.h:49
virtual void report_changes(void)
Definition: vrpn_Button.C:423
Generic connection class not specific to the transport mechanism.
unsigned char buffer[1024]
Definition: vrpn_Analog.h:75
virtual void mainloop(void)
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
vrpn_Streaming_Arduino(std::string name, vrpn_Connection *c, std::string port, int numchannels=1, int baud=115200)
virtual void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_LOW_LATENCY)
#define STATUS_SYNCING
#define MAX_TIME_INTERVAL
#define STATUS_READING
#define STATUS_RESETTING
@ vrpn_TEXT_ERROR
Header containing macros formerly duplicated in a lot of implementation files.
#define VRPN_MSG_WARNING(msg)
int vrpn_write_characters(int comm, const unsigned char *buffer, size_t bytes)
Write the buffer to the serial port.
Definition: vrpn_Serial.C:651
int vrpn_flush_input_buffer(int comm)
Throw out any characters within the input buffer.
Definition: vrpn_Serial.C:438
int vrpn_read_available_characters(int comm, unsigned char *buffer, size_t bytes)
Definition: vrpn_Serial.C:515
unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
Return number of microseconds between startT and endT.
Definition: vrpn_Shared.C:138
timeval vrpn_TimevalDiff(const timeval &tv1, const timeval &tv2)
Definition: vrpn_Shared.C:101
void vrpn_SleepMsecs(double dMilliSecs)
Definition: vrpn_Shared.C:166
#define vrpn_gettimeofday
Definition: vrpn_Shared.h:99
#define MAX_TCHANNELS