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;
}