Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/wiimote/src/external/wiicpp/wiic/events.c @ 10905

Last change on this file since 10905 was 9780, checked in by georgr, 11 years ago

WiiCpp library successfully (?) added - won't work without libbluetooth-dev

  • Property svn:executable set to *
File size: 25.4 KB
Line 
1/*
2 *    events.c
3 *
4 *        This file is part of WiiC, written by:
5 *              Gabriele Randelli
6 *              Email: randelli@dis.uniroma1.it
7 *
8 *    Copyright 2010
9 *             
10 *        This file is based on Wiiuse, written By:
11 *              Michael Laforest        < para >
12 *              Email: < thepara (--AT--) g m a i l [--DOT--] com >
13 *
14 *        Copyright 2006-2007
15 *
16 *    This program is free software; you can redistribute it and/or modify
17 *    it under the terms of the GNU General Public License as published by
18 *    the Free Software Foundation; either version 3 of the License, or
19 *    (at your option) any later version.
20 *
21 *    This program is distributed in the hope that it will be useful,
22 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
23 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 *    GNU General Public License for more details.
25 *
26 *    You should have received a copy of the GNU General Public License
27 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
28 *
29 *        $Header$
30 */
31
32/**
33 *      @file
34 *      @brief Handles wiimote events.
35 *
36 *      The file includes functions that handle the events
37 *      that are sent from the wiimote to us.
38 */
39
40#include <stdio.h>
41
42#include <sys/time.h>
43#include <unistd.h>
44#include <errno.h>
45#include <sys/types.h>
46#include <stdlib.h>
47#include <math.h>
48
49#include "definitions.h"
50#include "io.h"
51#include "wiic_internal.h"
52#include "dynamics.h"
53#include "ir.h"
54#include "nunchuk.h"
55#include "classic.h"
56#include "guitar_hero_3.h"
57#include "events.h"
58
59static void idle_cycle(struct wiimote_t* wm);
60void clear_dirty_reads(struct wiimote_t* wm);
61void propagate_event(struct wiimote_t* wm, byte event, byte* msg);
62static void event_data_read(struct wiimote_t* wm, byte* msg);
63static void event_status(struct wiimote_t* wm, byte* msg);
64static void handle_expansion(struct wiimote_t* wm, byte* msg);
65
66static void save_state(struct wiimote_t* wm);
67static int state_changed(struct wiimote_t* wm);
68
69/**
70 *      @brief Poll the wiimotes for any events.
71 *
72 *      @param wm               An array of pointers to wiimote_t structures.
73 *      @param wiimotes The number of wiimote_t structures in the \a wm array.
74 *
75 *      @return Returns number of wiimotes that an event has occured on.
76 *
77 *      It is necessary to poll the wiimote devices for events
78 *      that occur.  If an event occurs on a particular wiimote,
79 *      the event variable will be set.
80 */
81int wiic_poll(struct wiimote_t** wm, int wiimotes) {
82        int evnt = 0;
83
84        #if __APPLE__
85                /*
86                 *      MACOSX
87                 */
88                int i;
89               
90                if (!wm) return 0;
91
92                for (i = 0; i < wiimotes; ++i) {
93                        wm[i]->event = WIIC_NONE;
94                        if (wiic_io_read(wm[i])) {
95                                /* propagate the event */
96                                propagate_event(wm[i], wm[i]->event_buf[1], wm[i]->event_buf+2); 
97                                evnt += (wm[i]->event != WIIC_NONE);
98
99                                /* clear out the event buffer */
100                                memset(wm[i]->event_buf, 0, sizeof(wm[i]->event_buf));
101                        } else {
102                                idle_cycle(wm[i]);
103                        }       
104                }
105        #else
106                /*
107                 *      *nix
108                 */
109                struct timeval tv;
110                fd_set fds;
111                int r;
112                int i;
113                int highest_fd = -1;
114
115                if (!wm) return 0;
116
117                /* block select() for 1/2000th of a second */
118                tv.tv_sec = 0;
119                tv.tv_usec = 500;
120
121                FD_ZERO(&fds);
122
123                for (i = 0; i < wiimotes; ++i) {
124                        /* only poll it if it is connected */
125                        if (WIIMOTE_IS_SET(wm[i], WIIMOTE_STATE_CONNECTED)) {
126                                FD_SET(wm[i]->in_sock, &fds);
127
128                                /* find the highest fd of the connected wiimotes */
129                                if (wm[i]->in_sock > highest_fd)
130                                        highest_fd = wm[i]->in_sock;
131                        }
132
133                        wm[i]->event = WIIC_NONE;
134                }
135
136                if (highest_fd == -1)
137                        /* nothing to poll */
138                        return 0;
139
140                if (select(highest_fd + 1, &fds, NULL, NULL, &tv) == -1) {
141                        WIIC_ERROR("Unable to select() the wiimote interrupt socket(s).");
142                        perror("Error Details");
143                        return 0;
144                }
145
146                /* check each socket for an event */
147                for (i = 0; i < wiimotes; ++i) {
148                        /* if this wiimote is not connected, skip it */
149                        if (!WIIMOTE_IS_CONNECTED(wm[i]))
150                                continue;
151
152                        if (FD_ISSET(wm[i]->in_sock, &fds)) {
153                                /* clear out the event buffer */
154                                memset(wm[i]->event_buf, 0, sizeof(wm[i]->event_buf));
155
156                                /* clear out any old read requests */
157                                clear_dirty_reads(wm[i]);
158
159                                /* read the pending message into the buffer */
160                                r = read(wm[i]->in_sock, wm[i]->event_buf, sizeof(wm[i]->event_buf));
161                                if (r == -1) {
162                                        /* error reading data */
163                                        WIIC_ERROR("Receiving wiimote data (id %i).", wm[i]->unid);
164                                        perror("Error Details");
165
166                                        if (errno == ENOTCONN) {
167                                                /* this can happen if the bluetooth dongle is disconnected */
168                                                WIIC_ERROR("Bluetooth appears to be disconnected.  Wiimote unid %i will be disconnected.", wm[i]->unid);
169                                                wiic_disconnect(wm[i]);
170                                                wm[i]->event = WIIC_UNEXPECTED_DISCONNECT;
171                                        }
172
173                                        continue;
174                                }
175                                if (!r) {
176                                        /* remote disconnect */
177                                        wiic_disconnected(wm[i]);
178                                        evnt = 1;
179                                        continue;
180                                }
181
182                                /* propagate the event */
183                                propagate_event(wm[i], wm[i]->event_buf[1], wm[i]->event_buf+2);
184                                evnt += (wm[i]->event != WIIC_NONE);
185                        } else {
186                                idle_cycle(wm[i]);
187                        }
188                }
189        #endif
190
191        return evnt;
192}
193
194
195/**
196 *      @brief Called on a cycle where no significant change occurs.
197 *
198 *      @param wm               Pointer to a wiimote_t structure.
199 */
200static void idle_cycle(struct wiimote_t* wm) {
201        /*
202         *      Smooth the angles.
203         *
204         *      This is done to make sure that on every cycle the orientation
205         *      angles are smoothed.  Normally when an event occurs the angles
206         *      are updated and smoothed, but if no packet comes in then the
207         *      angles remain the same.  This means the angle WiiC reports
208         *      is still an old value.  Smoothing needs to be applied in this
209         *      case in order for the angle it reports to converge to the true
210         *      angle of the device.
211         */
212        if (WIIC_USING_ACC(wm) && WIIMOTE_IS_FLAG_SET(wm, WIIC_SMOOTHING)) {
213                apply_smoothing(&wm->gforce, wm->accel_calib.st_alpha);
214        }
215
216        /* clear out any old read requests */
217        clear_dirty_reads(wm);
218}
219
220
221/**
222 *      @brief Clear out all old 'dirty' read requests.
223 *
224 *      @param wm               Pointer to a wiimote_t structure.
225 */
226void clear_dirty_reads(struct wiimote_t* wm) {
227        struct read_req_t* req = wm->read_req;
228
229        while (req && req->dirty) {
230                WIIC_DEBUG("Cleared old read request for address: %x", req->addr);
231
232                wm->read_req = req->next;
233                free(req);
234                req = wm->read_req;
235        }
236}
237
238
239/**
240 *      @brief Analyze the event that occured on a wiimote.
241 *
242 *      @param wm               An array of pointers to wiimote_t structures.
243 *      @param event    The event that occured.
244 *      @param msg              The message specified in the event packet.
245 *
246 *      Pass the event to the registered event callback.
247 */
248void propagate_event(struct wiimote_t* wm, byte event, byte* msg) {
249        wiic_update_timestamp(wm);
250        save_state(wm);
251
252        switch (event) {
253                case WM_RPT_BTN:
254                {
255                        /* button */
256                        wiic_pressed_buttons(wm, msg);
257                        break;
258                }
259                case WM_RPT_BTN_ACC:
260                {
261                        /* button - motion */
262                        wiic_pressed_buttons(wm, msg);
263
264                        wm->accel.x = msg[2];
265                        wm->accel.y = msg[3];
266                        wm->accel.z = msg[4];
267
268                        /* calculate the gforces on each axis */
269                        calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce, WIIMOTE_IS_FLAG_SET(wm, WIIC_SMOOTHING));
270                                               
271                        /* calculate the remote orientation */
272                        calculate_orientation(&wm->gforce.vec, &wm->orient.angle);
273
274                        break;
275                }
276                case WM_RPT_READ:
277                {
278                        /* data read */
279                        event_data_read(wm, msg);
280
281                        /* yeah buttons may be pressed, but this wasn't an "event" */
282                        return;
283                }
284                case WM_RPT_CTRL_STATUS:
285                {
286                        /* controller status */
287                        event_status(wm, msg);
288
289                        /* don't execute the event callback */
290                        return;
291                }
292                case WM_RPT_BTN_EXP:
293                {
294                        /* button - expansion */
295                        wiic_pressed_buttons(wm, msg);
296                        handle_expansion(wm, msg+2);
297
298                        break;
299                }
300                case WM_RPT_BTN_ACC_EXP:
301                {
302                        /* button - motion - expansion */
303                        wiic_pressed_buttons(wm, msg);
304
305                        wm->accel.x = msg[2];
306                        wm->accel.y = msg[3];
307                        wm->accel.z = msg[4];
308
309                        calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce, WIIMOTE_IS_FLAG_SET(wm, WIIC_SMOOTHING));
310                        calculate_orientation(&wm->gforce.vec, &wm->orient.angle);
311
312                        handle_expansion(wm, msg+5);
313
314                        break;
315                }
316                case WM_RPT_BTN_ACC_IR:
317                {
318                        /* button - motion - ir */
319                        wiic_pressed_buttons(wm, msg);
320
321                        wm->accel.x = msg[2];
322                        wm->accel.y = msg[3];
323                        wm->accel.z = msg[4];
324                       
325                        calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce, WIIMOTE_IS_FLAG_SET(wm, WIIC_SMOOTHING));
326                        calculate_orientation(&wm->gforce.vec, &wm->orient.angle);
327
328                        /* ir */
329                        calculate_extended_ir(wm, msg+5);
330
331                        break;
332                }
333                case WM_RPT_BTN_IR_EXP:
334                {
335                        /* button - ir - expansion */
336                        wiic_pressed_buttons(wm, msg);
337                        handle_expansion(wm, msg+12);
338
339                        /* ir */
340                        calculate_basic_ir(wm, msg+2);
341
342                        break;
343                }
344                case WM_RPT_BTN_ACC_IR_EXP:
345                {
346                        /* button - motion - ir - expansion */
347                        wiic_pressed_buttons(wm, msg);
348
349                        wm->accel.x = msg[2];
350                        wm->accel.y = msg[3];
351                        wm->accel.z = msg[4];
352
353                        calculate_gforce(&wm->accel_calib, &wm->accel, &wm->gforce, WIIMOTE_IS_FLAG_SET(wm, WIIC_SMOOTHING));
354                        calculate_orientation(&wm->gforce.vec, &wm->orient.angle);
355
356                        handle_expansion(wm, msg+15);
357
358                        /* ir */
359                        calculate_basic_ir(wm, msg+5);
360
361                        break;
362                }
363                case WM_RPT_WRITE:
364                {
365                        /* write feedback - safe to skip */
366                        break;
367                }
368                default:
369                {
370                        WIIC_DEBUG("Unknown event, can not handle it [Code 0x%x].", event);
371                        return;
372                }
373        }
374
375        /* was there an event? */
376        if (state_changed(wm))
377                wm->event = WIIC_EVENT;
378}
379
380
381/**
382 *      @brief Find what buttons are pressed.
383 *
384 *      @param wm               Pointer to a wiimote_t structure.
385 *      @param msg              The message specified in the event packet.
386 */
387void wiic_pressed_buttons(struct wiimote_t* wm, byte* msg) {
388        short now;
389
390        /* convert to big endian */
391        now = BIG_ENDIAN_SHORT(*(short*)msg) & WIIMOTE_BUTTON_ALL;
392
393        /* pressed now & were pressed, then held */
394        /*
395         * FIXME - With motion sensing disabled and no other activities
396         * it's impossible to state if a button is held, since no other
397         * report will be sent.
398         */
399        wm->btns_held = (now & wm->btns);
400       
401        /* were pressed or were held & not pressed now, then released */
402        wm->btns_released = ((wm->btns | wm->btns_held) & ~now);
403
404        /* buttons pressed now */
405        wm->btns = now;
406}
407
408
409/**
410 *      @brief Received a data packet from a read request.
411 *
412 *      @param wm               Pointer to a wiimote_t structure.
413 *      @param msg              The message specified in the event packet.
414 *
415 *      Data from the wiimote comes in packets.  If the requested
416 *      data segment size is bigger than one packet can hold then
417 *      several packets will be received.  These packets are first
418 *      reassembled into one, then the registered callback function
419 *      that handles data reads is invoked.
420 */
421static void event_data_read(struct wiimote_t* wm, byte* msg) {
422        /* we must always assume the packet received is from the most recent request */
423        byte err;
424        byte len;
425        unsigned short offset;
426        struct read_req_t* req = wm->read_req;
427
428        wiic_pressed_buttons(wm, msg);
429
430        /* find the next non-dirty request */
431        while (req && req->dirty)
432                req = req->next;
433
434        /* if we don't have a request out then we didn't ask for this packet */
435        if (!req) {
436                WIIC_WARNING("Received data packet when no request was made.");
437                return;
438        }
439
440        err = msg[2] & 0x0F;
441
442        if (err == 0x08)
443                WIIC_WARNING("Unable to read data - address does not exist.");
444        else if (err == 0x07)
445                WIIC_WARNING("Unable to read data - address is for write-only registers.");
446        else if (err)
447                WIIC_WARNING("Unable to read data - unknown error code %x.", err);
448
449        if (err) {
450                /* this request errored out, so skip it and go to the next one */
451
452                /* delete this request */
453                wm->read_req = req->next;
454                free(req);
455
456                /* if another request exists send it to the wiimote */
457                if (wm->read_req)
458                        wiic_send_next_pending_read_request(wm);
459
460                return;
461        }
462
463        len = ((msg[2] & 0xF0) >> 4) + 1;
464        offset = BIG_ENDIAN_SHORT(*(unsigned short*)(msg + 3));
465        req->addr = (req->addr & 0xFFFF);
466
467        req->wait -= len;
468        if (req->wait >= req->size)
469                /* this should never happen */
470                req->wait = 0;
471
472        WIIC_DEBUG("Received read packet:");
473        WIIC_DEBUG("    Packet read offset:   %i bytes", offset);
474        WIIC_DEBUG("    Request read offset:  %i bytes", req->addr);
475        WIIC_DEBUG("    Read offset into buf: %i bytes", offset - req->addr);
476        WIIC_DEBUG("    Read data size:       %i bytes", len);
477        WIIC_DEBUG("    Still need:           %i bytes", req->wait);
478
479        /* reconstruct this part of the data */
480        memcpy((req->buf + offset - req->addr), (msg + 5), len);
481
482        #ifdef WITH_WIIC_DEBUG
483        {
484                int i = 0;
485                printf("Read: ");
486                for (; i < req->size - req->wait; ++i)
487                        printf("%x ", req->buf[i]);
488                printf("\n");
489        }
490        #endif
491
492        /* if all data has been received, execute the read event callback or generate event */
493        if (!req->wait) {
494                if (req->cb) {
495                        /* this was a callback, so invoke it now */
496                        req->cb(wm, req->buf, req->size);
497
498                        /* delete this request */
499                        wm->read_req = req->next;
500                        free(req);
501                } else {
502                        /*
503                         *      This should generate an event.
504                         *      We need to leave the event in the array so the client
505                         *      can access it still.  We'll flag is as being 'dirty'
506                         *      and give the client one cycle to use it.  Next event
507                         *      we will remove it from the list.
508                         */
509                        wm->event = WIIC_READ_DATA;
510                        req->dirty = 1;
511                }
512
513                /* if another request exists send it to the wiimote */
514                if (wm->read_req)
515                        wiic_send_next_pending_read_request(wm);
516        }
517}
518
519
520/**
521 *      @brief Read the controller status.
522 *
523 *      @param wm               Pointer to a wiimote_t structure.
524 *      @param msg              The message specified in the event packet.
525 *
526 *      Read the controller status and execute the registered status callback.
527 */
528static void event_status(struct wiimote_t* wm, byte* msg) {
529        int led[4] = {0};
530        int attachment = 0;
531        int ir = 0;
532        int exp_changed = 0;
533
534        /*
535         *      An event occured.
536         *      This event can be overwritten by a more specific
537         *      event type during a handshake or expansion removal.
538         */
539        wm->event = WIIC_STATUS;
540
541        wiic_pressed_buttons(wm, msg);
542
543        /* find what LEDs are lit */
544        if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_1)        led[0] = 1;
545        if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_2)        led[1] = 1;
546        if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_3)        led[2] = 1;
547        if (msg[2] & WM_CTRL_STATUS_BYTE1_LED_4)        led[3] = 1;
548
549        /* is an attachment connected to the expansion port? */
550        if ((msg[2] & WM_CTRL_STATUS_BYTE1_ATTACHMENT) == WM_CTRL_STATUS_BYTE1_ATTACHMENT)
551                attachment = 1;
552
553        /* is the speaker enabled? */
554        if ((msg[2] & WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED) == WM_CTRL_STATUS_BYTE1_SPEAKER_ENABLED)
555                WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_SPEAKER);
556
557        /* is IR sensing enabled? */
558        if ((msg[2] & WM_CTRL_STATUS_BYTE1_IR_ENABLED) == WM_CTRL_STATUS_BYTE1_IR_ENABLED)
559                ir = 1;
560
561        /* find the battery level and normalize between 0 and 1 */
562        wm->battery_level = (msg[5] / (float)WM_MAX_BATTERY_CODE);
563
564        /* expansion port */
565        if (attachment && !WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
566                /* send the initialization code for the attachment */
567                handshake_expansion(wm, NULL, 0);
568                exp_changed = 1;
569        } else if (!attachment && WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP)) {
570                /* attachment removed */
571                disable_expansion(wm);
572                exp_changed = 1;
573        }
574
575        /*
576         *      From now on the remote will only send status packets.
577         *      We need to send a WIIMOTE_CMD_REPORT_TYPE packet to
578         *      reenable other incoming reports.
579         */
580        if (exp_changed && WIIMOTE_IS_SET(wm, WIIMOTE_STATE_IR)) {
581                /*
582                 *      Since the expansion status changed IR needs to
583                 *      be reset for the new IR report mode.
584                 */
585                WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_IR);
586                wiic_set_ir(wm, 1);
587        } else 
588                wiic_set_report_type(wm);
589}
590
591
592/**
593 *      @brief Handle data from the expansion.
594 *
595 *      @param wm               A pointer to a wiimote_t structure.
596 *      @param msg              The message specified in the event packet for the expansion.
597 */
598static void handle_expansion(struct wiimote_t* wm, byte* msg) {
599        switch (wm->exp.type) {
600                case EXP_NUNCHUK:
601                        nunchuk_event(&wm->exp.nunchuk, msg);
602                        break;
603                case EXP_CLASSIC:
604                        classic_ctrl_event(&wm->exp.classic, msg);
605                        break;
606                case EXP_GUITAR_HERO_3:
607                        guitar_hero_3_event(&wm->exp.gh3, msg);
608                        break;
609                case EXP_MOTION_PLUS:
610                        motion_plus_event(&wm->exp.mp, msg);
611                        break;
612                case EXP_BALANCE_BOARD:
613                        balance_board_event(&wm->exp.bb, msg);
614                        break;
615                default:
616                        break;
617        }
618}
619
620
621/**
622 *      @brief Handle the handshake data from the expansion device.
623 *
624 *      @param wm               A pointer to a wiimote_t structure.
625 *      @param data             The data read in from the device.
626 *      @param len              The length of the data block, in bytes.
627 *
628 *      Tries to determine what kind of expansion was attached
629 *      and invoke the correct handshake function.
630 *
631 *      If the data is NULL then this function will try to start
632 *      a handshake with the expansion.
633 */
634void handshake_expansion(struct wiimote_t* wm, byte* data, unsigned short len) {
635        int id;
636
637        if (!data) {
638                if(WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP) || WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP_FAILED) || WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP_HANDSHAKE))
639                        return;
640
641                byte* handshake_buf;
642                byte buf = 0x00;
643
644                if (WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP))
645                        disable_expansion(wm);
646
647                wiic_write_data(wm, WM_EXP_MEM_ENABLE, &buf, 1);
648
649                /* get the calibration data */
650                handshake_buf = malloc(EXP_HANDSHAKE_LEN * sizeof(byte));
651                wiic_read_data_cb(wm, handshake_expansion, handshake_buf, WM_EXP_MEM_CALIBR, EXP_HANDSHAKE_LEN);
652
653                /* tell the wiimote to send expansion data */
654                WIIMOTE_ENABLE_STATE(wm, WIIMOTE_STATE_EXP);
655
656                return;
657        }
658
659        id = BIG_ENDIAN_LONG(*(int*)(data + 220));
660
661        /* call the corresponding handshake function for this expansion */
662        switch (id) {
663                case EXP_ID_CODE_NUNCHUK:
664                {
665                        if (nunchuk_handshake(wm, &wm->exp.nunchuk, data, len))
666                                wm->event = WIIC_NUNCHUK_INSERTED;
667                        break;
668                }
669                case EXP_ID_CODE_CLASSIC_CONTROLLER:
670                {
671                        if (classic_ctrl_handshake(wm, &wm->exp.classic, data, len))
672                                wm->event = WIIC_CLASSIC_CTRL_INSERTED;
673                        break;
674                }
675                case EXP_ID_CODE_GUITAR:
676                {
677                        if (guitar_hero_3_handshake(wm, &wm->exp.gh3, data, len))
678                                wm->event = WIIC_GUITAR_HERO_3_CTRL_INSERTED;
679                        break;
680                }
681                case EXP_ID_BALANCE_BOARD:
682                {
683                        if(balance_board_handshake(wm, &wm->exp.bb, data, len))
684                                wm->event = WIIC_BALANCE_BOARD_INSERTED;
685                        break;
686                }
687                default:
688                {
689                        WIIC_WARNING("Unknown expansion type. Code: 0x%x", id);
690                        break;
691                }
692        }
693
694        free(data);
695}
696
697
698
699/**
700 *      @brief Disable the expansion device if it was enabled.
701 *
702 *      @param wm               A pointer to a wiimote_t structure.
703 *      @param data             The data read in from the device.
704 *      @param len              The length of the data block, in bytes.
705 *
706 *      If the data is NULL then this function will try to start
707 *      a handshake with the expansion.
708 */
709void disable_expansion(struct wiimote_t* wm) {
710        if (!WIIMOTE_IS_SET(wm, WIIMOTE_STATE_EXP))
711                return;
712
713        /* tell the assoicated module the expansion was removed */
714        switch (wm->exp.type) {
715                case EXP_NUNCHUK:
716                        nunchuk_disconnected(&wm->exp.nunchuk);
717                        wm->event = WIIC_NUNCHUK_REMOVED;
718                        break;
719                case EXP_CLASSIC:
720                        classic_ctrl_disconnected(&wm->exp.classic);
721                        wm->event = WIIC_CLASSIC_CTRL_REMOVED;
722                        break;
723                case EXP_GUITAR_HERO_3:
724                        guitar_hero_3_disconnected(&wm->exp.gh3);
725                        wm->event = WIIC_GUITAR_HERO_3_CTRL_REMOVED;
726                        break;
727                case EXP_MOTION_PLUS:
728                        motion_plus_disconnected(&wm->exp.mp);
729                        wm->event = WIIC_MOTION_PLUS_REMOVED;
730                        break;
731                case EXP_BALANCE_BOARD:
732                        balance_board_disconnected(&wm->exp.bb);
733                        wm->event = WIIC_BALANCE_BOARD_REMOVED;
734                        break;
735                default:
736                        break;
737        }
738
739        WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP);
740        WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_FAILED);
741        WIIMOTE_DISABLE_STATE(wm, WIIMOTE_STATE_EXP_HANDSHAKE);
742        wm->exp.type = EXP_NONE;
743}
744
745
746/**
747 *      @brief Save important state data.
748 *      @param wm       A pointer to a wiimote_t structure.
749 */
750static void save_state(struct wiimote_t* wm) {
751        /* wiimote */
752        wm->lstate.btns = wm->btns;
753        wm->lstate.accel = wm->accel;
754
755        /* ir */
756        if (WIIC_USING_IR(wm)) {
757                wm->lstate.ir_ax = wm->ir.ax;
758                wm->lstate.ir_ay = wm->ir.ay;
759                wm->lstate.ir_distance = wm->ir.distance;
760        }
761
762        /* expansion */
763        switch (wm->exp.type) {
764                case EXP_NUNCHUK:
765                        wm->lstate.exp_ljs_ang = wm->exp.nunchuk.js.ang;
766                        wm->lstate.exp_ljs_mag = wm->exp.nunchuk.js.mag;
767                        wm->lstate.exp_btns = wm->exp.nunchuk.btns;
768                        wm->lstate.exp_accel = wm->exp.nunchuk.accel;
769                        break;
770
771                case EXP_CLASSIC:
772                        wm->lstate.exp_ljs_ang = wm->exp.classic.ljs.ang;
773                        wm->lstate.exp_ljs_mag = wm->exp.classic.ljs.mag;
774                        wm->lstate.exp_rjs_ang = wm->exp.classic.rjs.ang;
775                        wm->lstate.exp_rjs_mag = wm->exp.classic.rjs.mag;
776                        wm->lstate.exp_r_shoulder = wm->exp.classic.r_shoulder;
777                        wm->lstate.exp_l_shoulder = wm->exp.classic.l_shoulder;
778                        wm->lstate.exp_btns = wm->exp.classic.btns;
779                        break;
780
781                case EXP_GUITAR_HERO_3:
782                        wm->lstate.exp_ljs_ang = wm->exp.gh3.js.ang;
783                        wm->lstate.exp_ljs_mag = wm->exp.gh3.js.mag;
784                        wm->lstate.exp_r_shoulder = wm->exp.gh3.whammy_bar;
785                        wm->lstate.exp_btns = wm->exp.gh3.btns;
786                        break;
787                       
788                case EXP_MOTION_PLUS:
789                        wm->lstate.mp_acc_mode = wm->exp.mp.acc_mode;
790                        wm->lstate.mp_raw_gyro.roll = wm->exp.mp.raw_gyro.roll;
791                        wm->lstate.mp_raw_gyro.pitch = wm->exp.mp.raw_gyro.pitch;
792                        wm->lstate.mp_raw_gyro.yaw = wm->exp.mp.raw_gyro.yaw;
793                        break;
794                       
795                case EXP_BALANCE_BOARD:
796                        wm->lstate.pressure_raw_data.top_left = wm->exp.bb.pressure_raw_data.top_left;
797                        wm->lstate.pressure_raw_data.top_right = wm->exp.bb.pressure_raw_data.top_right;
798                        wm->lstate.pressure_raw_data.bottom_left = wm->exp.bb.pressure_raw_data.bottom_left;
799                        wm->lstate.pressure_raw_data.bottom_right = wm->exp.bb.pressure_raw_data.bottom_right;
800                        break;
801
802                case EXP_NONE:
803                        break;
804        }
805}
806
807
808/**
809 *      @brief Determine if the current state differs significantly from the previous.
810 *      @param wm       A pointer to a wiimote_t structure.
811 *      @return 1 if a significant change occured, 0 if not.
812 */
813static int state_changed(struct wiimote_t* wm) {
814        #define STATE_CHANGED(a, b)             if (a != b)                             return 1
815
816        #define CROSS_THRESH(last, now, thresh)                                                                         \
817                                do {                                                                                                                    \
818                                        if (WIIMOTE_IS_FLAG_SET(wm, WIIC_ORIENT_THRESH)) {              \
819                                                if ((diff_f(last.roll, now.roll) >= thresh) ||                  \
820                                                        (diff_f(last.pitch, now.pitch) >= thresh) ||            \
821                                                        (diff_f(last.yaw, now.yaw) >= thresh))                          \
822                                                {                                                                                                               \
823                                                        last = now;                                                                                     \
824                                                        return 1;                                                                                       \
825                                                }                                                                                                               \
826                                        } else {                                                                                                        \
827                                                if (last.roll != now.roll)              return 1;                               \
828                                                if (last.pitch != now.pitch)    return 1;                               \
829                                                if (last.yaw != now.yaw)                return 1;                               \
830                                        }                                                                                                                       \
831                                } while (0)
832
833        #define CROSS_THRESH_XYZ(last, now, thresh)                                                                     \
834                                do {                                                                                                                    \
835                                        if (WIIMOTE_IS_FLAG_SET(wm, WIIC_ORIENT_THRESH)) {              \
836                                                if ((diff_f(last.x, now.x) >= thresh) ||                                \
837                                                        (diff_f(last.y, now.y) >= thresh) ||                            \
838                                                        (diff_f(last.z, now.z) >= thresh))                                      \
839                                                {                                                                                                               \
840                                                        last = now;                                                                                     \
841                                                        return 1;                                                                                       \
842                                                }                                                                                                               \
843                                        } else {                                                                                                        \
844                                                if (last.x != now.x)            return 1;                                       \
845                                                if (last.y != now.y)            return 1;                                       \
846                                                if (last.z != now.z)            return 1;                                       \
847                                        }                                                                                                                       \
848                                } while (0)
849
850        #define CROSS_THRESH_RATE(last, now, thresh)                                                            \
851                                do {                                                                                                                    \
852                                        if (WIIMOTE_IS_FLAG_SET(wm, WIIC_ORIENT_THRESH)) {              \
853                                                if ((diff_f(last.roll, now.roll) >= thresh) ||                          \
854                                                        (diff_f(last.pitch, now.pitch) >= thresh) ||                            \
855                                                        (diff_f(last.yaw, now.yaw) >= thresh))                                  \
856                                                {                                                                                                               \
857                                                        last = now;                                                                                     \
858                                                        return 1;                                                                                       \
859                                                }                                                                                                               \
860                                        } else {                                                                                                        \
861                                                if (last.roll != now.roll)              return 1;                                       \
862                                                if (last.pitch != now.pitch)            return 1;                                       \
863                                                if (last.yaw != now.yaw)                return 1;                                       \
864                                        }                                                                                                                       \
865                                } while (0)
866                               
867        /* ir */
868        if (WIIC_USING_IR(wm)) {
869                STATE_CHANGED(wm->lstate.ir_ax, wm->ir.ax);
870                STATE_CHANGED(wm->lstate.ir_ay, wm->ir.ay);
871                STATE_CHANGED(wm->lstate.ir_distance, wm->ir.distance);
872        }
873
874        /* accelerometer */
875        if (WIIC_USING_ACC(wm)) {
876                /* raw accelerometer */
877                CROSS_THRESH_XYZ(wm->lstate.accel, wm->accel, wm->accel_threshold);
878
879                /* orientation */
880                CROSS_THRESH(wm->lstate.orient.angle, wm->orient.angle, wm->orient_threshold);
881        }
882
883        /* expansion */
884        switch (wm->exp.type) {
885                case EXP_NUNCHUK:
886                {
887                        STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.nunchuk.js.ang);
888                        STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.nunchuk.js.mag);
889                        STATE_CHANGED(wm->lstate.exp_btns, wm->exp.nunchuk.btns);
890
891                        CROSS_THRESH(wm->lstate.exp_orient.angle, wm->exp.nunchuk.orient.angle, wm->exp.nunchuk.orient_threshold);
892                        CROSS_THRESH_XYZ(wm->lstate.exp_accel, wm->exp.nunchuk.accel, wm->exp.nunchuk.accel_threshold);
893                        break;
894                }
895                case EXP_CLASSIC:
896                {
897                        STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.classic.ljs.ang);
898                        STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.classic.ljs.mag);
899                        STATE_CHANGED(wm->lstate.exp_rjs_ang, wm->exp.classic.rjs.ang);
900                        STATE_CHANGED(wm->lstate.exp_rjs_mag, wm->exp.classic.rjs.mag);
901                        STATE_CHANGED(wm->lstate.exp_r_shoulder, wm->exp.classic.r_shoulder);
902                        STATE_CHANGED(wm->lstate.exp_l_shoulder, wm->exp.classic.l_shoulder);
903                        STATE_CHANGED(wm->lstate.exp_btns, wm->exp.classic.btns);
904                        break;
905                }
906                case EXP_GUITAR_HERO_3:
907                {
908                        STATE_CHANGED(wm->lstate.exp_ljs_ang, wm->exp.gh3.js.ang);
909                        STATE_CHANGED(wm->lstate.exp_ljs_mag, wm->exp.gh3.js.mag);
910                        STATE_CHANGED(wm->lstate.exp_r_shoulder, wm->exp.gh3.whammy_bar);
911                        STATE_CHANGED(wm->lstate.exp_btns, wm->exp.gh3.btns);
912                        break;
913                }
914                case EXP_MOTION_PLUS:
915                {
916                        /* acceleration mode */
917                        STATE_CHANGED(wm->lstate.mp_acc_mode, wm->exp.mp.acc_mode);
918
919                        /* raw gyro rate */
920                        CROSS_THRESH_RATE(wm->lstate.mp_raw_gyro, wm->exp.mp.raw_gyro, wm->exp.mp.raw_gyro_threshold);
921                        break;
922                }
923                case EXP_BALANCE_BOARD:
924                {
925                        /* balance board */
926                        STATE_CHANGED(wm->lstate.pressure_raw_data.top_left,wm->exp.bb.pressure_raw_data.top_left);
927                        STATE_CHANGED(wm->lstate.pressure_raw_data.top_right,wm->exp.bb.pressure_raw_data.top_right);
928                        STATE_CHANGED(wm->lstate.pressure_raw_data.bottom_left,wm->exp.bb.pressure_raw_data.bottom_left);
929                        STATE_CHANGED(wm->lstate.pressure_raw_data.bottom_right,wm->exp.bb.pressure_raw_data.bottom_right);
930                        break;
931                }       
932                case EXP_NONE:
933                {
934                        break;
935                }
936        }
937
938        STATE_CHANGED(wm->lstate.btns, wm->btns);
939
940        return 0;
941}
Note: See TracBrowser for help on using the repository browser.