Go Down

Topic: Arduino with Air-Core meters (Read 10935 times) previous topic - next topic


I have adopted an old car instrument cluster and I have gotten the air-core meter (the speedometer, and tachometer) working with the Arduino Duemilanove using PWM and an h-bridge.

I am using the circuit described here: [1] , with some minor Arduino side pin changes (to keep the other PWM pins free):

1IN is attached to pin 2 on the Arduino (same as the schematic).
2IN is attached to pin 4 on the Arduino.
3IN is attached to pin 7 on the Arduino.
4IN is attached to pin 8 on the Arduino.

The "sine" coil is attached to the green (1OUT to SIN+) and orange (2OUT to SIN-) motor wires and the "cosine" coil to the purple (3OUT to COS+) and red (4OUT to COS-) motor wires.

It seems to work very well for the most part.

But as I increment over the 360 degrees of the meter, at the 0, 90, 180, and 270 degree points, the needle appears hesitate for a significant fraction of a second (over the course of several steps).

I am calculating the sine and cosine of the angle I want the meter to be at, multiplying that by 255 (the 100% duty cycle or 5 volts), and set the polarity of the coil depending on the sign of the result. I can drive the meter fairly smoothly through 360 degrees without any problem, except the hesitations at/near those angles.

I thought the h-bridge chip might be too slow in switching polarity, but if I am reading the datasheet [2] correctly (though I may not be), it should have a switching speed of up to about 5 kHz. That should be far faster than I would expect a human to be able to perceive. Is the L293D really that fast?

I have also tried making the code that changes the polarity pins faster (by exchanging the digitalWrite for PORTD and PORTB commands) based on my reading it should be executing in microseconds, but it didn't seem to make a noticeable difference.

I have tried using the Timer1 module ([3]) to increase the resolution of the analog outputs to 1023, but the problem still persists.

I have searched a variety of web sites, and while I find references to driving air-core movements using microcontrollers and PWM, no one seems to have the problem I am describing. All the searches seem to indicate I am doing the right thing. But perhaps I do not know the right terms to use in my googling.

I suspect it is because the polarity reversal is too slow at those transition points, either because of some software silliness I am doing, or because the L293D can't switch the polarity as fast as the Arduino can set the pins. But I don't know how to check that. Even if it was the case, how could I make the polarity reversal happen faster?

Any ideas what I am doing wrong?

[1] Link in next post
[2] Link in next post
[3] Link in next post

Here is the code I am using:

Code: [Select]
//the mathematical constant pi, because we are using radians instead of degrees.
// 360 degrees = 2*pi, 180 = pi, 90 = pi/2, etc.
const float pi = 3.14159;

const float stepSize = 0.01; //This is the step size for incrementing the angle (in radians)

//this is the maximum resolution of the PWM output.
const int maxAnalogRes = 255;

/* Define the pins the meter is connected to. We need 1 PWM to vary the voltage in
* each coil, and two more pins to reverse the polarity in that coil. So a total
* of 6 pins (3 per coil).
const int aircore1SinPin = 9; //this controls the voltage to the "sine" coil
const int aircore1CosPin = 10; //this controls the voltage to the "cosine" coil
const int aircore1SinDirPin1 = 2;  //these two control the polarity to the "sine" coil
const int aircore1SinDirPin2 = 4;
const int aircore1CosDirPin1 = 7;  //these two control the polarity to the "cosine" coil
const int aircore1CosDirPin2 = 8;

// milliseconds between each angular step.
int delayTime = 40;
void setup()
 //Set the pins to OUTPUT
 pinMode(aircore1SinPin, OUTPUT);
 pinMode(aircore1CosPin, OUTPUT);
 pinMode(aircore1SinDirPin1, OUTPUT);
 pinMode(aircore1SinDirPin2, OUTPUT);
 pinMode(aircore1CosDirPin1, OUTPUT);
 pinMode(aircore1CosDirPin2, OUTPUT);
 // Set the initial direction as "forward". Of course it could be "reverse" depending on which lead you connected :-).
 digitalWrite(aircore1SinDirPin1, HIGH);
 digitalWrite(aircore1SinDirPin2, LOW);
 digitalWrite(aircore1CosDirPin1, HIGH);
 digitalWrite(aircore1CosDirPin2, LOW);

void loop()                     // run over and over again
   //Rotate 0 through 360 degrees (OK, really 2*pi radians, but who cares?)
 for(float i = 0; i < (2*pi); i = i + stepSize){
   setMeterPosition(aircore1SinPin, aircore1SinDirPin1, aircore1SinDirPin2, aircore1CosPin, aircore1CosDirPin1, aircore1CosDirPin2, i);
   delay(delayTime);           // waits for delayTime milliseconds    

 //Rotate 360 through 0 degrees
 for(float i = (2*pi); i > 0; i = i - stepSize){
   setMeterPosition(aircore1SinPin, aircore1SinDirPin1, aircore1SinDirPin2, aircore1CosPin, aircore1CosDirPin1, aircore1CosDirPin2, i);
   delay(delayTime);           // waits for delayTime milliseconds

// setMeterPosition() - put it at the angle in radians
void setMeterPosition(int sinPin, int sinDirPin1, int sinDirPin2, int cosPin, int cosDirPin1, int cosDirPin2, float pos){

 // Calculate the voltage on the PWM pins based on the angle we want
 float sinCoilValue = maxAnalogRes*sin(pos);
 float cosCoilValue = maxAnalogRes*cos(pos);
 // change the polarity of the coil depending on the sign of the voltage level
 if (sinCoilValue<=0) {
   digitalWrite(sinDirPin1, LOW); //Change to LOW if using relays
   digitalWrite(sinDirPin2, HIGH); //comment this line out if using relays
 } else {
   digitalWrite(sinDirPin1, HIGH); //Change to HIGH if using relays
   digitalWrite(sinDirPin2, LOW); //comment this line out if using relays
 if (cosCoilValue<=0) {
   digitalWrite(cosDirPin1, LOW); //Change to LOW if using relays
   digitalWrite(cosDirPin2, HIGH); //comment this line out if using relays
 } else {
   digitalWrite(cosDirPin1, HIGH); //Change to HIGH if using relays
   digitalWrite(cosDirPin2, LOW); //comment this line out if using relays

 // take the absolute value because analogWrite doesn't take negatives
 sinCoilValue = abs(sinCoilValue);
 cosCoilValue = abs(cosCoilValue);

 analogWrite(sinPin, sinCoilValue);
 analogWrite(cosPin, cosCoilValue);


Sorry for the separate links, first post doesn't allow links (anti-spam measure).

[1] http://oomlout.com/L293/L293-Guide.pdf
[2] http://www.ti.com/lit/gpn/L293D
[3] http://www.arduino.cc/playground/Code/Timer1


Apr 18, 2010, 04:09 am Last Edit: Apr 18, 2010, 04:13 am by MarkT Reason: 1
What sort of H-bridge are you using - some have problems with extremes of duty-cycle.

[edit] Ah, I've found the data on that - its bipolar outputs so I think it shouldn't have any problem except perhaps at too high a frequency... But you've played with various drive frequencies.  Hmmm.  good luck.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]


Thanks for taking the time to look at it. I am using the L293D (the one that came with my as yet un-assembled motorshield).

You mentioned some have problems with extremes of duty cycle?

Would that be on the PWM side (the 1EN pin) or would it be the 1IN and 2IN pins where we are switching polarity?

Is there somewhere I can go to research that?


so what happened with that

any videos?


No videos yet. We ended up going with the MLX10407 to drive the meters. Since at least one person seems interested, I have a schematic and will post it and a shield board layout soon. I guess I can make a video as well. :-) It works really well, very smooth and pretty accurate (which one would expect since that chip is what the Mini Coopers are using).

I did try doing it with the CS8190, and with some help had it working with a variable resistor to control the meter, but couldn't get it working cleanly with the Arduino's PWM output. It also got extremely hot and melted my breadboard. :-( Since I was under a bit of a time crunch at that point I didn't pursue it more.

At some point I will revisit doing it without the MLX10407. Doing it with an H-bridge or even charlieplexing directly with the pins on the Arduino just isn't smooth enough to be reliable at the transition points. The accuracy was not great either. Maybe I just didn't know what was going on well enough to do it right.

I think the way to do it without using specialized chips is with op amps, but haven't really spent the time to figure it all out and put it together.


i made this


now i am waiting a bmw cluster

that has musch more pins back and aircores inside

so i am not sure if i can drive it from the input of th cluster or i need to drive directly the aircores


Jul 02, 2010, 09:26 am Last Edit: Jul 02, 2010, 09:27 am by herctrap Reason: 1
i got the BMW cluster yesterday

i dont have h-bridges

and i used 8 transistors and 4 pontesiometers to drive it manualy


*the rpm motor is not stable with the rest of the cluster so some times the neddle stocks

yes you can controll it with arduino but you will have to use 4 pwm pins

so thats not good

i will have to wait for the h-bridges to come



i was in a hurry so i used your code


0rpm for me is 1.6 and not 0
7.000 rpm is 6.28
and 11.000 rpm is 7.5

i think that i have a coil connected wrong

how can i find witch is the sin and wich is the cos coil?



Just scene your videos!  Really cool![smiley=thumbsup.gif]
I'm a total newbie -
Hoping to do something similar - any chance of posting a schematic and picture of your board?  PLease!!!


Hey Adam,

I've been referring to this forum for a while when I was trying to build my own gauge cluster, and during my testing phase I also noticed the delays with test data, (I'm using multiple L293D chips)  but in practice the rpm/speed input values don't typically stay smooth through in the +/-10 degrees of 0, 90, 180 or 270' to cause noticeable delay the way a for-loop does.

So even though a weird anomaly happens with test data, in the real world it isn't noticeable. :D

Go Up