Help with Serial Gateway System.sleep()

I’m hacking together an Electron (I am right now using Photon to test) with a (FTDI capable) Arduino Nano so that I can create a serial interface with my HA device.

My goal is to enable the Nano to process messages and react in the case of an inactive controller. If a high priority message is processed, it will wake the Photon and the Photon will transmit the message using Pushover (or SMS later).

It all works, but I wanted to add one more piece: I want to detect power loss. The Nano is powered by the controller’s USB port while it is in communication with the device.

Nano’s pin7 is wired to the Photon’s WKP pin; I have common ground. It is a 5.0Volt Nano.

It all works as expected, but when I get a power loss I get multiple wake-up events on the Photon. I believe it is because the Photon’s pin is in a floating state. I saw the advice not to use the internal pull-ups when interfacing wth 5V devices.

I tried pulling up WKP to 3V3 on the Photon, but it then never sees the drop when the power is disconnected from the Nano…

Can anyone recommend a clever/easy way to solve this problem? I’m thinking to use a transistor to switch the WKP pin from high to low. I don’t want to throw it together without understanding the impact of the 3.3 to 5V transition.

So my NANO code looks like this:

#include <SoftwareSerial.h>
#include <EEPROM.h>

struct DeviceSettings {
  uint32_t commTimeout = 1 * 60 * 1000UL;
} deviceSettings;


const size_t MAX_MESSAGE_LENGTH = 32;
SoftwareSerial Serial1(4, 5);

const int wakeupPin = 7;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(57600);
  pinMode(wakeupPin, OUTPUT);
  pinMode(13, OUTPUT);
  digitalWrite(wakeupPin, HIGH);
  //EEPROM.put(0, deviceSettings);
  EEPROM.get(0, deviceSettings);
}

void loop()
{
  static uint32_t pingMillis = 0;
  if (char* newMessage = checkForNewMessage(Serial, '\n'))
  {
    Serial.println(newMessage);
    if (strstr(newMessage, "WAKE"))
    {
      digitalWrite(wakeupPin, LOW);
      digitalWrite(13, HIGH);
      delay(10000);
      char alert[MAX_MESSAGE_LENGTH];
      strcpy(alert, newMessage);
      strtok(alert, ":");
      strcpy(alert, strtok(NULL, ":")); // just send the message part, not the header
      Serial1.println(alert);  
      Serial1.flush();
      digitalWrite(wakeupPin, HIGH);
      digitalWrite(13, LOW);
    }
    else if (strstr(newMessage, "PING"))
    {
      char ping[MAX_MESSAGE_LENGTH];
      strcpy(ping, newMessage);
      strtok(ping, ":");
      uint32_t newTimeout = atol(strtok(NULL, ":")) * 60 * 1000;
      if (newTimeout >= 0)
      {
        deviceSettings.commTimeout = constrain(newTimeout, 0, 60 * 60 * 1000UL);
        EEPROM.put(0, deviceSettings);
      }
      pingMillis = millis();
    }
    else if (strstr(newMessage, "SET"))
    {
      //
    }
  }
  if (millis() - pingMillis > deviceSettings.commTimeout)
  {
    digitalWrite(wakeupPin, HIGH);
    digitalWrite(13, HIGH);
    delay(10000);
    Serial1.println(F("Controller Down"));
    Serial1.flush();
    digitalWrite(wakeupPin, LOW);
    digitalWrite(13, LOW);
    pingMillis += deviceSettings.commTimeout;
  }
}

char* checkForNewMessage(Stream& stream, const char endMarker)
{
  static char incomingMessage[MAX_MESSAGE_LENGTH] = "";
  static byte idx = 0;
  if (stream.available())
  {
    incomingMessage[idx] = stream.read();
    if (incomingMessage[idx] == endMarker)
    {
      incomingMessage[idx] = '\0';
      idx = 0;
      return incomingMessage;
    }
    else
    {
      idx++;
      if (idx > MAX_MESSAGE_LENGTH - 1)
      {
        stream.print(F("{\"error\":\"message too long\"}\n"));
        idx = 0;
        incomingMessage[idx] = '\0';
      }
    }
  }
  return nullptr;
}

The Photon 's code looks like this:

const size_t MAX_MESSAGE_LENGTH = 33;
retained bool mssgSent = false;

void setup()
{
  Serial1.begin(57600);
  Serial.begin(115200);
  pinMode(D7, OUTPUT);
  pinMode(WKP, INPUT);
  uint32_t startupMillis = millis();
  while (millis() - startupMillis < 10000)
  {
    Particle.process();
  }
}

void loop()
{
  digitalWrite(D7, HIGH);
  if (Particle.connected() == false)
  {
    Particle.connect();
  }
  uint32_t startTimeOut = millis();
  while (millis() - startTimeOut < 20000 and !mssgSent)
  {
    Particle.process();
    if (const char* newMessage = checkForNewMessage(Serial1, '\n'))
    {
      Serial1.println(newMessage);
      Serial.println(newMessage);
      Particle.publish("pushover", newMessage, 60, PRIVATE);
      delay(1000);
      digitalWrite(D7, !digitalRead(D7));
      mssgSent = true;
    }
  }
  if (!mssgSent)
  {
    if(digitalRead(WKP))
    {
      Particle.publish("pushover", "Gateway Restart", 60, PRIVATE);
    }
    else
    {
      Particle.publish("pushover", "Power Loss Detected", 60, PRIVATE);
    }
  }
  startTimeOut = millis();
  while (millis() - startTimeOut < 5000)  // wait for message to be sent (Electron)
  {
    Particle.process();
  }
  digitalWrite(D7, LOW);
  mssgSent = false;
  System.sleep(WKP, CHANGE);
}

char* checkForNewMessage(Stream& stream, const char endMarker)
{
  static char incomingMessage[MAX_MESSAGE_LENGTH] = "";
  static byte idx = 0;
  if (stream.available())
  {
    if (stream.peek() == '\r')
    {
      (void)stream.read();
      return nullptr;
    }
    incomingMessage[idx] = stream.read();
    if (incomingMessage[idx] == endMarker)
    {
      incomingMessage[idx] = '\0';
      idx = 0;
      return incomingMessage;
    }
    else
    {
      idx++;
      if (idx > MAX_MESSAGE_LENGTH - 1)
      {
        stream.print(F("{\"error\":\"message too long\"}\n"));
        while (stream.available())
          (void)stream.read();
        idx = 0;
        incomingMessage[idx] = '\0';
      }
    }
  }
  return nullptr;
}

How do you Wake up more than once without going to sleep?

I don’t understand your question, @Viscacha

I guess the question is based on the assumption that, when woken once, the Photon won’t get back to sleep before the Nano gets power again and hence multi triggers would not upset your code as it’s only used for wake-up - but I guess the assumption is not valid.

But for your problem, I think you need to consider the three possible states of the Nano pin (HIGH, LOW, floating).
On a quick browse of your code I got the impression the default level of pin 7 is HIGH and will be pulled to LOW when the Photon should be woken, right?
Assuming that the Photon won’t go to sleep while the Nano pulls the pin LOW I’d think the external pull-resistor of your WKP pin should be tied to GND in order to give you a (default) HIGH to LOW edge once the Nano pin starts to float.
As you’ve currently got it, I get the feeling you’d expect to wake on a (default/active) HIGH to HIGH (pull-up driven).

The assumption is that if power is lost that indeed power may have been restored (i.e. momentary power loss) that is why I was checking for the WKP pin during a wakeup event that had no message.

I re-wired things to not use the WKP pin and it seems fine now. Instead I’m checking for power on A1, pulled down to ground. The Nano immediately powers the corresponding output on startup. So it is now working well… detecting any power loss and processing messages.

I’m not sure why I couldn’t get it to work using WKP pin, but I moved on to bigger challenges!

thanks @ScruffR

1 Like