Not your usual floating digitalRead (INPUT_PULLUP used...)

Hi,

I'm working on a project that requires the Arduino to go to sleep and awake upon state changes on some digital pins. The problem that I have is the old random digitalRead behaviour. The puzzling thing is:

  1. I'm aware of the required pullup resistors. Used pinMode INPUT_PULLUP to activate the internal one.
  2. Random reads are also occurring when the switch is closed. I did confirm that the switch is closed with a multimeter but sometimes I'm reading it open.

I double checked and the pins are not being reconfigured elsewhere, at least not by me directly. Could it be that when ATMega382 goes to sleep the pinMode configuration is lost?

I see below a snippet of the core code that configures and reads the digital pins. Sure can send the full if necessary. It got a bit long due to the sleeping piece and code to control XBee

Anybody has any idea of what could be happening?

Cheers,
Luiz

GND -----/ ------- Pin 2
...
GND -----/ ------- Pin 7

Code snippet

const uint8_t pins[numPins] = { 2, 3, 4, 5, 6, 7 };

void setup()
{

...

Serial.begin(9600);

for(int i=0;i<numPins;i++)
{
Serial.print(pins*);*

  • Serial.print(',');*
    pinMode(pins*, INPUT_PULLUP); //set the pin to input*
    //pinsState = digitalRead(pins*);*
    * }*

* Serial.println(' ');*

...
}
void loop()
{
* uint8_t buffer[numPins];*

* for(int i=0;i<numPins;i++)*
buffer = digitalRead(pins*) + '0';*
}
P.S. Just a thought: does powering off ADC affect digital reads also?

The pinMode stays the same, unless you use the WatchDog to wake up.
It is possible to go into deep sleep, and let the WatchDog time out to wake up the Arduino.

Turning off the ADC does not affect the digital reads, even the digital read for the analog pins is still okay.

You snippet shows a bug: "pins", I think you want to use "pins [ i ]".
Just for fun : http://snippets-r-us.com/

When you don't use code tags some of the square brackets don't show.

const uint8_t pins[numPins] = { 2, 3, 4, 5, 6, 7 };

void setup() 
{

...

  Serial.begin(9600);

  for(int i=0;i<numPins;i++)
  {
    Serial.print(pins[i]);
    Serial.print(',');
    pinMode(pins[i], INPUT_PULLUP);     //set the pin to input
    //pinsState[i] = digitalRead(pins[i]);
  }
  
  Serial.println(' ');
  
...

}

void loop() 
{
  uint8_t buffer[numPins];
  
  for(int i=0;i<numPins;i++)
    buffer[i] = digitalRead(pins[i]) + '0';
}

Thanks all for you help!
See attached code (bit bit large with the sleeping stuff and XBee).

Delta_G: I double checked and the code (attached) is fine. The error you saw would give a syntax error (just tried to confirm).

Peter_n: yes, indeed I'm using a watchdog to sleep but configure to not reset. Maybe the io digital IO configuration is a bit messed up upon returning from sleep?

Cheers,
Luiz

DigitalSensor.ino (10.7 KB)

Is it an ATmega328P ? as used on the Arduino Uno board ?
I have been reading in the datasheet, but I can't figure out if your code is okay. Sometimes a bit in the WatchDog registers change because another bit is set or cleared. Very confusing.
I did a search in my projects and I have used the WatchDog with and without the reset. But it's too long ago.
Is the interrupt change mode still working with the maximum power down sleep mode ?

Is your XBee requesting too much current ? Perhaps the trouble is caused by a voltage drop.

Please edit your post, select the code, and put it between [code] ... [/code] tags.

You can do that by hitting the "Code" button above the posting area (It looks like a scroll with < > inside it).

How to use this forum

Maybe the io digital IO configuration is a bit messed up upon returning from sleep?

No, that doesn't happen. It would be pretty useless if it did.

Hi,

Peter: yes I guess it could be a current spike, that would explain why things seems more stable at the bench where I use an Arduino UNO with USB power supply from a powered hub. The target board is an Modern Device RBBB (http://moderndevice.com/product/rbbb-kit/) running at 3.3V, regulator L493133 max 250mA, however I shouldn't be close to that. Measuring at the bench with UNO 5V I'm getting ~150mA full power. I can't measure peaks very well with my meter. Power is coming from 4AA Duracell, reasonably new.

Yeah, I have to confess I didn't write the sleep code, just copied from elsewhere.

Nick: see code below. I had to remove some comments as it was exceeding the 9K limit. It's hard to streamline further without knowing the cause of the problem.

Regards,
Luiz

#include <avr/sleep.h>
#include <avr/power.h>
#include <AltSoftSerial.h>
#include <PinChangeInt.h>
#include <XBee.h>

XBeeAddress64 COORDINATOR = XBeeAddress64(0x00, 0x00);

XBee xbee = XBee();

uint8_t ssRX = 2;
uint8_t ssTX = 3;
AltSoftSerial nss;

const int numPins = 6;
const int START_PIN = 2;
const int XBEE_SLEEP = 10;
const int PIN_LED = 11;
const uint8_t pins[numPins] = { 2, 3, 4, 5, 6, 7 };

const uint8_t NO_PIN = 255;
volatile bool interruptState = false;

int sleepIterations = 0;
volatile bool watchdogActivated = false;

#define LOGGING_FREQ_SECONDS 3600 // Seconds to wait before a new sensor reading is logged.

#define MAX_SLEEP_ITERATIONS LOGGING_FREQ_SECONDS / 8 // Number of times to sleep (for 8 seconds) before
// a sensor sending XBee ping to the coordinator.

// Define watchdog timer interrupt.
ISR(WDT_vect)
{
  // Set the watchdog activated flag.
  watchdogActivated = true;
}

void send(ZBTxRequest& zbTx)
{  
  // try a few times before giving up
  for(int i=0;i<20;i++)
  {
    xbee.send(zbTx);  
    if (xbee.readPacket(1000)) 
    {
      ZBTxStatusResponse tx = ZBTxStatusResponse();

      if (xbee.getResponse().getApiId() == ZB_TX_STATUS_RESPONSE)
      {
        xbee.getResponse().getZBTxStatusResponse(tx);
        int st = tx.getDeliveryStatus();
        Serial.print("TX status: ");
        Serial.println(st);

        // get the delivery status, the fifth byte
        if (st == SUCCESS)
        {
          Serial.println("Transmission OK");
          // Reset the number of sleep iterations.
          sleepIterations = 0;
          return;
        }
      }
    }
    Serial.println("Error sending command. Will try again");
    delay(500);
  }
  Serial.println("Failed to transmit!");
}

bool sendAtCommand(AtCommandRequest& atRequest, uint8_t* buffer, int& size) 
{
  Serial.println("Sending command to the XBee");

  AtCommandResponse atResponse = AtCommandResponse();

  // send the command
  xbee.send(atRequest);

  if (xbee.readPacket(5000))
  {
    // should be an AT command response
    if (xbee.getResponse().getApiId() == AT_COMMAND_RESPONSE)
    {
      xbee.getResponse().getAtCommandResponse(atResponse);

      if (atResponse.isOk())
      {
        Serial.print("Command [");
        Serial.print(atResponse.getCommand()[0]);
        Serial.print(atResponse.getCommand()[1]);
        Serial.println("] was successful!");

        if (atResponse.getValueLength() > 0) 
        {
          Serial.print("Command value length is ");
          Serial.println(atResponse.getValueLength(), DEC);

          Serial.print("Command value: ");

          uint8_t* r = atResponse.getValue();

          for (int i = 0; i < atResponse.getValueLength() && i<size; i++) 
          {
            Serial.print(r[i], HEX);
            buffer[i] = r[i];
            Serial.print(" ");
          }
          size = atResponse.getValueLength();

          Serial.println("");
          return true;
        }
      } 
      else 
      {
        Serial.print("Command return error code: ");
        Serial.println(atResponse.getStatus(), HEX);
      }
    } 
    else 
    {
      Serial.print("Expected AT response but got ");
      Serial.print(xbee.getResponse().getApiId(), HEX);
    }   
  } 
  else
  {
    // at command failed
    if (xbee.getResponse().isError())
    {
      Serial.print("Error reading packet.  Error code: ");  
      Serial.println(xbee.getResponse().getErrorCode());
    } 
    else
    {
      Serial.print("No response from radio");  
    }
  }
  return false;
}

void waitXBeeJoinNetwork()
{
  // serial high
  uint8_t shCmd[] = {'S','H'};
  // serial low
  uint8_t slCmd[] = {'S','L'};
  // association status
  uint8_t assocCmd[] = {'A','I'};
  uint8_t buffer[16];
  AtCommandRequest atRequest = AtCommandRequest(shCmd);
  // set command to AI

  atRequest.setCommand(assocCmd);

  Serial.println("Waiting XBee to join network...");
  for(int i=0;i<1000;i++)
  {
    int size = 16;
    if (sendAtCommand(atRequest,buffer,size) && buffer[0]==0)
    {
      Serial.print("Done. Number of tries: ");
      Serial.println(i+1, DEC);
      return;
    }
    Serial.println("Not yet, trying again...");
    delay(1000);
  }
}

void txStates(char reasonTX)
{
  uint8_t pinsState[numPins];

  digitalWrite(PIN_LED, HIGH);

  digitalWrite(XBEE_SLEEP, LOW);   // Wakeup Xbee
  delay(50);

  // Prepare for read
  for(int i=0;i<numPins;i++)
    pinMode(pins[i], INPUT_PULLUP);     //set the pin to input

  bool tx = true;

  while(tx)
  {
    char buffer[numPins+2];
    for(int i=0;i<numPins;i++)
    {
      buffer[i] = digitalRead(pins[i]) + '0';
    }

    buffer[numPins] = reasonTX;
    buffer[numPins+1] = 0;
    Serial.print("Sending ");
    Serial.println(buffer);
    ZBTxRequest zbTx = ZBTxRequest(COORDINATOR, (uint8_t*)buffer, numPins+2);
    send(zbTx);

    tx = false;

    // The code above can be slow. Check at the end if 
    // we are still current
    for(int i=0;i<numPins;i++)
    {
      if (buffer[i] != (digitalRead(pins[i]) + '0'))
      {
        tx = true;
        Serial.println("Retransmit required...");
        break;
      }
    }

  }
  digitalWrite(PIN_LED, LOW);
}

void setup() 
{
  pinMode(PIN_LED, OUTPUT);
  digitalWrite(PIN_LED, HIGH);

  Serial.begin(115200);
  Serial.print("PinChangeInt test on pins ");

  for(int i=0;i<numPins;i++)
  {
    Serial.print(pins[i]);
    Serial.print(',');
    pinMode(pins[i], INPUT_PULLUP);     //set the pin to input
    //pinsState[i] = digitalRead(pins[i]);
  }  
  Serial.println(' ');

  // Turn off the ADC while asleep.
  power_adc_disable();

  pinMode(XBEE_SLEEP, OUTPUT);     // XBee sleep control
  digitalWrite(XBEE_SLEEP, LOW);   // deassert to keep radio awake when sleep mode selected

  nss.begin(9600);

  // Tell XBee to use AltSoftwareSerial
  xbee.setSerial(nss);

  waitXBeeJoinNetwork();

  txStates('I');

  noInterrupts();

  // Set the watchdog reset bit in the MCU status register to 0.
  MCUSR &= ~(1<<WDRF);

  // Set WDCE and WDE bits in the watchdog control register.
  WDTCSR |= (1<<WDCE) | (1<<WDE);

  // Set watchdog clock prescaler bits to a value of 8 seconds.
  WDTCSR = (1<<WDP0) | (1<<WDP3);

  // Enable watchdog as interrupt only (no reset).
  WDTCSR |= (1<<WDIE);

  // Enable interrupts again.
  interrupts();

  digitalWrite(PIN_LED, LOW);

  Serial.println("setup completed!");
}

void sleepNow()         // here we put the arduino to sleep
{
  digitalWrite(XBEE_SLEEP, HIGH);   // put XBee to sleep

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here

  sleep_enable();          // enables the sleep bit in the mcucr register
  // so sleep is possible. just a safety pin 


  for(int i=0;i<numPins;i++) // setup interrupt handlers
    PCintPort::attachInterrupt(pins[i], interruptHandler, CHANGE);

  Serial.println("Going to sleep...");
  //delay(50);

  sleep_mode();            // here the device is actually put to sleep!!
  // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP

  sleep_disable();         // first thing after waking from sleep: disable sleep...

  //delay(50);
  Serial.println("Awake!");

  for(int i=0;i<numPins;i++) // remove interrupt handlers to avoid being called until next sleep cycle.
    PCintPort::detachInterrupt(pins[i]);      
}

void interruptHandler()
{
  interruptState = true;
  delay(500);
}

void loop() 
{

  // Check if awaken because of watchDog.
  if (watchdogActivated)
  {
    Serial.println("Watchdog");
    watchdogActivated = false;
    sleepIterations += 1;
    if (sleepIterations >= MAX_SLEEP_ITERATIONS)
    {
      Serial.println("Active!");
      sleepIterations = 0;
      txStates('F');
    }
  }

  if (interruptState)
  {
    Serial.println("Pin change");
    interruptState = false;
    txStates('C');
  }

  sleepNow();
}