Output devices

Project idea: using iPhone to charge, program and communicate with sensor peripherals, such as the EEG monitor that Charles and I are developing

Inspiration:
http://web.eecs.umich.edu/~prabal/projects/hijack/
http://code.google.com/p/hijack-main/

These documents basically describe how to do it:
http://cdn.energymicro.com/dl/an/pdf/an0054_efm32_phone_audio_jack_interface.pdf
http://web.eecs.umich.edu/~prabal/pubs/papers/kuo10hijack-islped.pdf

So I'm basically making a Fab-HiJack EEG monitor. Hopefully this will reveal the principles to allow people to easily build this capability into their devices and help the Fab network grow more tendrils in low-cost physiological / biological sensing.

Goal is to:

1) power FabECG and FabSampler hybrid board from iPhone audio jack
2) sample data from FabECG and FabSampler hybrid board from iPhone audio jack
3) program these boards via the iPhone audio jack

For number one, powering a micro-controller via the iPhone audio jack, I need a micro-transformer for this, which I don't have at the moment. So we'll have to just send data from an externally powered board this week. 3.5 mm plug fits in the iPhone jack. Therefore what we'll do is make a version of the FabSampler board that outputs over FSK-modulated audio instead of just over serial.

The basic way of doing this from an Arduino using FSK modulated audio is described here: http://arduino.cc/forum/index.php/topic,19648.0.html

I plan to adapt this for the Tiny44 using the David Mellis strategy of programming the Tiny44 using Arduino code and the Arduino IDE.

What we're going to do is take FabSampler board from Week 7 and replace the RGB LED with a FSK output channel to communicate with the iphone.

Removing the RGB LED frees up PAO, PA1 and PA2 on the ATtiny44 also known as pins 13, 12 and 11.






We can change which pin produces FSK output by editing the SoftModem.cpp file in the Softmodem library:

from #define SOFT_MODEM_TX_PIN (3) to #define SOFT_MODEM_TX_PIN (0) in this case.

To implement that, we'll follow this page on how to to modify one of the built in Arduino libraries:

http://www.ladyada.net/library/arduino/libraries.html

The libraries folder for Arduino on my Mac OSX is hidden inside the arduino app itself:

/Applications/Arduino.app/Contents/Resources/Java/Libraries

Couldn't see the softmodem library built in so downloaded from here:

http://code.google.com/p/arms22/downloads/detail?name=SoftModem-004.zip&can=2&q=

Got this error when compiling:

In file included from FabSamplerFSK_v1.cpp:2:
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.h:72: error: conflicting return type specified for 'virtual void SoftModem::write(uint8_t)'
/Applications/Arduino.app/Contents/Resources/Java/hardware/arduino/cores/arduino/Print.h:48: error: overriding 'virtual size_t Print::write(uint8_t)'

So using the patched version of SoftModem. But this gave a different set of errors:

/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp: In member function 'void SoftModem::begin()':
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:109: error: 'TCNT2' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:112: error: 'TCCR2A' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:113: error: 'TCCR2B' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp: In member function 'void SoftModem::end()':
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:120: error: 'TIMSK2' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:120: error: 'OCIE2A' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp: In member function 'void SoftModem::demodulate()':
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:139: error: 'TCNT2' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:142: error: 'TIFR2' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:142: error: 'TOV2' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:170: error: 'OCR2A' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:171: error: 'TIFR2' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:171: error: 'OCF2A' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:172: error: 'TIMSK2' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:172: error: 'OCIE2A' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp: In member function 'void SoftModem::recv()':
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:235: error: 'TIMSK2' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:235: error: 'OCIE2A' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp: In function 'void TIMER2_COMPA_vect()':
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:247: error: 'OCR2A' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp: In member function 'void SoftModem::modulate(uint8_t)':
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:284: error: 'OCR2B' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:285: error: 'TIFR2' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:285: error: 'OCF2B' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:290: error: 'OCR2B' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:291: error: 'TIFR2' was not declared in this scope
/Applications/Arduino.app/Contents/Resources/Java/libraries/SoftModem/SoftModem.cpp:291: error: 'OCF2B' was not declared in this scope

Finally tried SoftModem 005 from here: http://code.google.com/p/arms22/

Same issue.

With Arduino 1.0, but not 1.0.1, after I made a bunch of replacements of the register names as shown in the edited softmodem.cpp file, I was able to compile the program. More changes would have been necessary with Arduino 1.0.1. In general what I did was to convert things with a 2 to things with a 0, for instance OCF2B to OCF0B.

Here is the final copy of the Softmodem.cpp code for the SoftModem library that I used, whereas the header file was unchanged: SoftModem library for programming Tiny44 with Arduino 1.0

I first verified that this board works as a FabSampler over RS-232 serial with the computer by inputting analog waves from a function generator and verifying that these waves appear as integer digital samples on the terminal output via FTDI cable. Here the FTDI cable supplies power to an Arduino which creates the 3.3V Vin signal for the Tiny44 board, and also supplies ground and an RS232 serial Rx line to the computer.



Next, I hooked up the audio plug output to an oscilloscope and verified that it could produce FSK.



First it output an FSK signal representing an integer, then waited for one second using the delay() function, and then looped again ad infinitum.



Next the board digitally sampled input square waves output from a function generator, and wrote the corresponding integer values over FSK using the below code. We could see FSK modulation patterns with a period corresponding to that of the input square waves.






I next took a spectrogram using Audacity on the microphone input line to a desktop computer running Ubuntu, while manually changing the period of the input square wave over time using the function generator knob in the following sequence of Hz values: off, ~0, 1, 2,4, 8, 4, 2, 1, 0, off



This picture shows just a short segment of that spectogram where the square wave period was getting shorter:


You can also hear the raw audio output which sounds quite like an old dial-up modem: mp3

The input square waves were around 2.5V or so, into the AVR. Where I believe the ADC was referenced by default such that 5V = 1024. Note that the softmodem library takes 8bit unsigned integers as the argument to the write() function.

Therefore the next step is two write to the FSK modem using individual characters and come up with a one-higher-level coding scheme for integer samples that the iPhone / Android can understand. And then implement that on the iPhone / Android side as well. This will be the subject of next week.
Probably it will use something like this on the Android side: http://code.google.com/p/androino/wiki/AndroinoTerminal


Code: note I've taken out the bracket characters from the #include lines for HTML display

#include SoftwareSerial.h
#include SoftModem.h

#define rxPin 8
#define txPin 3
#define sensor1 7
#define sensor2 6

#define FSKpin 0

SoftModem modem;

SoftwareSerial mySerial(rxPin, txPin); //rx, tx

// the Tx and Rx pins are 5 and 10 on the ATtiny which in Arduino map to pin 8 and pin 3 of arduino

// the setup routine runs once when you press reset:
void setup() {
pinMode(sensor1, INPUT);
pinMode(sensor2, INPUT);
pinMode(txPin, OUTPUT);
pinMode(rxPin, INPUT);
pinMode(FSKpin, OUTPUT);
mySerial.begin(9600);
modem.begin();
}

// the loop routine runs over and over again forever:
void loop() {
int a = analogRead(sensor1);
//mySerial.println(a); // leave either the serial or the modem commented because they seem to interfere
modem.write(a);
}

Board file:
board file

Schematic:
schematic file




Since this is output devices week, not FSK week, I can also post earlier explorations with the Arduino controlling a stepper motor via the motor shield, with code and info harvested from various online sources:



http://www.instructables.com/id/Arduino-Motor-Shield-Tutorial/step6/Stepper-Motor/

Function Channel A Channel B
Direction Digital 12 Digital 13
Speed (PWM) Digital 3 Digital 11
Brake Digital 9 Digital 8
Current Sensing Analog 0 Analog 1

http://arduino.cc/en/Main/ArduinoMotorShieldR3

Use an external power supply. Don't power with more than 12V.

*************************************************************
Motor Shield Stepper Demo
by Randy Sarafan

For more information see:
http://www.instructables.com/id/Arduino-Motor-Shield-Tutorial/

*************************************************************

int delaylegnth = 30;

void setup() {

//establish motor direction toggle pins
pinMode(12, OUTPUT); //CH A -- HIGH = forwards and LOW = backwards???
pinMode(13, OUTPUT); //CH B -- HIGH = forwards and LOW = backwards???

//establish motor brake pins
pinMode(9, OUTPUT); //brake (disable) CH A
pinMode(8, OUTPUT); //brake (disable) CH B
}

void loop(){

digitalWrite(9, LOW); //ENABLE CH A
digitalWrite(8, HIGH); //DISABLE CH B

digitalWrite(12, HIGH); //Sets direction of CH A
analogWrite(3, 1023); //Moves CH A

delay(delaylegnth);

digitalWrite(9, HIGH); //DISABLE CH A
digitalWrite(8, LOW); //ENABLE CH B

digitalWrite(13, LOW); //Sets direction of CH B
analogWrite(11, 1023); //Moves CH B

delay(delaylegnth);

digitalWrite(9, LOW); //ENABLE CH A
digitalWrite(8, HIGH); //DISABLE CH B

digitalWrite(12, LOW); //Sets direction of CH A
analogWrite(3, 1023); //Moves CH A

delay(delaylegnth);

digitalWrite(9, HIGH); //DISABLE CH A
digitalWrite(8, LOW); //ENABLE CH B

digitalWrite(13, HIGH); //Sets direction of CH B
analogWrite(11, 1023); //Moves CH B

delay(delaylegnth);

}

Forward motion:

int delaylegnth = 500;

void setup() {

//establish motor direction toggle pins
pinMode(12, OUTPUT); //CH A -- HIGH = forwards and LOW = backwards???
pinMode(13, OUTPUT); //CH B -- HIGH = forwards and LOW = backwards???

//establish motor brake pins
pinMode(9, OUTPUT); //brake (disable) CH A
pinMode(8, OUTPUT); //brake (disable) CH B
}

void loop(){

digitalWrite(9, LOW); //ENABLE CH A
digitalWrite(8, HIGH); //DISABLE CH B

digitalWrite(12, HIGH); //Sets direction of CH A
analogWrite(3, 1023); //Moves CH A

delay(delaylegnth);

digitalWrite(9, HIGH); //DISABLE CH A
digitalWrite(8, LOW); //ENABLE CH B

digitalWrite(13, LOW); //Sets direction of CH B
analogWrite(11, 1023); //Moves CH B

delay(delaylegnth);

digitalWrite(9, LOW); //ENABLE CH A
digitalWrite(8, HIGH); //DISABLE CH B

digitalWrite(12, LOW); //Sets direction of CH A
analogWrite(3, 1023); //Moves CH A

delay(delaylegnth);

digitalWrite(9, HIGH); //DISABLE CH A
digitalWrite(8, LOW); //ENABLE CH B

digitalWrite(13, HIGH); //Sets direction of CH B
analogWrite(11, 1023); //Moves CH B

delay(delaylegnth);

}

Reverse motion:

int delaylegnth = 500;

void setup() {

//establish motor direction toggle pins
pinMode(12, OUTPUT); //CH A -- HIGH = forwards and LOW = backwards???
pinMode(13, OUTPUT); //CH B -- HIGH = forwards and LOW = backwards???

//establish motor brake pins
pinMode(9, OUTPUT); //brake (disable) CH A
pinMode(8, OUTPUT); //brake (disable) CH B
}

void loop(){

digitalWrite(9, LOW); //ENABLE CH A
digitalWrite(8, HIGH); //DISABLE CH B

digitalWrite(12, HIGH); //Sets direction of CH A
analogWrite(3, 1023); //Moves CH A

delay(delaylegnth);

digitalWrite(9, HIGH); //DISABLE CH A
digitalWrite(8, LOW); //ENABLE CH B

digitalWrite(13, HIGH); //Sets direction of CH B
analogWrite(11, 1023); //Moves CH B

delay(delaylegnth);

digitalWrite(9, LOW); //ENABLE CH A
digitalWrite(8, HIGH); //DISABLE CH B

digitalWrite(12, LOW); //Sets direction of CH A
analogWrite(3, 1023); //Moves CH A

delay(delaylegnth);

digitalWrite(9, HIGH); //DISABLE CH A
digitalWrite(8, LOW); //ENABLE CH B

digitalWrite(13, LOW); //Sets direction of CH B
analogWrite(11, 1023); //Moves CH B

delay(delaylegnth);

}