2

I have a problem i need to solve so i will simply it as much as possible.

Basically there is a system with a micro-controller where it reads 2 sensors. These 2 sensors are of variable resistance type, aka their measuring value is output as different resistance. For example one is a pressure sensor where at 0psi the sensor has a value of 20ohms to ground, and for 100psi it has a value of 120ohms. The sensor is connected between ground and a fixed value resistor so the output acts like a voltage divider.

The micro-controller seems to be multiplexing by switching pull up between each 2 sensors (probably because using a single ADC), at random times (as the microcontroller performs other functions) but approx. every 80ms for each sensor, and about 40ms between each sensor. So at 0ms it reads one sensor, at 40ms the other sensor, and at 80ms back at the first sensor and so on. I have no idea why they haven't used a fixed pull-up, but the pull-up is only done when the controller wants to read the sensor for only about 120us.

I wanted to read these sensors to input to an ECU, without affecting the original device. Thus the easiest solution i could think off as the pull-up isn't always high to perform a read, was to add another microcontroller (in my case a Digispark based on the ATtiny85 running at 16.5MHZ). Alternatively i could open up the device and modify it to use a fixed pull-up but let's say i want to avoid this route. The digispark essentially reads both sensor outputs and when it sees a voltage above ground it outputs it to the ECU. I have since then updated the program to check the ADC values it reads so that it is between the nominal range of the sensor. And while this works there are times where the ADC will read a wrong value and output it to the ECU which looks as random spikes and dips in the data.

Look at my following code:

  const int oil_in = 0; //P5
  const int temp_in = 1; //P2
  const int oil_out = 1; //P1
  const int temp_out = 4; //P4
  int oil_value = 0;
  int oil_value_good = 0;
  int temp_value = 0;
  int temp_value_good = 0;

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  pinMode(oil_out, OUTPUT);
  pinMode(temp_out, OUTPUT);
}

// the loop routine runs over and over again forever:
void loop() {
  oil_value = analogRead(oil_in);
  if (oil_value > 106 && oil_value < 466)
  {
    oil_value_good=oil_value;
    analogWrite (oil_out, oil_value_good/4);
  }

    temp_value = analogRead(temp_in);
  if (temp_value > 113 && temp_value < 920)
 {
    temp_value_good=temp_value;
    analogWrite (temp_out, temp_value_good/4);
  }
}

Each ADC pin has a range of acceptable values. These correspond to the MIN AND MAX nominal values for each sensor. However there are times where the ADC will read at the wrong time and it will output either a MIN or a MAX value depending on the condition. I can replicate this on the breadboard by having the input to the ADC reading a nominal value, and by shorting it to either ground or VCC (5V) i can sometimes trick the ADC to read a MIN or MAX value. And since the next measurement will be done after some time, the output will produce these spikes. Since there is no sync between the readings these error data can be sometimes be outputed for 1-1.5s.

I've tried thinking hard about this problem, but i am not sure for a certain solution that will eliminate this problem. ADC interrupts, changing ADC pre-scaler, filtering of data, problem the reading window is really small (100-120us).

Also nominal ADC input values for each sensors are between 0.56V-2.33V for one sensor, and 0.6V-4.68V for the other sensor. Not sure if the interrupt can even be activated at such low voltage.

How do you think i should proceed?

Kind regards

  • i can sometimes trick the ADC to read a MIN or MAX value - if you short it to Gnd or Vcc, don't you expect this? – Nick Gammon Aug 10 '17 at 7:30
  • Well yes and no, the problem is that the ADC sometimes reads on the falling or rising edge of the signal which might be in the acceptable range, therefore been passed as a "good" value. – TnF Aug 10 '17 at 13:07
3

I have no idea why they haven't used a fixed pull-up, but the pull-up is only done when the controller wants to read the sensor for only about 120us.

If you want to get a good resolution in the resistance measurement, the resistance of your pull-up should be in the same range as the resistance of your sensor. If your sensors have low resistance, this means the voltage divider will draw significant current. In order to save energy, it makes sense to power the voltage divider only while you are doing the measurement.

the reading window is really small (100-120us).

That's short indeed. If you analogRead() a single channel in a tight loop, you will get one reading every 108.6 µs (128×14 CPU cycles ÷ 16.5 MHz), and you have a small risk of missing the window. If you are reading two channels, then you have about 50% risk of missing the window.

Here is what I would do:

  1. Set the ADC prescaler to 64 instead of 128. This way you will get one reading roughly every 54 µs, and at least one sample (but more often two) during the reading window.

  2. Read a single channel in a tight loop, until you get a value above your minimum. Then take one more reading and use the largest of the two as your “good” value.

  3. Switch to the other channel and go back to step 2.

This assumes that outside the reading window your ADC input reads close to zero, as it is grounded through the sensor, with no pull-up. You then have, on each channel, a train of positive pulses, and you want to measure the height of those pulses. There is a small risk of you sampling the rising edge of a pulse, which could give a wrong value which is nevertheless above your minimum. If this happens, then the next sample will be near the center of the pulse. That's why I recommend taking one extra sample and using the largest of the two.

  • I've already set the prescaler to 16 actually since depending on the bootloader version i have 16.5Mhz is only when running it through usb, otherwise the cpu runs @16mhz, giving an effective prescaler of 1Mhz which is in the acceptable range in the Atmel datasheet. It didn't affect much to be honest in bench testing (of which i cannot actually replicate exactly 100% the conditions), but last night i've integrated the MovingAverage lib which performs exponential moving average filtering with very good results, although i will do more testing and report back. Will consider the other recs as well – TnF Aug 10 '17 at 13:15
  • @TnF: 1 MHz is kind of borderline for the ADC frequency: it should work, but expect degraded accuracy. An exponential moving average is very good at filtering out Gaussian white noise, but is far from optimal against sporadic glitches. A median filter would be way better. – Edgar Bonet Aug 10 '17 at 14:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.