I'm guessing the car is using the frequency and number of pulses but for some reason I can't get my head around how to do this

1. I would recommend putting a toothed wheel (11 teeth) to a driveshaft and one sensor to read that. Just have the wheel made and then saw it in two. A halfh flange for the driveshaft and mount the wheel to a flange. Two allen bolts on each side crosswise will clamp both sides to driveshaft securely. Select a good place to mount a fitting and mount a sensor to read pulses... https://www.aliexpress.com/item/Free-Sh ... 68401.htmlJack Bauer wrote: ↑Wed Feb 20, 2019 7:18 pm I am getting the Panzer back next week and have a problem with the speedometer. In the 8 series the speedo is fed by pulses from a reed switch and magnet mounted in the differential. As the diff is no longer there due to the Tesla drive unit swap I needed another way to feed the speedometer signal. So I mounted a hall sensor to the Tesla drive unit and pointed it at the heads of the 8 driveshaft bolts on one side. So now I get 8 pulses per turn of the wheel. Problem is the speedo expects 11 pulses per rev so naturally it reads low. So I want to use an arduino or similar to take in the 8 pulses and make them 11 if that makes sense? I am kind of confused though as to how to go about it in a way that will not create false speed signals. Any thoughts?
I'm guessing the car is using the frequency and number of pulses but for some reason I can't get my head around how to do this![]()
Code: Select all
#define SOURCE_PULSES 8
#define TARGET_PULSES 11.00
#define CLOCK_OVER_DIVISOR 250000.00 // 16MHz clock with divisor = 64
volatile int pulIn = 0;
volatile unsigned long now;
volatile float freqIn;
volatile float freqOut;
volatile unsigned long startMicros[SOURCE_PULSES];
volatile unsigned long periodMicros;
volatile boolean turnF = true;
const int pulseInPin = 2;
const int pulseOutPin = 9;
unsigned long lastMicros = 0;
void setup()
{
int i;
//Disable interrupts
cli();
// Clear Timer1 registers
TCCR1A = 0;
TCCR1B = 0;
// Set OCR1A (TOP): 0 to switch off pwm
OCR1A = 0;
// Configure Timer/Counter 1
// Select Phase & Frequency Correct PWM Mode, and OCR1A as TOP.
// Enable COM1A0 to toggle OC1A on compare match
TCCR1A |= ((1 << WGM10) | (1 << COM1A0));
TCCR1B |= ((1 << CS10) | (1 << CS11) | (1 << WGM13)); // Fcpu/64
for (i=0; i < SOURCE_PULSES; i++){
startMicros[i] = 0;
}
pinMode(pulseInPin, INPUT);
pinMode(pulseOutPin, OUTPUT); // set pin 9 as output - it's this pin that get toggled by PWM
// setup our interrupt for rising edge
noInterrupts();
attachInterrupt(0,countPulseIn, HIGH); // use interrupt 0 (pin 2) and run function countPulse when pin 2 goes HIGH
interrupts();
}
void loop(){
if (micros() - lastMicros > 1000000UL){
// not had a pulse for 1 seconds so stop output pulses
if (!turnF){
OCR1A = 0;
}
lastMicros = micros();
noInterrupts();
turnF = false;
interrupts();
}
}
void countPulseIn() {
now = micros();
turnF = true;
if (pulIn < SOURCE_PULSES -1){
pulIn++;
}else{
pulIn = 0;
}
if (startMicros[pulIn] > 0){
periodMicros = now - startMicros[pulIn];
freqIn = (float)1000000/periodMicros;
freqOut = (freqIn * TARGET_PULSES) / SOURCE_PULSES;
OCR1A = ((CLOCK_OVER_DIVISOR / ((freqOut/2.00) ))/32 -1);
}
startMicros[pulIn] = now;
}
@doobedoobedo, can this schetch be made to work with 0RPM input. Like say if you have 0rpm on the motor program would still give you 800rpm.
Code: Select all
/*
* © 2019 Ron Simpkin
* Public domain - do with it what you will
*
* Works on Arduino Uno and Pro-mini and probably any other 328p based Arduino running at 16MHz
*
* Limitations:
* lowest output frequency available is > 0.0595Hz
* highest output frequency is 4MHz -> but very poor resolution up this high (next highest is 2MHz)
*
* Digital Pins are _fixed_
* Digital pin 2 for incoming pulses
* Digital pin 9 for outgoing pulses
* Default pin for analog in is A2
*
* Un-comment ONE define to set the mode
*
* 1. MODE_FIXED:
* outputs a fixed frequency = RPM * PULSES_PER_REVOLUTION / 60
* change the #defines to suit your needs - all values must be postfixed UL
* 2. MODE_ANALOG_IN:
* outputs a frequency with a settable minimum and maximum depending on
* the voltage on an analog pin (default pin A2)
* 3. MODE_PULSE_IN
* takes an input signal on pin 2 and outputs a signal on pin 9
* set SOURCE_PULSES equal to the number of pulses in per revolution
* set PULSES_PER_REVOLUTION equal to the number of pulses you need to go out each full revolution
* set RPM_CUTOFF to the minimum RPM input you want to map directly to output pulses
* when the input drops below RPM_CUTOFF then the output is RPM_MIN
* NB. SOURCE_PULSES can't be set to 1. If it is actually 1 set it to 2 and double the other settings.
*/
//#define MODE_FIXED
//#define MODE_ANALOG_IN
#define MODE_PULSE_IN
const int pulseInPin = 2;
const int pulseOutPin = 9;
const int analogInPin = A2;
float fTarget, fMinimum, lastTarget;
#ifdef MODE_FIXED
#define PULSES_PER_REVOLUTION 1
#define RPM 2000UL
#endif
#ifdef MODE_ANALOG_IN
#define PULSES_PER_REVOLUTION 8
#define RPM_MIN 40
#define RPM_MAX 800UL
unsigned long analogValue;
unsigned long lastValue;
#endif
#ifdef MODE_PULSE_IN
#define SOURCE_PULSES 8
#define PULSES_PER_REVOLUTION 11
#define RPM_CUTOFF 40UL
#define RPM_MIN 0
volatile int pulIn = 0;
volatile int lastPul = 0;
volatile unsigned long now;
volatile unsigned long startMicros[SOURCE_PULSES];
volatile unsigned long lastPulMicros = 0;
volatile float fIn = 0;
volatile int suppressStart = 0;
float fCutoff;
#endif
void setup()
{
pinMode(pulseOutPin, OUTPUT); // set pin 9 as output - it's this pin that get togged by PWM
noInterrupts();
// Configure timer 1 (16bit resolution)
TCCR1A = 0;
TCCR1B = 0;
OCR1A = 0;
// Select Phase & Frequency Correct PWM Mode, and OCR1A as TOP.
// Enable COM1A0 to toggle OC1A on compare match (pin 9)
TCCR1A |= ((1 << WGM10) | (1 << COM1A0));
#ifdef MODE_FIXED
fTarget = (float) RPM * PULSES_PER_REVOLUTION / 60;
setPWM(fTarget, fTarget);
#endif
#ifdef RPM_MIN
fMinimum = (float) RPM_MIN * PULSES_PER_REVOLUTION / 60;
#endif
#ifdef MODE_PULSE_IN
fCutoff = (float)RPM_CUTOFF * PULSES_PER_REVOLUTION/ 60;
for (int i=0; i < SOURCE_PULSES; i++){
startMicros[i] = 0;
}
pinMode(pulseInPin, INPUT_PULLUP);
// use interrupt 0 (pin 2) and run function countPulse when pin 2 goes HIGH
attachInterrupt(digitalPinToInterrupt(pulseInPin), countPulseIn, RISING);
#endif
interrupts();
}
void loop(){
#ifdef MODE_ANALOG_IN
int pinVal = analogRead(analogInPin);
analogValue = map(pinVal, 0, 1023, RPM_MIN, RPM_MAX);
fTarget = (float) analogValue * PULSES_PER_REVOLUTION / 60;
if (lastValue != analogValue){
setPWM(fTarget, max(fTarget, fMinimum));
lastValue = analogValue;
}
#else if MODE_PULSE_IN
float f = fIn;
fTarget = f * PULSES_PER_REVOLUTION;
if (fTarget < fCutoff){
fTarget = fMinimum;
}
if (micros() - lastPulMicros > 1000000UL){
fTarget = fMinimum;
}
if (lastTarget != fTarget){
lastTarget = fTarget;
if (suppressStart == SOURCE_PULSES){
setPWM(fTarget, fTarget);
}
}
#endif
}
void setPWM(float fTgt, float fMin){
float pwmClock;
if (fTgt <= 0.0595){OCR1A = 0;return;}
/* Set all of TCCR1B in one operation - Binary assignment!*/
if (fMin > 61.1 ){
//TCCR1B |= ((1 << CS10) | (1 << WGM13)); // Fcpu/4 61.1Hz -> 4MHz
TCCR1B = B00010001;
pwmClock = 4000000.00;
}else if (fMin > 7.64){
//TCCR1B |= ((1 << CS11) | (1 << WGM13)); // Fcpu/32 7.64Hz-> 500KHz
TCCR1B = B00010010;
pwmClock = 500000.00;
}else if (fMin > 0.952){
//TCCR1B |= ((1 << CS10) | (1 << CS11) | (1 << WGM13)); // Fcpu/256 952mHz->62.5KHz
TCCR1B = B00010011;
pwmClock = 62500.00;
}else if (fMin > 0.239){
//TCCR1B |= ((1 << CS12) | (1 << WGM13)); // Fcpu/1024 239mHz -> 16.25 KHz
TCCR1B = B00010100;
pwmClock = 15625.00;
}else{
//TCCR1B |= ((1 << CS10) | (1 << CS12) | (1 << WGM13)); // Fcpu/4096 59.5mHz -> 3.9 KHz
TCCR1B = B00010101;
pwmClock = 3906.25;
}
OCR1A = round(pwmClock/fTgt);
}
#ifdef MODE_PULSE_IN
void countPulseIn() {
lastPul = pulIn;
now = micros();
if (pulIn < SOURCE_PULSES -1){
pulIn++;
}else{
pulIn = 0;
}
if (suppressStart > SOURCE_PULSES -1){
fIn = (float)1000000/(now - startMicros[pulIn]);
}else{
suppressStart++;
}
lastPulMicros = now;
startMicros[pulIn] = now;
}
#endif
I will definitely try this later this month thanks. It will help keeping my car in ECO mode without it having that in BSI. It may prove to be applicable for other vehicles also.doobedoobedo wrote: ↑Tue Mar 05, 2019 10:27 pm Really 3 sketches in one.
It actually doesn't matter if you have a missing tooth, or even if the teeth aren't evenly spaced. Just put in the correct number of pulses for a full revolution. The rotational speed is re-caclulated after each pulse based on that tooth's time for a full revolution, so if one is missing it'll just send out two at the same frequency before re-caclulating for the next real one.
So say you had a sproket which should have 9 teeth, but one is missing, you'd put 8 as the number of pulses in per revolution and 8 as the number out per revolution so the number of teeth is correct and the ratio of in to out is the same (it's the number of teeth and the ratio that matters), but it would actually send out 9 pulses just missing a calculation on the missing tooth.
Does that make sense?
Hidoobedoobedo wrote: ↑Wed May 08, 2019 6:37 pm 58 holes = 58 pulses per revolution for the input parameter
No, it calculates the time for one complete revolution on the rising edge of each pulse, it doesn't matter how long the pulse is as it calculates the current rpm on each one.
Then what is the use for the large hole? Why wouldnt they just use 60 holes. It would even be symetrical, as in 60*X/min(60s) = X....doobedoobedo wrote: ↑Thu May 09, 2019 10:01 pmNo, it calculates the time for one complete revolution on the rising edge of each pulse, it doesn't matter how long the pulse is as it calculates the current rpm on each one.
The missing holes are detected by the ECU and that allows the ECU to know where TDC is which is a requirement for correct ignition timing and injection timing. Whether or not the ECU throws a fault if the missing teeth are not detected will be interesting. 60-2 timing wheels are pretty common and standard on most BMW's too.