IR sensors ISR


what I did not found in the documentation …the call to
read_line_sensors_calibrated(sensor_values, readMode);

is blocking for the timeout …in worst case?
Could it be a better approach to let an ISR run in the background?
That would you give some clock cycles to do other things instead of polling in
a while loop …
//not sure if I got it right: while (time < _maxValue)
proposal: charge the capacitor every max timeout …and wait for
the interrupt of the sensor threshold interrupt …
read_line_sensors_calibrated(sensor_values, readMode);
could return immediately with the current values …even if the different
sensors are not consistant …as white will return much faster than black …
but you do not have better once
or you have a ISR which always returns as soon you have all sensors or timeout.

I fear a little bit to rewrite the whole lib.
(I´m more a script kid than a real programmer)


You could probably rewrite the sensor code to use an ISR, but it would be tricky. For example, the 8-bit timer currently overflows a few times during a read of the sensors. This is easy to keep track of in a tight loop, but if you use interrupts you’ll have to make sure to count the overflows correctly.


And using get_ticks()?

(edit some days later)
if I got it right the 3pi uses the digital sensor.
The IR reflectance is measured over the time it takes until
the capacitor voltage is dedected as digital low at the input pin.
As this trigger level is not realy well defined (its always a range)
and might change even after calibration …I wonder if the resolution of 1000 makes
sense. (special because of the non linear combination of capacitor and IR-transistor … but different story)

What I see …the approach is near the InPulse lib.
You would simply measure the lastHighPulse. But additional you need to switch the pin to output for 10 us.
And now the tricky question …how to keep the Pin Change Interrupt ISR handling compatible
between InPulse and IR Sensor lib …

what I wonder … as the IR sensors are at Port C … they could deliver analog values?
So we could do a voltage sampling …which makes the whole sensor reading more predictable
in terms of timing? In a static situation you could predict the final outcome of the measurement
and you do not have to wait until discharge is complete.

During calibration you might dertermine sampling rate. What I read was …it takes 128*13 cycles to get one analog value and you can have an interrupt for A/D complition … one measurement takes 83 us ; for 5 sensors 416 us;

But this timing makes it a bit tricky for the capacitor charge function …you would also have to load it
per pin shifted by 83 us … so you may load it 10us before the next measurement starts… and how good will be the result of the A/D with an falling voltage? And than you have to care for the A/D capacitor
also a comparator function with interrupt would not improve the situation much …
-> might somehow work …not realy fast and acurate
-> idea discarded

Short question about the 10 us delay during the load of the capacitor:
220 ohm and 2.2 nF gives RC time of ~500 ns … So 2.5 us should be enough.
Is it just a safety margin or did you made bad experiences with values lower than 10 us?


I think you are right about the time constants, but the same library is used for our QTR-1RC and QTR-8RC sensors, which have 10nF capacitors. Also, there is not much point in trying to save a few microseconds right before a delay of about 1ms.


ok, I agree and guess its peanuts …
but in the ISR version …that would be 3 mallocs :wink:
but yeah … I´m also thinking about …reusabiltiy and optimum for the 3pi
…as you have anyhow to compile …and modern dektop computer would be
fast enough … a solution with #define could cover the different HW setups
and save some MHz

by the way what I miss for the inpulse lib … to add pins later.
So if I have IRsensor pins and another function (like IR-RC decoder)
it would be more modular if I can add the pin (group) in 2 different classes


Why are you doing malloc in an ISR? That sounds like a really bad idea.


Hi Paul,

I was just joking …what I can get out of 7.5 us …as you mentioned some posts before that malloc is expensive

another strange academic observation …
my ISR (with inPulse) gives a bit more smooth statistical results than your polling method …
I can guess, that the ISR is more deterministic …but ISR handling takes some more time …
(not calibrated values)

what I also observe …that some values stick together for the different sensors
// is there a nice bbcode table ?
s0 s1 s2 s3 s4 s0 black s1 black s2 black
611,491,362,362,611 1782,319,319,391,626, 0,1620,189,189,689, 225,269,810,225,427,
625,502,381,381,625 1793,307,307,388,650, 0,1621,190,190,689, 231,301,824,231,440,
619,497,374,374,619 1785,300,300,378,635, 0,1620,189,189,689, 225,269,828,225,424,

I think it is unlikely that they got over a long range always the same value. (I show only a portion)
On the other side I can show that they are independed, when I move the black line to them (s0)
The pattern just moves.

…so I guess …someting with the ISR is wrong or with the HW …PCB layout?

… ok for a toy it is ok … we should not use it in medical environment.

the test above is not 100% fair.
When I run the standard lib without my new ISR based method (inPulse) than also this one looks
fine …
I would explain that with the ISR …which interrupts the while loop of the standard lib
if there is no ISR / interrupt …than the while loop runs predictable.

new interpretation of the equal sensor values in the ISR method. The interrupt trigger is that close
…that the ISR can not differentiate …-> minimum resolution for PCI method is the runtime of the ISR.

taken from the OO thread, Paul wrote:

Yes that is true …but this is the general issue if you want to do multiple things at the same time and in realtime.
And using blocking function is fine for sequencial programms. And as long every function returns fast enough it is fine.
Coming back to the IR RC code receiver … it might be the better approach to put only the PWM bit -> code detection
in a ISR … than you can poll the IR code every …~ 26 ms. But as I have shown already …as soon the 3pi readPrivate busy wait loop is interrupted by an ISR it becomes unstable too.

The D / I values are normaly based on delta t. To make it more accurate …determine the time between 2 measurements and calculate that in …finally you might end up in a lower sampling rate but in a more stable algorythm …allowing higher driving speed.

…compared to the demo PID …here you block until the slowest sensor has a PIC … this depends on the black level …so the time between to measurements depends from the black level

first demo line follower with ISR sensor read and IR RC receiver.
implemented is a speed control with remote control keys 1…9
blue key is start
any other key will stop the 3pi…for panic stop
I´m not that happy with the line follower …here is room for improvement.
It makes too many errors …and I do not reach max speed.
Might be the not plane underground has also impact.
proportion gets now speed and delta time between 2 measurements

long proportional = ((long)mot_high * deltaticks * pisi.pos_vec)

below the header file of the new sensor class


#ifndef F_CPU
#define F_CPU 20000000UL

#include <avr/io.h>
#include <pololu/OrangutanTime.h>
#include <pololu/OrangutanPulseIn.h>
#include <pololu/OrangutanDigital/OrangutanDigital.h>

#define CALIBRATED 1;
#define RAWVALUES 0;
class pisiIRsensor {

		pisiIRsensor (OrangutanPulseIn* p_pipulsein);
		void init();
		//sets the done=1; would be cool if we could also set the ISR inpulse here
		// but we have to consider the IRRC pin too ..
		void startMeasurement(); //init variables and load capacitor
		uint8_t read_sensors(uint8_t calibrated);
		uint8_t set_maxmin(); //updates max_min array for calibration
		uint8_t calc_position(); //determines where the black line is
		const static uint8_t num_sensors = 5;
		uint16_t sensors[num_sensors];
		uint8_t ready; //ready knows the pins with a result
		uint8_t done; //measurement has finished 0 = no, 1= yes, 2= timeout
		uint8_t on_line; //tells you if we have a black line
		int16_t pos_vec; //tells which direction the line is seen
		uint32_t end, now; //for timeout; calculation of measurement duration 
		OrangutanPulseIn* pipulsein_ptr; //points to the pulsein object
	//needed for the timeout maximum pulse length we have seen
	// but we might set it lower to speed up the measurement
	const static uint16_t TIMEOUT = 8000;
	const static uint16_t MAXVALUE = 1023; 
	// maximum calibrated value; min is per dinfinition 0
	// to hold the max min per sensor
	uint16_t max_values[num_sensors]; 
	uint16_t min_values[num_sensors];

#endif /* PISIIRSENSOR_H_ */

and one more …needs some tuning but I think interessting approach
if line is not detected …reduce speed, if detected increase …slowly
needs some tuning

if ((pisi.on_line==0) && mot_high >25) 
else if (online_cnt < 255) online_cnt++;
if ((online_cnt ==200) && (mot_high <220)) {

I found the cause for the instability. The movement is sometimes a bit to rough, so the IR sensors nudge to the ground. By that they loose the line and pi moves to the wrong direction. So I have mounted a round axial condensator near the IRsensors which solves the problem mechanical. I hope I will find a better sw solution which smooth the movement of the 3pi.