vrpn  07.33
Virtual Reality Peripheral Network
vrpn_GlobalHapticsOrb.C
Go to the documentation of this file.
1 // vrpn_GlobalHapticsOrb.C
2 // This is a driver for the Global Haptics "Orb" device.
3 // This box is a serial-line device that provides a sphere
4 // with many buttons, a trackball, and a spinning valuator that
5 // is treated here as a dial.
6 // This code is based on their driver code, which they
7 // sent to Russ Taylor to help get a public-domain driver
8 // written for the device.
9 
10 #include <stdio.h> // for perror, sprintf
11 
12 #include "vrpn_BaseClass.h" // for ::vrpn_TEXT_ERROR
13 #include "vrpn_GlobalHapticsOrb.h"
14 #include "vrpn_Serial.h"
15 #include "vrpn_Shared.h" // for timeval, vrpn_gettimeofday
16 
17 #undef VERBOSE
18 
19 static const double POLL_INTERVAL = 5e+6; // If we haven't heard, ask.
20 static const double TIMEOUT_INTERVAL = 10e+6; // If we haven't heard, complain.
21 
22 static const double REV_PER_TICK_WHEEL = 1.0/15; // How many revolutions per encoder tick (checked)
23 static const double REV_PER_TICK_BALL = 1.0/164; // How many revolutions per encoder tick (guess)
24 
25 static const unsigned char reset_char = 0x81; // Reset string sent to Orb
26 
27 // Defines the modes in which the box can find itself.
28 #define STATUS_RESETTING (-1) // Resetting the box
29 #define STATUS_SYNCING (0) // Looking for the first character of report
30 #define STATUS_READING (1) // Looking for the rest of the report
31 
32 // static
35 {
36  ((vrpn_GlobalHapticsOrb *) userdata)->clear_values();
37 
38  // Always return 0 here, because nonzero return means that the input data
39  // was garbage, not that there was an error. If we return nonzero from a
40  // vrpn_Connection handler, it shuts down the connection.
41  return 0;
42 }
43 
44 
45 // This creates a vrpn_GlobalHapticsOrb and sets it to reset mode. It opens
46 // the serial device using the code in the vrpn_Serial_Analog constructor.
48  const char * port, int baud) :
49  vrpn_Serial_Analog(name, c, port, baud),
50  vrpn_Button_Filter(name, c),
51  vrpn_Dial(name, c)
52 {
53  // Set the parameters in the parent classes
57 
58  // Set the status of the buttons, analogs and encoders to 0 to start
59  clear_values();
60 
61  // Set a callback handler for when the first connection is made so
62  // that it can reset the analogs and buttons whenever this happens.
66 
67  // Set the mode to reset
69 }
70 
72 {
73  int i;
74 
75  for (i = 0; i < num_buttons; i++) {
77  }
78  for (i = 0; i < num_channel; i++) {
80  }
81  for (i = 0; i < num_dials; i++) {
82  vrpn_Dial::dials[i] = 0.0;
83  }
84 }
85 
86 // This routine will reset the GlobalHapticsOrb. It verifies that it can
87 // communicate with the device by sending it the "enter direct mode"
88 // command (0x81) and waiting for it to respond with the single-byte
89 // message 0xFC.
90 
92 {
93  struct timeval timeout;
94  unsigned char inbuf[1]; // Response from the Orb
95  int ret;
96 
97  //-----------------------------------------------------------------------
98  // Set the values back to zero for all buttons, analogs and encoders
99  clear_values();
100 
101  //-----------------------------------------------------------------------
102  // Clear the input buffer to make sure we're starting with a clean slate.
103  // Send the "reset" command to the box, then wait for a response and
104  // make sure it matches what we expect.
106  vrpn_write_characters(serial_fd, &reset_char, 1);
107  timeout.tv_sec = 2;
108  timeout.tv_usec = 0;
109  ret = vrpn_read_available_characters(serial_fd, inbuf, 1, &timeout);
110  if (ret < 0) {
111  perror("vrpn_GlobalHapticsOrb::reset(): Error reading from Orb\n");
112  return -1;
113  }
114  if (ret == 0) {
115  send_text_message("vrpn_GlobalHapticsOrb::reset(): No response from Orb", d_timestamp, vrpn_TEXT_ERROR);
116  return -1;
117  }
118  if (inbuf[0] != 0xfc) {
119  char message[1024];
120  sprintf(message, "vrpn_GlobalHapticsOrb::reset(): Bad response from Orb (%02X)", inbuf[0]);
122  return -1;
123  }
124 
125  //-----------------------------------------------------------------------
126  // Figure out how many characters to expect in each report from the device,
127  // which is just 1 for the Orb.
128  d_expected_chars = 1;
129 
130  vrpn_gettimeofday(&d_timestamp, NULL); // Set watchdog now
131 
132  send_text_message("vrpn_GlobalHapticsOrb::reset(): Reset complete (this is good)", d_timestamp, vrpn_TEXT_ERROR);
133 
134  // Set the mode to synchronizing
136  return 0;
137 }
138 
139 // See if we have a report from the Orb. Each report is one character. There
140 // are separate characters for button up and button down for each button, and
141 // for the rocker switches. There are separate characters for left and right
142 // for the thumbwheel going left one tick and right one tick. There are
143 // separate characters for north, south, east, and west for the trackball.
144 // There are also a bunch of characters that are supposed to be ignored when
145 // they come. Previous versions of the Orb didn't send the release messages.
146 // The routine that calls this one
147 // makes sure we get a full reading often enough (ie, it is responsible
148 // for doing the watchdog timing to make sure the box hasn't simply
149 // stopped sending characters).
150 // Returns 1 if got a complete report, 0 otherwise.
151 
153 {
154  //--------------------------------------------------------------------
155  // The reports are each _expected_chars characters long (for the Orb,
156  // this is only one character so it is not very exciting.
157  //--------------------------------------------------------------------
158 
159  if (d_status == STATUS_SYNCING) {
160  // Try to get a character. If none, just return.
162  return 0;
163  }
164 
165  d_bufcount = 1;
167 
168  // Respond to the command, ignore it, or throw an error if it is
169  // one we don't know how to deal with.
170  switch (d_buffer[0]) {
171  // Button press and release codes for buttons 0 through 25
172  case 0x89: buttons[0] = 1; break; case 0xb9: buttons[0] = 0; break;
173  case 0x88: buttons[1] = 1; break; case 0xb8: buttons[1] = 0; break;
174  case 0x90: buttons[2] = 1; break; case 0xc0: buttons[2] = 0; break;
175  case 0x8e: buttons[3] = 1; break; case 0xbe: buttons[3] = 0; break;
176  case 0x8d: buttons[4] = 1; break; case 0xbd: buttons[4] = 0; break;
177  case 0x8c: buttons[5] = 1; break; case 0xbc: buttons[5] = 0; break;
178  case 0x8b: buttons[6] = 1; break; case 0xbb: buttons[6] = 0; break;
179  case 0x8a: buttons[7] = 1; break; case 0xba: buttons[7] = 0; break;
180  case 0x9b: buttons[8] = 1; break; case 0xcb: buttons[8] = 0; break;
181  case 0x9a: buttons[9] = 1; break; case 0xca: buttons[9] = 0; break;
182  case 0x99: buttons[10] = 1; break; case 0xc9: buttons[10] = 0; break;
183  case 0x98: buttons[11] = 1; break; case 0xc8: buttons[11] = 0; break;
184  case 0x9f: buttons[12] = 1; break; case 0xcf: buttons[12] = 0; break;
185  case 0x9e: buttons[13] = 1; break; case 0xce: buttons[13] = 0; break;
186  case 0x9d: buttons[14] = 1; break; case 0xcd: buttons[14] = 0; break;
187  case 0x9c: buttons[15] = 1; break; case 0xcc: buttons[15] = 0; break;
188  case 0x81: buttons[16] = 1; break; case 0xb1: buttons[16] = 0; break;
189  case 0x80: buttons[17] = 1; break; case 0xb0: buttons[17] = 0; break;
190  case 0x8f: buttons[18] = 1; break; case 0xbf: buttons[18] = 0; break;
191  case 0x86: buttons[19] = 1; break; case 0xb6: buttons[19] = 0; break;
192  case 0x85: buttons[20] = 1; break; case 0xb5: buttons[20] = 0; break;
193  case 0x84: buttons[21] = 1; break; case 0xb4: buttons[21] = 0; break;
194  case 0x83: buttons[22] = 1; break; case 0xb3: buttons[22] = 0; break;
195  case 0x82: buttons[23] = 1; break; case 0xb2: buttons[23] = 0; break;
196  case 0xa0: buttons[24] = 1; break; case 0xd0: buttons[24] = 0; break;
197  case 0x87: buttons[25] = 1; break; case 0xb7: buttons[25] = 0; break;
198 
199  // Pushbuttons are mapped as buttons 26 (left) and 27 (right)
200  case 0xa1: buttons[26] = 1; break; case 0xd1: buttons[26] = 0; break;
201  case 0xa2: buttons[27] = 1; break; case 0xd2: buttons[27] = 0; break;
202 
203  // Rocker up is mapped as buttons 28; rocker down is button 29
204  case 0x92: buttons[28] = 1; break; case 0xc2: buttons[28] = 0; break;
205  case 0x91: buttons[29] = 1; break; case 0xc1: buttons[29] = 0; break;
206 
207  // Thumbwheel left is negative, right is positive for dial 0.
208  // Increment/decrement by the number revolutions per tick to
209  // turn it into a dial value.
210  case 0xE1:
211  dials[0] -= REV_PER_TICK_WHEEL;
212  channel[0] -= REV_PER_TICK_WHEEL;
213  if (channel[0] < -1.0) { channel[0] = -1.0; };
214  break;
215  case 0xE0:
216  dials[0] += REV_PER_TICK_WHEEL;
217  channel[0] += REV_PER_TICK_WHEEL;
218  if (channel[0] > 1.0) { channel[0] = 1.0; };
219  break;
220 
221  // Trackball is two analogs and two dials: analog/dial 1
222  // is positive for north and negative for south.
223  // Analog/dial 2 is positive for east and
224  // negative for west. Increment/decrement by the number of
225  // revolutions per tick for the trackball.
226  case 0xF2:
227  dials[1] += REV_PER_TICK_BALL;
228  channel[1] += REV_PER_TICK_BALL;
229  if (channel[1] > 1.0) { channel[1] = 1.0; };
230  break;
231  case 0xF3:
232  dials[1] -= REV_PER_TICK_BALL;
233  channel[1] -= REV_PER_TICK_BALL;
234  if (channel[1] < -1.0) { channel[1] = -1.0; };
235  break;
236  case 0xF0:
237  dials[2] += REV_PER_TICK_BALL;
238  channel[2] += REV_PER_TICK_BALL;
239  if (channel[2] > 1.0) { channel[2] = 1.0; };
240  break;
241  case 0xF1:
242  dials[2] -= REV_PER_TICK_BALL;
243  channel[2] -= REV_PER_TICK_BALL;
244  if (channel[2] < -1.0) { channel[2] = -1.0; };
245  break;
246 
247  // There are several commands that are marked as "to be ignored."
248  case 0xfd:
249  case 0x00:
250  case 0xfe:
251  case 0x10:
252  case 0xfb:
253  case 0xfc:
254  return 1; // We got a report, we're just not doing anything about it.
255 
256  default:
257  send_text_message("vrpn_GlobalHapticsOrb::get_report(): Unknown character", d_timestamp, vrpn_TEXT_ERROR);
259  return 0;
260  }
261  } else {
262  send_text_message("vrpn_GlobalHapticsOrb::get_report(): Unknown mode, programming error.", d_timestamp, vrpn_TEXT_ERROR);
264  return 0;
265  }
266 
267  //--------------------------------------------------------------------
268  // Done with the decoding, send the reports and go back to syncing.
269  //--------------------------------------------------------------------
270 
271  report_changes();
273  d_bufcount = 0;
274  return 1;
275 }
276 
277 void vrpn_GlobalHapticsOrb::report_changes(vrpn_uint32 class_of_service)
278 {
282 
283  vrpn_Analog::report_changes(class_of_service);
286 }
287 
288 void vrpn_GlobalHapticsOrb::report(vrpn_uint32 class_of_service)
289 {
293 
294  vrpn_Analog::report(class_of_service);
297 }
298 
299 // This routine is called each time through the server's main loop. It will
300 // take a course of action depending on the current status of the Orb,
301 // either trying to reset it or trying to get a reading from it.
303 {
304  struct timeval last_poll_sent = {0,0};
305 
306  // Call the generic server mainloop, since we are a server
307  server_mainloop();
308 
309  switch(d_status) {
310  case STATUS_RESETTING:
311  reset();
312  break;
313 
314  case STATUS_SYNCING:
315  {
316  // It turns out to be important to get the report before checking
317  // to see if it has been too long since the last report. This is
318  // because there is the possibility that some other device running
319  // in the same server may have taken a long time on its last pass
320  // through mainloop(). Trackers that are resetting do this. When
321  // this happens, you can get an infinite loop -- where one tracker
322  // resets and causes the other to timeout, and then it returns the
323  // favor. By checking for the report here, we reset the timestamp
324  // if there is a report ready (ie, if THIS device is still operating).
325  while (get_report()) {}; // Keep getting reports as long as they come
326  struct timeval current_time;
327  vrpn_gettimeofday(&current_time, NULL);
328 
329  // If we haven't heard in a while (this can be normal), send a reset
330  // request to the device -- this will cause a response of 0xfc, which
331  // will be ignored when it arrives. Reset the poll interval when a
332  // poll is sent.
333  if ( vrpn_TimevalDuration(current_time, d_timestamp) > POLL_INTERVAL ) {
334  last_poll_sent = current_time;
335  vrpn_write_characters(serial_fd, &reset_char, 1);
336  }
337 
338  // If we still haven't heard from the device after a longer time,
339  // fail and go into reset mode.
340  if ( vrpn_TimevalDuration(current_time, d_timestamp) > TIMEOUT_INTERVAL ) {
341  send_text_message("Too long since last report, resetting", current_time, vrpn_TEXT_ERROR);
343  }
344  }
345  break;
346 
347  default:
348  send_text_message("vrpn_GlobalHapticsOrb: Unknown mode (internal error), resetting", d_timestamp, vrpn_TEXT_ERROR);
350  break;
351  }
352 }
vrpn_BaseClassUnique::register_autodeleted_handler
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.
Definition: vrpn_BaseClass.C:503
vrpn_Serial_Analog
Definition: vrpn_Analog.h:63
vrpn_Dial::report
virtual void report(void)
Definition: vrpn_Dial.C:82
vrpn_GlobalHapticsOrb.h
vrpn_Button::report_changes
virtual void report_changes(void)
Definition: vrpn_Button.C:422
vrpn_GlobalHapticsOrb
Definition: vrpn_GlobalHapticsOrb.h:21
vrpn_BaseClass.h
vrpn_TimevalDuration
unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
Return number of microseconds between startT and endT.
Definition: vrpn_Shared.C:129
vrpn_Button_Filter::report_changes
virtual void report_changes(void)
Definition: vrpn_Button.C:382
vrpn_Analog::channel
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:38
vrpn_GlobalHapticsOrb::d_status
int d_status
Definition: vrpn_GlobalHapticsOrb.h:32
vrpn_GlobalHapticsOrb::vrpn_GlobalHapticsOrb
vrpn_GlobalHapticsOrb(const char *name, vrpn_Connection *c, const char *port, int baud)
Definition: vrpn_GlobalHapticsOrb.C:47
STATUS_RESETTING
#define STATUS_RESETTING
Definition: vrpn_GlobalHapticsOrb.C:28
vrpn_Analog::report
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
vrpn_BaseClassUnique::userdata
void * userdata
Definition: vrpn_BaseClass.h:287
vrpn_GlobalHapticsOrb::handle_firstConnection
static int VRPN_CALLBACK handle_firstConnection(void *userdata, vrpn_HANDLERPARAM)
Clear all of the values when we get our first client connection request.
Definition: vrpn_GlobalHapticsOrb.C:33
vrpn_Analog::timestamp
struct timeval timestamp
Definition: vrpn_Analog.h:41
vrpn_got_first_connection
const char * vrpn_got_first_connection
These are the strings that define the system-generated message types that tell when connections are r...
Definition: vrpn_Connection.C:184
vrpn_GlobalHapticsOrb::clear_values
virtual void clear_values(void)
Definition: vrpn_GlobalHapticsOrb.C:71
vrpn_Serial.h
vrpn_Serial: Pulls all the serial port routines into one file to make porting to new operating system...
vrpn_Button::num_buttons
vrpn_int32 num_buttons
Definition: vrpn_Button.h:47
POLL_INTERVAL
#define POLL_INTERVAL
Definition: vrpn_IDEA.C:26
vrpn_GlobalHapticsOrb::d_buffer
unsigned char d_buffer[512]
Definition: vrpn_GlobalHapticsOrb.h:35
vrpn_Button::buttons
unsigned char buttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:44
vrpn_BaseClassUnique::d_connection
vrpn_Connection * d_connection
Connection that this object talks to.
Definition: vrpn_BaseClass.h:224
vrpn_Connection::register_message_type
virtual vrpn_int32 register_message_type(const char *name)
Definition: vrpn_Connection.C:5074
vrpn_TEXT_ERROR
@ vrpn_TEXT_ERROR
Definition: vrpn_BaseClass.h:103
vrpn_flush_input_buffer
int vrpn_flush_input_buffer(int comm)
Throw out any characters within the input buffer.
Definition: vrpn_Serial.C:435
vrpn_Dial
Definition: vrpn_Dial.h:21
vrpn_GlobalHapticsOrb::d_timestamp
struct timeval d_timestamp
Definition: vrpn_GlobalHapticsOrb.h:38
vrpn_HANDLERPARAM
This structure is what is passed to a vrpn_Connection message callback.
Definition: vrpn_Connection.h:44
vrpn_Shared.h
vrpn_GlobalHapticsOrb::get_report
virtual int get_report(void)
Definition: vrpn_GlobalHapticsOrb.C:152
vrpn_Button::timestamp
struct timeval timestamp
Definition: vrpn_Button.h:48
vrpn_GlobalHapticsOrb::d_expected_chars
unsigned d_expected_chars
Definition: vrpn_GlobalHapticsOrb.h:34
vrpn_Dial::dials
vrpn_float64 dials[vrpn_DIAL_MAX]
Definition: vrpn_Dial.h:26
vrpn_Dial::report_changes
virtual void report_changes(void)
Definition: vrpn_Dial.C:54
STATUS_SYNCING
#define STATUS_SYNCING
Definition: vrpn_GlobalHapticsOrb.C:29
vrpn_Serial_Analog::serial_fd
int serial_fd
Definition: vrpn_Analog.h:72
vrpn_Connection
Generic connection class not specific to the transport mechanism.
Definition: vrpn_Connection.h:510
vrpn_Analog::num_channel
vrpn_int32 num_channel
Definition: vrpn_Analog.h:40
vrpn_gettimeofday
#define vrpn_gettimeofday
Definition: vrpn_Shared.h:89
vrpn_Button::lastbuttons
unsigned char lastbuttons[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:45
vrpn_read_available_characters
int vrpn_read_available_characters(int comm, unsigned char *buffer, size_t bytes)
Definition: vrpn_Serial.C:512
vrpn_Analog::last
vrpn_float64 last[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:39
vrpn_write_characters
int vrpn_write_characters(int comm, const unsigned char *buffer, size_t bytes)
Write the buffer to the serial port.
Definition: vrpn_Serial.C:643
vrpn_BaseClassUnique::send_text_message
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.
Definition: vrpn_BaseClass.C:568
vrpn_Analog::report_changes
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
vrpn_GlobalHapticsOrb::reset
virtual int reset(void)
Definition: vrpn_GlobalHapticsOrb.C:91
vrpn_Dial::num_dials
vrpn_int32 num_dials
Definition: vrpn_Dial.h:27
vrpn_Dial::timestamp
struct timeval timestamp
Definition: vrpn_Dial.h:28
vrpn_GlobalHapticsOrb::mainloop
virtual void mainloop()
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
Definition: vrpn_GlobalHapticsOrb.C:302
vrpn_BaseClassUnique::server_mainloop
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
Definition: vrpn_BaseClass.C:603
vrpn_GlobalHapticsOrb::d_bufcount
unsigned d_bufcount
Definition: vrpn_GlobalHapticsOrb.h:36
vrpn_Button_Filter
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition: vrpn_Button.h:65