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
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
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
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
442
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_Analog_Output(const char *name, vrpn_Connection *c=NULL)
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.
virtual void report_changes(void)
vrpn_Button_Filter(const char *, vrpn_Connection *c=NULL)
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.
vrpn_Serial_Analog(const char *name, vrpn_Connection *connection, const char *port, int baud=9600, int bits=8, vrpn_SER_PARITY parity=vrpn_SER_PARITY_NONE, bool rts_flow=false)
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