Firmata, ping sensor, arduino, and OF problem

I am coming from Pure Data where I had a ping sensor sending the distance in inches to Pure Data that used the distance information to play a video and different speeds.

I’d like to do the same in OF because OF handles video better.

I am getting hung up on getting the arduino to send the distance information to OF. The distance in inches should show up in the serial monitor of the arduino software, but I just get jumble data.

Here is the code I have on the arduino (.pde file):

#include <Firmata.h>

byte digitalPin;
char firstChar;
char secondChar;
int pingPin = 7;

void digitalWriteCallback(byte pin, int value)
{
pinMode(pin,OUTPUT);
analogWrite(pin, value);
}

void setup()
{
Firmata.setFirmwareVersion(0, 1);
Firmata.attach(ANALOG_MESSAGE, digitalWriteCallback);
Firmata.begin();
}

void loop()
{
while(Firmata.available()) {
Firmata.processInput();
}
for(digitalPin = 0; digitalPin < TOTAL_DIGITAL_PINS; digitalPin++) {
if(digitalPin == 7)
Firmata.sendDigitalPort(digitalPin, computeDistance());
}
}

long computeDistance(){
long duration, inches, cm;
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.
// We give a short LOW pulse beforehand to ensure a clean HIGH pulse.
pinMode(pingPin, OUTPUT);
digitalWrite(pingPin, LOW);
delayMicroseconds(2);
digitalWrite(pingPin, HIGH);
delayMicroseconds(5);
digitalWrite(pingPin, LOW);

// The same pin is used to read the signal from the PING))): a HIGH
// pulse whose duration is the time (in microseconds) from the sending
// of the ping to the reception of its echo off of an object.
pinMode(pingPin, INPUT);
duration = pulseIn(pingPin, HIGH);

// convert the time into a distance
return microsecondsToInches(duration);
//cm = microsecondsToCentimeters(duration);

}

long microsecondsToInches(long microseconds)
{
// According to Parallax’s datasheet for the PING))), there are
// 73.746 microseconds per inch (i.e. sound travels at 1130 feet per
// second). This gives the distance travelled by the ping, outbound
// and return, so we divide by 2 to get the distance of the obstacle.
// See: http://www.parallax.com/dl/docs/prod/ac-…-G-v1.3.pdf
return microseconds / 74 / 2;
}

long microsecondsToCentimeters(long microseconds)
{
// The speed of sound is 340 m/s or 29 microseconds per centimeter.
// The ping travels out and back, so to find the distance of the
// object we take half of the distance travelled.
return microseconds / 29 / 2;
}

Once I get the distance data to show up in the serial monitor with firmata, I will move on to getting OF to read the data.

I have scoured the internet for examples but they all deal with analog data. The ping sensor is digital so I need to be able to receive digital data in OF…

Any insights would be greatly appreciated!!
Thanks
Tim

hey tim,

can you use the code tag so it keep indentation?

I’m not a firmata expert, but typically, I’ve done the requests to read in firmata (ie c++), since it handles timing. the problem with you pushing the data every frame:

  
  
Firmata.sendDigitalPort(digitalPin, computeDistance());   
  

is that our firmata implementation is setup so that in c++ you’d say:

  
  
arduino.getDigital(int pin);  
  

and ask for the data every frame. I think that’s the difference, and the firmata stuff actually handles the getting / settings. I realize that what you need might be more complex so I’m gonna ping the firmata folks.

you might also look at the serial example that ships with OF – it’s not serial, but can show you a call and response type model for C++ -> serial communication.

take care!
zach

hi Tim,

in your code, it looks like this function here:

  
ong computeDistance(){  
long duration, inches, cm;  
// The PING))) is triggered by a HIGH pulse of 2 or more microseconds.  
// We give a short LOW pulse beforehand to ensure a clean HIGH pulse.  
pinMode(pingPin, OUTPUT);  
digitalWrite(pingPin, LOW);  
delayMicroseconds(2);  
digitalWrite(pingPin, HIGH);  
delayMicroseconds(5);  
digitalWrite(pingPin, LOW);  
  
// The same pin is used to read the signal from the PING))): a HIGH  
// pulse whose duration is the time (in microseconds) from the sending  
// of the ping to the reception of its echo off of an object.  
pinMode(pingPin, INPUT);  
duration = pulseIn(pingPin, HIGH);  
  
// convert the time into a distance  
return microsecondsToInches(duration);  
//cm = microsecondsToCentimeters(duration);  
  
}  

is Arduino code in the middle of your C++ program (testApp.cpp).

there should not be Arduino code in your C++ program. you cannot call pinMode, digitalWrite, etc from C++: you must instead communicate with the Arduino using a protocol (such as the Firmata protocol), which will then translate your requests into proper Arduino calls.

is this what you’ve done? if so, you’ll need to separate it out somehow.

Hey, so I’ve been wondering about how to create a custom firmata app as well. Normally you’d be computing the distance in the OF app using the Firmata library to run the computeDistance() method except that firmata doesn’t support pulseIn() calls afaik. I think for sending that data back you want to do something like this (totally untested):

  
void loop()  
{  
   while(Firmata.available()) {  
      Firmata.processInput();  
   }  
  
   Firmata.sendAnalog(sensorPin, computeDistance());  
}  
  

I’d check but I don’t have any boards with me and I don’t know the firmata libs super well. The Arduino board might be a better place to ask also.

Thanks for all your replies. I will post on Arduino’s forum and as them. In the meantime since zach asked, I am reposting my arduino code within the code tags for easier reading.

I am running some tests and will be post those results…

  
  
#include <Firmata.h>  
  
byte digitalPin;  
char firstChar;   
char secondChar;  
int pingPin = 7;  
  
void digitalWriteCallback(byte pin, int value)  
{  
    pinMode(pin,OUTPUT);  
    analogWrite(pin, value);  
}  
  
void setup()  
{  
    Firmata.setFirmwareVersion(0, 1);  
    Firmata.attach(ANALOG_MESSAGE, digitalWriteCallback);  
    Firmata.begin();  
}  
  
void loop()  
{  
    while(Firmata.available()) {  
        Firmata.processInput();  
    }  
    for(digitalPin = 0; digitalPin < TOTAL_DIGITAL_PINS; digitalPin++) {  
      if(digitalPin == 7)    
        Firmata.sendDigitalPort(digitalPin, computeDistance());   
    }  
}  
  
long computeDistance(){  
 long duration, inches, cm;  
   // The PING))) is triggered by a HIGH pulse of 2 or more microseconds.  
  // We give a short LOW pulse beforehand to ensure a clean HIGH pulse.  
  pinMode(pingPin, OUTPUT);  
  digitalWrite(pingPin, LOW);  
  delayMicroseconds(2);  
  digitalWrite(pingPin, HIGH);  
  delayMicroseconds(5);  
  digitalWrite(pingPin, LOW);  
  
  // The same pin is used to read the signal from the PING))): a HIGH  
  // pulse whose duration is the time (in microseconds) from the sending  
  // of the ping to the reception of its echo off of an object.  
  pinMode(pingPin, INPUT);  
  duration = pulseIn(pingPin, HIGH);  
  
  // convert the time into a distance  
  return microsecondsToInches(duration);  
  //cm = microsecondsToCentimeters(duration);  
    
}  
  
long microsecondsToInches(long microseconds)  
{  
  // According to Parallax's datasheet for the PING))), there are  
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per  
  // second).  This gives the distance travelled by the ping, outbound  
  // and return, so we divide by 2 to get the distance of the obstacle.  
  // See: [http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf](http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf)  
  return microseconds / 74 / 2;  
}  
  
long microsecondsToCentimeters(long microseconds)  
{  
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.  
  // The ping travels out and back, so to find the distance of the  
  // object we take half of the distance travelled.  
  return microseconds / 29 / 2;  
}  
  

So I simplified the code based on the input given and here is what I have come up with:

  
#include <Firmata.h>  
  
byte digitalPin;  
char firstChar;   
char secondChar;  
int pingPin = 7;  
  
  
void setup()  
{  
    Firmata.begin();  
}  
  
void loop()  
{  
   while(Firmata.available()) {  
      Firmata.processInput();  
   }  
  
   Firmata.sendDigitalPort(pingPin, computeDistance());  
}  
  
long computeDistance(){  
 long duration, inches, cm;  
   // The PING))) is triggered by a HIGH pulse of 2 or more microseconds.  
  // We give a short LOW pulse beforehand to ensure a clean HIGH pulse.  
  pinMode(pingPin, OUTPUT);  
  digitalWrite(pingPin, LOW);  
  delayMicroseconds(2);  
  digitalWrite(pingPin, HIGH);  
  delayMicroseconds(5);  
  digitalWrite(pingPin, LOW);  
  
  // The same pin is used to read the signal from the PING))): a HIGH  
  // pulse whose duration is the time (in microseconds) from the sending  
  // of the ping to the reception of its echo off of an object.  
  pinMode(pingPin, INPUT);  
  duration = pulseIn(pingPin, HIGH);  
  
  // convert the time into a distance  
  return microsecondsToInches(duration);  
  //cm = microsecondsToCentimeters(duration);  
    
}  
  
long microsecondsToInches(long microseconds)  
{  
  // According to Parallax's datasheet for the PING))), there are  
  // 73.746 microseconds per inch (i.e. sound travels at 1130 feet per  
  // second).  This gives the distance travelled by the ping, outbound  
  // and return, so we divide by 2 to get the distance of the obstacle.  
  // See: [http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf](http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf)  
  return microseconds / 74 / 2;  
}  
  
long microsecondsToCentimeters(long microseconds)  
{  
  // The speed of sound is 340 m/s or 29 microseconds per centimeter.  
  // The ping travels out and back, so to find the distance of the  
  // object we take half of the distance travelled.  
  return microseconds / 29 / 2;  
}  
  

but now, in the arduino serial monitor, I get:

? ? ? ? ? ? ? ? ? ? ? ? ? ?

instead of a number.

Is this because its digital information that needs to be converted somehow?

I tried : sendAnalog too as joshuajnoble suggested but I still get jumble.

thanks for your help!

Ah, yeah, I shoulda caught the ‘pin 7’ reference to realize you need to read the digital pin, anyways. If you take a look at the firmata cpp file, it calls this when you call sendDigital

  
// send 14-bits in a single digital message (protocol v1)  
// send an 8-bit port in a single digital message (protocol v2)  
void FirmataClass::sendDigitalPort(byte portNumber, int portData)  
{  
	Serial.print(DIGITAL_MESSAGE | (portNumber & 0xF),BYTE);  
	Serial.print(portData % 128, BYTE); // Tx bits 0-6  
	Serial.print(portData >> 7, BYTE);  // Tx bits 7-13  
}  

So you’ll need to reassemble that into an int on the other side to read it as an int. It’s kind of assumed that if you’re using firmata it’s to communicate with a Processing or OF app. Take a look at the ofStandardFirmata file, it’ll show you how firmata assumes you want the data from the Arduino, which doesn’t work with the Arduino Serial monitor. You might want to just use the firmataExample sample that comes with the download, clean it up just to read the digital pin, and use that for testing?

It looks like you’re trying to send a distance value from a ping sensor. The easiest approach would be to send it with Firmata.sendAnalog(sensorPin, computeDistance()); as josh suggested, and then deal with it on the oF side as if it was a regular value reported for an analog pin. I.e. you read it with arduino.getAnalog(sensorPin).

But for this to work the long value that computeDistance() returns has to be, or be range mapped to, a value in the 0-16384 range before you send it. I don’t think the ping sensor can return a distance larger than 16384 inches though so you are probably safe…

Anyway, if the value you want to sen is outside of this range, for example if it can be negative or larger than 16384, then you have to implement your own way of sending it as a sysex message…

This is a bit complicated but the reasoning would go somewhere along these lines: A long is 32 bits but you can only send one data byte (8-bits) at a time through firmata and each data byte has to have a value within the 0-127 range. So to send a full long you’d have to split the long into 32/8*2 = 8 bytes. Then you send these 8 bytes as a sysex message and them put them back together into an long on the receiver side.

If you want to know exactly how to do this and why Firmata works like it does then you’re probably better off asking in the Arduino forum…

Another option would be to swap the ping sensor you have to an ultrasonic (for example the Maxbotix LV-EZ0) or IR (sharp has some nice ones) range finder that outputs an analog value… A sensor like that would be plug and play with StandardFirmata.pde and ofArduino. I can highly recommend sharps IR range sensors, they are cheap and usually work much better than the ultrasonic ones in the budget price range…

In any case the distance you get from the ping sensor is an analog value so sending it with Firmata.sendDigitalPort(pingPin, computeDistance()); is probably not what you want to do…

// E

so even though the ping sensor is plugged into the digital pin the microsecondsToInches() function returns an analog value and sends it back to OF as analog? do I understand that correctly?

I’m looking into the IR sensors now… found this one:
http://www.parallax.com/Store/Sensors/O-…-roductName

Looked at the sharp ones too - do those work with the arduino platform ok?

thanks for your help again!
T

I am using the FirmataExample from of_preRelease_v0.06_xcode.

I modified a few lines in the testApp.h and the testApp.cpp files.

I’m attaching the files.

I added a integer variable called distance. Then I modified the testApp.cpp file to try and get the data from the arduino with ard.getAnalog(pingPin);

I keep getting an initial value for the distance as -15821 and when I press the mouse button I get -1. Not sure why I’m getting those numbers…

Here is the code I added from testApp.h:

  
int	distance;  

and here is the whole testApp.cpp file:

  
#include "testApp.h"  
  
  
//--------------------------------------------------------------  
void testApp::setup(){  
  
	ofSetVerticalSync(true);  
	ofSetFrameRate(60);  
  
	ofBackground(255,0,130);  
  
	bgImage.loadImage("firmata.png");  
	font.loadFont("franklinGothic.otf", 20);  
  
	ard.connect("/dev/tty.usbserial-A6008dhC", 57600);  
  
	bSetupArduino	= false;	// flag so we setup arduino when its ready, you don't need to touch this :)  
	pingPin = 7;					  
}  
  
//--------------------------------------------------------------  
void testApp::update(){  
  
  
	if ( ard.isArduinoReady()){  
  
		// 1st: setup the arduino if haven't already:  
		if (bSetupArduino == false){  
			setupArduino();  
			bSetupArduino = true;	// only do this once  
		}  
		// 2nd do the update of the arduino  
		updateArduino();  
	}  
  
}  
  
//--------------------------------------------------------------  
void testApp::setupArduino(){  
  
	// this is where you setup all the pins and pin modes, etc  
	for (int i = 0; i < 13; i++){  
		ard.sendDigitalPinMode(i, ARD_OUTPUT);  
	}  
	  
	ard.sendDigitalPinMode(13, ARD_OUTPUT);  
	ard.sendAnalogPinReporting(0, ARD_ANALOG);	// AB: report data  
	ard.sendDigitalPinMode(7, ARD_PWM);		// on diecimelia: 11 pwm?*/  
	  
}  
  
//--------------------------------------------------------------  
void testApp::updateArduino(){  
  
	// update the arduino, get any data or messages:  
	ard.update();  
	ard.sendPwm(11, (int)(128 + 128 * sin(ofGetElapsedTimef())));   // pwm...  
}  
  
  
//--------------------------------------------------------------  
void testApp::draw(){  
	bgImage.draw(0,0);  
  
	if (!ard.isArduinoReady()){  
		font.drawString("arduino not ready\n", 545, 40);  
	} else {  
		font.drawString("analog pin "+ ofToString(pingPin) +": " + ofToString(distance) +  
						"\nsending pwm: " + ofToString((int)(128 + 128 * sin(ofGetElapsedTimef()))), 545, 40);  
  
	}  
}  
  
//--------------------------------------------------------------  
void testApp::keyPressed  (int key){  
  
}  
  
//--------------------------------------------------------------  
void testApp::keyReleased(int key){  
  
}  
  
//--------------------------------------------------------------  
void testApp::mouseMoved(int x, int y ){  
  
}  
  
//--------------------------------------------------------------  
void testApp::mouseDragged(int x, int y, int button){  
  
}  
  
//--------------------------------------------------------------  
void testApp::mousePressed(int x, int y, int button){  
	ard.sendDigital(13, ARD_HIGH);  
	ard.sendDigitalPinMode(7, ARD_OUTPUT);  
	distance = ard.getAnalog(pingPin);  
}  
  
//--------------------------------------------------------------  
void testApp::mouseReleased(int x, int y, int button){  
	ard.sendDigital(13, ARD_LOW);  
}  
  
//--------------------------------------------------------------  
void testApp::windowResized(int w, int h){  
  
}  
  

ok I got distance data from the arduino into my OF program. Next step is to get the movie working right…

On the arduino side:

  
void loop()  
{  
   while(Firmata.available()) {  
      Firmata.processInput();  
   }  
  
   Firmata.sendAnalog(pingPin, computeDistance());  
}  

On the of side I just used:

  
arduino.getAnalog();  

tip: make sure there are no typo’s in your device address in arduino.connect(); !