Cardboard QR Scanning without Unity SDK

I've been trying to figure out how to scan and use the QR codes provided with Cardboard devices without needing to use the Unity API. I've already written SCN-VR for SceneKit based VR for iOS devices with Obj-c and I would want scanning QR codes to also work to make setting up profiles simpler.

I've seen a QR code scan to goo.gl/pdNRON, which leads to a page on how to download the Google Cardboard app, but what HTTP service is the Google Cardboard app going to download the actual profile?


The QR codes can be parsed with Google's protocol buffers (https://developers.google.com/protocol-buffers/docs/cpptutorial?hl=en0). The shortened URL scanned from the code is redirected to an URL that contains the actual info in the p= query field. For example, your URL (goo.gl/pdNRON) redirects to https://www.google.com/get/cardboard/download/?p=CgxNYXR0ZWwsIEluYy4SGFZJRVctTUFTVEVS4oSiIFZSIFZJRVdFUh0xCCw9JWiRbT0qEAAASEIAAEhCAABIQgAASEJYATUpXA89OggpXA8-SOE6P1AAYAM (which displays as the Cardboard download site in a browser). The string you want is the long one starting with CgxNYXR. The field is base64 encoded.

The protocol buffer definition file is available at https://github.com/google/wwgc/blob/master/www/CardboardDevice.proto. Once you have the Protocol Buffers compiler built (protoc, from the tutorial link above), just run it on CardboardDevice.proto, and include the output .cc and .h files in your project. You can access the info through the DeviceParams type, after sending the decoded data to it.

For building the Protocol Buffers library and including it in your iOS project: https://gist.github.com/BennettSmith/9487468ae3375d0db0cc. Or if you have problems generating/linking the libraries, try this as an alternative: Google protocol buffers on iOS

Here's some code to get you started:

// Retrieve HEAD only (don't want the whole page) NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@", myURL]]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0f]; [request setHTTPMethod:@"HEAD"]; // Start the request [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { if (connectionError) { NSLog(@"%@", [connectionError localizedDescription]); } else { // Find the p attribute NSArray *comps = [[[response URL] query] componentsSeparatedByString:@"&"]; for (NSString *comp in comps) { NSArray *subComps = [comp componentsSeparatedByString:@"="]; if ([subComps count] == 2 && [subComps[0] isEqualToString:@"p"]) { NSString *base64 = subComps[1]; // Replace _ with /, - with +, and pad with = to multiple of 4 base64 = [base64 stringByReplacingOccurrencesOfString:@"_" withString:@"/"]; base64 = [base64 stringByReplacingOccurrencesOfString:@"-" withString:@"+"]; base64 = [base64 stringByPaddingToLength:(([base64 length]+3)/4)*4 withString:@"=" startingAtIndex:0]; // Decode from base 64 NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:base64 options:NSDataBase64DecodingIgnoreUnknownCharacters]; // Get the device parameters DeviceParams deviceParams; deviceParams.ParseFromArray([decodedData bytes], (int)[decodedData length]); // Do stuff with deviceParams // eg deviceParams.inter_lens_distance() break; } } } }];


FWIW, if you're using Swift and don't want a protobuf dependency you can use this to do the decoding.


  • android DRM problem
  • ViewData, ViewBag and TempData violates MVC? [closed]
  • Google app engine not accepting binary file upload anymore
  • Convert from hex-encoded CLOB to BLOB in Oracle
  • Finding number of samples in a .wav header
  • Exception while trying to make Hazelcast cluster work with JCache compliant client
  • SOAP with Attachment / MIME content
  • php DOMDocument - manipulating and encoding
  • HABTM associations in Rails : collecting and counting the categories of a model's children
  • Determining the length of a read stream in node js
  • WPF Run Animation in a separate Thread
  • Crafting a LINQ based solution to determine if a set of predicates are satisfied for a pair of colle
  • Android application not restoring state when installed from .apk, works fine from eclipse
  • c++ search a vector for element first seen position
  • Convert RSA pem key String to der byte[]
  • Serve file to user over http via php
  • incomplete type 'struct' error in C
  • Is there any purpose for h2-h6 headings in HTML5?
  • CSS - Cannot get one spanned style to override another inherited style and align left
  • what makes a request a new request in asp.net C#
  • During installation of Django, why do I keep getting ImportError: No module named django?
  • Suppressing passwd when calling sqlplus from shell script
  • pillow imaging ImportError
  • Using a canvas object in a thread to do simple animations - Java
  • System.InvalidCastException: Specified cast is not valid
  • iOS: Detect app start via notification press
  • Play WS (2.2.1): post/put large request
  • Read a local file using javascript
  • How to access EntityManager inside Entity class in EJB3
  • All Classes Conforming to Protocol Inherit Default Implementation
  • formatting the colorbar ticklabels with SymLogNorm normalization in matplotlib
  • Why is the size of this struct 32?
  • Apache 2.4 and php-fpm does not trigger apache http basic auth for php pages
  • How to redirect a user to a different server and include HTTP basic authentication credentials?
  • vba code to select only visible cells in specific column except heading
  • Websockets service method fails during R startup
  • AT Commands to Send SMS not working in Windows 8.1
  • Windows forms listbox.selecteditem displaying “System.Data.DataRowView” instead of actual value
  • apache spark aggregate function using min value
  • Sorting a 2D array using the second column C++