vrpn 07.35
Virtual Reality Peripheral Network
Loading...
Searching...
No Matches
vrpn_raw_sgibox.C
Go to the documentation of this file.
1/* vrpn_raw_sgibox.C
2 * This file contains the implementation for a controller to
3 * the SGI dial and button boxes. This is an implementation that
4 * goes directly through the serial port rather than using the
5 * SGI GL library. The two connect together and then to a serial
6 * port at 9600 baud, 8 bits, 1 start 1 stop, no parity. The single-
7 * byte command 0x20 resets both of them; they respond with 0x20.
8 * The dial box returns 3-byte values whenever a dial is moved.
9 * There are 200 ticks per rotation, and there is an internal counter
10 * that counts up to 16 bits and then rolls over. The message format
11 * that is returned is 3Z XX YY, where Z is the dial number and XXYY
12 * is the new positional value for the dial. The dials are clamped to
13 * the range -0.5<-->0.5, and this is twice VRPN_DIAL_RANGE. If the
14 * dials are turned past this, the values are set to clamp. As soon
15 * as the dial is turned the other direction, it will lower the value;
16 * this provides a "saturating" dial effect.
17 * The button box takes commands to turn the lights on or off; the
18 * command is 0x75 followed by 4 bytes; the bits of byte 1 turn on or
19 * off the first 8 lights, with MSB turning on light 7 and LSB controlling
20 * byte 0. Lights 8-15 are byte 2, 16-23 byte 3, 24-31 byte 4. When a
21 * button is pressed or released, a single-byte message is returned.
22 * For press, D8 is button 0, D9 is 1... DF is 7; D0 is 8...D7 is 15;
23 * C8 is 16...CF is 23, C0 is 24...C7 is 31. For release, F8 is 0...
24 * FF is 7, F0 is 8...F7 is 15, E8 is 16...EF is 23, E0 is 24...E7 is 31.
25 * The button box needs to have its buttons enabled and activated
26 * using the 0x73 and 0x71 commands, each followed by FFFFFFFF to turn
27 * on them all. The dial box enable command is 0x50 followed by xxFF,
28 * where xx is ignored and FF turns on all of the dials.
29 */
30
31//#define VERBOSE
32
33#include <stdio.h> // for perror, fprintf, printf, etc
34#include <string.h> // for memcpy
35
36#include "vrpn_Connection.h" // for vrpn_HANDLERPARAM, etc
37#include "vrpn_Serial.h"
38#include "vrpn_Shared.h" // for vrpn_SleepMsecs, timeval
39#include "vrpn_Types.h" // for vrpn_float64, vrpn_int16, etc
40#include "vrpn_raw_sgibox.h"
41
42static const unsigned char BBOX_RESET = 0x20;
43static const int VRPN_DIAL_RANGE = 200;
44
45static int VRPN_CALLBACK sgibox_raw_con_cb(void * userdata, vrpn_HANDLERPARAM p);
46static int VRPN_CALLBACK sgibox_raw_alert_handler(void * userdata, vrpn_HANDLERPARAM);
47
49 char *serialPortName):
50 vrpn_Analog(name, c), vrpn_Dial(name, c), vrpn_Button_Filter(name, c)
51{
52 char message[1024];
53 serialfd = -1;
54
55 // Open the serial port that will be used to communicate to
56 // the dial and button box. Then reset the boxes.
57 serialfd = vrpn_open_commport(serialPortName, 9600);
58 if (serialfd < 0) {
59 sprintf(message,"vrpn_raw_SGIBox: error opening serial port: %s\n",serialPortName);
60 perror(message);
61 return;
62 }
63 reset();
64
68
69 // We can use either autodeleted handler; choose the one in Analog
71 register_autodeleted_handler(alert_message_id,sgibox_raw_alert_handler, this);
72
73 set_alerts(1); //turn on alerts from toggle filter class to notify
74 //local sgibox that lights should be turned on/off
75}
76
77int vrpn_raw_SGIBox::reset() { /* Button/Dial box setup */
78 int i;
79 int ret;
80 unsigned char inbuf[100];
81#ifdef VERBOSE
82 unsigned char lightson[5] = {0x75, 0xff, 0xff, 0xff, 0xff};
83 unsigned char lightsoff[5] = {0x75, 0x00, 0x00, 0x00, 0x00};
84#endif
85 unsigned char activatebuttons[5] = {0x73, 0xff, 0xff, 0xff, 0xff};
86 unsigned char enablebuttons[5] = {0x71, 0xff, 0xff, 0xff, 0xff};
87 unsigned char enabledials[3] = {0x50, 0xff, 0xff};
88
89
90 // Clear the incoming serial buffer of all characters
91 // Send the reset message to the dials and buttons. Wait for any return
92 // messages, which should be 1 or 2 "0x20" bytes.
93
94 if (serialfd != -1) { // Write reset command
95 if (vrpn_flush_input_buffer(serialfd) == -1) {
96 perror("vrpn_raw_SGIBox::reset(): Can't flush incoming buffer");
97 return -1;
98 }
99 if (vrpn_write_slowly(serialfd, &BBOX_RESET,1,1) != 1) {
100 perror("vrpn_raw_SGIBox::reset(): Can't write reset command");
101 return -1;
102 }
103 if (vrpn_write_slowly(serialfd, &BBOX_RESET,1,1) != 1) {
104 perror("vrpn_raw_SGIBox::reset(): Can't write reset command");
105 return -1;
106 }
107 }
108 vrpn_SleepMsecs(1000.0*1); // Give the box time to respond
109 if ( (ret=vrpn_read_available_characters(serialfd, inbuf, 2)) <= 0) {
110 //XXX Turn this into a vrpn text message
111 perror("vrpn_raw_SGIBox::reset(): Can't read or no data from serial port");
112 return -1;
113 }
114#ifdef VERBOSE
115 printf("vrpn_raw_SGIBox::reset(): Box's response to reset command: %02x\n", inbuf[0]);
116#endif
117
118 for (i = 0; i < ret; i++) {
119 if (inbuf[i] != BBOX_RESET) {
120 //XXX Turn this into a vrpn text message
121 fprintf(stderr,"vrpn_raw_SGIBox::reset(): Bad response to reset command : %02x- please restart sgiBox vrpn server\n",inbuf[i]);
122 return -1;
123 }
124 }
125
126#ifdef VERBOSE
127 if (serialfd != -1) {
128 printf("vrpn_raw_SGIBox: flashing the lights on then off...\n");
129 if (vrpn_write_slowly(serialfd, lightson,5,1) != 5) {
130 perror("vrpn_raw_SGIBox::reset(): Can't turn the lights on");
131 return -1;
132 }
133 vrpn_SleepMsecs(1000.0*5);
134 if (vrpn_write_slowly(serialfd, lightsoff,5,1) != 5) {
135 perror("vrpn_raw_SGIBox::reset(): Can't turn the lights off");
136 return -1;
137 }
138 }
139#endif
140
141 // Active and enable all of the buttons, enable the dials
142
143 if (serialfd != -1) {
144 // for some reason, enabling the dials disables the buttons
145 // so we have to enable the dials first
146 if (vrpn_write_slowly(serialfd, enabledials,3,1) != 3) {
147 perror("vrpn_raw_SGIBox::reset(): Can't enable dials");
148 return -1;
149 }
150#ifdef VERBOSE
151 else {
152 printf("vrpn_raw_SGIBOX::reset() : Enabled Dials\n");
153 }
154#endif
155 // for some reasn the box doesn't always understand the enable buttons
156 // command the first time. So we send it twice to make sure
157 for (i=0; i < 2; i++) {
158
159 if (vrpn_write_slowly(serialfd, enablebuttons,5,1) != 5) {
160 perror("vrpn_raw_SGIBox::reset(): Can't enable buttons");
161 return -1;
162 }
163#ifdef VERBOSE
164 else {
165 printf("vrpn_raw_SGIBOX::reset() : Enabled Buttons\n");
166 }
167#endif
168 if (vrpn_write_slowly(serialfd, activatebuttons,5,1) != 5) {
169 perror("vrpn_raw_SGIBox::reset(): Can't activate buttons\n");
170 return -1;
171 }
172#ifdef VERBOSE
173 else {
174 printf("vrpn_raw_SGIBOX::reset() : Activated Buttons\n");
175 }
176#endif
177 } // end of loop to send enable and activate commands
178
179 }
180
181 // Reset the button and, analog, and dial values to zero, since the dial box
182 // and button box are now reset.
183
184 for (i=0; i<vrpn_SGI_NUM_BUTTONS; i++) {
185 buttons[i] = lastbuttons[i] = 0; // The buttons are released
186 //XXX Reset the button-light handling registers
187 }
188
189 for (i=0; i<vrpn_SGI_NUM_DIALS; i++) {
190 mid_values[i] = 0; // The middle of saturating analog range
191 last[i] = channel[i] = 0; // The analog values are reset to 0
192 dials[i] = 0; // Reset the dials to zero
193 last_values[i] = 0; // Reset the values used by dial code to zero
194 }
195
196 // Set the lights to how they should be
198
199 return 0;
200}
201
202// This checks one bank of eight buttons to see if the command refers
203// to a press event in that bank. If it does, it will set the button.
204// If not, it does nothing.
205void vrpn_raw_SGIBox::check_press_bank(int base_button, unsigned char base_command,
206 unsigned char command) {
207 if ( (command >= base_command) && (command < (base_command+8)) ) {
208 buttons[base_button + (command-base_command)] = 1;
209 }
210}
211
212// This checks one bank of eight buttons to see if the command refers
213// to a release event in that bank. If it does, it will clear the button.
214// If not, it does nothing.
215void vrpn_raw_SGIBox::check_release_bank(int base_button, unsigned char base_command,
216 unsigned char command) {
217 if ( (command >= base_command) && (command < (base_command+8)) ) {
218 buttons[base_button + (command-base_command)] = 0;
219 }
220}
221
222
223// See what reports have come in over the serial port. When something
224// comes in, figure out if it is a dial report or a button report
225// and act on it accordingly. If we get a partial report, go ahead
226// and wait for the rest of the report before leaving the loop.
227// XXX This should be modified to handle partial reports the same way
228// that the trackers do.
229
231 unsigned char command;
232 int ret;
233
234#ifdef VERBOSE
235 printf("."); fflush(stdout);
236#endif
237 // Read a character if there is one. See if it matches one of
238 // the known command start bytes. If it does not, there is
239 // something wrong.
240 ret = vrpn_read_available_characters(serialfd, &command, 1);
241 if (ret == 0) { // Nothing there, we're done
242 return;
243 }
244 if (ret == -1) { // Error in the read; try resetting.
245#ifdef VERBOSE
246 perror("vrpn_raw_SGIBOX::get_report(): error reading serial port - reseting...");
247#endif
248 reset();
249 return;
250 }
251
252#ifdef VERBOSE
253 printf("vrpn_raw_SGIBox::get_report(): Got %02x\n", command);
254#endif
255 // If this is a reset command, we can skip it and get the next command
256 // next time.
257 if (command == 0x20) {
258 perror("vrpn_raw_SGIBOX::get_report(): Got reset response when we didn't expect it - reseting...\n");
259 reset();
260 return;
261 }
262
263 // See if this is a button report, which are only a single byte.
264 if ( command >= 0xC0 ) {
265 // Due to the strange layout of the commands to buttons,
266 // we need to check each group of 8 button messages in chunks,
267 // both for the press commands and the release commands.
268 check_press_bank(24, 0xC0, command);
269 check_press_bank(16, 0xC8, command);
270 check_press_bank(8, 0xD0, command);
271 check_press_bank(0, 0xD8, command);
272
273 check_release_bank(24, 0xE0, command);
274 check_release_bank(16, 0xE8, command);
275 check_release_bank(8, 0xF0, command);
276 check_release_bank(0, 0xF8, command);
277 }
278
280
281
282 // Parse the dial turn results, which are more than single-byte
283 // results so will require reading in the rest of the command.
284 // We will block until either we get them or get an error or time
285 // out. If there is an error or timeout, try resetting.
286 if ( (command >= 0x30) && (command <= 0x37) ) {
287#ifdef VERBOSE
288 printf("vrpn_raw_SGIBOX::get_report(): Got dial event\n");
289#endif
290 unsigned char dial_value[2];
291 int i = command - 0x30; // Which dial
292 vrpn_int16 value;
293 struct timeval timeout = {0, 10000}; // 10 milliseconds
294
295 // Attempt to read both new values until we run out of time
296 // and give up. If we give up or have an error, try a reset.
297 if (vrpn_read_available_characters(serialfd, dial_value, 2, &timeout) != 2) {
298 perror("vrpn_raw_SGIBOX: starting getting a dial command from box, but message wasn't completed -reseting ...");
299 reset();
300 return;
301 }
302#ifdef VERBOSE
303 printf("vrpn_raw_SGIBOX::get_report(): Dial event %02x:[ %02x %02x] \n",
304 command,dial_value[0],dial_value[1]);
305#endif
306
307 // Make sure to sign-extend the 16-bit value properly.
308 value = static_cast<short>((dial_value[0]<<8) | dial_value[1]);
309
310 // Figure out the value and adjust the corresponding analog value.
311 // The analogs are set up to clamp at both the to and bottom end of
312 // the scale, but will back off immediately if you turn it the other
313 // direction. The range of outputs (channels) is from -0.5 to 0.5.
314 int temp = value - mid_values[i];
315 if (temp > VRPN_DIAL_RANGE) {
316 channel[i] = 0.5;
317 mid_values[i] = value - VRPN_DIAL_RANGE;
318 } else if (temp < -VRPN_DIAL_RANGE) {
319 channel[i] = -0.5;
320 mid_values[i] = value + VRPN_DIAL_RANGE;
321 } else {
322 channel[i] = temp/400.0;
323 }
324
325 // Figure out the change for the dial report, and report fraction
326 // turned.
327 dials[i] = (value-last_values[i])/(double)(VRPN_DIAL_RANGE);
328 last_values[i] = value;
329 }
332
333 // check for an unrecognized command from the box
334 if (! (
335 ( (command >= 0x30) && (command <= 0x37) ) ||
336 (command >= 0xC0)
337 ))
338 {
339 perror("vrpn_raw_SGIBOX: unrecognized command from sgiBox - reseting...");
340 reset();
341 }
342}
343
345{
347 get_report();
348}
349
351 int bank, i;
352 unsigned char lights[4]; // Array to hold the light-control bits
353 unsigned char msg[5]; // Message to send to turn on lights
354
355 // Figure out which lights should be on, and pack them into the
356 // four bytes that describe which should be on.
357 for (bank = 0; bank < 4; bank++) {
358 lights[bank] = 0; //one bit per button light
359 for (i = 0; i < 8; i++) {
360 int buttonLightNumber = bank*8 + i;
361 if (buttonstate[buttonLightNumber]==vrpn_BUTTON_TOGGLE_ON) {
362 lights[bank]=static_cast<unsigned char>(lights[bank]|1<<i);
363 }
364 }
365 }
366
367 // Prepare the control message to turn the lights on, then
368 // send it.
369
370 msg[0] = 0x75; memcpy(&msg[1],lights,4);
371 if (vrpn_write_slowly(serialfd, msg, 5,1) != 5) {
372 perror("Could not write light control message");
373 //XXX Should be vrpn_Text message
374 reset();
375 return -1;
376 }
377 return 0;
378}
379
380// Turn on all the lights that are currently enabled. This ignores
381// the HANDLERPARAM parameter, since it is not required to figure this
382// out.
383
384static int VRPN_CALLBACK sgibox_raw_alert_handler(void * userdata, vrpn_HANDLERPARAM)
385{
386 vrpn_raw_SGIBox *me=(vrpn_raw_SGIBox *)userdata;
387
388 me->send_light_command();
389 return 0;
390}
391
392static int VRPN_CALLBACK sgibox_raw_con_cb(void * userdata, vrpn_HANDLERPARAM)
393{
394
395 printf("vrpn_raw_SGIBox::Get first new connection, reset the box\n");
396 ((vrpn_raw_SGIBox *)userdata) ->reset();
397 return 0;
398}
399
vrpn_float64 last[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:39
vrpn_float64 channel[vrpn_CHANNEL_MAX]
Definition: vrpn_Analog.h:38
vrpn_int32 num_channel
Definition: vrpn_Analog.h:40
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.
void server_mainloop(void)
Handles functions that all servers should provide in their mainloop() (ping/pong, for example) Should...
All button servers should derive from this class, which provides the ability to turn any of the butto...
Definition: vrpn_Button.h:66
vrpn_int32 alert_message_id
Definition: vrpn_Button.h:79
virtual void report_changes(void)
Definition: vrpn_Button.C:383
vrpn_int32 buttonstate[vrpn_BUTTON_MAX_BUTTONS]
Definition: vrpn_Button.h:68
void set_alerts(vrpn_int32)
Definition: vrpn_Button.C:169
vrpn_int32 num_buttons
Definition: vrpn_Button.h:48
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
vrpn_float64 dials[vrpn_DIAL_MAX]
Definition: vrpn_Dial.h:26
vrpn_int32 num_dials
Definition: vrpn_Dial.h:27
void mainloop()
Called once through each main loop iteration to handle updates. Remote object mainloop() should call ...
void check_press_bank(int base_button, unsigned char base_command, unsigned char command)
vrpn_raw_SGIBox(char *name, vrpn_Connection *c, char *serialDevName)
void check_release_bank(int base_button, unsigned char base_command, unsigned char command)
This structure is what is passed to a vrpn_Connection message callback.
const int vrpn_BUTTON_TOGGLE_ON
Definition: vrpn_Button.h:21
#define VRPN_CALLBACK
const char * vrpn_got_first_connection
These are the strings that define the system-generated message types that tell when connections are r...
int vrpn_write_slowly(int comm, const unsigned char *buffer, size_t bytes, int millisec_delay)
Definition: vrpn_Serial.C:678
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
int vrpn_open_commport(const char *portname, long baud, int charsize, vrpn_SER_PARITY parity, bool rts_flow)
Open a serial port, given its name and baud rate.
Definition: vrpn_Serial.C:54
vrpn_Serial: Pulls all the serial port routines into one file to make porting to new operating system...
void vrpn_SleepMsecs(double dMilliSecs)
Definition: vrpn_Shared.C:166
#define vrpn_SGI_NUM_DIALS
#define vrpn_SGI_NUM_BUTTONS