After a recent post on current sensing, I talked to a couple of friends at NYC Resistor (my usual sounding board) and got some great advice (as usual).
One thing I didn’t remember was the difference between passive and active filtering, and that it might take more than a simple RC circuit to filter out the noise I was seeing. I was pointed to a few good reads:
http://ww1.microchip.com/downloads/en/devicedoc/41233A.pdf
http://www.maxim-ic.com/app-notes/index.mvp/id/1762
http://www.swarthmore.edu/NatSci/echeeve1/Ref/DataSheet/IntroToFilters.pdf
But before I got too far into digital signal processing land and making hardware based 2nd or 3rd order filters, I wanted to see if there was a better way. I got in touch with JD Warren, one of authors of Arduino Robotics. One of the projects in the book used a custom built motor driver with a hall effect current sensor. I’ve shied away from these until now because they seemed to be more voodoo than the more intuitive current sensing resistor method, but after reading parts of his book and talking with JD, I ordered up some ACS712s and some surface mount to breadboard adapters from Digikey. He always had good luck with them so hopefully that luck rubs off and they’ll be less noisy than the method I’m using now.
I also did some more research and found that the L298 h-bridge, basically the next step up from the L293 that comes on the Adafruit Motor Shield, has current sensing already built in! Luckily both the Sparkfun Ardumoto Shield and the Arduino Motor Shield use this h-bridge, but only the Arduino shield has those pins broken out. So I ordered both, figuring I could hack the Sparkfun one by cutting the traces and soldering some little jumper wires if necessary (I studied the schematics for quite a while before convincing myself this would be possible). From what I can gather from the datasheet, I still need to add a current sensing resistor, and it uses basically the same method I was using before, but something about the integrated solution I suppose could make a difference. We’ll see.
First Test: Arduino Motor Shield
The documentation on this shield is a little spotty, but from the schematic and Eagle files, I figured out that the current sensing resistors were already integrated along with an op-amp that presumably amplifies the tiny voltage drop. The hardware page says the resolution is 1.65 A/V, so it looks like they did the math already on what resistors and what gain was used to get an actual current rating from a detectable voltage drop. So I wrote up some code (below) and after a little tweaking got this:
Isn’t that beautiful?!? A little jumpy still but might be good. I think it’s time to swap this in where the Adafruit Shield is now and see what happens with the robot arm.
[code lang=”arduino”]
/*
Curent sensing with the Arduino Motor Shield: http://arduino.cc/it/Main/ArduinoMotorShieldR3
One small DC motor attached to screw terminal block A
External power from a 4 x AA batter pack
CC-BY-SA
by Dustyn Roberts
6/12/2012
*/
// declare pins used on channel A
int directionPin = 12;
int pwmPin = 3;
int brakePin = 9;
int currentPin = A0;
int switchPin = 2; // motor on/off switch
// connect one side of switch to power, the other to ground through a 10k resistor
// read the switch state where the switch and resistor meet into digital pin 2
// similar to this lab: http://itp.nyu.edu/physcomp/Labs/DigitalInOut
// constants
float volt_per_amp = 1.65; // resolution according to hardware page
// variables
float currentRaw; // the raw analogRead ranging from 0-1023
float currentVolts; // raw reading changed to Volts
float currentAmps; // Voltage reading changed to Amps
void setup() {
pinMode(directionPin, OUTPUT);
pinMode(pwmPin, OUTPUT); // necessary according to forum post http://arduino.cc/forum/index.php/topic,89468.0.html
Serial.begin(9600);
}
void loop() {
// read the switch input:
if (digitalRead(switchPin) == HIGH) {
// if the switch is closed:
digitalWrite(pwmPin, HIGH); // necessary according to forum post http://arduino.cc/forum/index.php/topic,89468.0.html
digitalWrite(directionPin, HIGH);
}
else {
// if the switch is open:
digitalWrite(pwmPin, LOW); // necessary according to forum post http://arduino.cc/forum/index.php/topic,89468.0.html
digitalWrite(directionPin, LOW);
}
currentRaw = analogRead(currentPin);
currentVolts = currentRaw *(5.0/1024.0);
currentAmps = currentVolts/volt_per_amp;
Serial.println(currentAmps);
}
[/code]
You also want to have a stable Vref voltage. The Leonardo’s Vref pin is connected to a low-pass filter which is connected to 5V. I have found that it needs more capacitance to be “stable.”
Thanks! How would you suggest adding capacitance to the Vref? I’m using the default so there is not external Vref reference.
Here’s some code to test the noise on your Vref pin. The best result comes from using the on-board linear regulator. The 5 volts from the USB plug tends to be noisy. Your environment may be different, so I would run the “sketch” to check the noise level. I’ve tested the code on the Uno and Leonardo boards.
[sourcecode language=”cpp”]
void setup() {
#if defined(USBCON)
ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#else
ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#endif
delay(2); // Wait for Vref to settle
Serial.begin(9600);
}
uint16_t min_reading=1023;
uint16_t max_reading=0;
uint16_t reading;
void loop() {
if (millis() % 1000) {
ADCSRA |= _BV(ADSC); // start convertion
loop_until_bit_is_clear(ADCSRA,ADSC);
reading = ADCL;
reading |= ADCH<<8;
min_reading = min(min_reading, reading);
max_reading = max(max_reading, reading);
} else {
Serial.print ("Vref noise: ");
Serial.print ((max_reading – min_reading) * 4.88);
Serial.println(" mV");
min_reading=1023;
max_reading=0;
delay(1);
}
}
[/sourcecode]
Wow great thanks!! Do you find the Vref is less noisy when the Arduino is plugged into a wall wart?
Yes