vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
vrpn_BiosciencesTools.C
Go to the documentation of this file.
1// vrpn_BiosciencesTools.C
2// This is a driver for the BiosciencesTools temperature controller.
3// It was written in October 2012 by Russ Taylor.
4
5// INFO about how the device communicates, taken from the user manual:
6// Note: They say that the device uses <cr>, but they also say that it
7// uses \n for this character. In fact, the \r character is <cr> and \n
8// is newline. In fact, \r is what works in the end.
9
10/*
11Using a standard DB-9 cable (female-female connectors on both ends with
12straight-through connections from each pin)
13connect the controller (middle DB-9 connector) to a serial port of your computer.
14Set the serial port at 115,200 speed, 8 bits, 1 stop bit,
15NONE parity, and Hardware flow control.
16The following is the list of text commands supported.
17NOTE: Each command should follow by \r <CR> code:
18(The following notes LIE: There is no space before the . or before the C,
19 and sometimes the C is an E. Also, the order is incorrect. The actual
20 order is stage 1, bath 1, external 1, stage 2, bath 2, external 2.)
21T1<CR> returns temperature readings from STAGE1 sensor: 37 .1 C
22T2<CR> returns temperature readings from BATH1 sensor: 36 .9 C
23T5<CR> returns SET temperature: 37 .0 C
24T3<CR> returns temperature readings from STAGE2 sensor: 37 .1 C
25T4<CR> returns temperature readings from BATH2 sensor: 36 .9 C
26T6<CR> returns SET temperature: 37 .0 C
27CTn<CR> returns readings from n (n=1 - STAGE1, 2 - BATH1, 3 - STAGE2, 4 - BATH2) sensor: 37 .1 C
28ON<CR> turns temperature control ON
29OFF<CR> turns temperature control OFF
30S1 037 0<CR> sets reference temperature for channel I (NOTE: all four digits should be sent to the controller)
31S2 037 0<CR> sets reference temperature for channel II
32*/
33
34#include <stddef.h> // for size_t
35#include <stdio.h> // for sprintf, fprintf, stderr, etc
36#include <string.h> // for strlen, NULL
37
38#include "vrpn_BaseClass.h" // for ::vrpn_TEXT_ERROR, etc
40#include "vrpn_Serial.h" // for vrpn_write_characters, etc
41#include "vrpn_Shared.h" // for vrpn_unbuffer, timeval, etc
42#include "vrpn_MessageMacros.h" // for VRPN_MSG_INFO, VRPN_MSG_WARNING, VRPN_MSG_ERROR
43
44#undef VERBOSE
45
46// Defines the modes in which the device can find itself.
47#define STATUS_RESETTING (-1) // Resetting the device
48#define STATUS_SYNCING (0) // Looking for the first character of report
49#define STATUS_READING (1) // Looking for the rest of the report
50
51#define TIMEOUT_TIME_INTERVAL (2000000L) // max time between reports (usec)
52
53
54// This creates a vrpn_BiosciencesTools. It opens
55// the serial device using the code in the vrpn_Serial_Analog constructor.
56// It uses hardware flow control.
57
59 const char * port, float temp1, float temp2, bool control_on):
60 vrpn_Serial_Analog(name, c, port, 115200, 8, vrpn_SER_PARITY_NONE, true),
61 vrpn_Analog_Output(name, c),
62 vrpn_Button_Filter(name, c)
63{
64 num_channel = 6;
65 o_num_channel = 3;
66 num_buttons = 1;
67 buttons[0] = control_on;
68
69 // Fill in the arguments to send to the device at reset time.
70 o_channel[0] = temp1;
71 o_channel[1] = temp2;
72 o_channel[2] = control_on;
73
74 // Set the mode to reset
76
77 // Register to receive the message to request changes and to receive connection
78 // messages.
79 if (d_connection != NULL) {
81 this, d_sender_id)) {
82 fprintf(stderr,"vrpn_BiosciencesTools: can't register handler\n");
83 d_connection = NULL;
84 }
86 this, d_sender_id)) {
87 fprintf(stderr,"vrpn_BiosciencesTools: can't register handler\n");
88 d_connection = NULL;
89 }
91 this, d_sender_id)) {
92 fprintf(stderr,"vrpn_BiosciencesTools: can't register handler\n");
93 d_connection = NULL;
94 }
95 } else {
96 fprintf(stderr,"vrpn_BiosciencesTools: Can't get connection!\n");
97 }
98
99}
100
101// Command format described in document:
102// S1 037 0<CR> sets reference temperature for channel 1
103// (NOTE: all four digits should be sent to the controller)
104// Actual command format:
105// S1 0370<CR> Sets reference temperature for channel 1 to 37.0 deg C
106// S2 0421<CR> Sets reference temperature for channel 2 to 42.1 deg C
107
108bool vrpn_BiosciencesTools::set_reference_temperature(unsigned channel, float value)
109{
110 char command[128];
111
112 // Fill in the command with the zero-padded integer output for
113 // above the decimal and then a single value for the first point
114 // past the decimal.
115 int whole = static_cast<int>(value);
116 int dec = static_cast<int>(value*10) - whole*10;
117 sprintf(command, "S%d %03d%d\r", channel+1, whole,dec);
118
119 // Send the command to the serial port
120 return (static_cast<size_t>(vrpn_write_characters(serial_fd, (unsigned char *)(command), strlen(command))) == strlen(command));
121}
122
123// Command format:
124// ON<CR> sets control on
125// OFF<CR> sets control off
126
128{
129 char command[128];
130
131 if (on) {
132 sprintf(command, "ON\r");
133 } else {
134 sprintf(command, "OFF\r");
135 }
136
137 // Send the command to the serial port
138 return (static_cast<size_t>(vrpn_write_characters(serial_fd, (unsigned char *)(command), strlen(command))) == strlen(command));
139}
140
141// Command format:
142// T1<CR> returns temperature readings from STAGE1 sensor: 37.1C
143// T2<CR> returns temperature readings from BATH1 sensor: 36.9C
144// T5<CR> returns SET temperature: 37.0C
145// T3<CR> returns temperature readings from STAGE2 sensor: 37.1C
146// T4<CR> returns temperature readings from BATH2 sensor: 36.9C
147// T6<CR> returns SET temperature: 37.0C
148// NOTE: Sometimes the C is an E when there is no reading.
149
151{
152 char command[128];
153
154 sprintf(command, "T%d\r", channel+1);
155#ifdef VERBOSE
156 printf("Sending command: %s", command);
157#endif
158
159 // Send the command to the serial port
160 return (static_cast<size_t>(vrpn_write_characters(serial_fd, (unsigned char *)(command), strlen(command))) == strlen(command));
161}
162
163// Convert the four bytes that have been read into a signed integer value.
164// The format (no quotes) looks like: "- 37.1C\r" or " 000.00E\r".
165// I don't think that the - means a minus sign, and it has a space
166// between it and the number.
167// Returns -1000 if there is an error.
169{
170 float val;
171 char c;
172
173 // Skip any leading minus sign.
174 if (*buf == '-') { buf++; }
175
176 // Read a fractional number.
177 if (sscanf(buf, "%f%c", &val, &c) != 2) {
178 return -1000;
179 }
180
181 // See if we get and E or C after the number,
182 // or (since E can be part of a floating-point
183 // number) if we get \r.
184 if ( (c != 'E') && (c != 'C') && (c != '\r') ) {
185 return -1000;
186 }
187
188 return val;
189}
190
191
193{
194 //-----------------------------------------------------------------------
195 // Sleep less thana second and then drain the input buffer to make sure we start
196 // with a fresh slate.
197 vrpn_SleepMsecs(200);
199
200 //-----------------------------------------------------------------------
201 // Set the temperatures for channel 1 and 2 and then set the temperature
202 // control to be on or off depending on what we've been asked to do.
203 if (!set_reference_temperature(0, static_cast<float>(o_channel[0]))) {
204 fprintf(stderr,"vrpn_BiosciencesTools::reset(): Cannot send set ref temp 0, trying again\n");
205 return -1;
206 }
207 if (!set_reference_temperature(1, static_cast<float>(o_channel[1]))) {
208 fprintf(stderr,"vrpn_BiosciencesTools::reset(): Cannot send set ref temp 1, trying again\n");
209 return -1;
210 }
211 if (!set_control_status(o_channel[0] != 0)) {
212 fprintf(stderr,"vrpn_BiosciencesTools::reset(): Cannot send set control status, trying again\n");
213 return -1;
214 }
215
216 //-----------------------------------------------------------------------
217 // Send the command to request input from the first channel, and set up
218 // the finite-state machine so we know which thing to request next.
221 fprintf(stderr,"vrpn_BiosciencesTools::reset(): Cannot request temperature, trying again\n");
222 return -1;
223 }
224
225 // We're now waiting for any responses from devices
227 VRPN_MSG_WARNING("reset complete (this is normal)");
228 vrpn_gettimeofday(&timestamp, NULL); // Set watchdog now
229 return 0;
230}
231
232// This function will read characters until it has a full report, then
233// put that report into analog fields and call the report methods on these.
234// The time stored is that of the first character received as part of the
235// report.
236
238{
239 int ret; // Return value from function call to be checked
240
241 //--------------------------------------------------------------------
242 // If we're SYNCing, then the next character we get should be the start
243 // of a report. If we recognize it, go into READing mode and tell how
244 // many characters we expect total. If we don't recognize it, then we
245 // must have misinterpreted a command or something; reset
246 // and start over
247 //--------------------------------------------------------------------
248
249 if (status == STATUS_SYNCING) {
250 // Try to get a character. If none, just return.
251 if (vrpn_read_available_characters(serial_fd, (unsigned char *)(d_buffer), 1) != 1) {
252 return 0;
253 }
254
255 // Got the first character of a report -- go into READING mode
256 // and record that we got one character at this time. Clear the
257 // rest of the buffer to 0's so that we won't be looking at old
258 // data when we parse.
259 // The time stored here is as close as possible to when the
260 // report was generated.
261 d_bufcount = 1;
264 size_t i;
265 for (i = 1; i < sizeof(d_buffer); i++) {
266 d_buffer[i] = 0;
267 }
268#ifdef VERBOSE
269 printf("... Got the 1st char\n");
270#endif
271 }
272
273 //--------------------------------------------------------------------
274 // Read as many bytes of this report as we can, storing them
275 // in the buffer.
276 //--------------------------------------------------------------------
277
278 while ( 1 == (ret = vrpn_read_available_characters(serial_fd, (unsigned char *)(&d_buffer[d_bufcount]), 1))) {
279 d_bufcount++;
280 }
281 if (ret == -1) {
282 VRPN_MSG_ERROR("Error reading");
284 return 0;
285 }
286#ifdef VERBOSE
287 if (ret != 0) printf("... got %d total characters\n", d_bufcount);
288#endif
289 if (d_buffer[d_bufcount-1] != '\r') { // Not done -- go back for more
290 return 0;
291 }
292
293 //--------------------------------------------------------------------
294 // We now have enough characters to make a full report. Check to make
295 // sure that its format matches what we expect. If it does, the next
296 // section will parse it.
297 // Store the report into the appropriate analog channel.
298 //--------------------------------------------------------------------
299
300#ifdef VERBOSE
301 printf(" Complete report: \n%s\n",d_buffer);
302#endif
303 float value = convert_bytes_to_reading(d_buffer);
304 if (value == -1000) {
305 char msg[256];
306 sprintf(msg,"Invalid report, channel %d, resetting", d_next_channel_to_read);
307 VRPN_MSG_ERROR(msg);
309 }
311
312#ifdef VERBOSE
313 printf("got a complete report (%d chars)!\n", d_bufcount);
314#endif
315
316 //--------------------------------------------------------------------
317 // Request a reading from the next channe.
318 //--------------------------------------------------------------------
319
322 char msg[256];
323 sprintf(msg,"Can't request reading, channel %d, resetting", d_next_channel_to_read);
324 VRPN_MSG_ERROR(msg);
326 }
327
328 //--------------------------------------------------------------------
329 // Done with the decoding, send the reports and go back to syncing
330 //--------------------------------------------------------------------
331
334 d_bufcount = 0;
335
336 return 1;
337}
338
339bool vrpn_BiosciencesTools::set_specified_channel(unsigned channel, vrpn_float64 value)
340{
341 // XXX Check return status of the set commands?
342 switch (channel) {
343 case 0: // Reference temperature for channels 1 and 2
344 case 1: // Reference temperature for channels 1 and 2
345 set_reference_temperature(channel, static_cast<float>(value));
346 o_channel[channel] = value;
347 break;
348 case 2: // Turn on temperature control if this is nonzero.
349 o_channel[2] = value;
350 buttons[0] = ( value != 0 );
351 set_control_status( value != 0);
352 break;
353 default:
354 return false;
355 }
356 return true;
357}
358
360{
361 const char *bufptr = p.buffer;
362 vrpn_int32 chan_num;
363 vrpn_int32 pad;
364 vrpn_float64 value;
366
367 // Read the parameters from the buffer
368 vrpn_unbuffer(&bufptr, &chan_num);
369 vrpn_unbuffer(&bufptr, &pad);
370 vrpn_unbuffer(&bufptr, &value);
371
372 // Set the appropriate value, if the channel number is in the
373 // range of the ones we have.
374 if ( (chan_num < 0) || (chan_num >= me->o_num_channel) ) {
375 char msg[1024];
376 sprintf(msg,"vrpn_BiosciencesTools::handle_request_message(): Index out of bounds (%d of %d), value %lg\n",
377 chan_num, me->num_channel, value);
379 return 0;
380 }
381
382 me->set_specified_channel(chan_num, value);
383 return 0;
384}
385
387{
388 int i;
389 const char* bufptr = p.buffer;
390 vrpn_int32 num;
391 vrpn_int32 pad;
393
394 // Read the values from the buffer
395 vrpn_unbuffer(&bufptr, &num);
396 vrpn_unbuffer(&bufptr, &pad);
397 if (num > me->o_num_channel) {
398 char msg[1024];
399 sprintf(msg,"vrpn_BiosciencesTools::handle_request_channels_message(): Index out of bounds (%d of %d), clipping\n",
400 num, me->o_num_channel);
402 num = me->o_num_channel;
403 }
404 for (i = 0; i < num; i++) {
405 vrpn_unbuffer(&bufptr, &(me->o_channel[i]));
406 me->set_specified_channel(i, me->o_channel[i]);
407 }
408
409 return 0;
410}
411
415{
417
419 return 0;
420}
421
422void vrpn_BiosciencesTools::report_changes(vrpn_uint32 class_of_service)
423{
425 vrpn_Analog::report_changes(class_of_service);
427}
428
429void vrpn_BiosciencesTools::report(vrpn_uint32 class_of_service)
430{
432 vrpn_Analog::report(class_of_service);
434}
435
444{
445 char errmsg[256];
446
448
449 switch(status) {
450 case STATUS_RESETTING:
451 reset();
452 break;
453
454 case STATUS_SYNCING:
455 case STATUS_READING:
456 {
457 // It turns out to be important to get the report before checking
458 // to see if it has been too long since the last report. This is
459 // because there is the possibility that some other device running
460 // in the same server may have taken a long time on its last pass
461 // through mainloop(). Trackers that are resetting do this. When
462 // this happens, you can get an infinite loop -- where one tracker
463 // resets and causes the other to timeout, and then it returns the
464 // favor. By checking for the report here, we reset the timestamp
465 // if there is a report ready (ie, if THIS device is still operating).
466 while (get_report()) {}; // Keep getting reports so long as there are more
467
468 struct timeval current_time;
469 vrpn_gettimeofday(&current_time, NULL);
471 sprintf(errmsg,"Timeout... current_time=%ld:%ld, timestamp=%ld:%ld",
472 current_time.tv_sec, static_cast<long>(current_time.tv_usec),
473 timestamp.tv_sec, static_cast<long>(timestamp.tv_usec));
474 VRPN_MSG_ERROR(errmsg);
476 }
477 }
478 break;
479
480 default:
481 VRPN_MSG_ERROR("Unknown mode (internal error)");
482 break;
483 }
484}
vrpn_float64 o_channel[vrpn_CHANNEL_MAX]
vrpn_int32 request_channels_m_id
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
int register_autodeleted_handler(vrpn_int32 type, vrpn_MESSAGEHANDLER handler, void *userdata, vrpn_int32 sender=vrpn_ANY_SENDER)
Registers a handler with the connection, and remembers to delete at destruction.
vrpn_Connection * d_connection
Connection that this object talks to.
vrpn_int32 d_sender_id
Sender ID registered with the connection.
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.
vrpn_int32 d_ping_message_id
Ask the server if they are there.
virtual void report(vrpn_uint32 class_of_service=vrpn_CONNECTION_RELIABLE)
send report whether or not changed
static int VRPN_CALLBACK handle_connect_message(void *userdata, vrpn_HANDLERPARAM p)
Responds to a connection request with a report of the values.
vrpn_BiosciencesTools(const char *name, vrpn_Connection *c, const char *port, float temp1, float temp2, bool control_on)
static int VRPN_CALLBACK handle_request_channels_message(void *userdata, vrpn_HANDLERPARAM p)
Responds to a request to change multiple channels at once.
virtual void mainloop()
Called once through each main loop iteration to handle updates.
bool request_temperature(unsigned channel)
bool set_reference_temperature(unsigned channel, float value)
float convert_bytes_to_reading(const char *buf)
bool set_specified_channel(unsigned channel, vrpn_float64 value)
static int VRPN_CALLBACK handle_request_message(void *userdata, vrpn_HANDLERPARAM p)
Responds to a request to change one of the values by setting the channel to that value.
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)
vrpn_int32 num_buttons
Definition vrpn_Button.h:48
virtual void report_changes(void)
unsigned char buttons[vrpn_BUTTON_MAX_BUTTONS]
Definition vrpn_Button.h:45
Generic connection class not specific to the transport mechanism.
This structure is what is passed to a vrpn_Connection message callback.
const char * buffer
#define STATUS_SYNCING
#define STATUS_READING
#define STATUS_RESETTING
All types of client/server/peer objects in VRPN should be derived from the vrpn_BaseClass type descri...
@ vrpn_TEXT_ERROR
#define TIMEOUT_TIME_INTERVAL
const vrpn_uint32 vrpn_CONNECTION_RELIABLE
Classes of service for messages, specify multiple by ORing them together Priority of satisfying these...
Header containing macros formerly duplicated in a lot of implementation files.
#define VRPN_MSG_ERROR(msg)
#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.
int vrpn_flush_input_buffer(int comm)
Throw out any characters within the input buffer.
int vrpn_read_available_characters(int comm, unsigned char *buffer, size_t bytes)
vrpn_Serial: Pulls all the serial port routines into one file to make porting to new operating system...
@ vrpn_SER_PARITY_NONE
Definition vrpn_Serial.h:16
VRPN_API int vrpn_unbuffer(const char **buffer, timeval *t)
Utility routine for taking a struct timeval from a buffer that was sent as a message.
unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
Return number of microseconds between startT and endT.
void vrpn_SleepMsecs(double dMilliSecs)
#define vrpn_gettimeofday
Definition vrpn_Shared.h:99