This is a work in progress. Trying to make a working arcade button to USB interface with the Nucleo F411RE.

Dependencies:   USBDevice mbed

This project was inspired by USB Joystick Device

The goal of this project is to build an simple interface that I will connect numerous Arcade buttons and joysticks to the Nucleo board, and the Nucleo will present itself as a USB Gamepad to Windows or Linux. The joysticks are Arcade joysticks that have four switches, up/down/left/right. It is not an analog joystick, just a set of switches. These will be connected to the Nucleo's pins configured as inputs.

The code will then continuously loop and test which buttons are closed, and transfer this data to the host via USBGamepad protocol.

Much thanks to Wim Huiskamp for his original project, documentation, and later reaching out to me to guide me to solving a last minute problem. Wim clued me into the fact that I needed a 1k5 pullup resistor between the 3v3 pin and the PA_12/D+ pin. Once I did this Windows detected the device as a Gamepad and registered it correctly! Yay!

Connecting USB cable to the board is as follows:

You will need a USB data cable (the one I used had a micro usb on one end and regular usb on the other). I cut off the micro USB end, and cut the insulation back about 30mm. This exposed four wires, Red, Black, White and Green. You will then either crimp some header connectors or solder directly to the Nucleo header pins as follows:

  • Green USB D+ to PA_12
  • White USB D- to PA_11
  • Red USB 5V to E5V (with jumper JP5 set to E5V)
  • Black USB GND to GND

As an extra debugging measure, you can connect both the ST/Link USB and the PA_12/11 USB to the Windows machine to run both at the same time, and you can see printf messages from within ST/Link.

We can verify the HID Vendor and Product IDs by looking at the Device Manager, and look for the HID Game Controller:

/media/uploads/thetazzbot/arcade_controller_hid.jpg

I used pid.codes to register my own Vendor_ID and Product_ID

If you go to the USB Game Controller control panel widget, you will see the new entry for Arcade Gamepad:

/media/uploads/thetazzbot/arcade_controller_controlpanel.jpg

And here we can see all 32 buttons:

/media/uploads/thetazzbot/arcade_controller_buttons.jpg

On the Nucleo board you may have difficulties depending on the revision. The board I am using is an STM32F411RE Revision C03, which has resistors and solder joints (bottom) to allow the use of the Crystal on the STLink board for USB purposes. After programming via STLink, remove the USB cable from the STLink, the jumper must be set to E5V to power the board from the PC's usb port. Plug the new cable into the PC.

When you're ready to install it in the arcade cabinet, or project, just remember to setup the jumper JP5 to E5V and you only need the single USB connection to the host.

Here are some useful links that I used to grasp all the little things involved in this project:

Committer:
thetazzbot
Date:
Tue Dec 13 04:13:37 2016 +0000
Revision:
2:bdf03de86660
Parent:
1:ad6066c16dbd
Child:
4:05f4ace9508a
Removed more Pinscape code

Who changed what in which revision?

UserRevisionLine numberNew contents of line
thetazzbot 1:ad6066c16dbd 1 /* Modified for RetroPie MW 2016 */
thetazzbot 1:ad6066c16dbd 2 /* Copyright 2014 M J Roberts, MIT License
thetazzbot 1:ad6066c16dbd 3 *
thetazzbot 1:ad6066c16dbd 4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
thetazzbot 1:ad6066c16dbd 5 * and associated documentation files (the "Software"), to deal in the Software without
thetazzbot 1:ad6066c16dbd 6 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
thetazzbot 1:ad6066c16dbd 7 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
thetazzbot 1:ad6066c16dbd 8 * Software is furnished to do so, subject to the following conditions:
thetazzbot 1:ad6066c16dbd 9 *
thetazzbot 1:ad6066c16dbd 10 * The above copyright notice and this permission notice shall be included in all copies or
thetazzbot 1:ad6066c16dbd 11 * substantial portions of the Software.
thetazzbot 1:ad6066c16dbd 12 *
thetazzbot 1:ad6066c16dbd 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
thetazzbot 1:ad6066c16dbd 14 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
thetazzbot 1:ad6066c16dbd 15 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
thetazzbot 1:ad6066c16dbd 16 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
thetazzbot 1:ad6066c16dbd 17 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
thetazzbot 1:ad6066c16dbd 18 */
thetazzbot 1:ad6066c16dbd 19
thetazzbot 1:ad6066c16dbd 20 //
thetazzbot 1:ad6066c16dbd 21 // - Button input wiring. 24 of the KL25Z's GPIO ports are mapped as digital inputs
thetazzbot 1:ad6066c16dbd 22 // for buttons and switches. The software reports these as joystick buttons when
thetazzbot 1:ad6066c16dbd 23 // it sends reports to the PC. These can be used to wire physical pinball-style
thetazzbot 1:ad6066c16dbd 24 // buttons in the cabinet (e.g., flipper buttons, the Start button) and miscellaneous
thetazzbot 1:ad6066c16dbd 25 // switches (such as a tilt bob) to the PC. Visual Pinball can use joystick buttons
thetazzbot 1:ad6066c16dbd 26 // for input - you just have to assign a VP function to each button using VP's
thetazzbot 1:ad6066c16dbd 27 // keyboard options dialog. To wire a button physically, connect one terminal of
thetazzbot 1:ad6066c16dbd 28 // the button switch to the KL25Z ground, and connect the other terminal to the
thetazzbot 1:ad6066c16dbd 29 // the GPIO port you wish to assign to the button. See the buttonMap[] array
thetazzbot 1:ad6066c16dbd 30 // below for the available GPIO ports and their assigned joystick button numbers.
thetazzbot 1:ad6066c16dbd 31 // If you're not using a GPIO port, you can just leave it unconnected - the digital
thetazzbot 1:ad6066c16dbd 32 // inputs have built-in pull-up resistors, so an unconnected port is the same as
thetazzbot 1:ad6066c16dbd 33 // an open switch (an "off" state for the button).
thetazzbot 1:ad6066c16dbd 34 //
thetazzbot 1:ad6066c16dbd 35 //
thetazzbot 1:ad6066c16dbd 36 // STATUS LIGHTS: The on-board LED on the KL25Z flashes to indicate the current
thetazzbot 1:ad6066c16dbd 37 // device status. The flash patterns are:
thetazzbot 1:ad6066c16dbd 38 //
thetazzbot 1:ad6066c16dbd 39 // two short red flashes = the device is powered but hasn't successfully
thetazzbot 1:ad6066c16dbd 40 // connected to the host via USB (either it's not physically connected
thetazzbot 1:ad6066c16dbd 41 // to the USB port, or there was a problem with the software handshake
thetazzbot 1:ad6066c16dbd 42 // with the USB device driver on the computer)
thetazzbot 1:ad6066c16dbd 43 //
thetazzbot 1:ad6066c16dbd 44 // short red flash = the host computer is in sleep/suspend mode
thetazzbot 1:ad6066c16dbd 45 //
thetazzbot 1:ad6066c16dbd 46 // long red/green = the LedWiz unti number has been changed, so a reset
thetazzbot 1:ad6066c16dbd 47 // is needed. You can simply unplug the device and plug it back in,
thetazzbot 1:ad6066c16dbd 48 // or presss and hold the reset button on the device for a few seconds.
thetazzbot 1:ad6066c16dbd 49 //
thetazzbot 1:ad6066c16dbd 50 // long yellow/green = everything's working, but the plunger hasn't
thetazzbot 1:ad6066c16dbd 51 // been calibrated; follow the calibration procedure described above.
thetazzbot 1:ad6066c16dbd 52 // This flash mode won't appear if the CCD has been disabled. Note
thetazzbot 1:ad6066c16dbd 53 // that the device can't tell whether a CCD is physically attached;
thetazzbot 1:ad6066c16dbd 54 // if you don't have a CCD attached, you can set the appropriate option
thetazzbot 1:ad6066c16dbd 55 // in config.h or use the Windows config tool to disable the CCD
thetazzbot 1:ad6066c16dbd 56 // software features.
thetazzbot 1:ad6066c16dbd 57 //
thetazzbot 1:ad6066c16dbd 58 // alternating blue/green = everything's working
thetazzbot 1:ad6066c16dbd 59 //
thetazzbot 1:ad6066c16dbd 60 #include "mbed.h"
thetazzbot 1:ad6066c16dbd 61 #include "math.h"
thetazzbot 1:ad6066c16dbd 62 #include "USBJoystick.h"
thetazzbot 1:ad6066c16dbd 63
thetazzbot 1:ad6066c16dbd 64
thetazzbot 1:ad6066c16dbd 65 #define DECL_EXTERNS
thetazzbot 1:ad6066c16dbd 66 #include "config.h"
thetazzbot 1:ad6066c16dbd 67
thetazzbot 1:ad6066c16dbd 68
thetazzbot 1:ad6066c16dbd 69 // ---------------------------------------------------------------------------
thetazzbot 1:ad6066c16dbd 70 // utilities
thetazzbot 1:ad6066c16dbd 71
thetazzbot 1:ad6066c16dbd 72 // number of elements in an array
thetazzbot 1:ad6066c16dbd 73 #define countof(x) (sizeof(x)/sizeof((x)[0]))
thetazzbot 1:ad6066c16dbd 74
thetazzbot 1:ad6066c16dbd 75 // floating point square of a number
thetazzbot 1:ad6066c16dbd 76 inline float square(float x) { return x*x; }
thetazzbot 1:ad6066c16dbd 77
thetazzbot 1:ad6066c16dbd 78 // floating point rounding
thetazzbot 1:ad6066c16dbd 79 inline float round(float x) { return x > 0 ? floor(x + 0.5) : ceil(x - 0.5); }
thetazzbot 1:ad6066c16dbd 80
thetazzbot 1:ad6066c16dbd 81
thetazzbot 1:ad6066c16dbd 82 // --------------------------------------------------------------------------
thetazzbot 1:ad6066c16dbd 83 //
thetazzbot 1:ad6066c16dbd 84 // USB product version number
thetazzbot 1:ad6066c16dbd 85 //
thetazzbot 1:ad6066c16dbd 86 const uint16_t USB_VERSION_NO = 0x0007;
thetazzbot 1:ad6066c16dbd 87
thetazzbot 1:ad6066c16dbd 88
thetazzbot 1:ad6066c16dbd 89 //
thetazzbot 1:ad6066c16dbd 90 // Build the full USB product ID. If we're using the LedWiz compatible
thetazzbot 1:ad6066c16dbd 91 // vendor ID, the full product ID is the combination of the LedWiz base
thetazzbot 1:ad6066c16dbd 92 // product ID (0x00F0) and the 0-based unit number (0-15). If we're not
thetazzbot 1:ad6066c16dbd 93 // trying to be LedWiz compatible, we just use the exact product ID
thetazzbot 1:ad6066c16dbd 94 // specified in config.h.
thetazzbot 1:ad6066c16dbd 95 #define MAKE_USB_PRODUCT_ID(vid, pidbase, unit) \
thetazzbot 1:ad6066c16dbd 96 ((vid) == 0xFAFA && (pidbase) == 0x00F0 ? (pidbase) | (unit) : (pidbase))
thetazzbot 1:ad6066c16dbd 97
thetazzbot 1:ad6066c16dbd 98
thetazzbot 1:ad6066c16dbd 99 // --------------------------------------------------------------------------
thetazzbot 1:ad6066c16dbd 100 //
thetazzbot 1:ad6066c16dbd 101 // Joystick axis report range - we report from -JOYMAX to +JOYMAX
thetazzbot 1:ad6066c16dbd 102 //
thetazzbot 1:ad6066c16dbd 103 #define JOYMAX 4096
thetazzbot 1:ad6066c16dbd 104
thetazzbot 1:ad6066c16dbd 105
thetazzbot 1:ad6066c16dbd 106 // ---------------------------------------------------------------------------
thetazzbot 1:ad6066c16dbd 107 //
thetazzbot 1:ad6066c16dbd 108 // On-board RGB LED elements - we use these for diagnostic displays.
thetazzbot 1:ad6066c16dbd 109 //
thetazzbot 1:ad6066c16dbd 110 //TODO: FIXME for Nucleo
thetazzbot 1:ad6066c16dbd 111 DigitalOut ledR(LED1), ledG(LED2), ledB(LED3);
thetazzbot 1:ad6066c16dbd 112
thetazzbot 1:ad6066c16dbd 113 // ---------------------------------------------------------------------------
thetazzbot 1:ad6066c16dbd 114 //
thetazzbot 1:ad6066c16dbd 115 // Button input
thetazzbot 1:ad6066c16dbd 116 //
thetazzbot 1:ad6066c16dbd 117
thetazzbot 1:ad6066c16dbd 118 // button input map array
thetazzbot 1:ad6066c16dbd 119
thetazzbot 1:ad6066c16dbd 120 DigitalIn *buttonDigIn[NUM_OF_BUTTONS]; // config.h
thetazzbot 1:ad6066c16dbd 121
thetazzbot 1:ad6066c16dbd 122 // button state
thetazzbot 1:ad6066c16dbd 123 struct ButtonState
thetazzbot 1:ad6066c16dbd 124 {
thetazzbot 1:ad6066c16dbd 125 // current on/off state
thetazzbot 1:ad6066c16dbd 126 int pressed;
thetazzbot 1:ad6066c16dbd 127
thetazzbot 1:ad6066c16dbd 128 // Sticky time remaining for current state. When a
thetazzbot 1:ad6066c16dbd 129 // state transition occurs, we set this to a debounce
thetazzbot 1:ad6066c16dbd 130 // period. Future state transitions will be ignored
thetazzbot 1:ad6066c16dbd 131 // until the debounce time elapses.
thetazzbot 1:ad6066c16dbd 132 int t;
thetazzbot 1:ad6066c16dbd 133 } buttonState[NUM_OF_BUTTONS];
thetazzbot 1:ad6066c16dbd 134
thetazzbot 1:ad6066c16dbd 135 // timer for button reports
thetazzbot 1:ad6066c16dbd 136 static Timer buttonTimer;
thetazzbot 1:ad6066c16dbd 137
thetazzbot 1:ad6066c16dbd 138 // initialize the button inputs
thetazzbot 1:ad6066c16dbd 139 void initButtons()
thetazzbot 1:ad6066c16dbd 140 {
thetazzbot 1:ad6066c16dbd 141 // create the digital inputs
thetazzbot 1:ad6066c16dbd 142 for (int i = 0 ; i < countof(buttonDigIn) ; ++i)
thetazzbot 1:ad6066c16dbd 143 {
thetazzbot 1:ad6066c16dbd 144 if (i < countof(buttonMap) && buttonMap[i] != NC)
thetazzbot 1:ad6066c16dbd 145 buttonDigIn[i] = new DigitalIn(buttonMap[i]);
thetazzbot 1:ad6066c16dbd 146 else
thetazzbot 1:ad6066c16dbd 147 buttonDigIn[i] = 0;
thetazzbot 1:ad6066c16dbd 148 }
thetazzbot 1:ad6066c16dbd 149
thetazzbot 1:ad6066c16dbd 150 // start the button timer
thetazzbot 1:ad6066c16dbd 151 buttonTimer.start();
thetazzbot 1:ad6066c16dbd 152 }
thetazzbot 1:ad6066c16dbd 153
thetazzbot 1:ad6066c16dbd 154
thetazzbot 1:ad6066c16dbd 155 // read the button input state
thetazzbot 1:ad6066c16dbd 156 uint32_t readButtons()
thetazzbot 1:ad6066c16dbd 157 {
thetazzbot 1:ad6066c16dbd 158 // start with all buttons off
thetazzbot 1:ad6066c16dbd 159 uint32_t buttons = 0;
thetazzbot 1:ad6066c16dbd 160
thetazzbot 1:ad6066c16dbd 161 // figure the time elapsed since the last scan
thetazzbot 1:ad6066c16dbd 162 int dt = buttonTimer.read_ms();
thetazzbot 1:ad6066c16dbd 163
thetazzbot 1:ad6066c16dbd 164 // reset the timef for the next scan
thetazzbot 1:ad6066c16dbd 165 buttonTimer.reset();
thetazzbot 1:ad6066c16dbd 166
thetazzbot 1:ad6066c16dbd 167 // scan the button list
thetazzbot 1:ad6066c16dbd 168 uint32_t bit = 1;
thetazzbot 1:ad6066c16dbd 169 DigitalIn **di = buttonDigIn;
thetazzbot 1:ad6066c16dbd 170 ButtonState *bs = buttonState;
thetazzbot 1:ad6066c16dbd 171 for (int i = 0 ; i < countof(buttonDigIn) ; ++i, ++di, ++bs, bit <<= 1)
thetazzbot 1:ad6066c16dbd 172 {
thetazzbot 1:ad6066c16dbd 173 // read this button
thetazzbot 1:ad6066c16dbd 174 if (*di != 0)
thetazzbot 1:ad6066c16dbd 175 {
thetazzbot 1:ad6066c16dbd 176 // deduct the elapsed time since the last update
thetazzbot 1:ad6066c16dbd 177 // from the button's remaining sticky time
thetazzbot 1:ad6066c16dbd 178 bs->t -= dt;
thetazzbot 1:ad6066c16dbd 179 if (bs->t < 0)
thetazzbot 1:ad6066c16dbd 180 bs->t = 0;
thetazzbot 1:ad6066c16dbd 181
thetazzbot 1:ad6066c16dbd 182 // If the sticky time has elapsed, note the new physical
thetazzbot 1:ad6066c16dbd 183 // state of the button. If we still have sticky time
thetazzbot 1:ad6066c16dbd 184 // remaining, ignore the physical state; the last state
thetazzbot 1:ad6066c16dbd 185 // change persists until the sticky time elapses so that
thetazzbot 1:ad6066c16dbd 186 // we smooth out any "bounce" (electrical transients that
thetazzbot 1:ad6066c16dbd 187 // occur when the switch contact is opened or closed).
thetazzbot 1:ad6066c16dbd 188 if (bs->t == 0)
thetazzbot 1:ad6066c16dbd 189 {
thetazzbot 1:ad6066c16dbd 190 // get the new physical state
thetazzbot 1:ad6066c16dbd 191 int pressed = !(*di)->read();
thetazzbot 1:ad6066c16dbd 192
thetazzbot 1:ad6066c16dbd 193 // update the button's logical state if this is a change
thetazzbot 1:ad6066c16dbd 194 if (pressed != bs->pressed)
thetazzbot 1:ad6066c16dbd 195 {
thetazzbot 1:ad6066c16dbd 196 // store the new state
thetazzbot 1:ad6066c16dbd 197 bs->pressed = pressed;
thetazzbot 1:ad6066c16dbd 198
thetazzbot 1:ad6066c16dbd 199 // start a new sticky period for debouncing this
thetazzbot 1:ad6066c16dbd 200 // state change
thetazzbot 1:ad6066c16dbd 201 bs->t = 25;
thetazzbot 1:ad6066c16dbd 202 }
thetazzbot 1:ad6066c16dbd 203 }
thetazzbot 1:ad6066c16dbd 204
thetazzbot 1:ad6066c16dbd 205 // if it's pressed, OR its bit into the state
thetazzbot 1:ad6066c16dbd 206 if (bs->pressed)
thetazzbot 1:ad6066c16dbd 207 buttons |= bit;
thetazzbot 1:ad6066c16dbd 208 }
thetazzbot 1:ad6066c16dbd 209 }
thetazzbot 1:ad6066c16dbd 210
thetazzbot 1:ad6066c16dbd 211 // return the new button list
thetazzbot 1:ad6066c16dbd 212 return buttons;
thetazzbot 1:ad6066c16dbd 213 }
thetazzbot 1:ad6066c16dbd 214
thetazzbot 1:ad6066c16dbd 215 // ---------------------------------------------------------------------------
thetazzbot 1:ad6066c16dbd 216 //
thetazzbot 1:ad6066c16dbd 217 // Customization joystick subbclass
thetazzbot 1:ad6066c16dbd 218 //
thetazzbot 1:ad6066c16dbd 219
thetazzbot 1:ad6066c16dbd 220 class MyUSBJoystick: public USBJoystick
thetazzbot 1:ad6066c16dbd 221 {
thetazzbot 1:ad6066c16dbd 222 public:
thetazzbot 1:ad6066c16dbd 223 MyUSBJoystick(uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
thetazzbot 1:ad6066c16dbd 224 : USBJoystick(vendor_id, product_id, product_release, true)
thetazzbot 1:ad6066c16dbd 225 {
thetazzbot 1:ad6066c16dbd 226 suspended_ = false;
thetazzbot 1:ad6066c16dbd 227 }
thetazzbot 1:ad6066c16dbd 228
thetazzbot 1:ad6066c16dbd 229 // are we connected?
thetazzbot 1:ad6066c16dbd 230 int isConnected() { return configured(); }
thetazzbot 1:ad6066c16dbd 231
thetazzbot 1:ad6066c16dbd 232 // Are we in suspend mode?
thetazzbot 1:ad6066c16dbd 233 int isSuspended() const { return suspended_; }
thetazzbot 1:ad6066c16dbd 234
thetazzbot 1:ad6066c16dbd 235 protected:
thetazzbot 1:ad6066c16dbd 236 virtual void suspendStateChanged(unsigned int suspended)
thetazzbot 1:ad6066c16dbd 237 { suspended_ = suspended; }
thetazzbot 1:ad6066c16dbd 238
thetazzbot 1:ad6066c16dbd 239 // are we suspended?
thetazzbot 1:ad6066c16dbd 240 int suspended_;
thetazzbot 1:ad6066c16dbd 241 };
thetazzbot 1:ad6066c16dbd 242
thetazzbot 1:ad6066c16dbd 243
thetazzbot 1:ad6066c16dbd 244
thetazzbot 1:ad6066c16dbd 245 // ---------------------------------------------------------------------------
thetazzbot 1:ad6066c16dbd 246 //
thetazzbot 1:ad6066c16dbd 247 // Main program loop. This is invoked on startup and runs forever. Our
thetazzbot 1:ad6066c16dbd 248 // main work is to read our devices, process
thetazzbot 1:ad6066c16dbd 249 // the readings into nudge and plunger position data, and send the results
thetazzbot 1:ad6066c16dbd 250 // to the host computer via the USB joystick interface.
thetazzbot 1:ad6066c16dbd 251 //
thetazzbot 1:ad6066c16dbd 252 int main(void)
thetazzbot 1:ad6066c16dbd 253 {
thetazzbot 1:ad6066c16dbd 254 // turn off our on-board indicator LED
thetazzbot 1:ad6066c16dbd 255 ledR = 1;
thetazzbot 1:ad6066c16dbd 256 ledG = 1;
thetazzbot 1:ad6066c16dbd 257 ledB = 1;
thetazzbot 1:ad6066c16dbd 258
thetazzbot 1:ad6066c16dbd 259 // we're not connected/awake yet
thetazzbot 1:ad6066c16dbd 260 bool connected = false;
thetazzbot 1:ad6066c16dbd 261 time_t connectChangeTime = time(0);
thetazzbot 1:ad6066c16dbd 262
thetazzbot 1:ad6066c16dbd 263 // initialize the button input ports
thetazzbot 1:ad6066c16dbd 264 initButtons();
thetazzbot 1:ad6066c16dbd 265
thetazzbot 1:ad6066c16dbd 266 // we don't need a reset yet
thetazzbot 1:ad6066c16dbd 267 bool needReset = false;
thetazzbot 1:ad6066c16dbd 268 // Create the joystick USB client.
thetazzbot 1:ad6066c16dbd 269 MyUSBJoystick js(
thetazzbot 1:ad6066c16dbd 270 USB_VENDOR_ID,
thetazzbot 2:bdf03de86660 271 MAKE_USB_PRODUCT_ID(USB_VENDOR_ID, USB_PRODUCT_ID, 0x08),
thetazzbot 1:ad6066c16dbd 272 USB_VERSION_NO);
thetazzbot 1:ad6066c16dbd 273
thetazzbot 1:ad6066c16dbd 274 // last report timer - we use this to throttle reports, since VP
thetazzbot 1:ad6066c16dbd 275 // doesn't want to hear from us more than about every 10ms
thetazzbot 1:ad6066c16dbd 276 Timer reportTimer;
thetazzbot 1:ad6066c16dbd 277 reportTimer.start();
thetazzbot 1:ad6066c16dbd 278
thetazzbot 1:ad6066c16dbd 279 // set up a timer for our heartbeat indicator
thetazzbot 1:ad6066c16dbd 280 Timer hbTimer;
thetazzbot 1:ad6066c16dbd 281 hbTimer.start();
thetazzbot 1:ad6066c16dbd 282 int hb = 0;
thetazzbot 1:ad6066c16dbd 283 uint16_t hbcnt = 0;
thetazzbot 1:ad6066c16dbd 284 uint16_t x=0,y=0,zrep=0;
thetazzbot 1:ad6066c16dbd 285 // we're all set up - now just loop, processing sensor reports and
thetazzbot 1:ad6066c16dbd 286 // host requests
thetazzbot 1:ad6066c16dbd 287 for (;;)
thetazzbot 1:ad6066c16dbd 288 {
thetazzbot 2:bdf03de86660 289
thetazzbot 1:ad6066c16dbd 290 // update the buttons
thetazzbot 1:ad6066c16dbd 291 uint32_t buttons = readButtons();
thetazzbot 1:ad6066c16dbd 292 uint16_t statusFlags;
thetazzbot 1:ad6066c16dbd 293
thetazzbot 1:ad6066c16dbd 294 // If it's been long enough since our last USB status report,
thetazzbot 1:ad6066c16dbd 295 // send the new report. We throttle the report rate because
thetazzbot 1:ad6066c16dbd 296 // it can overwhelm the PC side if we report too frequently.
thetazzbot 1:ad6066c16dbd 297 // VP only wants to sync with the real world in 10ms intervals,
thetazzbot 1:ad6066c16dbd 298 // so reporting more frequently only creates i/o overhead
thetazzbot 1:ad6066c16dbd 299 // without doing anything to improve the simulation.
thetazzbot 1:ad6066c16dbd 300 if (reportTimer.read_ms() > 15)
thetazzbot 1:ad6066c16dbd 301 {
thetazzbot 1:ad6066c16dbd 302 // read the accelerometer
thetazzbot 1:ad6066c16dbd 303 int xa, ya;
thetazzbot 1:ad6066c16dbd 304
thetazzbot 1:ad6066c16dbd 305 // confine the results to our joystick axis range
thetazzbot 1:ad6066c16dbd 306 if (xa < -JOYMAX) xa = -JOYMAX;
thetazzbot 1:ad6066c16dbd 307 if (xa > JOYMAX) xa = JOYMAX;
thetazzbot 1:ad6066c16dbd 308 if (ya < -JOYMAX) ya = -JOYMAX;
thetazzbot 1:ad6066c16dbd 309 if (ya > JOYMAX) ya = JOYMAX;
thetazzbot 1:ad6066c16dbd 310
thetazzbot 1:ad6066c16dbd 311 // store the updated accelerometer coordinates
thetazzbot 1:ad6066c16dbd 312 x = xa;
thetazzbot 1:ad6066c16dbd 313 y = ya;
thetazzbot 1:ad6066c16dbd 314
thetazzbot 1:ad6066c16dbd 315 // Send the status report. Note that we have to map the X and Y
thetazzbot 1:ad6066c16dbd 316 // axes from the accelerometer to match the Windows joystick axes.
thetazzbot 1:ad6066c16dbd 317 // The mapping is determined according to the mounting direction
thetazzbot 1:ad6066c16dbd 318 // set in config.h via the ORIENTATION_xxx macros.
thetazzbot 1:ad6066c16dbd 319 js.update(x, y, zrep, buttons, statusFlags);
thetazzbot 1:ad6066c16dbd 320
thetazzbot 1:ad6066c16dbd 321 // we've just started a new report interval, so reset the timer
thetazzbot 1:ad6066c16dbd 322 reportTimer.reset();
thetazzbot 1:ad6066c16dbd 323 }
thetazzbot 1:ad6066c16dbd 324
thetazzbot 1:ad6066c16dbd 325
thetazzbot 1:ad6066c16dbd 326
thetazzbot 1:ad6066c16dbd 327 #ifdef DEBUG_PRINTF
thetazzbot 1:ad6066c16dbd 328 if (x != 0 || y != 0)
thetazzbot 1:ad6066c16dbd 329 printf("%d,%d\r\n", x, y);
thetazzbot 1:ad6066c16dbd 330 #endif
thetazzbot 1:ad6066c16dbd 331
thetazzbot 1:ad6066c16dbd 332 // check for connection status changes
thetazzbot 1:ad6066c16dbd 333 int newConnected = js.isConnected() && !js.isSuspended();
thetazzbot 1:ad6066c16dbd 334 if (newConnected != connected)
thetazzbot 1:ad6066c16dbd 335 {
thetazzbot 1:ad6066c16dbd 336 // give it a few seconds to stabilize
thetazzbot 1:ad6066c16dbd 337 time_t tc = time(0);
thetazzbot 1:ad6066c16dbd 338 if (tc - connectChangeTime > 3)
thetazzbot 1:ad6066c16dbd 339 {
thetazzbot 1:ad6066c16dbd 340 // note the new status
thetazzbot 1:ad6066c16dbd 341 connected = newConnected;
thetazzbot 1:ad6066c16dbd 342 connectChangeTime = tc;
thetazzbot 1:ad6066c16dbd 343 }
thetazzbot 1:ad6066c16dbd 344 }
thetazzbot 1:ad6066c16dbd 345
thetazzbot 1:ad6066c16dbd 346 // provide a visual status indication on the on-board LED
thetazzbot 1:ad6066c16dbd 347 if (hbTimer.read_ms() > 1000)
thetazzbot 1:ad6066c16dbd 348 {
thetazzbot 1:ad6066c16dbd 349 if (!newConnected)
thetazzbot 1:ad6066c16dbd 350 {
thetazzbot 1:ad6066c16dbd 351 // suspended - turn off the LED
thetazzbot 1:ad6066c16dbd 352 ledR = 1;
thetazzbot 1:ad6066c16dbd 353 ledG = 1;
thetazzbot 1:ad6066c16dbd 354 ledB = 1;
thetazzbot 1:ad6066c16dbd 355
thetazzbot 1:ad6066c16dbd 356 // show a status flash every so often
thetazzbot 1:ad6066c16dbd 357 if (hbcnt % 3 == 0)
thetazzbot 1:ad6066c16dbd 358 {
thetazzbot 1:ad6066c16dbd 359 // disconnected = red/red flash; suspended = red
thetazzbot 1:ad6066c16dbd 360 for (int n = js.isConnected() ? 1 : 2 ; n > 0 ; --n)
thetazzbot 1:ad6066c16dbd 361 {
thetazzbot 1:ad6066c16dbd 362 ledR = 0;
thetazzbot 1:ad6066c16dbd 363 wait(0.05);
thetazzbot 1:ad6066c16dbd 364 ledR = 1;
thetazzbot 1:ad6066c16dbd 365 wait(0.25);
thetazzbot 1:ad6066c16dbd 366 }
thetazzbot 1:ad6066c16dbd 367 }
thetazzbot 1:ad6066c16dbd 368 }
thetazzbot 1:ad6066c16dbd 369 else if (needReset)
thetazzbot 1:ad6066c16dbd 370 {
thetazzbot 1:ad6066c16dbd 371 // connected, need to reset due to changes in config parameters -
thetazzbot 1:ad6066c16dbd 372 // flash red/green
thetazzbot 1:ad6066c16dbd 373 hb = !hb;
thetazzbot 1:ad6066c16dbd 374 ledR = (hb ? 0 : 1);
thetazzbot 1:ad6066c16dbd 375 ledG = (hb ? 1 : 0);
thetazzbot 1:ad6066c16dbd 376 ledB = 0;
thetazzbot 1:ad6066c16dbd 377 }
thetazzbot 1:ad6066c16dbd 378 else
thetazzbot 1:ad6066c16dbd 379 {
thetazzbot 1:ad6066c16dbd 380 // connected - flash blue/green
thetazzbot 1:ad6066c16dbd 381 hb = !hb;
thetazzbot 1:ad6066c16dbd 382 ledR = 1;
thetazzbot 1:ad6066c16dbd 383 ledG = (hb ? 0 : 1);
thetazzbot 1:ad6066c16dbd 384 ledB = (hb ? 1 : 0);
thetazzbot 1:ad6066c16dbd 385 }
thetazzbot 1:ad6066c16dbd 386
thetazzbot 1:ad6066c16dbd 387 // reset the heartbeat timer
thetazzbot 1:ad6066c16dbd 388 hbTimer.reset();
thetazzbot 1:ad6066c16dbd 389 ++hbcnt;
thetazzbot 1:ad6066c16dbd 390 }
thetazzbot 1:ad6066c16dbd 391
thetazzbot 1:ad6066c16dbd 392 }
thetazzbot 1:ad6066c16dbd 393 }