Manage a gate alarm by interrupt

published: 15 November 2020 / updated 16 November 2020

Lire cette page en français

 


Preamble

It all started with a request from an ARDUINO programmer who wrote his code by a friend:

#include <Arduino.h>
#include <Wire.h>
#include <SoftwareSerial.h>
#include <LiquidCrystal_I2C.h>
/*  Déclaration de l'écran LCD  */
LiquidCrystal_I2C lcd(0x27,16,2);
/*  Déclaration des variables  */
bool portail_ferme = false;
bool portail_ouvert = false;
/*  Paramétrage des différentes broche  */
void setup(){
    lcd.init();
    lcd.home();
    lcd.clear();
    lcd.backlight();
    pinMode(9,INPUT);
    pinMode(10,OUTPUT);
    pinMode(13,OUTPUT);
}
/*  Traitement  */
void loop(){
    if(((digitalRead(9))==(1))){
        digitalWrite(10,1);
        tone(13,440,2000); // write to buzzer
        if (portail_ouvert == false){
          portail_ouvert = true;
          portail_ferme = false;
          lcd.clear();
          lcd.print( " portail  ouvert " );
        }
        delay(2000);
        _delay(1);
    }else{
        digitalWrite(10,0);
        if (portail_ferme == false){
          portail_ouvert = false;
          portail_ferme = true;
          lcd.clear();
          lcd.print( " portail  ferme" );
        }
    }
}
void _delay(float seconds){
  long endTime = millis() + seconds * 1000;
  while(millis() < endTime)_loop();
}
void _loop(){}

Functionally, this program simply does this:

The problem with the C language program is that it does just that, in a endless loop. Unfortunately, this is a classic mistake for the novice programmer.

The FORTH solution with interruption

With FlashForth on ARDUINO NANO, we will use a hardware interrupt.

On the gate side, we mount a contactor of this type:

This contactor has two outputs:

Each output changes state depending on whether or not this switch is pressed.

For the gate, we have chosen the output which is OFF when the contactor is pressed. Opening the gate switches the selected output to the ON state when the contactor is released. We therefore have the following truth table:

This is how this contactor is connected to our ARDUINO NANO card:

The contactor is supplied with 5V. Contactor output is returned to PBO (8) which is an input controlling PCINT0.

Here, on the assembly, the LED indicates the state of the alarm when the gate is open. On the actual assembly, we leave it to you to connect buzzer, flashing light and display.

Before handling the interruption, here is how to handle our portal contact.

Portal contact management

In this program, we do not take care of the display or the buzzer. We leave you the pleasure of adding these features.

-defPin 
marker -defPin 
\ require pinsDefinitions.txt - source: 
\ https://github.com/MPETREMANN11/ARDUINO-FORTH/blob/master/pinsDefinitions.txt 
 
-alarm 
marker -alarm 
 
\ Registers for ALARM 
$25 constant PORTB 
flash 
PORTB %00100000 defPIN: ALARM  ( portB PB5) 
PORTB %00000001 defPIN: BUTTON ( portB PB0) 
ram 
 
: tempo ( ---) 
   3000 for next ; \ adjust the value 3000 if necessary 
 
: alarm-test ( ---) 
    BUTTON pin@ 
    tempo 
    if      ALARM high 
    else    ALARM low 
    then 
  ; 

We can test the proper functioning of alarm-test. If the button is active, the alarm is high. It is after ALARM high that you can add the words that display the state of the portal, as well as the triggering of the buzzer.

We encapsulate our word alarm-test in the definition of alarm-action:

: alarm-action ( ---) 
    di  \ disable interrupt 
    alarm-test 
    ei  \ ei = Enable Interrupt 
  ;i 

The word alarm-action must not be executed from the terminal. It is this word which will be executed by the hardware interrupt.

Interrupt setting

The interrupt setting applies to the button connected to the bit B0 from PORTB. This bit triggers the PCINT0 interrupt.

-interr 
marker -interr 
 
\ EXTERNAL_INTERRUPT 
$0004 constant PCINT0addr   \ Pin Change Interrupt Request 0 
$6b constant PCMSK0         \ Pin Change Mask Register 0 (107) 
$68 constant PCICR          \ Pin Change Interrupt Control Register (104) 
 
: int-enable ( ---) 
    ['] alarm-action PCINT0addr int! 
    ei  \ ei = Enable Interrupt 
  ; 
 
: init.interrupt ( ---) 
    int-enable 
    %00000001 PCMSK0 mset 
    %00000111 PCICR  mset 
  ; 

This code is very trivial. Only thing that can change if you use this one for another application is in the sequence ['] alarm-action PCINT0addr where the word alarm-action will be replaced by your own definition handled by interrupt.

Et enfin, on itialise les ports et pins:

: init.ports ( ---) 
    ALARM output 
    ALARM low 
    BUTTON input    \ set pin as input 
  ; 
 
: init ( ---) 
    init.ports 
    init.interrupt 
  ; 

Once init has been executed, just press the button or release it to see the interrupt work.

Between two interrupt executions, the resources of the ARDUINO NANO card are fully available.

As it is, the above program occupies 500 bytes in flash memory.

Conclusion

The aim of this article is not to oppose FORTH to the C language.

The goal is to show that with very simple equipment, here an ARDUINO NANO card, by managing a hardware interruption, the loop management of the contactor is avoided.

With the hardware interrupt, the resources of the ARDUINO NANO card are fully operational for other tasks between two interruptions.