How to measure the voltage of a trough in a waveform - Arduino or something else?

Thread Starter

Rich:-)

Joined Oct 14, 2016
61
The project I've set myself is to make an Arduino Nano based datalogger to aid with tuning a motorcycle.

The signals I need to capture are RPM, TPS (throttle position), MAP (manifold vacuum) and AFR (exhaust O2).

The one that is giving me a headache is the MAP.
I thought it would be a moving (but heavily smoothed) voltage signal but after watching it on an oscilloscope I can see it is actually a ripple.

It looks something like this but more like a saw tooth (a faster drop and slower recovery).

waveform.png

It sits at around 3.6v at atmospheric pressure and the dips represent the vacuum when that cylinder is on the intake stroke, and this is what I need to capture.

The Arduino will already be pretty busy I think as it will be constantly counting & converting the rpm square wave pulses to a numerical value, then as often as possible (hopefully every 500ms) it will read all the inputs and write those to the SD card.

Is there a way the Arduino can watch a particular analogue input, to store the lowest voltage for a set period of time and then watch for the next lowest voltage?

If not is there another device that could do this and then output a voltage level to represent the last lowest voltage?
One thing I have considered is using a second Nano to just watch that signal, capture the value and signal the voltage via binary to the datalogger Nano. So if there is a way to do this with Nano I can use a second one to take the load away from the main one as I don't want it to keep loosing count, like every time it pauses to write to the SD card.

Thanks in advance for looking, Rich.
 

panic mode

Joined Oct 10, 2011
4,864
MAP frequency is some 80-180Hz. lets call that 50Hz (20ms period) just to be sure we cover one full wave. so at least collect values during that 20ms window. analog read is about 100-110us so
pseudo code is simple

Code:
min = 1023 ; assume highest value
for n=1 to 200 ; 20ms / 0.1ms = 200
  tmp = analogread()
  if tmp<min then
      min=tmp
  endif
next
 

panic mode

Joined Oct 10, 2011
4,864
but assuming you don't want to only look at 20ms window and 1ms sampling is good enough, you can use an intterrupt... ChatGPT proposed something like:

Code:
const int analogPin = A0;
volatile int minValue = 1023;          // shared between ISR and loop
volatile unsigned int sampleCount = 0;
const unsigned int totalSamples = 500; // 500 ms at 1 ms/sample
volatile bool finished = false;

ISR(TIMER1_COMPA_vect) {
  int value = analogRead(analogPin);

  if (value < minValue) {
    minValue = value;
  }

  sampleCount++;

  if (sampleCount >= totalSamples) {
    finished = true;
    TIMSK1 &= ~(1 << OCIE1A); // disable interrupt
  }
}

void setup() {
  Serial.begin(115200);

  // Configure Timer1 for 1 kHz interrupts (every 1 ms)
  noInterrupts();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;
  OCR1A = 15999;           // (16 MHz / (1 * 1000 Hz)) - 1 = 15999
  TCCR1B |= (1 << WGM12);  // CTC mode
  TCCR1B |= (1 << CS10);   // prescaler = 1
  TIMSK1 |= (1 << OCIE1A); // enable compare match interrupt
  interrupts();
}

void loop() {
  if (finished) {
    noInterrupts();
    int result = minValue;
    interrupts();

    Serial.print("Lowest value in 500 ms: ");
    Serial.println(result);

    // Reset for next run
    delay(1000);
    minValue = 1023;
    sampleCount = 0;
    finished = false;

    // Re-enable Timer1 interrupt
    TIMSK1 |= (1 << OCIE1A);
  }
}
 

Thread Starter

Rich:-)

Joined Oct 14, 2016
61
MAP frequency is some 80-180Hz. lets call that 50Hz (20ms period) just to be sure we cover one full wave. so at least collect values during that 20ms window. analog read is about 100-110us so
pseudo code is simple

Code:
min = 1023 ; assume highest value
for n=1 to 200 ; 20ms / 0.1ms = 200
  tmp = analogread()
  if tmp<min then
      min=tmp
  endif
next
Ok.. interesting.
Yes, the frequency will be 8.3 to 83.3 Hz (based on a 4 stroke max'ing out 10,000 rpm).
But the ECU ignores the MAP sensors above 25% TPS so I think I'd only need to worry about up to 6,000 rpm.
So 50 - 3,000 pulses per minute, or 8.3 to 50 Hz = one full waveform every 120ms to 20ms.
 

Thread Starter

Rich:-)

Joined Oct 14, 2016
61
but assuming you don't want to only look at 20ms window and 1ms sampling is good enough, you can use an intterrupt... ChatGPT proposed something like:
I think I need to try this as that might work fast enough that it could all be done on one Nano, which would save me from having to communicate between two.
 

Thread Starter

Rich:-)

Joined Oct 14, 2016
61
but assuming you don't want to only look at 20ms window and 1ms sampling is good enough, you can use an intterrupt... ChatGPT proposed something like:
Just out of interest (to help me better learn to use ChatGPT), can you remember what you asked it in order to generate that code please?
 

panic mode

Joined Oct 10, 2011
4,864
no different than talking to human... like manager talking to an employee..

"Suppose we have an Arduino Nano. Task is to read an analog input for 500ms and determine lowest value. Sampling can be every millisecond. Can you write the code for that? If possible use an interrupt..."
 

panic mode

Joined Oct 10, 2011
4,864
Grok with above request:


Below is an Arduino Nano sketch that reads an analog input on pin A0 for 500ms, sampling every 1ms, and determines the lowest value using a timer interrupt. The Timer1 interrupt is used to achieve precise 1ms sampling.

cpp:
volatile int lowestValue = 1023; // Initialize to max ADC value
volatile bool samplingDone = false;
volatile uint32_t sampleCount = 0;
const uint32_t SAMPLE_DURATION = 500; // 500ms sampling period

void setup() {
  Serial.begin(9600); // Initialize serial for output

  // Configure Timer1 for 1ms interrupts
  cli(); // Disable interrupts during setup
  TCCR1A = 0; // Clear Timer1 control registers
  TCCR1B = 0;
  TCNT1 = 0; // Clear Timer1 counter
  OCR1A = 15999; // Set compare match for 1ms at 16MHz (16000000 / 1000 - 1)
  TCCR1B |= (1 << WGM12); // CTC mode
  TCCR1B |= (1 << CS10); // Prescaler = 1
  TIMSK1 |= (1 << OCIE1A); // Enable Timer1 compare interrupt
  sei(); // Enable interrupts
}

ISR(TIMER1_COMPA_vect) {
  if (sampleCount < SAMPLE_DURATION) {
    int currentValue = analogRead(A0); // Read analog input from A0
    if (currentValue < lowestValue) {
      lowestValue = currentValue; // Update lowest value
    }
    sampleCount++;
  } else {
    samplingDone = true; // Stop sampling after 500ms
    TCCR1B = 0; // Stop Timer1
  }
}

void loop() {
  if (samplingDone) {
    Serial.print("Lowest value: ");
    Serial.println(lowestValue);
    samplingDone = false; // Reset for potential reuse
    while (1); // Halt (remove this line to allow repeated sampling)
  }
}

Explanation:
  1. Analog Input: Reads from pin A0 (can be changed to another analog pin, e.g., A1-A7 on Nano).
  2. Timer1 Interrupt: Configured to trigger every 1ms (based on 16MHz clock). The OCR1A value (15999) is calculated as (16MHz / 1000) - 1 for 1ms intervals.
  3. Sampling: Inside the interrupt service routine (ISR), the analog value is read, and the lowest value is updated if the current reading is smaller.
  4. Duration: Sampling stops after 500 samples (500ms). The samplingDone flag signals completion.
  5. Output: The lowest value is printed to the Serial Monitor once sampling is complete.
  6. Interrupt Safety: Variables shared with the ISR (lowestValue, sampleCount, samplingDone) are volatile to ensure proper handling.
Notes:
  • Connect the analog signal (0-5V) to pin A0. Voltages above 5V require level shifting or protection.
  • The ADC resolution is 10 bits (0-1023 for 0-5V).
  • The while(1) halts the program after one run. Remove it for continuous sampling.
  • Ensure the Serial Monitor is set to 9600 baud to view the output.
  • If you need a different pin or adjustments, let me know!
 

Thread Starter

Rich:-)

Joined Oct 14, 2016
61
I'm going to buy a signal generator that can give me an offset signwave (looking at many options on eBay now).
Once I have that I can simulated all 4 signals at once and then that will be a great setup to develop this datalogger without having to have my motorcycle running to test it.

I'll report back once I've been able to try the code.

Many thanks, Rich.
 
Top