I’ve made an Arduino thing that can wirelessly talk to a mobile device over BLE and can now meter the revolutions of a wheel with an optical tachometer. I’m using these two hardware features to make a virtual reality (VR) cycling experience and I’ve got a working demo to share! Here’s how this works:
First, the Arduino thing is positioned to point at the back tires of a stationary bike. (I’m using a mountain bike on an indoor trainer but the beauty of this non-invasive approach is that you could use it with treadmills, ellipticals, rowing machines, or anything that has a looping/revolving surface.)
A strip of paper is taped to the tire. Each time the wheel makes a complete rotation, the Arduino will detect when the piece of paper passes by and then sends a wireless message to the mobile phone.
The mobile phone is placed in a viewer that is strapped to my face. (I used a $10 headset that more comfortably fits the iPhone 6S Plus, but any ol’ viewer will do.)
In software, a virtual bike is created to travel in a virtual environment. The virtual bike will only nudge forward whenever the app receives a message from the Arduino reporting that the physical bike’s wheel has made a complete rotation. We are effectively mapping the physical action of pedaling to movement in the virtual space to make an oversized gaming controller.
The virtual environment is constructed in Unity. To render for virtual reality, I’m using Google Cardboard – a free software SDK for mobile VR. The SDK for Unity is drop dead simple – just drag and drop a prefab into your scene and you’ll instantly have a stereo camera rig to manipulate.
This camera rig is set to move along a spline path whenever the app receives a BLE ping from the Arduino. That’s pretty much all it takes! So there you have it, a VR cycling experience for a whopping total of $40:
- Arduino thing? $30.
- Mobile VR headset? $10.
- Software? Priceless.
Yes it’s DIY, but to put things in perspective, take a look at the current offerings on the market today:
- Peloton: $2,000 bike with built in touchscreen monitor. No VR. Pedaling does not affect the video screensaver 😦
- Virzoom: $300 bike ($600 Oculus + ~$2,000 PC not included)
- Ciclotte: $10,700 bike?!
This was a fun little personal project and I’ve learned what I set out to learn, so this is where I move on – but if you’re going to try something similar, here’s some things I’d consider in retrospect: It wouldn’t be too much of a stretch to have the physical bike steer the direction of the virtual bike. Why not give the player total freedom to explore? Also, does the virtual bike have to be a bike? I’ll just leave this right here: http://goo.gl/fRWY6Z.
For the interested Makers out there, below is a schematic of the IR sensor along with the Arduino code. Tinker away!
…and here’s my Arduino sketch:
#include SPI.h #include "Adafruit_BLE_UART.h" // nRF8001 pins: SCK:13, MISO:12, MOSI:11, REQ:10, ACI:X, RST:9, 3Vo:X #define ADAFRUITBLE_REQ 10 #define ADAFRUITBLE_RST 9 #define ADAFRUITBLE_RDY 2 Adafruit_BLE_UART uart = Adafruit_BLE_UART(ADAFRUITBLE_REQ, ADAFRUITBLE_RDY, ADAFRUITBLE_RST); unsigned long time = 0l; boolean connection = false; uint8_t btm = 65; uint8_t out = btm; uint8_t cap = 90; #define persec 30 #define sendat (1000/persec) int irPin = 7; int irSensorPin = 5; int testLEDPin = 4; int tripTime = 0; int lastTrip = 0; int tripBetween; boolean detectState = false; boolean lastDetectState = false; void setup(void) { Serial.begin(9600); pinMode(irPin, OUTPUT); pinMode(irSensorPin, INPUT); pinMode(testLEDPin, OUTPUT); uart.setDeviceName("YanBLE"); /* define BLE name: 7 characters max! */ uart.setRXcallback(rxCallback); uart.setACIcallback(aciCallback); uart.begin(); } void loop() { pollIR(); // IR sensor uart.pollACI(); // BLE } void pollIR() { digitalWrite(irPin, HIGH); if (digitalRead(irSensorPin) == LOW) { detectState = true; if (detectState != lastDetectState) { // run the first time reflection is detected Serial.println("message sent via BLE"); if (connection == true) { sendBlueMessage("1"); // dummy data passed here, this can be any value. We just need to ping the app } lastDetectState = true; } else { // here we are seeing the same reflection over several frames // turn test LED on to give visual indication of a positive reflection digitalWrite(testLEDPin, HIGH); } } else { detectState = false; lastDetectState = false; digitalWrite(testLEDPin, LOW); } } /**************************************************************************/ /*! BLE-related functions below this point */ /**************************************************************************/ void aciCallback(aci_evt_opcode_t event) { // this function is called whenever select ACI events happen switch (event) { case ACI_EVT_DEVICE_STARTED: Serial.println(F("Advertising started")); break; case ACI_EVT_CONNECTED: Serial.println(F("Connected!")); connection = true; break; case ACI_EVT_DISCONNECTED: Serial.println(F("Disconnected")); connection = false; break; default: break; } } void rxCallback(uint8_t *buffer, uint8_t len) { // this function is called whenever data arrives on the RX channel } void sendBlueMessage(String message) { uint8_t sendbuffer[20]; message.getBytes(sendbuffer, 20); char sendbuffersize = min(20, message.length()); Serial.print(F("\n* Sending -> \"")); Serial.print((char *)sendbuffer); Serial.println("\""); // write the data uart.write(sendbuffer, sendbuffersize); }
That is super sweet! Did you post code to git by any chance-especially your BLE integration?
very cool! I made a similar thing with pebble + google cardboard + unity + 360 degree videos 😉
blog post in Turkish & app on Google Play:
http://blog.sehirbisikleti.com/arttirilmis-gerceklikle-bisiklet-kullanma-deneyimi/
https://goo.gl/ybDbQm
Shut up and take my money
Seriously, I love to cycle but have a really hard time pounding away on the trainer. This would make the winter experience much more bearable.
Sure, it could use a little feature development such as steering. Or play/pause on a youtube 360 cycling video https://www.youtube.com/results?search_query=360+cycling
I would encourage you to productize this (even if it’s just as far as amazon associate links & and a donate button)
I’d pay $5-10 for the app alone.
Check out http://www.virtualtraining.eu . You use a commercially available speed sensor and pedal through videos of real courses. The video playback speed is varied with your pedaling speed. Ride routes in Italy, the Tour de France, and hundreds more. It’s a great way to shake the winter trainer blues.
I love it. When can I buy one ?
Awesome Project. Why don’t you create a multiplayer racing game? 🙂
Cool. So now you just need to integrate the google earth flight simulator:
http://gizmodo.com/391955/google-earth-flying-bicycle-doesnt-include-basket-et
Do you want come on ‘All Things 3D” this Friday to talk about your little project? We do this through Google Hangouts On Air and we would like you to come on at 9:30 a.m. PST.
BTW, love the look 🙂
9-axis DOF is overkill, but yes my thoughts exactly, mount the whole thing in a custom case and mount it to the top pinion nut of the handle bars.
Are you making your shield and Arduino sketches available, as well as a Unity PreFab? Email: mike@makermedia.com about getting this in ‘Make’ magazine.
Again, I hope you can join us Friday.
You should check out Golden Cheetah (www.goldencheetah.org) for the trainer/speed control part (they’ve got that nailed down, with way more detail that what you have. Just contribute your VR piece to extend their already existing Google Street View support, and this would be awesome!
This is so awesome and looks plain fun. And thanks for teaching us how its done.
My pleasure! 🙂
Coo stuff but how did you integrate Bluetooth to Unity???
Check out my previous post: BLE and Unity
Hey, I am working on the same thing as you. But I use a reed-switch for detecting the turn rate of the wheel. Currently I am working on an endless runner for this. Maybe we stay in contact.