vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
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
14#include "vrpn_Serial.h"
15#include "vrpn_Shared.h" // for timeval, vrpn_gettimeofday
16
17#undef VERBOSE
18
19static const double POLL_INTERVAL = 5e+6; // If we haven't heard, ask.
20static const double TIMEOUT_INTERVAL = 10e+6; // If we haven't heard, complain.
21
22static const double REV_PER_TICK_WHEEL = 1.0/15; // How many revolutions per encoder tick (checked)
23static const double REV_PER_TICK_BALL = 1.0/164; // How many revolutions per encoder tick (guess)
24
25static 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
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
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.
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
273 d_bufcount = 0;
274 return 1;
275}
276
287
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
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_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
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.
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)
vrpn_int32 num_buttons
Definition vrpn_Button.h:48
struct timeval timestamp
Definition vrpn_Button.h:49
virtual void report_changes(void)
unsigned char lastbuttons[vrpn_BUTTON_MAX_BUTTONS]
Definition vrpn_Button.h:46
unsigned char buttons[vrpn_BUTTON_MAX_BUTTONS]
Definition vrpn_Button.h:45
Generic connection class not specific to the transport mechanism.
virtual vrpn_int32 register_message_type(const char *name)
virtual void report_changes(void)
Definition vrpn_Dial.C:54
struct timeval timestamp
Definition vrpn_Dial.h:28
vrpn_float64 dials[vrpn_DIAL_MAX]
Definition vrpn_Dial.h:26
virtual void report(void)
Definition vrpn_Dial.C:82
vrpn_int32 num_dials
Definition vrpn_Dial.h:27
vrpn_GlobalHapticsOrb(const char *name, vrpn_Connection *c, const char *port, int baud)
virtual void clear_values(void)
virtual void mainloop()
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
unsigned char d_buffer[512]
static int VRPN_CALLBACK handle_firstConnection(void *userdata, vrpn_HANDLERPARAM)
Clear all of the values when we get our first client connection request.
This structure is what is passed to a vrpn_Connection message callback.
#define STATUS_SYNCING
#define STATUS_RESETTING
All types of client/server/peer objects in VRPN should be derived from the vrpn_BaseClass type descri...
@ vrpn_TEXT_ERROR
const char * vrpn_got_first_connection
These are the strings that define the system-generated message types that tell when connections are r...
#define POLL_INTERVAL
Definition vrpn_IDEA.C:26
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...
unsigned long vrpn_TimevalDuration(struct timeval endT, struct timeval startT)
Return number of microseconds between startT and endT.
#define vrpn_gettimeofday
Definition vrpn_Shared.h:99