Allows mbed to send data to an HTML5 web browser through a 4 pole mini jack.

Dependents:   MicIO-Example

The Problem:

*Sending data from a microcontroller to any smartphone*

While you can use the usb interface on non-mobile devices, very few smartphones allow you to use this easily. For example iOS requires the purchasing of a 100k+ liscense.

Then there's bluetooth with its gazillions of native code libraries to hook into: OS X, Windows, Linux, iOS, Motorolla, HTC, Samsung, LG,....ew.

With HTML5's web audio API, we can create a hardware bus similar to how Square's Credit Card readers works.

I've successfully tested this with my Macbook Air with Firefox v28 and Chrome v33. IE will not work under any circumstances as it currently does not support the HTML5 Web Audio API.

Protocol Overview

Sending a data playoad

Javascript is asynchronous, and setIntervals can vary by +/- a few ms. The way around this is to create a master slave bus. Our JavaScript master dictates to the microcontroller slave when it wants data through a square wave clock signal. When the square wave goes from High (1) to low (0) (falling edge), the microcontroller should be sending the data payload. Here's a screenshot of this in action: http://colinbookman.com/content/images/2014/Mar/Screen_Shot_2014_03_23_at_4_12_32_PM.png

And here's is one of the sinusoids zoomed in: /media/uploads/cbookman3/screen_shot_2014-03-24_at_11.13.43_am.png

This is a video of the bus in action:

Each data playload is a sinusoid. As of right now the library generates 16 distinct frequencies, where each frequency represents a number from 0 to 15.

The MicIO in its current form has an error rate of 5%, and transfer rate of 16bits/sec. This can easily be sped up by using a faster microcontroller, as well as modulating sinusoids in the payload. For example 0xFF would be represented by: sin(2π*1000) + sin(2π*1500) +sin(2π*2000).

Parsing a data payload

HTML5's web audio api allows us to perform a mathematical operation called a Fast Fourier Transform (FFT - http:en.wikipedia.org/wiki/Fast_Fourier_transform. FFT's basically allow us to parse out the frequencies in our audio stream. Below is a table converting hex/decimal to its payload sinusoid frequency.

Hex | Sinusoid Frequency (Hz)

0x0 | 818

0x1 | 1076

0x2 | 1335

0x3 | 1335

0x4 | 1894

0x5 | 2153

0x6 | 2411

0x7 | 2670

0x8 | 2971

0x9 | 3229

0xA | 3488

0xB | 3746

0xC | 4048

0xD | 4306

0xE | 4565

0xF | 4823

NULL | < 500 or >5500

Wiring

4 pole mini jack

http://colinbookman.com/content/images/2014/Mar/4poleminiJack.jpg

1. Left Music = Clock In/Out 2. Right Music = Master Data not yet implemented 3. Ground 4. Microphone In = Slave Data

Currently, MicIO does not support the sending of data to the microcontroller, but it'd be trivial to add in future versions.

Here is the actual wiring schematic with the mbed NXP LPC1768: https:www.sparkfun.com/products/9564. Do to my cad software not having a 4 pole mini jack, Its wired up to two 3 pole mini jacks. The left minijack should go to the mic prong, where-as the right minijack goes to left Music. http://colinbookman.com/content/images/2014/Mar/micIO_Schematic.png

Software

Slave - MBED

The Code repo can be found on the mbed webpage (http:mbed.org/users/cbookman3/code/MicIO/ I've also published an example micIO application http:mbed.org/users/cbookman3/code/MicIO-Example/.

Basically it takes in a string array (or number). Then micIO sends the data in half byte payloads each time the master requests more data. If there is no more data, it simply does not generate any sinusoids, aka frequency of 0.

Master

This code can be found on https:github.com/cobookman/HTML5.MicIO.

The HTML5 MicIO library when instantiated, will try to bind to the microphone. Upon sucessful binding, it'll begin to request for data. There's two javascript files you must include:

  • js/clock.js - Generates a square wave clock for MicIO w/some helpers
  • js/index.js - The MicIO library

make sure that the volume on your computer/mobile device is all the way up.

To create a new MicIO master instance you simply run:

Example javascript master usage

var micIO = new MicIO(function onDataRecieved(halfByteArr) {
  //do stuff with the data E.g:
  var byteArr = [];
  for(var i = 0; i < halfByteArr.length; ++i) {
    var byteIndex = Math.floor(i/2);
      if(i%2 === 0) {		      //first half of the byte
	byteArr[byteIndex] = (halfByteArr[i] << 4) & 0xF0;
      } else {  			//second half of the byte
         byteArr[byteIndex] += halfByteArr[i] & 0x0F;
      }
    }
 });
Committer:
cbookman3
Date:
Sun Mar 23 20:43:55 2014 +0000
Revision:
2:c90f916f0b08
Parent:
1:1dfc4deed2cb
Child:
3:1630409f9bd6
fixed a few things;

Who changed what in which revision?

UserRevisionLine numberNew contents of line
cbookman3 0:294495efee3f 1 #include "MicIO.h"
cbookman3 0:294495efee3f 2 #include "TextLCD.h"
cbookman3 2:c90f916f0b08 3 TextLCD lcd(p24, p25, p26, p27, p28, p29);
cbookman3 2:c90f916f0b08 4
cbookman3 0:294495efee3f 5 MicIO::MicIO(PinName micOut, PinName clockIn) : _micOut(micOut), _clockIn(clockIn) {
cbookman3 2:c90f916f0b08 6 clockPeriod = 0.25;
cbookman3 0:294495efee3f 7 wait(0.015); // Wait 15ms to ensure powered up
cbookman3 0:294495efee3f 8 _genSinTable(); //Generate the sin table
cbookman3 0:294495efee3f 9 }
cbookman3 0:294495efee3f 10
cbookman3 0:294495efee3f 11 /* Send byte array through micIO */
cbookman3 0:294495efee3f 12 void MicIO::send(const char * inputStr, int length) {
cbookman3 0:294495efee3f 13 unsigned char * outputStr;
cbookman3 0:294495efee3f 14 outputStr = (unsigned char*) inputStr;
cbookman3 0:294495efee3f 15 int numberOf4BitPairs = length * 2;
cbookman3 0:294495efee3f 16 int current4BitIndex = 0;
cbookman3 0:294495efee3f 17 int fourBits;
cbookman3 0:294495efee3f 18 float sinSeed; //default at something ridculously high to stop an inf loop occuring
cbookman3 0:294495efee3f 19 int clockState = clock(); //default the clock state to correct value
cbookman3 0:294495efee3f 20
cbookman3 0:294495efee3f 21 /*
cbookman3 0:294495efee3f 22 Parse out the 4 bits we're sending this clock cycle
cbookman3 0:294495efee3f 23 */
cbookman3 0:294495efee3f 24 if(current4BitIndex %2 == 0) { //upper 4 bits
cbookman3 0:294495efee3f 25 fourBits = upper4Bits(outputStr[current4BitIndex/2]);
cbookman3 0:294495efee3f 26 } else { //lower 4 bits
cbookman3 0:294495efee3f 27 fourBits = lower4Bits(outputStr[current4BitIndex/2]);
cbookman3 0:294495efee3f 28 }
cbookman3 0:294495efee3f 29 sinSeed = _getSinSeed(fourBits); //get new sinSeed
cbookman3 0:294495efee3f 30
cbookman3 0:294495efee3f 31
cbookman3 0:294495efee3f 32 while(current4BitIndex < numberOf4BitPairs) {
cbookman3 1:1dfc4deed2cb 33 if(clock() == 0) { //Clock changed to low
cbookman3 0:294495efee3f 34
cbookman3 0:294495efee3f 35 /*
cbookman3 0:294495efee3f 36 Output sinusoid pulse
cbookman3 0:294495efee3f 37 */
cbookman3 0:294495efee3f 38 _sendSin(sinSeed);
cbookman3 0:294495efee3f 39 /*
cbookman3 0:294495efee3f 40 We're at the next clock cycle, and recieved a low.
cbookman3 0:294495efee3f 41 master done reading the sinusoid. Lets move on to the next 4bits
cbookman3 0:294495efee3f 42 */
cbookman3 0:294495efee3f 43 clockState = 0;
cbookman3 0:294495efee3f 44 ++current4BitIndex;
cbookman3 0:294495efee3f 45 /*
cbookman3 0:294495efee3f 46 Parse out the next 4 bits we're sending this clock cycle
cbookman3 0:294495efee3f 47 */
cbookman3 0:294495efee3f 48 if(current4BitIndex %2 == 0) { //upper 4 bits
cbookman3 0:294495efee3f 49 fourBits = upper4Bits(outputStr[current4BitIndex/2]);
cbookman3 0:294495efee3f 50 } else { //lower 4 bits
cbookman3 0:294495efee3f 51 fourBits = lower4Bits(outputStr[current4BitIndex/2]);
cbookman3 0:294495efee3f 52 }
cbookman3 2:c90f916f0b08 53 lcd.printf("%i",fourBits);
cbookman3 0:294495efee3f 54 sinSeed = _getSinSeed(fourBits); //get new sinSeed
cbookman3 0:294495efee3f 55 }
cbookman3 0:294495efee3f 56 }
cbookman3 0:294495efee3f 57 }
cbookman3 0:294495efee3f 58 /* Read the current value of the input clock */
cbookman3 0:294495efee3f 59 int MicIO::clock() {
cbookman3 0:294495efee3f 60 static int clockState = 0; //start clock @ low
cbookman3 2:c90f916f0b08 61 float average = 0;
cbookman3 2:c90f916f0b08 62 for(int i =0; i < 10; ++i) {
cbookman3 2:c90f916f0b08 63 average += _clockIn.read();
cbookman3 2:c90f916f0b08 64 }
cbookman3 2:c90f916f0b08 65 average = average/10;
cbookman3 0:294495efee3f 66 //Foce there to be high->low->high->low pattery w/clockState static int
cbookman3 0:294495efee3f 67 if(average > 0.6 && clockState == 0){ //actually ~0.71
cbookman3 0:294495efee3f 68 clockState = 1;
cbookman3 0:294495efee3f 69 return 1;
cbookman3 0:294495efee3f 70 } else if (average < 0.485 && clockState == 1) { //actually ~0.43
cbookman3 0:294495efee3f 71 clockState = 0;
cbookman3 0:294495efee3f 72 return 0;
cbookman3 0:294495efee3f 73 }
cbookman3 0:294495efee3f 74 return -1; //NO CHANGE TO CLOCK
cbookman3 0:294495efee3f 75 }
cbookman3 0:294495efee3f 76 /* Extracts the lower 4 bits of a byte */
cbookman3 0:294495efee3f 77 unsigned char MicIO::lower4Bits(unsigned char byte) {
cbookman3 0:294495efee3f 78 return (byte & 0x0F);
cbookman3 0:294495efee3f 79 }
cbookman3 0:294495efee3f 80 /* Extracts the upper 4 bits of a byte */
cbookman3 0:294495efee3f 81 unsigned char MicIO::upper4Bits(unsigned char byte) {
cbookman3 0:294495efee3f 82 return (byte >> 4) & 0x0F;
cbookman3 0:294495efee3f 83 }
cbookman3 0:294495efee3f 84 /* Send a sin Wave - bursts of 40 cycles*/
cbookman3 0:294495efee3f 85 void MicIO::_sendSin(float sinSeed) {
cbookman3 2:c90f916f0b08 86 int cycles = _numCycles(sinSeed);
cbookman3 2:c90f916f0b08 87 for(int c = 0; c < cycles; ++c) {
cbookman3 0:294495efee3f 88 for(float i = 0; i < 360; i+=sinSeed) {
cbookman3 0:294495efee3f 89 _micOut = _sinTable[(int) i];
cbookman3 0:294495efee3f 90 }
cbookman3 0:294495efee3f 91 }
cbookman3 0:294495efee3f 92 _micOut = 0.5; //Null Output
cbookman3 0:294495efee3f 93
cbookman3 0:294495efee3f 94 }
cbookman3 0:294495efee3f 95 /* Generate a sin Table */
cbookman3 0:294495efee3f 96 void MicIO::_genSinTable() {
cbookman3 0:294495efee3f 97 for(int i = 0; i < 361; ++i) {
cbookman3 0:294495efee3f 98 float temp = i;
cbookman3 0:294495efee3f 99 _sinTable[i] = sin(temp/180*3.141)*0.5+0.5;
cbookman3 0:294495efee3f 100 }
cbookman3 0:294495efee3f 101 }
cbookman3 0:294495efee3f 102 /* Go from 4 bits to a sin seed */
cbookman3 0:294495efee3f 103 float MicIO::_getSinSeed(unsigned char bits4) {
cbookman3 0:294495efee3f 104 /*
cbookman3 0:294495efee3f 105 By having sinSeed @ 1024 by default, if for some reason its not assigned, graceful
cbookman3 0:294495efee3f 106 degradation occurs.
cbookman3 0:294495efee3f 107 */
cbookman3 0:294495efee3f 108 float sinSeed = 1024; //Default it to a large number for graceful degregation.
cbookman3 0:294495efee3f 109 switch(bits4 & 0xF) { //switch just the 4 bits (force to 4 bits)
cbookman3 0:294495efee3f 110 case 0x0 : sinSeed = 0.75; break;
cbookman3 0:294495efee3f 111 case 0x1 : sinSeed = 1.00; break;
cbookman3 0:294495efee3f 112 case 0x2 : sinSeed = 1.25; break;
cbookman3 0:294495efee3f 113 case 0x3 : sinSeed = 1.50; break;
cbookman3 0:294495efee3f 114 case 0x4 : sinSeed = 1.75; break;
cbookman3 0:294495efee3f 115 case 0x5 : sinSeed = 2.00; break;
cbookman3 0:294495efee3f 116 case 0x6 : sinSeed = 2.25; break;
cbookman3 0:294495efee3f 117 case 0x7 : sinSeed = 2.50; break;
cbookman3 0:294495efee3f 118 case 0x8 : sinSeed = 2.75; break;
cbookman3 0:294495efee3f 119 case 0x9 : sinSeed = 3.00; break;
cbookman3 0:294495efee3f 120 case 0xA : sinSeed = 3.25; break;
cbookman3 0:294495efee3f 121 case 0xB : sinSeed = 3.50; break;
cbookman3 0:294495efee3f 122 case 0xC : sinSeed = 3.75; break;
cbookman3 0:294495efee3f 123 case 0xD : sinSeed = 4.00; break;
cbookman3 0:294495efee3f 124 case 0xE : sinSeed = 4.25; break;
cbookman3 0:294495efee3f 125 case 0xF : sinSeed = 4.50; break;
cbookman3 0:294495efee3f 126 }
cbookman3 0:294495efee3f 127 return sinSeed;
cbookman3 0:294495efee3f 128 }
cbookman3 2:c90f916f0b08 129 /*
cbookman3 2:c90f916f0b08 130 number of cycles sinusoid needs to run...kind of guesswork
cbookman3 2:c90f916f0b08 131 */
cbookman3 2:c90f916f0b08 132 int MicIO::_numCycles(float sinSeed) {
cbookman3 2:c90f916f0b08 133 float quarterPeriod = clockPeriod/2;
cbookman3 2:c90f916f0b08 134 float timePerSinusoidPeriod = 1/(sinSeed*1000);
cbookman3 2:c90f916f0b08 135 return static_cast<int>((quarterPeriod/timePerSinusoidPeriod)); //floor to integer
cbookman3 2:c90f916f0b08 136 }