Problem with ESP8266 interrupts

I am exploring the possibility of using an ESP8266 in place of an Attiny1634 and nRF24L01+ for my model train controller. The ESP8266 is marginally smaller and requires a lot less connections because the MCU and the wireless are all in one package.

I wonder if anyone has any ideas about why my QRE1113 reflective optical sensor reliably triggers interrupts to detect pulses when connected to an Atmega 328 (3v, 8MHz) but not when connected to an ESP8266 running virtually the same program. The QRE1113 is detecting a white spot on a disk rotating on the shaft of a small motor to measure the speed. It is wired like this (same as this Sparkfun circuit, but I am not just using their breakout board).

QRE1113-SparkfunAnalogCircuit.png

I wrote a short program for the Atmega 328 so it could output pulses equivalent to what come from the QRE1113 and the ESP8266 detected them with rock solid reliability and accuracy (i.e it was registering the same intervals as the Atmega 328 was generating).

When I look at the output of the QRE1113 with my oscilloscope it is producing a very clean HIGH and LOW at a very consistent interval (motor running at constant speed). The only difference between the output from the QRE1113 and the Atmega 328 is that the transitions with the Atmega 328 are much sharper - almost instantaneous.

From all of this I reckon there is something about the QRE1113 behaviour that is at the margin of what is acceptable to the ESP8266. But I cannot think what it might be, how to test for it, or how to improve things.

One thought is to feed the QRE1113 output into a Schmidt trigger, or an OpAmp - but I don't want a solution that requires extra parts as I don't have space for them and I don't have the ability to use small SMD components.

Thanks.

...R

It works on the ESP8266 without using interrupts but not when using interrupts you say?

Please post complete wiring diagram and (probably even more important) your actual ESP8266 code.

wvmarle:
It works on the ESP8266 without using interrupts but not when using interrupts you say?

No.

I have only tried a program using interrupts - essentially identical programs on the ESP8266 and on the Atmega 328.

The program works properly on the ESP8266 when the pulses are generated by another program on the Atmega 328 but it does not work properly when the pulses are generated by the QRE1113 attached to the small motor.

On the other hand, the interrupt program works properly on the Atmega 328 when it is connected to the QRE1113. I have arranged things so I can switch the jumper wires (for the QRE1113 and the motor driver) from the ESP8266 to the Atmega 328 without disturbing anything else.

It seems to me there is something different about the input circuits within the ESP8266 but I have been unable to find any discussion to confirm or deny that suspicion.

You will see, in the code below, that I have been fiddling around with INPUT and INPUT_PULLUP. When I use INPUT_PULLUP some of the pulses seem to be missing when viewed with my 'scope. This also fuels the suspicion in my previous paragraph.

The diagram in the Original Post is the complete circuit. The connection marked OUT goes to GPIO5 on the ESP8266 (or to pin 2 on the Atmega 328). The motor driver (DRV8833) is connected to GPIO14 and 12 (or pins 5 and 6 on the Atmega 328).

// python-build-start
// action, upload
// board, esp8266com:esp8266:generic
// port, /dev/ttyUSB1
// ide, 1.6.10
// python-build-end


#define ledPin  16
#define PWMpinCW  14
#define PWMpinCCW  12
#define interruptPin 5 // 13

	// variables used by the ISR
volatile unsigned long isrRevMicros = 0;
volatile unsigned long isrRevCount = 0;
volatile bool newRevMicros = true;

unsigned long revCount;

unsigned long revMicros;
unsigned long prevRevMicros;
unsigned long prevIsrMicros;
unsigned long latestIsrMicros;
byte PWMval = 90;

unsigned long prevPrintMillis;
unsigned long printIntervalMillis = 300;



void setup() {
	Serial.begin(500000);
	Serial.println("ESP8266RevMicrosTest.ino");

	analogWriteRange(255);

	analogWrite(PWMpinCCW, 0);
	analogWrite(PWMpinCW, (byte) PWMval);

	//~ pinMode(interruptPin, INPUT_PULLUP);
	//~ pinMode(interruptPin, INPUT);
	attachInterrupt(interruptPin, revDetectorISR, FALLING);
	//~ pinMode(interruptPin, INPUT_PULLUP);
	pinMode(interruptPin, INPUT);
}

void loop() {
		// update revMicros
	if (newRevMicros == true) {
		prevIsrMicros = latestIsrMicros;
		noInterrupts();
			latestIsrMicros = isrRevMicros;
			revCount = isrRevCount;
			newRevMicros = false;
		interrupts();
	}

	if (millis() - prevPrintMillis >= printIntervalMillis) {
		prevPrintMillis += printIntervalMillis;
		revMicros = latestIsrMicros - prevIsrMicros;
		Serial.print("Rev Micros "); Serial.println(revMicros);
	}
}

void revDetectorISR() {
	isrRevMicros = micros();
	isrRevCount ++;
	newRevMicros = true;
}

All bright ideas will be welcome.

...R

GPIO5 should be fine - nothing special about that pin (pin 0, 2 and 15 are used in the boot process so care has to be taken when using them).

For my interrupt routine to work (and prevent lots of random crashes) I found out that I must tell the compiler to keep it in memory at all times by adding the CACHE_RAM_ATTR flag like this:

void ICACHE_RAM_ATTR revDetectorISR() {
  // ISR code
}

Other than that I don't see any issues.

wvmarle:
Other than that I don't see any issues.

Thanks. I'll try that.

Out of curiosity, where did you learn about that?

...R

Robin2:
Out of curiosity, where did you learn about that?

The ICACHE_RAM_ATTR you mean?
Googling... Lots of googling... Then reading forums mostly and some blogs, but forums tend to be the best (including the ones at esp8266.com but not really following those myself).

I got random crashes, took a long time to track down what caused it, then it took an hour or two of searching to find out about this flag, and then some more time to find out on how to actually use it.

It did not make any difference to my program. But that might be because the program is not doing any WiFi stuff so I will keep it in mind.

Incidentally I had to add a function prototype at the top of my program to get it to compile when I added the ICACHE_RAM_ATTR

I have since made a version of the program that captures 20 measurements into an array and then prints them all so that I can see the complete sequence. It is behaving in a way that I dimly recall seeing when I first tried using the QRE1113 on an Uno. That is, in between the correct values it has short and long revMicros values which, when added together represent the correct value. For example if the correct value is 8000 there might be a 5800 followed by a 2200. I must delve into my old programs and notes to see how I got that to stop. I do remember at one stage implementing a debounce period which eventually proved to be unnecessary.

...R

Update ...

Further testing shows that using the interrupt mode FALLING is giving almost the same results as mode CHANGE. That is, most of the time it is showing separately the duration of the LOW and the HIGH parts of the pulse. (The time for a revolution is the sum of those two parts). Some of the time the FALLING mode shows the combined value whereas the CHANGE mode does not seem to.

I did wonder if the Arduino code for attachInterrupt() might be wrong but it does seem to be setting the ESP8266 register correctly.

I guess one option is to use CHANGE mode and sum all the intervals.

What bothers me, however, is the risk that it will behave differently after I have taken the trouble to build a really small version of the thing so it will fit in the model railway locomotive.

...R

Robin2:
Further testing shows that using the interrupt mode FALLING is giving almost the same results as mode CHANGE. That is, most of the time it is showing separately the duration of the LOW and the HIGH parts of the pulse. (The time for a revolution is the sum of those two parts). Some of the time the FALLING mode shows the combined value whereas the CHANGE mode does not seem to.

That sounds to me as if you have some form of bounce, which I wouldn't expect from an optical sensor.
I have so far only used falling edge interrupts on the ESP, not needing others so far, but no reason to believe they don't work as advertised. That's why I suspect your signal to be imperfect, which doesn't make much sense either.

You see the same if you only look at RISING?
Any chance to connect it to a scope for a closer look at the actual signal?

wvmarle:
That's why I suspect your signal to be imperfect,

That is my suspicion also. But why is there no problem with an Atmega 328?

You see the same if you only look at RISING?

RISING produces garbage - lots of short pulses. The output from the QRE1113 is normally high and the white blob causes a LOW pulse.

Any chance to connect it to a scope for a closer look at the actual signal?

I have been doing that. The problem is that if I expand the timebase in an attempt to see a very short lived glitch I lose track of the bigger picture. I am by no means expert with a 'scope as I very rarely use it.

When I wrote the above about RISING producing garbage it made me wonder exactly what is the trigger point that sets off the interrupt. On the scope there is a tiny bit of wiggle in the voltage when it is HIGH. But HIGH is around 3v and my understanding is that the ESP8266 considers anything over 0.75 Vcc is HIGH and anything below 0.25 Vcc is LOW. But I don't know if the voltage must cross that gap to cause an interrupt. I'm wondering if wiggling above and below 0.75Vcc would be sufficient? But wiggling like that would not account for the output when I use FALLING or CHANGE unless the threshold alters for the different modes.

...R

When testing I found the pin changes from HIGH to LOW or the other way around at approx. 0.5 Vcc. The values you mention (I though it was more like 1/3 and 2/3) are where it's guaranteed to be read as HIGH or LOW.

Maybe a small filtering cap can help?

wvmarle:
Maybe a small filtering cap can help?

I have been wondering about that also. Where should I put it? And what sort of value? I am very weak on that sort of stuff and tend to do all the things I warn programmers against - like mindlessly sticking a piece in here to see what happens.

I tried a 100nF cap in the signal line and between signal and GND and signal and Vcc none of which seemed to make any difference.

I put 10 µF in the breadboard across Vcc and GND for the QRE1113 and that made no difference.

If I was getting random measurements I would be more convinced about noise - for example interference from the PWM lines going to the motor. But the measurements are very definitely not random. I must write myself an essay about in the hope of getting all my thoughts in one place.

...R

Another update ...

I connected a potentiometer so its centre pin would trigger the interrupts while I measured the voltage at which the trigger happens. I discovered that at any voltage between 0.57v and 1.97v (max voltage was 2.92v) a constant stream of interrupts was produced whether RISING or FALLING mode was chosen. It was impossible to rotate the pot from low to high and get a single interrupt.

That leads me to think that the unwanted interrupt with my QRE1113 is because the rise (and fall) time is relatively slow. Maybe that slow rise is not significant when the QRE1113 is connected to the much slower 8MHz Atmega 328.

I have modified my ESP8266 program to use the CHANGE mode (to prevent the possibility of a "spurious" correct reading) and to sum every pair of readings (i.e. the widths of the low and the high periods) after discarding the occasional really short measurement. That seems to give reliable measurements of the microseconds per revolution at a range of speeds.

...R

Adding an 74LVC1G14 is maybe an option, this should give you a fast enough rise time.

Ciao, Ale.

A Schmidt trigger may help - but strange that the ESP8266 produces so many interrupts for RISING or FALLING yet just one for CHANGE. That just doesn't sound right.

ilguargua:
Adding an 74LVC1G14 is maybe an option, this should give you a fast enough rise time.

I don't want to add extra parts. Doing that would eliminate the advantage (easier construction) compared to my existing working nR24L01+ system.

wvmarle:
A Schmidt trigger may help - but strange that the ESP8266 produces so many interrupts for RISING or FALLING yet just one for CHANGE. That just doesn't sound right.

Sorry for not being clearer. It behaves the same when I use CHANGE.

...R

Whilst this is an old post and has not been posted in for over a year, the problem is not old and I have been trying to tame the same problem for the last two days.
I am not sure of the original poster solved the problem, but he has not changed it to a solved status, so it is worth commenting.

I had the same problem after a converted the code that ran quite OK on a + 5V Arduino Pro mini which controls the frequency of a diesel alternator, to a ESP8266, so that I can monitor it with MQTT/NodeRed etc.

I had previously tested this set up with another Arduino Pro mini as a signal generator capable of generating steps frequencies of 47 to 53 Hz in 1 Hz. steps. This worked well and the system never misses a beat( or adds an extra beat...hi hi)

Not so, with the ESP8266. I used the same test generator code on a ESP8266 as well. I got lots of false triggering and even with Kalman filtering caused heaps of problems. I tried everything (except the final solution) and had all sorts of problems.

It seems that the ESP8266's have more common mode spikes on the power supplies than what the ATMEL chips do.
When running the test setup from one PC and the pulse generator from another PC (or even a battery) , I could not stable
results.

I seems that -ve glitches on the test signal sooner or later coincided with +ve glitches on the controller side and palse interrupts would occur, and this is especially more likely with 3.3V logic , with less margin.

I then decided to run on a common power supply, whether it be my desktop or notebook and the same code does not miss a beat.

If this was a problem earlier with the ATMEL boards, I never noticed it and/or I was very lucky not to to be struck with the problem.

Moral of the story.
Especially when using interrupts, but probably at all times, run all the sensors from the same power supply as the controller, so that the common mode noise is the same.

In my case the interrupt source will be , like the OP's, a Hall effect device which will happily run on 3.3V with a three wire connecting lead.

I hope that this saves someone time!

rexlee:
Whilst this is an old post and has not been posted in for over a year,

I have given up the idea of using the ESP8266 because its energy consumption is much higher than the Atmega + nRF24 combination.

All of my tests used a single (battery) power supply.

...R

I have the same problem using interrupt on esp8266. I create a simple circuit to control the office light. the circuit is powered from a 110vac to 3.3v dc transformer circuit and it is on the same powerline as the bathroom fan. Whenever the fan turn OFF from ON state an unwanted interrupt is triggered and it switchs my light. The light is program to be switched On or Off using either wifi (web brower) or the switch in the office connecting to the interrupt pin on the 8266.
Any suggestion would be appreciated.
Thx

If it's the same problem, look through this thread for suggestions.

Otherwise, please start your own - and don't forget to add all required information for informed replies (see the sticky).