With World Wide Web is evolving faster than ever. We've seen recent developments in Virtual Reality and it's path to the web. And with tools that is available to use, it's getting easier and easier to build Virtual Reality experiences.
It's Christmas time again and growing up I've always seen these Christmas Snow Globe. It's so peaceful to see how snow falls down slowly. Growing up in country where there's now snow, it's fun to see the christmas snow globe.
What if we can experience being inside the snow globe. What would it be like? What if I have a snow globe, connect it to my VR device and get transported inside a snow globe.
I built a proof of concept device that I think would start an industry of VR connected devices.
**Updates: Checkout the next version of this project
HTC ViveVive is a first-of-its-kind virtual reality system. Let yourself be visually, physically and emotionally amazed by new virtual worlds. I have one and it's amazing. You can move around and have controllers that is used to interact with virtual world.
According to https://aframe.io/ : "A-Frame is a web framework for building virtual reality experiences. It was started by Mozilla VR to make WebVR content creation easier, faster, and more accessible.
A-Frame lets you build scenes with just HTML while having unlimited access to JavaScript, three.js, and all existing Web APIs. A-Frame uses an entity-component-system pattern that promotes composition and extensibility. It is free and open source with a welcoming community and a thriving ecosystem of tools and components."
Chrome Experimental BuildI recently found out that A-frame supports HTC Vive using this Chrome experimental build.
https://webvr.info/get-chrome/
So I downloaded this to my computer and followed the instructions to setup.
I tried this sample app from Aframe.io
- https://aframe.io/examples/showcase/tracked-controllers/
And it was awesome. It's open source, you can read the code. Very easy to follow
The key is just adding these entities to the scene
<a-entity hand-controls="left" aabb-collider="objects: .cube;" grab></a-entity>
<a-entity hand-controls="right" aabb-collider="objects: .cube;" grab></a-entity>
Inside the Vive, you can actually see your controllers, grab box or paint. I'm hooked. Time to learn what I can do with these technology.
I also found these projects:
https://github.com/captainpainway/a-frame-snowman
It's a Virtual Reality Snow man with Snow particle system.
Then I looked around and found another project where you can throw a ball inside Vive with a physics system.
https://github.com/bryik/aframe-ball-throw
Now my mind is really pumped at this point. Snowman, throwing snowballs in Javascript and HTML, can you believe that?
I asked myself: What if it's inside an Arduino? Can it be done? I recently found out that you can host your website inside the arduino? Can a Virtual Reality website hosted too?
How I made the Snow Globe VR!Arduino MKR1000 has been designed to offer a practical and cost effective solution for makers seeking to add Wi-Fi connectivity to their projects with minimal previous experience in networking.
The MKR1000 made it really easy to host website inside an Arduino. I based this project from this tutorial:
https://www.arduino.cc/en/Tutorial/Wifi101SimpleWebServerWiFi
It connects to your wifi and opens up port 80
char ssid[] = "yourNetwork"; // your network SSID (name)
char pass[] = "secretPassword"; // your network password
int keyIndex = 0; // your network key Index number (needed only for WEP)
int status = WL_IDLE_STATUS;
WiFiServer server(80);
void setup() {
Serial.begin(9600); // initialize serial communication
pinMode(9, OUTPUT); // set the LED pin mode
// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
while (true); // don't continue
}
// attempt to connect to Wifi network:
while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to Network named: ");
Serial.println(ssid); // print the network name (SSID);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
// wait 10 seconds for connection:
delay(10000);
}
server.begin(); // start the web server on port 80
printWifiStatus(); // you're connected now, so print out the status
}
You can serve your website using this
void loop() {
WiFiClient client = server.available(); // listen for incoming clients
if (client) { // if you get a client,
Serial.println("new client"); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println();
// the content of the HTTP response follows the header:
client.print("Click <a href=\"/H\">here</a> turn the LED on pin 9 on<br>");
client.print("Click <a href=\"/L\">here</a> turn the LED on pin 9 off<br>");
// The HTTP response ends with another blank line:
client.println();
// break out of the while loop:
break;
}
else { // if you got a newline, then clear currentLine:
currentLine = "";
}
}
else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
// Check to see if the client request was "GET /H" or "GET /L":
if (currentLine.endsWith("GET /H")) {
digitalWrite(9, HIGH); // GET /H turns the LED on
}
if (currentLine.endsWith("GET /L")) {
digitalWrite(9, LOW); // GET /L turns the LED off
}
}
}
// close the connection:
client.stop();
Serial.println("client disonnected");
}
}
I added an accelerometer, I used the one that comes with Grove starter kit for Intel Edison. It's based on Freescale's MMA7660FC chip.
http://wiki.seeed.cc/Grove-3-Axis_Digital_Accelerometer-1.5g/
Followed the instructions on how to create a WebServer and install the driver for Digital Accelerometer.
I got the webserver in Arduino. I started changing it to add WebVR capabilities. To simplify the process, I stored the javascript and assets libraries on Github. Technically, it can also be located in SD Card module attached in Arduino and served as webserver.
If I go to http://<arduino-ip-address>/VR
it will server the VR website. If it's just the http://<arduino-ip-address>/
it will return data from accelerometer.
Got the webvr site working, but now how do I send accelerometer data from arduino the browser? I thought of different strategies like sending data thru a cloud, or do websockets, mqtt.
I thought to myself, simplify the process. Don't overcomplicate stuff. I'm only serving 1 user, no need to do cloud. Think small not big. No big data. How about polling?
The classic polling method. Why not? It's only limited to 1 client, it's not serving multiple users (although it can). I can just instruct my web client to ask the arduino webserver to return the accelerometer data every 2 seconds. It doesn't have to be realtime.
Here's the key instruction:
client.println("setInterval(function() { checkUpdates('/updates') }, 2000);");
client.println("function checkUpdates(url) {");
client.println("var xhr = new XMLHttpRequest();");
client.println("xhr.open('GET', url);");
client.println(" xhr.onreadystatechange = function() {");
client.println(" if (xhr.readyState == 4 && xhr.status == 200) { ");
client.println(" var result = JSON.parse(xhr.responseText); ");
Every 2 seconds, run CheckUpdate function that does XML http request. I don't even need a jquery library. just plain old javascript. The arduino webserver returns json file with accelerometer data. the Checkupdate function would also parse the result then now I can detect if the snow globe was tilted or not.
client.println("Content-Type: application/json");
client.println("Connection: close"); // the connection will be closed after completion of the response
client.println();
float ax,ay,az;
accelemeter.getAcceleration(&ax,&ay,&az);
client.print("{\"channel0\":");
//client.print(sensorReading);
client.print(az);
Serial.print(az);
client.print("}");
Think small. It's a snow globe world after all...
Having snow in WebVR using the aframe particle system works. But only small amount of snow. I want more? So I tinkered with settings on particle system. I got stuck. I've changed variables and it seems to make it worse.
So here's what I did. I found out you can manipulate the DOM in A-frame. So you can add entities dynamically.
client.println("function addSnow() {");
client.println(" var newSnow = document.createElement('a-entity');");
client.println(" newSnow.setAttribute('color', '#FF9500');");
client.println(" newSnow.setAttribute('mixin', 'snowTemplate');");
client.println(" return newSnow;");
client.println("}");
client.println("var docFrag = document.createDocumentFragment();");
client.println("for(var i = 0; i < maxSnow; i++) {");
client.println(" docFrag.appendChild(addSnow()); // Note that this does NOT go to the DOM");
client.println("}");
client.println("snowList.appendChild(docFrag);");
Having less snow....Once I have a bunch of snow particle systems. I tried disabling each one of those particle systems...
client.println("function disableSnow(snowElement) {");
client.println(" if (snowElement != null && snowElement.components['particle-system'] != null) {");
client.println(" var pg = snowElement.components['particle-system'].particleGroup;");
client.println(" pg.emitters[0].disable();");
client.println(" }");
client.println("}");
I tried manipulating the DOM first, adding and removing particle systems, but it slows down the site. It actually crashes the browser.
So disabling the emitters and enabling the emitters is the best way to go.
Every 2 seconds, it will start to disable emitters. When the snow globe is tilted, it will enable all the emitters again.
Future developmentsI want to expand this project to make it easy just to tap your phone to the snow globe and BOOM - get transported to VR world. I think it can be accomplished.
My daughter likes to the song 'Let it go' from Frozen. What if she can use the HTC Vive to build Ice Castle or build Olaf.
If this project made you interested in developing with Arduino, Virtual Reality, A-Frame, please click "respect project" button and follow me. Thanks
Comments