RGB LCD Backpack - Interrupt

by robreeves49 on Thu Jul 26, 2012 7:46 pm

I saw an earlier post where you explained using polling instead of interrupts for button detection, but I'm wondering if there is a way to trigger an interrupt when any button is pushed? My sketch could be off doing whatever when I push the button, and I'd want to interrupt whatever to poll the buttons.
User avatar
robreeves49
 
Posts: 26
Joined: Sun Mar 13, 2011 6:51 pm

Re: RGB LCD Backpack - Interrupt

by adafruit_support_rick on Thu Jul 26, 2012 8:54 pm

The arduino library supports two interrupts: one on digital pin 2, and the other on digital pin 3. You can read about the attachInterrupt function here:
http://arduino.cc/en/Reference/AttachInterrupt
The ATMega328 chip also supports pin-change interrupts. These are not directly supported by any arduino libraries, but you can still use them. Chapter 13 of the ATmega328 datasheet describes both the two external interrupts used by arduino, and the pin-change interrupts available on the other digital pins
User avatar
adafruit_support_rick
 
Posts: 10959
Joined: Tue Mar 15, 2011 10:42 am
Location: Buffalo, NY

Re: RGB LCD Backpack - Interrupt

by mtbf0 on Thu Jul 26, 2012 9:23 pm

yes. the mcp23017 is capable of generating pin change interrupts. you'll have to enable interrupts on the pins used for the buttons. this will involve hacking the library a little to enable and configure interrupts on the button pins and installing a wire jumper between pin 20 of the mcp23017 and whichever arduino pin you'd like the interrupt to occur on. this will likely be pin 2 or pin 3, because they can be used with the attachInterrupt function.

have a read of the mcp23017 datasheet.

no doubt a fun and worthwhile project.
"i want to lead a dissipate existence, play scratchy records and enjoy my decline" - iggy pop, i need more
User avatar
mtbf0
 
Posts: 1645
Joined: Fri Nov 09, 2007 11:59 pm
Location: oakland ca

Re: RGB LCD Backpack - Interrupt

by robreeves49 on Fri Jul 27, 2012 4:51 am

So, tie pin 20 to an Arduino interrupt pin and hack the library so that a button push triggers an interrupt (a state change) on pin 20, right?
User avatar
robreeves49
 
Posts: 26
Joined: Sun Mar 13, 2011 6:51 pm

Re: RGB LCD Backpack - Interrupt

by mtbf0 on Fri Jul 27, 2012 7:33 am

that should be it.

i ordered a second lcd shield for hacking and it arrived yesterday. this is one of the things i wanted to try along with moving the buttons off board, playing with the other 4 i/o lines and replacing the chip with a micro controller to give it some local intelligence.

as driverblock pointed out, you can use any i/o pin on the arduino to generate a pin change interrupt. it's not any more difficult than using attachInterrupt once you've done a little reading in the datasheet. there are third party libraries available, too.
"i want to lead a dissipate existence, play scratchy records and enjoy my decline" - iggy pop, i need more
User avatar
mtbf0
 
Posts: 1645
Joined: Fri Nov 09, 2007 11:59 pm
Location: oakland ca

Re: RGB LCD Backpack - Interrupt

by adafruit_support_rick on Fri Jul 27, 2012 10:13 am

Whether you you arduino pin-change interrupts or mcp23017 pin chance interrupts, remember that one interrupt line serves up to 8 port pins.

If you have more than one button, you'll have to figure out which button caused the interrupt. The mcp23017 helps you out with this a little bit by providing the INTCAP register, which will tell you which pin caused the interrupt. But that's not necessarily foolproof, because other pin changes can accumulate while you're processing the first interrupt. So, while it may not be likely, it's *possible* to get more than one pin change in an interrupt.

Arduino pin change interrupts don't have an INTCAP register, so it's up to you to keep track of the state of the port register so that you can figure out what's changed when you get an interrupt. If you always remember the state of the port every time you read it, you can XOR the remembered state with the current state to get the equivalent of the INTCAP register. Again, it's *possible* to get more than one pin change in an interrupt.

Once you have your change flags, remember that you will get one interrupt when a button is pressed, and another interrupt when the button is released. The INTCAP will look the same for both events, since it is simply flagging which pins changed state. You'll have to examine the port register to determine if the change(s) were caused by a press or a release.
User avatar
adafruit_support_rick
 
Posts: 10959
Joined: Tue Mar 15, 2011 10:42 am
Location: Buffalo, NY

Re: RGB LCD Backpack - Interrupt

by robreeves49 on Fri Jul 27, 2012 12:17 pm

Maybe I'm oversimplifying, but my plan was to call the LCD library readbuttons() method after detecting the interrupt, and let it do the work of figuring out what button was pushed. Anyway, won't know until I try it.
User avatar
robreeves49
 
Posts: 26
Joined: Sun Mar 13, 2011 6:51 pm

Re: RGB LCD Backpack - Interrupt

by adafruit_support_rick on Fri Jul 27, 2012 12:51 pm

readButtons just returns a bitmap of of the current state of the buttons. You'll still have to figure out which buttons changed state.

For instance, if you press and hold button 1, you'll get your interrupt, and readButtons will tell you that button 1 is pressed.

Now, press and release button 2. You'll get an interrupt for the press, and readButtons will tell you that buttons 1 and 2 are pressed. Then you'll get another interrupt for the release, and readButtons will tell you that button 1 is pressed.

Maybe, in your application, it's not important for you to be able to tell the difference between the first and third interrupt. Maybe it's also not important for you to know that button 1 was only pressed once.

But in many applications, it's critical to disambiguate those three interrupt states in order to know what the user is doing and how to respond.
User avatar
adafruit_support_rick
 
Posts: 10959
Joined: Tue Mar 15, 2011 10:42 am
Location: Buffalo, NY

Re: RGB LCD Backpack - Interrupt

by mtbf0 on Sat Jul 28, 2012 7:05 am

the falling edge should be easy to identify since it will happen only after you read the register on the mcp23017. debouncing still ought to be a lot of fun. the shield library doesn't address it at all. it might be amusing to write a little sketch that continually reads the buttons to see how many changes are detected per press.

also it looks like it might be worthwhile to do a thorough rewrite of the library. it appears to be a pretty straightforward adaptation of the existing lcd library and uses analogs of pinMode and digitalWrite to do a lot of things one bit at a time that it would make a whole lot more sense to do in parallel when the lcd is connected through a port expander.
"i want to lead a dissipate existence, play scratchy records and enjoy my decline" - iggy pop, i need more
User avatar
mtbf0
 
Posts: 1645
Joined: Fri Nov 09, 2007 11:59 pm
Location: oakland ca

Re: RGB LCD Backpack - Interrupt

by tedcool on Wed Oct 03, 2012 7:24 pm

OK - got it working - not polished yet, but you'll be off to the races with this...

Add to Adafruit_MCP23017.h:
Code: Select all | TOGGLE FULL SIZE
uint16_t readInterrupts();
  void interruptEnable(uint8_t p, uint8_t d);
  void defaultvalue(uint8_t p, uint8_t d);
  void interruptControl(uint8_t p, uint8_t d);
  void interruptMirror(uint8_t d);
  void interruptPolarity(uint8_t d);


Adafruit_MCP23017.cpp:

digitalwrite is rewritten ( write to OLAT. DO NOT read first - interrupt will be reset. Declare gpio static ). Functions are added
Code: Select all | TOGGLE FULL SIZE

void Adafruit_MCP23017::digitalWrite(uint8_t p, uint8_t d) {
  static uint8_t gpio;
  uint8_t gpioaddr, olataddr;

  // only 16 bits!
  if (p > 15)
    return;

  if (p < 8) {
    olataddr = MCP23017_OLATA;
    gpioaddr = MCP23017_GPIOA;
  } else {
    olataddr = MCP23017_OLATB;
    gpioaddr = MCP23017_GPIOB;
    p -= 8;
  }

  // read the current GPIO output latches
  //Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  //wiresend(olataddr);
  //Wire.endTransmission();

  //Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
  // gpio = wirerecv();

  // set the pin and direction
  if (d == HIGH) {
    gpio |= 1 << p;
  } else {
    gpio &= ~(1 << p);
  }

  // write the new GPIO
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(olataddr);
  wiresend(gpio);
  Wire.endTransmission();
}
//read interrupt
uint16_t Adafruit_MCP23017::readInterrupts() {
  uint16_t ba = 0;
  uint8_t a;

  // read the interrupt capture buffers
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(MCP23017_INTCAPA);
  Wire.endTransmission();

  Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 2);
  a = wirerecv();
  ba = wirerecv();
  ba <<= 8;
  ba |= a;

  return ba;
}

//  interrupt enable

void Adafruit_MCP23017::interruptEnable(uint8_t p, uint8_t d) {
  uint8_t gpinten;
  uint8_t gpintenaddr;

  // only 16 bits!
  if (p > 15)
    return;

  if (p < 8)
    gpintenaddr = MCP23017_GPINTENA;
  else {
    gpintenaddr = MCP23017_GPINTENB;
    p -= 8;
  }


  // read the current interrupt enable set
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(gpintenaddr);
  Wire.endTransmission();

  Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
  gpinten = wirerecv();

  // set the pin enabled state
  if (d == HIGH) {
    gpinten |= 1 << p;
  } else {
    gpinten &= ~(1 << p);
  }

  // write the new INTEN
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(gpintenaddr);
  wiresend(gpinten);
  Wire.endTransmission();
}

// interrupt enable end

// interrupt default value compare

void Adafruit_MCP23017::defaultvalue(uint8_t p, uint8_t d) {
  uint8_t gpdefval;
  uint8_t gpdefvaladdr;

  // only 16 bits!
  if (p > 15)
    return;

  if (p < 8)
    gpdefvaladdr = MCP23017_DEFVALA;
  else {
    gpdefvaladdr = MCP23017_DEFVALB;
    p -= 8;
  }


  // read the current interrupt enable set
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(gpdefvaladdr);
  Wire.endTransmission();

  Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
  gpdefval = wirerecv();

  // set the pin enabled state
  if (d == HIGH) {
    gpdefval |= 1 << p;
  } else {
    gpdefval &= ~(1 << p);
  }

  // write the new DEFVAL
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(gpdefvaladdr);
  wiresend(gpdefval);
  Wire.endTransmission();

}
// interrupt default value compare end

// interrupt control

void Adafruit_MCP23017::interruptControl(uint8_t p, uint8_t d) {
  uint8_t gpintcon;
  uint8_t gpintconaddr;

  // only 16 bits!
  if (p > 15)
    return;

  if (p < 8)
    gpintconaddr = MCP23017_INTCONA;
  else {
    gpintconaddr = MCP23017_INTCONB;
    p -= 8;
  }


  // read the current interrupt enable set
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(gpintconaddr);
  Wire.endTransmission();

  Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
  gpintcon = wirerecv();

  // set the pin enabled state
  if (d == HIGH) {
    gpintcon |= 1 << p;
  } else {
    gpintcon &= ~(1 << p);
  }

  // write the new INTCON
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(gpintconaddr);
  wiresend(gpintcon);
  Wire.endTransmission();
}
// interrupt control end

// Interrupt mirror bit

void Adafruit_MCP23017::interruptMirror(uint8_t d) {
  uint8_t iocon;
  uint8_t ioconaddr;


  ioconaddr = MCP23017_IOCONA;


  // read the current interrupt enable set
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(ioconaddr);
  Wire.endTransmission();

  Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
  iocon = wirerecv();

  // set the interrupt mirror state
  if (d == HIGH) {
    iocon |= 1 << 6; // bit 6 = mirror
  } else {
    iocon &= ~(1 << 6);
  }

  // write the new IOCON
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(ioconaddr);
  wiresend(iocon);
  Wire.endTransmission();
}

// Interrupt mirror bit end

// Interrupt polarity bit

void Adafruit_MCP23017::interruptPolarity(uint8_t d) {
  uint8_t iocon;
  uint8_t ioconaddr;


  ioconaddr = MCP23017_IOCONA;


  // read the current interrupt enable set
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(ioconaddr);
  Wire.endTransmission();

  Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
  iocon = wirerecv();

  // set the interrupt polarity state
  if (d == HIGH) {
    iocon |= 1 << 1; // bit 1 = polarity
  } else {
    iocon &= ~(1 << 1);
  }

  // write the new IOCON
  Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
  wiresend(ioconaddr);
  wiresend(iocon);
  Wire.endTransmission();
}

// Interrupt polarity bit end


Adafruit_RGBLCDShield.h:

Code: Select all | TOGGLE FULL SIZE
uint16_t readButtons();


Adafruit_RGBLCDShield.cpp:

Adafruit_RGBLCDShield::begin ( fragment )

Code: Select all | TOGGLE FULL SIZE
if (_i2cAddr != 255) {
    //_i2c.begin(_i2cAddr);
    //Wire.begin();
    _i2c.begin();

    _i2c.pinMode(8, OUTPUT);
    _i2c.pinMode(6, OUTPUT);
    _i2c.pinMode(7, OUTPUT);
    setBacklight(0x7);

    if (_rw_pin)
      _i2c.pinMode(_rw_pin, OUTPUT);

    _i2c.pinMode(_rs_pin, OUTPUT);
    _i2c.pinMode(_enable_pin, OUTPUT);

    _i2c.interruptMirror(1);
   _i2c.interruptPolarity(1);

for (uint8_t i=0; i<4; i++)
      _i2c.pinMode(_data_pins[i], OUTPUT);

    for (uint8_t i=0; i<5; i++) {
      _i2c.pinMode(_button_pins[i], INPUT);
      _i2c.pullUp(_button_pins[i], 1);

      _i2c.defaultvalue(_button_pins[i], 1);
     _i2c.interruptControl(_button_pins[i], 1);
      _i2c.interruptEnable(_button_pins[i], 1);

    }
  }



write4bits speedup disabled

Code: Select all | TOGGLE FULL SIZE
void Adafruit_RGBLCDShield::write4bits(uint8_t value) {

 //  if (_i2cAddr != 255) {

   if (1 != 1) {


    uint16_t out = 0 ;

     out = _i2c.readGPIOAB();

    // speed up for i2c since its sluggish
    for (int i = 0; i < 4; i++) {
      out &= ~_BV(_data_pins[i]);
      out |= ((value >> i) & 0x1) << _data_pins[i];
    }

    // make sure enable is low
    out &= ~ _BV(_enable_pin);

    _i2c.writeGPIOAB(out);

    // pulse enable
    delayMicroseconds(1);
    out |= _BV(_enable_pin);
    _i2c.writeGPIOAB(out);
    delayMicroseconds(1);
    out &= ~_BV(_enable_pin);
    _i2c.writeGPIOAB(out);
    delayMicroseconds(100);

  } else {
    for (int i = 0; i < 4; i++) {
     // _pinMode(_data_pins[i], OUTPUT);

     _digitalWrite(_data_pins[i], (value >> i) & 0x01);
     
    }
    pulseEnable();
  }
 }



readbuttons changed:

Code: Select all | TOGGLE FULL SIZE
uint16_t Adafruit_RGBLCDShield::readButtons(void) {
  uint16_t reply ;

  //for (uint8_t i=0; i<5; i++) {
   // reply &= ~((_i2c.digitalRead(_button_pins[i])) << i);
  //}
   reply = _i2c.readInterrupts();

 return reply;
}


Ugly Arduino example - switches bounce like crazy - must keep reading OLAT until pin resets

Code: Select all | TOGGLE FULL SIZE
/*********************

Example code for the Adafruit RGB Character LCD Shield and Library

This code displays text on the shield, and also reads the buttons on the keypad.
When a button is pressed, the backlight changes color.

**********************/

 //include the library code:
#include <Wire.h>

 #include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>



// The shield uses the I2C SCL and SDA pins. On classic Arduinos
// this is Analog 4 and 5 so you can't use those for analogRead() anymore
// However, you can connect other I2C sensors to the I2C bus and share
// the I2C bus.
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();

// These #defines make it easy to set the backlight color
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7

volatile uint16_t buttons, b2, test , test2;

volatile boolean bRead;

unsigned long tnow;

void setup() {
  // Debugging output
  Serial.begin(9600);
  // set up the LCD's number of rows and columns:
  lcd.begin(20, 4);

  // Print a message to the LCD. We track how long it takes since
  // this library has been optimized a bit and we're proud of it :)
  int time = millis();
  lcd.print("Hello, world!");
  time = millis() - time;
  Serial.print("Took "); Serial.print(time); Serial.println(" ms");
  lcd.setBacklight(0);
 
  pinMode(1,INPUT);     //set the pin to input
  digitalWrite(1, LOW);

  //Enable Pin Change Interrupt
  PCMSK1 |= (1 << PCINT9); 
  PCICR |= (1 << PCIE1); // 8 - 15
 
  test = 255;
 
   tnow = millis();
}

ISR(PCINT1_vect)
{
  if (digitalRead(1) == HIGH){
  buttons += 1;
 
  b2 +=1;
 
  bRead = true;
  }
 
   
}


void loop() {
 
  if (bRead == true )
 
  {
   
    while (digitalRead(1) == HIGH){
    test = lcd.readButtons();
   
       
    b2 --;
   
    }
    bRead = false;
   // buttons = 0;
  }
 
  if (millis() - tnow >= 1000 ) {
   
    tnow = millis();
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 1);
  // print the number of seconds since reset:
  lcd.print(millis()/1000);
 
  lcd.setCursor(0, 2);
  lcd.print(buttons);
 
  lcd.setCursor(0, 3);
  lcd.print(test);
 
 
 

 // if (buttons) {
    //lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("                    ");
    lcd.setCursor(0,0);
    if (~test & BUTTON_UP) {
      lcd.print("UP ");
      Serial.println("UP ");
   //   lcd.setBacklight(RED);
    }
    if (~test & BUTTON_DOWN) {
      lcd.print("DOWN ");
      Serial.println("DOWN ");
   //   lcd.setBacklight(YELLOW);
    }
    if (~test & BUTTON_LEFT) {
      lcd.print("LEFT ");
      Serial.println("LEFT ");
    //  lcd.setBacklight(GREEN);
    }
    if (~test & BUTTON_RIGHT) {
      lcd.print("RIGHT ");
      Serial.println("RIGHT ");
   //   lcd.setBacklight(TEAL);
    }
    if (~test & BUTTON_SELECT) {
      lcd.print("SELECT ");
      Serial.println("SELECT ");
   //   lcd.setBacklight(VIOLET);
    } 
 // }
 
  }
}
tedcool
 
Posts: 3
Joined: Thu Jul 19, 2012 2:11 pm

Re: RGB LCD Backpack - Interrupt

by tedcool on Wed Oct 03, 2012 7:31 pm

The I2C speedup to 400 KHZ helps hugely, too!
tedcool
 
Posts: 3
Joined: Thu Jul 19, 2012 2:11 pm

Re: RGB LCD Backpack - Interrupt

by bateske on Sun Mar 24, 2013 1:14 am

tedcool, I tried to use your code but I haven't had any success. The LCD flashes on and then off again quickly.

I haven't made any changes to the wiring, it looks like pin 20 on the MCP23017 should go to pin 1 on the arduino? I didn't want to make any physical changes unless I had to, not sure if this is why it is crashing?

I've double checked that I made the changes correct but maybe if you could upload your files? Or maybe this is an incompatibility with new Arduino? I'm running 1.0.3

thanks,
bateske
 
Posts: 1
Joined: Sat Dec 18, 2010 11:58 pm

Re: RGB LCD Backpack - Interrupt

by joanba on Sat Jul 20, 2013 6:05 am

I don't know if you've solved the issue but I've followed instructions in post

http://www.adafruit.com/forums/viewtopi ... 31&t=32813

and it worked for me. Well explained, maybe a complete example sketch could be better but after figuring how it works I've been able to use the code in my application. I post here the code, derived from the HelloWorld example in Adafruit library:

Code: Select all | TOGGLE FULL SIZE
/*********************

Example code for the Adafruit RGB Character LCD Shield and Library

This code displays text on the shield, and also reads the buttons on the keypad.
When a button is pressed, the backlight changes color.

**********************/

// include the library code:
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Adafruit_RGBLCDShield.h>

// The shield uses the I2C SCL and SDA pins. On classic Arduinos
// this is Analog 4 and 5 so you can't use those for analogRead() anymore
// However, you can connect other I2C sensors to the I2C bus and share
// the I2C bus.
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();

// These #defines make it easy to set the backlight color
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7

// Interruptons: pin 3 = interrupt 1 in Arduino Uno & Mega
volatile boolean isTriggered = false;

void setup() {
  // Debugging output
  Serial.begin(9600);
 
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);

  // Print a message to the LCD. We track how long it takes since
  // this library has been optimized a bit and we're proud of it :)
  int time = millis();
  lcd.print("Hello, world!");
  time = millis() - time;
  Serial.print("Took "); Serial.print(time); Serial.println(" ms");
  lcd.setBacklight(WHITE);
 
  //Catch the external interrupt from the button input
  attachInterrupt(1,ISR_Button, FALLING);
  //Tell the MCP23017 to start making interrupts
  lcd.enableButtonInterrupt();
}

uint8_t i=0;
void loop() {
  uint8_t buttons;
 
  // set the cursor to column 0, line 1
  // (note: line 1 is the second row, since counting begins with 0):
  lcd.setCursor(0, 1);
  // print the number of seconds since reset:
  lcd.print(millis()/1000);

  if (isTriggered) {
   
    //while (!digitalRead(3)) {
    isTriggered = false;
    buttons = lcd.readButtons();
    //} 

    if (buttons) {
      lcd.clear();
      lcd.setCursor(0,0);
      if (buttons & BUTTON_UP) {
        lcd.print("UP ");
        lcd.setBacklight(RED);
      }
      if (buttons & BUTTON_DOWN) {
        lcd.print("DOWN ");
        lcd.setBacklight(YELLOW);
      }
      if (buttons & BUTTON_LEFT) {
        lcd.print("LEFT ");
        lcd.setBacklight(GREEN);
      }
      if (buttons & BUTTON_RIGHT) {
        lcd.print("RIGHT ");
        lcd.setBacklight(TEAL);
      }
      if (buttons & BUTTON_SELECT) {
        lcd.print("SELECT ");
        lcd.setBacklight(VIOLET);
      }
    }
  }
}

//ISR to service the button interrupt
void ISR_Button() {
  isTriggered = true;
}


I hope to help !!!

But I also have some doubts, because if attachInterrupt uses mode LOW or CHANGE, the sketch hangs. Any idea ?

Regards,
Joan
joanba
 
Posts: 18
Joined: Tue Oct 30, 2012 5:57 am