ESP8266 GPIO expander missing interrupts - arduino

I have a program that lets an LED pulse. I also connected the PC8574 GPIO expander with a push button. I want to evaluate the keypress. However, I can only read the status of the INT (interrupt) while the program is in the part between making the LED brighter and making it darker again (between the two for loops)
I know that the problem is the delays withing the for loops but I have no idea how to avoid that.
Would it be possible to evaluate the interrupt related code more often or like a real interrupt - always when the actual key is pressed? And if so, how?
I use this library: https://github.com/WereCatf/PCF8574_ESP
/*LED_Breathing.ino Arduining.com 20 AUG 2015
Using NodeMCU Development Kit V1.0
Going beyond Blink sketch to see the blue LED breathing.
A PWM modulation is made in software because GPIO16 can't
be used with analogWrite().
*/
#include <pcf8574_esp.h>
#include <Wire.h>
TwoWire testWire;
// Initialize a PCF8574 at I2C-address 0x20, using GPIO5, GPIO4 and testWire for the I2C-bus
PCF857x pcf8574(0x20, &testWire);
#define LED D1 // Led in NodeMCU at pin GPIO16 (D0).
#define BRIGHT 300 //max led intensity (1-500)
#define INHALE 1250 //Inhalation time in milliseconds.
#define PULSE INHALE*1000/BRIGHT
#define REST 1000 //Rest Between Inhalations.
#define PIN_INT D5
#define PIN_SDA D7
#define PIN_SCL D8
//----- Setup function. ------------------------
void setup() {
Serial.begin(115200);
Wire.pins(PIN_SDA, PIN_SCL);//SDA - D1, SCL - D2
Wire.begin();
pinMode(PIN_INT, INPUT_PULLUP);
pcf8574.begin( 0xFF);
pcf8574.resetInterruptPin();
pinMode(LED, OUTPUT); // LED pin as output.
}
bool CheckKey(byte key, byte num){ //0, 1, 2, 3
return key & (1 << num);
}
//----- Loop routine. --------------------------
void loop() {
//ramp increasing intensity, Inhalation:
for (int i=1;i<BRIGHT;i++){
digitalWrite(LED, LOW); // turn the LED on.
delayMicroseconds(i*10); // wait
digitalWrite(LED, HIGH); // turn the LED off.
delayMicroseconds(PULSE-i*10); // wait
delay(0); //to prevent watchdog firing.
}
if( digitalRead(PIN_INT)==LOW ){
delay(50);
byte b = pcf8574.read8();
Serial.println( "INT: " + String(b));
byte keys = ((~b)) & 0x0F;
if( CheckKey(keys, 8) ){
Serial.println( "KEY 7");
delay(2000);
}
}
//ramp decreasing intensity, Exhalation (half time):
for (int i=BRIGHT-1;i>0;i--){
digitalWrite(LED, LOW); // turn the LED on.
delayMicroseconds(i*10); // wait
digitalWrite(LED, HIGH); // turn the LED off.
delayMicroseconds(PULSE-i*10); // wait
i--;
delay(0); //to prevent watchdog firing.
}
delay(REST); //take a rest...
}

You could use the PCF8574 INT pin as an interrupt to the ESP8266 via Arduino's attachInterrupt() function, but you wouldn't gain much from that, since in order to detect which key was pressed you need to call pcf8574.read8(), and you can't do that from the interrupt handler.
The ESP8266 Arduino core is based on the Espressif NONOS SDK, so you can't have a separate thread to monitor key presses. I would suggest defining a helper function that checks if a key is currently being pressed, and then calling that function as often as you can in your main loop, e.g. at every iteration of each of your two for loops. The LED brightness ramps would be slightly disrupted when there is a key press, but I think it wouldn't be noticeable to the human eye.
So the helper function could be defined as:
byte GetKeyPress(void) {
if (digitalRead(PIN_INT) == LOW) {
return ~pcf8574.read8();
}
else {
return 0;
}
}
Then, at the beginning of the loop() function declare a static variable static byte keyPress;, and call the above function in each of the two for loops:
if (keyPress == 0) {
keyPress = GetKeyPress();
}
When you want to process a key press (e.g. between the two for loops like in your code), you can do like that:
if (keyPress) {
/* Do your stuff. */
keyPress = 0;
}

Related

Arduino IRremote Send and Receive simultaneously

I'm currently working on a lasertag game with several weapons.
I would like to use one arduino nano in each weapon. It should be able to receive the IR-signals of the opponents as well as send IR-signals if a button is triggered.
So now there comes my problem:
I implemented an interrupt for the IR-receiver pin, so that an opponent's shot is always detected even when I'm shooting.
When the button is permanently pressed, the IR LED will shoot every 300 milliseconds (the send-function takes approximately 70ms and I implemented a delay of 230ms).
Unfortunately, the Nano won't detect any signal of the receiver in those 300ms.
However, if I disconnect the IR-LED everything seems to work perfectly.
Now I'm wondering, why the IR-LEDs connection has an effect on the functionality of my code.
Do you know any way I could solve this problem?
Here you can see the entire code I implemented:
#define IR_SEND_PIN 3
#define BUTTON_PIN 10
#define LED_PIN 12
#include <IRremote.hpp>
#include <Arduino.h>
uint8_t sAddress = 0;
uint8_t sCommand = 0x59;
uint8_t sRepeats = 0;
volatile uint8_t hitData;
void setup() {
IrSender.begin();
IrReceiver.begin(IR_RECEIVE_PIN);
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(IR_RECEIVE_PIN), HIT, CHANGE);
}
void HIT() {
if (IrReceiver.decode()) {
hitData = IrReceiver.decodedIRData.command;
}
IrReceiver.resume();
}
void loop() {
if (digitalRead(BUTTON_PIN) == LOW) {
IrSender.sendNEC(sAddress, sCommand, sRepeats);
delay(120);
}
if (hitData == sCommand) { // indicates received signal 0x59
hitData = 0x00;
IrReceiver.resume();
digitalWrite(LED_PIN, HIGH);
delay(8);
digitalWrite(LED_PIN, LOW);
}
}```

Arduino infiniti tone

I am making a smoke detector.
When it detects smoke iz should alert with buzzer.
Is there any way I could make it to buzz forever until external interupt such as restart pin?
Or could I just remove timing from tone() function.
Here is the code I use.
int sensorValue;
int digitalValue;
int green = 8;
int red = 7;
void setup(){
Serial.begin(9600);
pinMode( 0, INPUT);
pinMode(green, OUTPUT);
pinMode(red, OUTPUT);
}
void start(){
digitalWrite(green, HIGH);
}
void loop() {
sensorValue = analogRead(0);
digitalValue = digitalRead(0);
Serial.println(sensorValue,DEC);
Serial.println(digitalValue,DEC);
delay(2000);
if(analogRead(0) < 100){
tone(9,200,1000);
digitalWrite(red,HIGH);
}
}
Playing a sound "forever" is straightforward:
if(analogRead(A0) < 100 ) {
tone(9,2000); // once triggered, will play the sound forever
}
To switch it off, you seem to like the RESET button. So there's no need to ever call
noTone(9);
BTW: what about reading the reference ?
There is lots of ways:
Change your logic that activate the buzzer.
while (analogRead(0) < 100){
tone(9,200,1000);
}
Just use an infinite loop:
while (1) {
tone(9,200,1000);
}
Reset the Arduino to get out of the infinite loop.
An variation on this would be to replace (1) with the code that checks a pin to exit the loop or reads the sensor.
if you're really bent on using interrupts
you didn't specify what board you're working with but
for uno the 2 3 pins can be attached as interrupts and just trigger a function that turns off the tone
check out this:
attachinterrupt

Arduino Interfacing with Magnetic Pickup

Currently I have a diesel engine with magnetic pickup attached to it. I want to use Arduino (Uno/Nano) to measure engine RPM.
Magnetic Pickup Description: A magnetic pickup is installed over a gear, (most commonly the flywheel inside a vehicle’s bell housing) and as the gear turns the pickup will create an electric pulse for each tooth on the gear. These pulses are then read by the instrument which interprets it to indicate the correct RPMs or speed.The signal from the magnetic speed Sensor, teeth per second(HZ), is directly proportional to engine speed.
Magnetic Pickup Image:
MP - Self Powered
I've tried to rectify the signal using diode then limit the current using a resistor with .1Uf capacitor to filter the noise, then connected it to Optocopler 4N35 and the output from Opto to Arduino interrupt pin, by just observing Arduino interrupt ping is highly affected by surroundings.
Also I have tried to directly connect the magnetic pickup to "A0" pin and use analogue read and connect a led to pin 13 just to monitor the pulses from MP.
int sensorPin = A0;
int ledPin = 13;
int sensorValue = 0;
void setup() {
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
// read the value from the sensor:
sensorValue = analogRead(sensorPin);
digitalWrite(ledPin, HIGH);
delay(sensorValue);
digitalWrite(ledPin, LOW);
Serial.println(sensorValue);
Serial.println(" ");
}
Using analogueRead works with the LED as indicator for pulses generated by pickup. (Tested using small motor and small gear to protect Arduino).
Also I tried to use LM139 Comparator but the readings make no sense
(ex: 60 RPM, 1500 RPM,2150 RPM, 7150 RPM).
LM139 Circuit
Code used with LM139:
// read RPM
volatile int rpmcount = 0;
//see http://arduino.cc/en/Reference/Volatile
int rpm = 0;
unsigned long lastmillis = 0;
void setup() {
Serial.begin(9600);
attachInterrupt(0, rpm_fan, RISING);
//interrupt cero (0) is on pin two(2).
}
void loop() {
if (millis() - lastmillis == 500) {
/*Update every one second, this will be equal to reading frequency (Hz).*/
detachInterrupt(0); //Disable interrupt when calculating
rpm = rpmcount * 60;
/* Convert frequency to RPM, note: this works for one interruption per full rotation. For two interrupts per full rotation use rpmcount * 30.*/
Serial.print(rpm); // print the rpm value.
Serial.println(" ");
rpmcount = 0; // Restart the RPM counter
lastmillis = millis(); // Update lastmillis
attachInterrupt(0, rpm_fan, RISING); //enable interrupt
}
}
void rpm_fan() {
/* this code will be executed every time the interrupt 0 (pin2) gets low.*/
rpmcount++;
}
// Elimelec Lopez - April 25th 2013
What is the best way or approach to interface a magnetic pickup with Arduino to display RPM?
Your use of analogRead is wrong. Besides, analogRead will not get you anywhere close to what you want to achieve.
What you want from your pickup is a clear 0-5v digital signal. You can obtain that by playing with the input resistor on your opto-coupler. I'd do some measurements, and place a trimpot + resistors on the board do the actual value can be tweaked after the system is installed.
Once you get the electrical signal as clean as you can get, you can the use an interrupt pin on the Arduino to keep count of the number of pulses.
#define SENSOR_PIN (2) // using define instead of variable for constants save memory.
#define LED_PIN (13)
#define READ_DELAY (100) // in milliseconds.
// we'll get a reading every 100ms, so 8 bits are enough to keep
// track of time. You'd have to widen to unsigned int if you want
// READ_DELAY to exceed 255 ms.
//
typedef delay_type unsigned char;
typedef unsigned int counter_type; // You may want to use
// unsigned long, if you
// experience overflows.
volatile counter_type pulseCount = 0; // volatile is important here
counter_type lastCount = 0;
delay_type lastTime = 0;
// pulse interrupt callback, keep short.
void onSensorPulse()
{
++pulseCount;
// the following may already be too long. Use for debugging only
// digitalWrite() and digitalRead() are notoriously slow.
//
//
// digitalWrite(LED_PIN, !digitalRead(LED_PIN));
//
// using fastest direct port access instead. (for ATMega)
//
if (pulseCount & 1)
PORTB |= (1 << PB5);
else
PORTB &= ~(1 << PB5);
}
void setup()
{
pinMode(SENSOR_PIN, INPUT);
attachInterrupt(digitalPinToInterrupt(SENSOR_PIN), onSensorPulse, RISING);
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop()
{
// control frequency of readings
//
delay_type now = (delay_type)millis();
if (now - lastTime < READ_DELAY)
{
return;
}
lastTime = now;
// get a reading. must disable interrupts while doing so.
// because pulseCount is multi-bytes.
//
noInterrupts();
counter_type curCount = pulseCount;
interrupts();
// get the number of pulses since last reading.
//
counter_type delta = curCount - lastCount;
lastCount = curCount;
// to convert to RPMs, you will need to use this formula:
// note the use of long (UL) to avoid overflows in the
// computation. 60000 = miliseconds per minute.
//
// RPM = delta * 60000UL / (READ_DELAY * TEETH_COUNT);
// send delta to client for now.
//
Serial.println(delta);
}

External digital interrupt and dht11

I have an Arduino Pro Mini 5v, 16 mhz and it is connected to a digital switch on pin 2. This switch is used to wake the Arduino from sleep using a external digital interrupt. I also have a DHT11 temperature sensor connected to pin 9. What I want to achieve is the when the Arduino is awake for 5 seconds and also when the switch on pin 2 is HIGH, I want to read the temperature sensor and return the temperature. I am using the DHT11 library by Tillart and when I do this, it returns a TIME_OUT error. The only possible explanation I have for this is that somehow the voltage is changed when both the DHT11 and the switch on pin 2 is being read together? Any pointers to a solution will be greatly appreciated. Thank you.
Edit 1: Added code
#include <LowPower.h>
#include <dht.h>
int pin2 = 2;
dht DHT;
#define DHT11_PIN 9
void pin2interrupt(void)
{
// Function called when awoken from sleep
// Detach interrupt to stop it from continuosly firing when in normal mode
}
void enterSleep(void)
{
attachInterrupt(0, pin2interrupt, HIGH);
Serial.println("Sleeping");
delay(100);
LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
Serial.println("Awake!");
}
void setup()
{
Serial.begin(115200);
pinMode(pin2, INPUT);
pinMode(DHT11_PIN, INPUT);
}
int seconds = 0;
void loop()
{
delay(1000);
seconds++;
Serial.println("Awake in the loop!");
Serial.println(seconds);
if (digitalRead(pin2) == LOW && seconds == 5)
{
seconds = 0;
Serial.println("No child detected, so going to sleep!");
delay(200);
enterSleep();
}
else if (seconds == 5)
{
Serial.print("DHT11, \t");
int chk = DHT.read11(DHT11_PIN);
switch (chk)
{
case DHTLIB_OK:
Serial.print("OK,\t");
break;
case DHTLIB_ERROR_CHECKSUM:
Serial.print("Checksum error,\t");
break;
case DHTLIB_ERROR_TIMEOUT:
Serial.print("Time out error,\t");
break;
default:
Serial.print("Unknown error,\t");
break;
}
// DISPLAY DATA
Serial.println(DHT.temperature, 1);
delay(2000);
seconds = 0;
}
}
Edit 2: I also forgot to to mention that I am using the LowPower library by RocketScream to put the Arduino to sleep. The library can be found here: https://github.com/rocketscream/Low-Power
As discussed in the issues section on the official Github page of the DHT11 library by Rob Tillart, the problem is caused because some DHT11 sensors take longer to transfer data back to the board then the 50ms or so specified on the datasheet. Therefore, if you are encountering this problem try increasing the DHTLIB_TIMEOUT on the dht header file by reducing the the value dividing the F_CPU value to around 400 and try again. This allows the board to wait longer than 50ms for the board to receive data back from the sensor. If this fix doesn't work, you might want to try measuring the response time using an oscilloscope as it seems some DHT11 are built differently.

Arduino - Using interrupts freezes processing and serial output?

So, the interrupts seem to work insofar as "interrupting" when an event happens. My only problem is that I the interrupts will occur 2-3 times and everything essentially stops (Serial out, everything).
I was programming the board to output serially a calculated distance based on the output of the HC-SR04 distance IC. The distances are calculated accurately but, like I said earlier, everything seems to freeze. Below is both the code and an image of the serial monitor.
#define TRIGPIN 4
#define ECHOPIN 3
#define RED 2
#define GREEN 13
#define INTNUM 1 //interrupt pin 1 is digital pin 3 on the duemilanove
#define PULSE 10 //microseconds
#define CYCLETIME 50 //milliseconds
void ledWrite(int), trigPulse(), getTime();
int millisNow, millisPrev = 0;
int microsPrev;
boolean isHigh = false;
void setup() {
Serial.begin (9600);
pinMode(TRIGPIN, OUTPUT);
pinMode(ECHOPIN, INPUT);
pinMode(RED, OUTPUT);
pinMode(GREEN, OUTPUT);
attachInterrupt(INTNUM, getTime, CHANGE);
}
void loop() {
trigPulse();
// some other code while waiting on HC-SR04 to interrupt us when echo goes HIGH
}
void trigPulse(){
if( (millisNow = millis()) - millisPrev >= CYCLETIME){ //sufficient cycle time
digitalWrite(TRIGPIN, HIGH);
delayMicroseconds(PULSE);
digitalWrite(TRIGPIN, LOW);
millisPrev = millisNow; //reset clock
}
return;
}
void ledWrite(int dTime){
int distance = dTime/58.2;
if (distance < 4) {
digitalWrite(RED,HIGH);
digitalWrite(GREEN,LOW);
}
else {
digitalWrite(RED,LOW);
digitalWrite(GREEN,HIGH);
}
if (distance >= 200 || distance <= 0){
Serial.println("Out of range");
}
else {
Serial.print(distance);
Serial.println(" cm");
}
}
void getTime(){
int timeNow = micros();
Serial.println("Interrupted");
if(isHigh == false){
microsPrev = timeNow; //get time now, pin LOW->HIGH
isHigh = true;
Serial.println("Returning ..");
return;
}
else { //pin HIGH->lOW
ledWrite(timeNow - microsPrev);
isHigh = false;
microsPrev = micros();
Serial.println("Returning ..");
return;
}
return;
}
I know this is an old thread, but I just came by it having my own problems. The problem here is that you cannot use:
Serial.Print()
Within an interrupt service routine. The reason that the Serial.Print() doesn't work within an ISR is that it uses interrupts to pull the characters out of the serial buffer, but interrupts of a certain level are masked within the ISR. What basically happens is that the arduino throws out all other interrupts that are of a lower priority, which Serial.Read() falls into.
It is documented in a number of places: link1, link2, link3
I think you are getting interrupt while you are already processing interrupt. You should try disabling the interrupt as soon as you are in interrupt function and re-enable it again when you are done processing just before return. Hence I would advice to have just one return so that you don't have to repeat code of enabling interrupt. Also make sure the function which you are calling inside your interrupt code do not re-enable the interrupt. It may happen that the function micros() or any of the Serial function are re-enabling the interrupt.
I would suggest instead of calling function directly in you interrupt code try using some flags and set/reset in interrupt and use these flags in main loop to call your regular function.

Resources