super I/O gargle blaster!

23 feb 2015

a common application for a microcontroller is to provide an electrical interface to the world outside a desktop/laptop computer. a nice idea in theory, but in practice, like a lot of things, the devil is in the details: how to think about and process what a sensor (or switch, or other outside-the-computer physical thing) is doing, and what data from it means. and then import that information into the computer (in other words, inter-computer communication).

as is common elsewhere, here at CalArts a common solution is to "roll your own" sensor/microcontroller/desktop communication paradigm and code it up, for each project. that's not the hard part; turning raw numbers from a sensor into actual information is.

with superIOgargleBlaster you can quite literally connect a simple sensor like a photoresistor, potentiometer, switch, etc to any analog pin and immediately -- zero code -- get easy to read event information from the arduino via USB serial. by default, all pins are inputs -- with no changes to the code each input reports events (changes) to the pin. unused inputs are simply silent.

you can easily reproduce the main features of the interesting-but-fatally-flawed Makey-Makey interfacer. let's call mine fakey makey.

superIOgargleBlaster supports most arduino boards. there is a compile-time option for Uno (and other "small" boards) vs. Mega 2560 (many more pins).

installation

the current version of the software is now on github. you will need to get the WPS_Timer and WPS_PID libraries also on github. links are below. all of these libraries are very small (and useful).

superIOgargleBlaster consists of an Arduino sketch (three tabs/source files) of the same name and a library called PID. these are installed in the same manner as all Arduino sketches and libraries are installed.

IMPORTANT NOTE: for any (any) arduino program, you only have to "burn" it once with the Arduino IDE (integrated development environment, the big fat program you downloaded and run on your laptop). the arduino retains the most-recent program you've uploaded ("burned") to it -- forever. you do not need to run the Arduino IDE to use an arduino -- only to modify the arduino's burned program. i emphasize this because about half the students i work with don't seem to realize this. once burned with your program, the arduino remembers it forever (even if unplugged) until you intentionally change it. this is the whole point of flash-programmed microcontrollers! maybe this seems unusual in the current era, where every action seems to require "permission" from some authority or something.

demonstration and test

the easiest way to check it out is to simply install and use it.

i'm using a spare Duemilanove board and a crappy photoresistor i found laying on my bench.
load the arduino IDE, and open the sketch. make sure your board type is set right. burn the code, etc.
stick the two leads into the arduino; one lead to any of the Analog Inputs (A3 was convenient for me) and the other lead to GND.
click to open the built-in serial monitor window. the program will announce itself and generate some messages about the state of the pins.
now wave your hand over the sensor; no i did not skip a step, there really is no further configuration needed.
note that in the serial monitor window a brief message, "A3=xx" appears every time you wave your hand over the sensor. superIOgargleBlaster constantly adjusts to the current ambient light level, whatever it is (dim room to full sunlight) and reports only changes to inputs, automatically. read the text for full explanation.

how it works

superIOgargleBlaster consists of three parts that you need to know about before you start. the first, and most important one, is the working paradigm, the plan, the mental model. it's easy, because I am lazy.

1 of 3: the I/O paradigm

you might imagine that there are some synergies to exploit here. you would be correct. superIOgargleBlaster code in the Arduino microcontroller monitors many sensor types, extracts information from them, and communicates changes to those sensor(s) back to the host computer. the host can also command the controller to Do Things. details follow.

2 of 3: arduino::processing communication

the lovely host/controller synergy is really great; however, mimicing the larger culture that produced these things, co-operation and communication is left as an afterthought, there are few tools and even fewer discussions as to how that might proceed, you're given some wonderfully complicated Stuff and left to your own devices (sic). people cobble up all sorts of awful and cumbersome solutions.

superIOgargleBlaster uses a simple but reliable and extensible communication scheme for the host and controller computers to talk to each other. since it's based on ASCII text, humans can read it too, and even type in data that the host or controller understands. this seemingly small detail has vast implications when designing, writing, debugging actual programs. you can simulate sensor data, or manually monitor sensors without a host. this affects workflow.

the scheme is this: simple text messages, arranged as short "sentences" are sent one at a time from controller to host, or host to controller, whenever necessary (aka asynchronously). each sentence contains a "command" and an "argument". for example:

A3=321

... says 'analog pin A3 says 321'. the technical details of the actual format of the sentence is described elsewhere, but it's fairly simple, and can be generated or read by any programming language easily enough; examples in Processing (java) follow.

communication is asynchronous, meaning data flies between host and controller at arbitrary times. the protocol allows for, but superIOgargleBlaster does not implement, flow control, so in theory it is possible to send too much data and have characters dropped on the ground. but the usual environment nearly guarantees this will not happen -- the controller is vastly less-capable than the host, and can't issue data (sentences) faster than the USB serial link is configured for, which is orders of magnitude slower than the host is capable of; conversely, the relationship between host and controller is such that rarely does the host send lots of data to the controller. host-to-controller data is usually incidental (configuring controller functions, etc) or slow (physical motion control eg. motors, servos, solenoids, the very things the host is terrible at). it is a bad idea, generally speaking, to try to use a controller for fast/big data applications (audio, video) and besides, those are things that the host has direct support for. all of this is the basis for the synergy between host and controller.

in any case, i've been using variants of this scheme for three decades in microcontroller work and it is simple, robust, extensible, easy to code and debug.

3 of 3: signal processing

a deep part of the background assumptions i'm making about sensor/controller/host relations, the base of the paradigm, is that you want the host to respond to changes, events, in the physical world. neither you, i, nor the host computer care if a switch is on or off; you want to know when it changes, because that change means something, eg. a person or other active agent or natural process acted.

this is very important: sensor data doesn't mean anything. the meaning is in our bodies and our heads, and implied in the rules (software) we write to meddle with the switches or sensors.

in fact I could go down a bottomless rabbit-hole discussing the two divergent paradigms of data-driven vs. event-driven computing models but I will refrain... suffice to say, the controller/host model is event-driven. we care when things happen -- physically. things with mass, things you can stub your toe on.

signal processing is a set of techniques through which data is interpreted to extract (generate...) information.

digital inputs

the Arduino has two kinds of input pins; so-called analog and digital. digital pins are easy, two states, on or off, perfect for sensors like switches, buttons, etc. digital inputs are fairly straightforward to signal-process: superIOgargleBlaster monitors them periodically (default: 50 times/second) and reports to the host when that pin changes (from 0 to 1, or 1 to 0). for things that do not change often (in human time scale) this frees the host from constantly looking at the switch; the controller just sends brief and occasional messages at each change.

analog inputs

many sensors however (most interesting ones) produce some electrical output that is proportional to some physical stimuli, eg. a photoresistor lowers it's electrical resistance when photons smack into it; more photons, lower resistance. the resolution (fineness of measureable change) is extremely small. the Arduino's analog input pins translate voltages applied to them into a ratio (a fraction), some number-of-1024ths of the maximum 5 volts (sometimes 3.3V). with a tiny bit of built-in trickery a photoresistor can be connected directly to an analog input, and the controller sees the sensor's "infinite" range reduced to 1024 possible values -- "good enough" for most any task.

extracting information from mere data

actually the photoresistor is a great example of the purpose, and need for, signal processing. so you've arranged a photoresistor onto an arduino, powered it up and read raw data values from it, say once per second, using this simple arduino program:

void setup () {
  Serial.begin (9600);
}

void loop() {
  Serial.println (analogRead (A0));
  delay (1000);
}

what might you see? something like this:

...
310
309
307
308
301
303
289
278
265
...

perfectly meaningless. specifically, there really is no meaning there. it's just data. not even information, because it's not situated anywhere -- why, for a specific example, 307? all that number means is that given the particular but arbitrary photosensor, whatever the amount of light impinging on it is (almost unknowable in itself; see below for why), some arbitrary number of electrons/second passed through it, and the analog input tells you three hundred thirty four/one thousand twenty fourths of five volts, eg. 334/1024 * 5V. NOT USEFUL.

what does it mean then? the value here (sic) is then in how the numbers change over time, not the numbers themselves. if the numbers decrease over time, one might (probably correctly) surmise that less and less light is impinging on the sensor over that time. even that's not all that interesting -- what is interesting is what is causing that change in light --if the light sensor were placed near a doorway, the drop in impinging light is likely a person walking past the sensor and causing a shadow. in that instance, the specific amount of light itself has no meaning. the change, from light, through shadow, to light, means -- to us, the human observer -- that a person walked by.

in the above example, the light, sensor, arduino, series-of-numbers is simply a way of seeing, a tool like a telescope or a blind person's red-tipped cane. it is a means to some end (since this is a silly example, we have no real application).

superIOgargleBlaster contains software that will in fact extract that information from a sensor such as a photoresistor -- it generates an event (reported by a sentence sent from controller to host) when light on the sensor drops quickly, and again when light increases. this specifically ignores any particular number. light that changes slowly over time is ignored. light that changes quickly generates an event. the specifics of "slowly" and "quickly" are tuneable, but the defaults are pretty good as-is for things moving at a human rate of speed and in light levels within a typically lit room.

(technically -- the analog signal is passed through a modified PID (Proportional Integral Derivative) algorithm, normally used for closed-loop industrial process control, but here modified to generate an output of zero. when the system is perturbed (shadow over sensor, as above) then PID self-adjusts to bring the output back to zero. the result of that adjustment is the event.)

(detailed description of the PID library is in progress nov 2013.)

outputs

superIOgargleBlaster initially makes all of the Arduino's pins into inputs. it is likely you want to control things too (LEDs, motors, whatever). you can send commands from the host to the controller to configure pins as outputs and then control those output pins with further commands.

the Arduino provides sort-of two kinds of output pins, digital (either high/low on/off 1/0, etc) and (i can't really force myself to call them this as it is incorrect) analog outputs, which are really PWM (Pulse Width Modulation) outputs. though not at all continuously-variable analog outs, for some purposes they are good-enough facsimiles. (what the blah blah Wikipedia page is trying but failing to tell you, is that the Arduino pin is turned on-and-off very rapidly, with the ON to OFF time proportion varied so that the average energy send to the Thing Being Controlled (LED, motor, etc) is the desired fraction; from 0 to 1/256th on to 255/256ths on. some electrical devices don't like that. LEDs and motors are fine with that.

anyway, superIOgargleBlaster accommodates PWM-capable pins. code on your host computer simply sends a command in essence saying "set pin X to value Y". done and done.

example host to controller communication in Processing

interestingly (at least to me) laptop/desktop computing has strayed so far from anything physical that the code on the Processing side of things is more fraught with foolishness and complexity than the Arduino side. taking the sentences apart on the Processing side involves messing with regexps (regular expressions), an innocuous name for a very difficult but very powerful software technique. luckily i've done the heavy lifting in the superIOGargleBlaster tuner program. please refer to that for examples, as it is more likely to be correct than this web page (i say this because the Processing 2.1 release broke the old "ReadStringUntil()" function, requiring a modified approach, robustly accommodated in the code).

commands to superIOgargleBlaster

commands are brief text "sentences" sent to the arduino via the USB serial connection. the commands are all plain text, easily typed on the keyboard or sent via an appropriate "print" statement from any programming language. the easiest way to get started is to use the Arduino programming environment's serial debug window.

examples below are explained in the detailed sections forther on, but just to show you what they look like: RD3=10 sets sample Rate of digital pin D3 to 10 milliseconds. OD11 digital pin D11 as an Output. PA6=0 turns the Pullup resistor on analog pin A6 off (0).

a further major convenience is that you can send any number of commands in one line/one "print" statement, as long as you remember to put a space between each command. for example the following is perfectly acceptable:

RA0=25 TA0=10 FA0=1 OD3 OD4

IMPORTANT NOTE: when typing any command manually into the debug window, you must remember to follow every command line with a space. this is because the debug window "eats" the newline (ENTER key) itself and does not send it to superIOgargleBlaster. sent via a programming language "write" function, the final character of a command can be a space or a newline.

analog pin responses

the analog input pins generate reports (responses) when the criteria configured for them is met. the default is PID event unless commanded otherwise, with a threshhold of 50 and a rate of 40 milliseconds (but refer to the current code for the actual values or set them yourself).

each input pin is read repeatedly at the set rate; 25mS is 40 times per second. the numeric value (0 - 1023) corresponding to the input voltage (0 - 5V) is passed through the algorithm. if the resulting calculated value is the same as the last time, no response is generated (only changes are ever reported). if the calculated value is less than the threshhold, no response is generated. this vastly reduces the amount of responses sent to Processing (or to the debug screen when testing manually).

an example might illuminate. imagine you have a simple light sensor attached to A0, and it is configured for PID magnitude (FA0=2). assume the threshhold is 10 (TA0=10) and that the sensor is in a typically lit room on a table. after waiting a second for everything to stabilize, the last thing that superIOgargleBlaster will output is:

A0=0

meaning: input A0 has stabilized, which means that the voltage applied to A0 is unvarying (meaning, the amount of light impinging on the sensor is steady) or at least the variations are below the threshhold. now hold your hand over the sensor causing a shadow. superIOgargleBlaster will report something like the following (the actual numbers will be different):

A0=20
A0=45
A0=64
A0=67
A0=54
A0=32
A0=15
A0=0

note that the actual numbers do not matter, the rate-of-change is what matters. "54" doesn't mean anything, by itself. but the over all trend of numbers is, it was 0, then it went up, and then went down to zero again. (your hand remains over the sensor).

what happened was this: the sensor had "some light" on it from the room ambient and it was steady (reported change is 0). when you first put your hand over it, there was a large and rapid change in the light (the shadow). PID magnitude calculates the rate of change and the magnitude of change... now after a large fraction of a second, the shadow is now steady and unchanging; therefore this is the "new normal" for PID, which only cares about changes. eventually it responds, again, with A0=0, meaning the new situation, which you and i know is a hand causing a shadow, is the new steady-state normal.

when you remove your hand, the same-but-opposite-sign happens. more light impinges, the voltage changes again, and superIOgargleBlaster reports something like:

A0=-9
A0=-16
A0=-25
A0=-48
A0=-66
A0=-61
A0=-51
A0=-39
A0=-22
A0=-15
A0=0

the "new situation" of increased light is an event -- in the opposite direction. (keep in mind that the computer is stone stupid, and knows no meaning, it just sees changes to a scalar value and calculates accordingly. you and i know causality (meaning) but code does not.) shadow-to-light is a big change, is reported thusly, but in less than a second, the light is (once again) the new normal and eventually reports A0=0.

on the Processing end, this is easier to write code to detect than looking at raw sensor values. it is smoothed and filtered and threshholded. the absolute value of the highest number is a relative measure of both how light-to-dark the light and shadow are, but for a given light situation, it also indicates how fast someone walked by -- the rate of change of light is reflected in the magnitude. the sign indicates darkening vs. lightening.

but! if all you care about is detecting when someone walks by, then specify PID event algorithm (FA0=1). PID event changes every positive non-zero value to 1, and every negative non-zero value to -1. but here is the critical feature -- because only changes are reported, in the above example, the entire light-to-shadow event reduces to a single response:

A0=1

now this is very easy to handle in code such as Processing. superIOgargleBlaster does all of the heavy lifting. the converse, shadow-to-light event, of course returns A0=-1. you can choose to ignore that event if you only need to know when someone passed by the sensor -- or pushed a switch, turned a knob, etc.

analog pin internal processing

this is a detailed description of specifically how, and in which order, the various internal operations are applied. each pin has a timer (sample rate), threshhold, and a function. when the timer time is reached, the raw value is read from the pin. then:

digital pin responses

since digital pins are only capable of on/off, 1/0, high/low, etc, responses are fairly simple and there are few settings for them. output pins don't generate responses (since you control them). only changes to inputs generates responses, so in essence digital input pins report events by default (similar to the behavior of analog pins PID event algorithm). this implies that the current state of the input pin is the same as the last-reported state of the pin.

the per-pin sample rate determines how quickly the arduino "looks" at the input pin, and the sample rate needs to be much faster than you expect the input to change, otherwise you will miss events. (eg. if you only look at the pin once a second, if it's turned on for only 1/2 second it is likely you will miss the event.)

D5=1 digital pin D5 just changed from 0 to 1, within the last sample-rate milliseconds.
D11=0 digital pin D11 just changed from 1 to 0, within the last sample-rate milliseconds.

command summary

digital and analog pins have different purposes and operational details, so there are separate commands for each.

commands below are shown with an example; of course substitute pin number and numeric value accordingly.

rate and threshhold are tunable parameters and will need to be determined experimentally by you. the defaults are fairly high, the effect of which is that "small" changes are not reported. this is intentional, and prevents superIOgargleBlaster from generating huge amounts of output about possibly meaningless events.

rate determines how often, in milliseconds, the pin is "looked at". the sample rate needs to be substantially faster than you expect the input voltage to change, or the arduino will not see the changes and will likely misinterpret things. a good rule of thumb is 10 samples per "event", which could be 100 milliseconds for slow-moving events (a person walking slowly causing a shadow to pass over a light sensor, for example).

analog pin commands

RA2=25 set sample rate for analog input A2 to be 25 milliseconds. values from 1 to 65535 are allowed, even if they are silly.
TA3=25 set analog pin A3's threshhold for magnitude of events to 25; changes smaller than this are not reported, eg. generate no event.
PA3=0 turn analog input A3's pullup resistor off. by default, they are on, which lets you stick a simple sensor in with no other components. even if you have more complex circuitry or a real circuit installed you can usually leave this on, but it's easy to turn off.
FA5=2 Configure analog input A5 as PID magnitude algorithm. each sample of the analog input pin is run through PID and the response (see below) indicates magnitude and sign of events which occur; the magnitude (absolute value of the response) is proportional to the speed and amount of the change; the sign (+ or -) indicates whether it is increasing or decreasing.
FA5=1 configure analog input A5 as PID event processing algorithm. this is the same as PID magnitude except that the output range is reduced to 0, 1, or -1; 0 means steady-state (no change), 1 is returned for all positive events, -1 for all negative-going events. the threshhold value for this input is applied internally before this reduction to 1 or -1.
FA5=0 configure analog input pin A5 as the average of the analog value. this returns the value read from the pin averaged over the last 32 readings (but refer to the code for the exact number). this filters out noise and small changes, such as when reading potentiometers, etc.

digital pin commands are fairly simple. digital pins can be configured as inputs (default) or outputs.

IMPORTANT NOTE: digital pins 0 and 1 are not generally available for arduino programs. those are used for the USB serial port send and receive lines. since superIOgargleBlaster uses USB serial access to them is not provided here.

RD2=250 set sample rate for digital input D2 to be 250 milliseconds. values from 1 to 65535 are allowed, even if they are silly.
OD13 configure digital pin D13 to be an output. you will need to issue this command for every pin you want to use as an output.
ID13 configure pin D13 to be an input, which is the default setting, hence rarely used, and only if you've previously commanded that pin to be an output.
D13=1 assuming you have previously commanded the pin to be an output, this command sets digital output pin D13 high.
D2=0 assuming you have previously commanded the pin to be an output, this command sets digital output pin D2 low.
PD7=0 turn digital input pin D7's internal pullup resistor off.
PD6=1 turn digital input pin D6's internal pullup resistor on (default setting).

miscellaneous commands

/ print a nicely readable summary of all of the pin settings.
! once you have configured all of the pins to your liking for your project and particular set of sensors, you can save the settings in the arduino's built-in EEPROM memory, so that when you reset or power on the arduino the settings are immediately restored. please see the EEPROM section for details.write current settings to EEPROM and print out a string of commands that also configures the superIOgargleBlaster to the current state. note that this command also prints out an equivalent string of commands that you could cut-and-paste into superIOgargleBlaster manually, or send via a "write" command in your favorite programming language. it's perfectly acceptable to change settings "on the fly" within your program.
Z reset everything to original, as-programmed state. of course no one ever makes mistakes, but if you ever do, you can reset the settings to the default state; all pins inputs, threshhold and rates set to default, etc,with the Z command.
Q re-send all pin current states. since reports are only sent when pin(s) change, under certain circumstances your program might lose track of the current state of the pins. Q forces a report for all pins.
? this command prints out a brief (too brief) summary of commands.
+ prints out the number of analog inputs and the software version. used by the tuner program.
$ this dumps the settings for all analog inputs, one line each for sample rate, threshhold and function. not very pretty. used by the tuner program.

fakey makey

sorry, terrible name. but hey!

the makey makey is a sort-of nice idea, though a little simplistic (and more than a little condescending) (and overpriced) (and proprietary, definitely not in the spirit of open source). makey-makey uses a very simple input circuit, passing a microscopic current through anything moderately conductive (fruit, people, clay etc). in a structured way, it turns a simple physical bodily event (touched fruit) into a message that a computer can use. sadly, it's only usable as a "HID" device such as a keyboard; its output can't be mapped into a program to do some specific thing. as long as it's connected the computer's local keyboard function is activated all the time (eg. touching the banana while doing ecommerce is not recommended).

it's very simple to do this in superIOgargleBlaster. for each input pin you'd like to use for a "fakey makey" touch input, turn off the pullup resistor (eg. PA0=0) then connect a 22 megohm resistor from each input pin to +5V. then connect your input banana to the input and ground as per makey-makey. the default signal processing will work as described above somewhere.