CREDIT
This project is basically a port of the work done by
Robots Everywhere
mkb(at)libero.it
http://www.robots-everywhere.com/re_wiki/pub/web/Cookbook.AudioSerial.html
Porting by
Justin Brooks
jwbrooks(at)users.sourceforge.net
PURPOSE
Device with modern web browser (HTML5 + JavaScript) and headphone jack -> Serial input on MCU/MPU
Check the website for Robots Everywhere above for a schematic of how to interface with your MCU/MPU.
I wanted to make the functionality from Robots Everywhere more
accessible for everyone. All modern browsers for PC, Mac, Linux, and Android
support HTML5 and JavaScript, which includes everything we need.
USAGE
In your webpage's JavaScript, you write something like this.
var s = new Serial();
var msg = 'Hello world.';
s.send(msg);
// msg must be a string or you'll get runtime errors
var data = [ ... ];
s.sendBytes(data);
// data must be an [] of numbers (by JavaScript definition)
// the least significant 8 bits of data[i] will be sent
By default, transmission is 9600,8,N,1. Please note that most phones have a sample rate of 48000.
Baud rates that divide into the sample rate evenly work best. Max baud = sample rate / 2. You can
adjust the baud by saying
s.baud = 24000;
Your MCU/MPU will receive a byte[]. If you are working with strings, you'll see something like this.
<garbled random bytes>UUUUUUUUUUUUUHello world.<null>
This is the result of fighting the system every step of the way. None of the hardware
involved was designed for this to work. It is an abuse of design that it works at
all. More details included in createWaveform() if interested. The short version is that
you can't start transmitting your first byte's frame right away (you have to stabilize the
line first), and you can't start transmitting one byte's frame immediately after the previous
byte's frame (you have to leave a dead time between the end of one and the start of the next.)
You cannot say, "My message is 10 characters, which is 80 bits (true), 100 bits formatted for 8N1 (true),
so at 9600 baud it will take 1/96 seconds (wrong.)" You have to factor the dead time between
transmission frames into the picture. Typically, it doesn't matter, I only mention it incase your
project leads you down that rabbit hole.
ENCODING NOTES
You may want to pick some control characters, something that wouldn't appear in your content otherwise.
This will let you parse it more easily on the MCU/MPU end. For strings, this is easy, especially since
most characters are between 32-126. Ranges 0-31 and 127-255 are wide open.
start character (';' = 59)
field delimiter ('=' = 61)
end character ('?' = 63, or <null> if you want the end of transmission to auto-end)
If you need the full range of 0-255 available for use, you may need to homebrew your own header format.
Here are some fields you may wish to consider using.
mode (does this transmission contain bytes or ASCII?)
length of message (if your MCU/MPU parsing function assumes this field is always 8/16/32 bits (parseInt), you can skip the next field)
length of length field (if your length field is dynamic in length, you have to specify how many bytes it takes up)
Regardless of whether you choose to use control characters or headers or nothing, you'll need to manage it in your application layer.
(Add them to your content before sending.) This Serial class only provides the transmission layer.
Control Characters
msg = startCharacter + msg + endCharacter;
s.send(msg);
data.unshift(startCharacter.charCodeAt(0)); // or .unshift(startByte);
data.push(endCharacter.charCodeAt(0)); // or .push(endByte);
s.sendBytes(data);
Headers
mode = 1; // whatever you want it to mean
length = data.length;
header = [mode, length, whyNotAdd | SomeFlags]; // may be reusable in your case
s.sendBytes(header.concat(data));
FURTHER DEVELOPMENT
Including a set of defaults for popular models so you could create a Serial object by saying
var s = new Serial('Pixel'); // or 'SamsungS4' or 'iPhone4s'
and settings would be adjusted according to what works best for those models. Not sure if this
is even an issue. Let me know if you find better presets and what device you were using. It's
assumed that testing is done at full volume.
Could make it work for bitdepths other than 8, but that would be super niche and anyone working with
that could probably tweak this themselves.
Could add the ability to specify left or right channel in case someone wants to control two UARTs with one jack.
Looking to implement mic -> rx for 2.0.
https://source.android.com/devices/accessories/headset/plug-headset-spec