Jump to content
IGNORED

Built a digital carb sync


tz89

Recommended Posts

Built a digital sync and you can, too!

 

 

After reading this forum about installing an aftermarket TCI and in other places about easy to use microprocessing platforms, it occurred to me that one might be able to use multiple MAP sensors to set the carb sync on a bike. I'm no expert on either motorcycles or electronics, so it had to be easy if I was going to do it. And it was easy. The hardest thing for me was figuring out what certain connectors are called and then finding a reasonable source.

 

 

Point your search engine at “arduino” or go directly to this link: www.arduino.cc to get an idea for the microprocessing end of this.

 

 

I used the Arduino Uno R3. You can use any, but you'll have to modify my program or write your own. In particular, if you use the kind of LCD display connection I did (IIC) it interferes with two of the ports on the board. On the Uno it's analog 4 and 5, leaving a nice grouping of 0-3 for my 4 sensors to connect. My end goal is to remove the LCD and use a wireless smartphone as the display. When I get that working then this thing could do a 6 carb bike. Goldwing heaven! (On the Leonardo board it's digital 2 and 3.)

 

 

Here's the parts list. I'll edit this post to improve accuracy as I can.

 

 

The hard parts (for me):

 

 

MAP sensors. I found 6 old GM 1 bar MAP sensors for $40 on Ebay and was quite happy.

6 3-tower GM weather-pack housings with male connectors. Spendy with rip-off shipping.

You don't really need the plastic housing. You can just solder wires to loose male connectors, and maybe use some heat shrink to tidy up. Save big $$.

 

The rest:

 

 

Arduino Uno R3. $23.

Arduino IDE software (free download).

Sensor shield (optional - makes connections easier). $10.

LCD IIC display 4x20. $7.

4 count 3-wire board connectors with one end removed and soldered to the weather-packs. $5.

1 count 4-wire board connector to attach the LCD with the +/- polarity rewired. $2.

2 feet 1/4” hose. Cut into 6” lengths and attached to the MAPs.

10 feet 3/16” hose.

4 count ¼ to 3/16 connectors. $5.

4 count caps for the 3/16 hose. $3.

 

 

The Arduino IDE includes sample programs. I modified one that keeps a running average for input smoothing to load arrays of readings for each cylinder and calculate the peak and average readings. I'm an old software guy but never something like this and never in C. But those old skills came in handy and I wasn't shy about this part.

 

 

The Arduino doesn't report overflow errors at compile or run time, so I had to do a little digging to debug odd results. For example, if I set the readings array to more than 180, the LCD stopped working. What I figured out was that the sensitivity scale of the readings multiplied by the number of readings had to be less than the max integer value (32k) or it would overflow. If the array got too big the RAM would overflow. I could change to long integers or floating point math I suppose. I could change to a chip or board with more memory because there is a memory size limitation. My rainy weather project will be to go back through and clean up the code. Basically, I just hacked my way forward and didn't worry about the elegance of the code like I did when I was working in software. I didn't know how the compiler or board really worked so why worry about style.

 

 

The key thing I did was code a self-calibration step. The first time through loading the reading array (with the bike not running), it takes an average of the average of all the MAPS and uses that to establish a calibration adjustment to be applied to all subsequent readings. This way all the MAP sensor circuits are normed to a constant value. It could be normed to zero, but then negative numbers would be more common. To test this live, I hooked up all 4 MAP sensors in turn to the same cylinder on a running bike to see if it would return a consistent value. It did. The nice thing is that build precision is less of a factor which is great for me. It just works with what you have for MAP sensors, wire harness and soldering skills.

 

 

Assembly is just plugging the shield into the top of the Uno, then plugging in the LCD and the 4 MAP sensors. For power I plugged in a cigarette adapter into my battery maintainer plug, plugged a USB converter into that, and ran a USB cable to the Uno. There are other ways but I had all this handy. The software was already loaded; you just plug the USB into the computer running the IDE and upload.

 

 

At power up it does the calibration and when you hit the reset button. Nothing to lose by trying.

 

 

I hooked up 4 hoses to the bike and capped them. Then I tested #1 against all 4 sensors. It read the same, about 159 peak. Then I hooked all 4 up. The bike was in good sync, which I knew because it runs good. But I tweaked all 3 sync adjustments to get them the same. Everything ended up peaking at 158-159. Then I capped the hoses and tested #3 against all 4 sensors. All the same.

 

 

After that I took it for a ride. Seemed smoother but it was running great already.

 

 

The numbers don't really mean anything. They depend on how 'sensitive' the software is set. The MAP sensors run at 5v and drop the signal voltage from that as vacuum increases. The UNO board reads an analog voltage and returns a range of integer values. The rest is software. One nice thing - you can crack the throttle without worries. In fact, since you can compare manifold pressure at higher RPMs you may get some diagnostic value if you've got pin holes in a diaphragm or a sticky slide. Hopefully, some engine experts will weigh in on that notion or with better ideas.

 

 

Things to consider.

 

 

If you can't find cheap MAP sensors, don't bother. They should all be the same or similar, but the calibration step allows for variances. Try to get the weather-pack connectors included because you'll go broke if you have to buy a small quantity at full retail. If anyone has a better source please let me know. Late 80's GM cars in a junkyard may be a good place to start. There are also less expensive vacuum sensors available for non-automotive applications that are not as rugged.

 

 

Everything you need can be bought online if you know what to call it. The board connector names still elude me. I just bought stuff until I had something that worked.

 

 

I'll post the code in a subsequent post. I will be glad for suggestions. I will be especially interested in a) how to build a cool enclosure and b) thoughts about better analysis techniques. I would never post it anywhere else, because the purists would flame you. But this forum is different. Best $12 ever!

 

 

Lots of pix. If there's a better way to post them I don't know it.

 

 

Tom

IMG_0726.JPG

IMG_0734.JPG

IMG_0745.JPG

Edited by tz89
grammar spelling and new facts
Link to comment
Share on other sites

  • Replies 67
  • Created
  • Last Reply

Top Posters In This Topic

/* Here's the code. Just copy everything and paste into the Arduino IDE. */

 

/*

 

Modified Smoothing (below) by Tom Hogue 2012

for CarbSync Tool

Accepts 0-5v from GM 1 bar MAP sensors

Creates arrays of values for all cylinders

v01r01 is the basic development version

v01r02 adds LCD display

v01r03 adds input calibration at startup

 

Future mods

smartphone display

bluetooth connectivity

tach

live smooth and sensitivity adjustments

 

Smoothing

 

Reads repeatedly from an analog input, calculating a running average

and printing it to the computer. Keeps ten readings in an array and

continually averages them.

 

The circuit:

* Analog sensor (potentiometer will do) attached to analog input 0

 

Created 22 April 2007

By David A. Mellis

modified 9 Apr 2012

by Tom Igoe

http://www.arduino.cc/en/Tutorial/Smoothing

 

This example code is in the public domain.

 

 

 

*/

#include

#include

 

LiquidCrystal_I2C lcd(0x27,20,4);

 

//set the LCD address to 0x27 for a 20 chars and 4 line display

 

 

/* Define the number of samples to keep track of. The higher the number,

the more the readings will be smoothed, but the slower the output will

respond to the input. Using a constant rather than a normal variable lets

use this value to determine the size of the readings array.

Uno board drives the LCD with A4 and A5, leaving only A0-3 for 4 MAP sensors

*/

const int numReadingsMax = 100; // this should be maybe 5-150. 190+ overflows lcd

const int numCylinders = 4; // max is 4 . Reducing to actual will improve processing only slightly

 

 

int readings[numCylinders][numReadingsMax]; // the readings from the analog input

int numReadings = numReadingsMax; // allow this to change to vary smooth vs speed

int index = 0; // the index of the current reading

int sensitivity = 200; // how fine input is parsed usually 20-100. 1023 max.

int thisCylinder = 0; // the index of the cylinder loops

int total[numCylinders]; // the running total

int average[numCylinders]; // the running average

int runPeak [numCylinders]; // running peak value

int inputPin[numCylinders]; // the input pin array

int pinOffset = 0; // setting to 0 maps cylinders 1-4 to pins 0-3

int calibrate [numCylinders]; // an adjustment for input voltage variance

int calibrateCylinder = 0; //

int calibrationMean = 0; // the input adjustment added to each reading

int calibrationFlag = 1; // use the first readings to calibrate

String outString = " "; //

int dispCylinder = 0; //

 

void setup()

{

// initialize serial communication with computer:

// Serial.begin(9600);

// initialize LCD

lcd.init();

lcd.backlight();

 

 

// initialize all the readings to 0:

for (thisCylinder = 0; thisCylinder

{

inputPin[thisCylinder] = thisCylinder + pinOffset; // set cylinder pin plus an offset

total[thisCylinder] = 0;

average[thisCylinder] = 0;

runPeak[thisCylinder] = 0;

calibrate[thisCylinder] = 0;

for (int thisReading = 0; thisReading

{ readings [thisCylinder][thisReading] = 0;

}

 

 

}

}

 

void loop() {

// while the serial stream is not open, do nothing: need for leonardo board

// while (!Serial) ; using UNO board and LCD not serial output

/* for each reading read all cylinders then increment

*/

for (thisCylinder = 0; thisCylinder

{

 

// subtract the last reading:

total[thisCylinder] = total[thisCylinder] - readings[thisCylinder][index];

/* read from the sensor. As vacuum increases MAP sensor output signal voltage drops

so use map command to reverse output so that

more vacuum == lower voltage == higher data points

*/

readings[thisCylinder][index] = map(analogRead(inputPin[thisCylinder]), 0, 1023, sensitivity, 0) + calibrate[thisCylinder];

// add the reading to the total:

total[thisCylinder] = total[thisCylinder] + readings[thisCylinder] [index];

// calculate the average:

average[thisCylinder] = total[thisCylinder] / (index + 1);

// calculate the peak - look for more efficient method depending on output method

runPeak[thisCylinder] = 0;

for (int thisPeak = 0; thisPeak

{

runPeak[thisCylinder] = max(runPeak[thisCylinder], readings[thisCylinder][thisPeak]);

}

 

 

}

 

// advance to the next reading position in the array:

index = index + 1;

 

// if we're at the end of the array...

if (index >= numReadings)

{ index = 0; // ...wrap around to the beginning

display();

 

}

 

/* set a short delay between

readings to add stability

set to approx 1 ?? when running live

*/

delay (1);

 

}

 

void display(){

if (calibrationFlag == 1)

{

calibrationFlag = 0;

calibrationMean = 0;

for (calibrateCylinder = 0; calibrateCylinder

{calibrationMean = calibrationMean + average[calibrateCylinder];

}

calibrationMean = calibrationMean / numCylinders;

for (calibrateCylinder = 0; calibrateCylinder

{calibrate [calibrateCylinder] = calibrationMean - average [calibrateCylinder];

}

// display the results and pause

lcd.clear();

delay(5);

for (calibrateCylinder = 0; calibrateCylinder

{

outString = "Calibrate C";

outString += calibrateCylinder + 1;

outString += " Val ";

outString += calibrate[calibrateCylinder];

lcd.setCursor(0, calibrateCylinder);

lcd.print (outString);

delay(5);

}

delay(3000);

}

else {

/* send results to the lcd */

lcd.clear();

delay(5);

for (dispCylinder = 0; dispCylinder

{

outString = "C";

outString += dispCylinder + 1;

outString += ": PEAK ";

outString += runPeak[dispCylinder];

lcd.setCursor(0, dispCylinder);

lcd.print (outString);

delay(5);

outString = " AVG ";

int chop = average[dispCylinder];

outString += chop;

lcd.setCursor(12, dispCylinder);

lcd.print (outString);

delay(5);

}

}

/* commented out this code for a test

// reset all the readings to 0:

for (dispCylinder = 0; dispCylinder

{

total[dispCylinder] = 0;

average[dispCylinder] = 0;

runPeak[dispCylinder] = 0;

for (int dispReading = 0; dispReading

{ readings [dispCylinder][dispReading] = 0;

}

 

 

}

commented out */

}

Edited by tz89
Link to comment
Share on other sites

Yes I used it today and it worked quite well. I've never used a carb tune just the oil in the tube method. Very easy. Each cylinder shows a value. You just adjust the sync until you are satisfied. And since all 4 cylinders show at once and there is no oil lag I was done in a few minutes. I'm hoping for good improvement suggestions and I'll revise as I go. It's probably cheaper just to buy a carb tune, but what's the fun in that?!?!

Edited by tz89
Link to comment
Share on other sites

Wow,,, :thumbsup2:

 

That is way beyond impressing. Do you actually realise Tom that if/when you finalize this product with user friendly controls/installations/readings and you pack this up in a cool looking casing and market it you could be into serious business ? I have actually quite often wondered when using my carbtune why in the world these things were not made digital. Always thought there was some kind of a technical threshold that prevented it to be possible. But you did it !!! :clap2:

 

Make sure that if you ever want to sell one of these for the second Gen then put me on the top of your list - I´ll come running !

 

Thanks for a great article and for sharing this with us. I´ll be watching closely.

Maybe you should form a company around this - Digi-CarbTune :whistling:

 

All the best from Iceland,

 

Jonas aka StarFan

Link to comment
Share on other sites

Portability............doesnt look to portable, great idea, now if shrunk down to fit in maybe a small leather pouch thats not to bulky it would be a great item to have, I think you would be very wise to make sure someone else hasnt done this and maybe put a patent or copyright on it..............It is indeed very cool. I know your just in the beginning stages of this so size didnt matter, you gotta make sure it works before you try to shrink it down............GOOD JOB, way over my head!!!!

Link to comment
Share on other sites

Shrinking it down and stuffing it all in a nice small box is the easy part, You already did the hard part.

 

I wonder if you could count the vacuum pulses from one of the sensors to get a tach reading??? that way you do not have to tap into the bike electrics anywhere or need an additional input. Like if you are doing a 2nd gen where there ain't no tach. (hey they had to make a tough choice tach or cassette......:whistling:)

 

Nice job, it looks very interesting.

 

 

 

And made for 2 cylinders.

Link to comment
Share on other sites

Yes this will work on a 4 cylinder bike (even 6 once I get beyond the conflicting LCD display).

 

A tach should be doable without too much trouble. All you need is an induction coil and some software to translate that into RPM. You could just wrap some wire around a clothespin and clip it to the plug wire on #1. I'll add this to my list of future mods.

 

To do a tach with just the vacuum pulse data coming in is a non-trivial math problem of trying to detect the period of the pulse with very noisy data. Sounds challenging at least. If only my algebra teacher had said it would be useful for motorcycles I might have paid attention. Any Fast Fourier Transformation experts out there? I wonder if there is a simple way given that we know it's a 4 cycle engine with RPM between ~ 500 and 5000.

Edited by tz89
Link to comment
Share on other sites

/*

 

Modified Smoothing (below) by Tom Hogue 2012

for CarbSync Tool

This code is in the public domain.

Accepts 0-5v from GM 1 bar MAP sensors

Creates arrays of values for all cylinders

v01r01 is the basic development version

v01r02 adds LCD display

v01r03 adds input calibration at startup published on venturerider.org 9-7-12

v01r04 cleans up the code a bit

v01r05 more clean up

v01r06 localize some variables published on venturerider.org 9-9-12

 

Future mods

smartphone display

bluetooth connectivity

tach

live smooth and sensitivity adjustments

 

Smoothing

 

Reads repeatedly from an analog input, calculating a running average

and printing it to the computer. Keeps ten readings in an array and

continually averages them.

 

The circuit:

* Analog sensor (potentiometer will do) attached to analog input 0

 

Created 22 April 2007

By David A. Mellis

modified 9 Apr 2012

by Tom Igoe

http://www.arduino.cc/en/Tutorial/Smoothing

 

This example code is in the public domain.

 

 

 

*/

#include

#include

 

LiquidCrystal_I2C lcd(0x27,20,4);

 

//set the LCD address to 0x27 for a 20 chars and 4 line display

 

 

/* Define the number of samples to keep track of. The higher the number,

the more the readings will be smoothed, but the slower the output will

respond to the input. Using a constant rather than a normal variable lets

use this value to determine the size of the readings array.

Uno board drives the LCD IIC with A4 and A5, leaving A0-3 for 4 MAP sensors

Leonardo board drives LCD IIC with D2 and D3, leaving A0-5 for sensors

*/

const int numReadingsMax = 100; // this should be maybe 5-150. Too large overflows RAM.

const int numCylinders = 4; // max is 4 . Reducing to actual will improve processing only slightly

 

 

int readings[numCylinders][numReadingsMax]; // the readings from the analog input

int numReadings = numReadingsMax; // allow this to change to vary smooth vs speed

int index = 0; // the index of the current reading

int sensitivity = 200; // how fine input is parsed usually 20-100. 1023 max.

int thisCylinder = 0; // the index of the cylinder loops

long total[numCylinders]; // the running total

int average[numCylinders]; // the running average

int runPeak [numCylinders]; // running peak value

int inputPin[numCylinders] = {0,1,2,3}; // set the input pin array per board info above

int calibrate [numCylinders]; // an adjustment for input voltage variance

int calibrationFlag = 1; // use the first readings pass to calibrate

 

 

void setup()

{

// initialize serial communication with computer: NOT USED

// Serial.begin(9600);

 

// initialize LCD

lcd.init();

lcd.backlight();

 

 

// initialize all the readings to 0:

for (thisCylinder = 0; thisCylinder

{

 

total[thisCylinder] = 0;

average[thisCylinder] = 0;

runPeak[thisCylinder] = 0;

calibrate[thisCylinder] = 0;

for (int thisReading = 0; thisReading

{ readings [thisCylinder][thisReading] = 0;

}

 

 

}

}

 

void loop() {

// while the serial stream is not open, do nothing: need for leonardo board

// while (!Serial) ; using UNO board and LCD not serial output

 

/* for each reading read all cylinders then increment

*/

for (thisCylinder = 0; thisCylinder

{

 

// subtract the last reading:

total[thisCylinder] = total[thisCylinder] - readings[thisCylinder][index];

/* read from the sensor. As vacuum increases MAP sensor output signal voltage drops

so use map command to reverse output so that

more vacuum == lower voltage == higher data points

*/

readings[thisCylinder][index] = map(analogRead(inputPin[thisCylinder]), 0, 1023, sensitivity, 0) + calibrate[thisCylinder];

// add the reading to the total:

total[thisCylinder] = total[thisCylinder] + readings[thisCylinder] [index];

// calculate the average:

average[thisCylinder] = total[thisCylinder] / (index + 1);

// calculate the peak - look for more efficient method depending on output method

// this should not be needed runPeak[thisCylinder] = 0;

for (int thisPeak = 0; thisPeak

{

runPeak[thisCylinder] = max(runPeak[thisCylinder], readings[thisCylinder][thisPeak]);

}

 

 

}

 

// advance to the next reading position in the array:

index = index + 1;

 

// if we're at the end of the array...

if (index >= numReadings)

{ index = 0; // ...wrap around to the beginning

display();

 

}

 

/* set a short delay between

readings to add stability

set to approx 1ms when running live

*/

delay (1);

 

}

 

void display(){

int calibrateCylinder = 0; //

int calibrationMean = 0; // the input adjustment added to each reading

String outString; //

int dispCylinder = 0; //

 

if (calibrationFlag == 1)

{

calibrationFlag = 0; // only do calibration once

calibrationMean = 0;

for (calibrateCylinder = 0; calibrateCylinder

{calibrationMean = calibrationMean + average[calibrateCylinder];

}

calibrationMean = calibrationMean / numCylinders;

for (calibrateCylinder = 0; calibrateCylinder

{calibrate [calibrateCylinder] = calibrationMean - average [calibrateCylinder];

}

// display the results and pause

lcd.clear();

for (calibrateCylinder = 0; calibrateCylinder

{

outString = "Calibrate C";

outString += calibrateCylinder + 1;

outString += " Val ";

outString += calibrate[calibrateCylinder];

lcd.setCursor(0, calibrateCylinder);

lcd.print (outString);

}

delay(3000);

lcd.clear();

}

else {

/* send results to the lcd */

for (dispCylinder = 0; dispCylinder

{

outString = "C";

outString += dispCylinder + 1;

outString += ": PEAK ";

outString += runPeak[dispCylinder];

lcd.setCursor(9, dispCylinder);

lcd.print ("___");

lcd.setCursor(0, dispCylinder);

lcd.print (outString);

outString = " AVG ";

outString += average[dispCylinder];

lcd.setCursor(17, dispCylinder);

lcd.print ("___");

lcd.setCursor(12, dispCylinder);

lcd.print (outString);

}

}

// reset all the readings to 0:

for (dispCylinder = 0; dispCylinder

{

runPeak[dispCylinder] = 0; // must reset this. others are kept running for now

/* total[dispCylinder] = 0;

average[dispCylinder] = 0;

for (int dispReading = 0; dispReading

{ readings [dispCylinder][dispReading] = 0;

}

*/

 

}

}

Edited by tz89
Link to comment
Share on other sites

Here's the ebay item # for the LCD I used. 271024721907

 

Attached is the add-in Arduino library I used to make the LCD work.

 

Be sure to virus scan the zip file. Always be safe.

 

If you attempt to build one let me know!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...