Thanks very much for the test files. I can confirm that it runs perfectly. This at least proves that my hardware is OK, and that I am programming it correctly. Unfortunately, my sketches still aren't working. As much as I hate to post large sketches, I'm at the point where I don't know what else to do.
First is the Mega sketch, followed by the ESP sketch. The Mega sends the command [NTP] to the ESP. The ESP responds with [NTP,UNIX time]. The ESP also sends this same message 30s after startup. Both sketches perform correctly when running separately. I send messages on the Serial link and I get the correct responses. When connected together, I don't think serial data is happening.
The only curious thing is that when I startup the ESP, it sends about 100 characters of random junk out the serial port. I have put countermeasures in - a limit of 250 chars per message. I can't see how this would affect anything, but it's odd.
Mega code
// real-time clock
// Author: Marc Hillman VK3OHM
// Features:
// LCD TFT display
// inbuilt RTC
// WiFi
//Programming the Mega
// DIP switches OFF OFF ON ON OFF OFF OFF OFF
// press Reset then MODE
// upload code
// to run
// DIP switches ON ON ON ON OFF OFF OFF OFF
// Tx/Rx switch set to 3
// press Reset code will run
//#define DEBUG true // setting true will enable Serial print statements
//
#define SERIAL_BAUD 115200
// TFT LCD display
#include <UTFT.h> // Core graphics library
#include <SPI.h>
#include <SD.h>
#include <TimeLib.h>
#include "Grotesk32x64.c"
#include "wifi.c" // wifi logo to indicate link status
// PicoDev RV3028 RTC
#include <RV-3028-C7.h>
RV3028 rtc; // only the UNIX clock of the rtc is used. Time of day clock is not used.
/*
pin INTx interrupt number for attachInterrupt()
3 PE5 INT5 1
2 PE4 INT4 0
18 TX1 PD3 INT3 5
19 RX1 PD2 INT2 4
20 SDA PD1 INT1 3
21 SCL PD0 INT0 2
(nc) PE6 INT6 -
(nc) PE7 INT7 - */
#define interruptPin 2 // use pin 2 (INT4) for clock input
// Declare which fonts we will be using
extern uint8_t SmallFont[];
extern uint8_t BigFont[];
tmElements_t tod; // time broken into components
UTFT myGLCD(ILI9481, 38, 39, 40, 41); // create panel object
#define PORTRAIT 0
#define LANDSCAPE 1
#include <StringSplitter.h>
String receivedCommand = "";
bool dataIn = false;
volatile bool requestNTP = false; // set to request NTP update
int timezone = 0;
const char *tz[] = { "UTC", "Local" };
const char *DaysOfWeek[] = {" Sunday ", " Monday ", " Tuesday ", "Wednesday", " Thursday", " Friday ", " Saturday" }; // strings for day of week - padded to width 9
char buffer[100]; // general purpose buffer
void setup() {
Serial.begin(SERIAL_BAUD);
Serial.println(F("Entered setup"));
Serial3.begin(SERIAL_BAUD);
// Set LCD
myGLCD.InitLCD(LANDSCAPE);
myGLCD.clrScr();
// Setup RTC
Wire.begin();
if (rtc.begin() == false) {
Serial.println(F("Something went wrong, check wiring"));
//while (1);
} else
Serial.println(F("RTC online!"));
rtc.enableTrickleCharge(TCR_3K); // trickle charge backup battery
Serial.print(F("UNIX time is ")); Serial.println(rtc.getUNIX());
// The master date/time is the UNIX value in the RTC. The calendar (year, month, day, etc) is ignored.
// The system clock is regularly synced to the master clock
setSyncProvider(syncProvider); // the function to get the time from the RTC
setSyncInterval(60 * 60); // onboard clock is terrible. Needs synching at least every hour
if (timeStatus() != timeSet)
Serial.println(F("Unable to sync with the RTC"));
else
Serial.println(F("RTC has set the system time"));
rtc.disableClockOut();
delay(1000);
rtc.enableClockOut(FD_CLKOUT_1); // Start the 1 Hz timer
pinMode(interruptPin, INPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), displayTime, FALLING); // 1Hz pulse from rtc
}
void loop() {
if (requestNTP) {
// Request an NTP update
wifiIcon(false); // indicate time may be invalid by removing icon
Serial3.println(F("[NTP]")); // request NTP time
requestNTP = false;
}
// listen for commands from other processor
while (Serial3.available()) {
char c = Serial3.read(); //read it
if (c == '[') {
//this is the start of the command string
receivedCommand = "";
dataIn = true;
}
//otherwise, we are still reading the command string:
else if (dataIn && c != ']') {
if (receivedCommand.length() < 250) // enforce maximum command length
receivedCommand += c;
else {
// maximum command length exceeded. Restart
receivedCommand = "";
dataIn = false;
Serial.println(F("[ERR]")); // unrecognised command
}
} else if (dataIn && c == ']') {
//finished receiving the command, process it
Serial.print(receivedCommand); Serial.println(F(" -> Arduino"));
StringSplitter *splitter = new StringSplitter(receivedCommand, ',', 10); // parse the command
String command = splitter->getItemAtIndex(0); // process based on first word
if (command == "NTP" and splitter->getItemCount() == 2) {
wifiIcon(true); // indicate time is valid by showing wifi icon
// set the RTC time. The Real Time and UNIX time are independent, so need to set both
time_t epoch = splitter->getItemAtIndex(1).toInt(); // get UNIX TS
Serial.print("Setting system clock to: "); Serial.println(epoch);
setTime(epoch); // set the time of day clock
rtc.setUNIX(epoch); // and the RTC
// Update rtc Time of Day
breakTime(epoch, tod); // break unix time into components
rtc.setTime(tod.Second, tod.Minute, tod.Hour, tod.Wday, tod.Day, tod.Month, tod.Year);
} else
Serial.println(F("[ERR]")); // unrecognised command
delete splitter; // destroy when finished
receivedCommand = "";
dataIn = false;
}
}
delay(100);
//displayTime();
}
uint32_t syncProvider()
{
// You cannot read the RTC during an interrupt, so set the onboard clock to sync to RTC
return rtc.getUNIX();
}
void wifiIcon(bool display) {
// display the wifi Icon (or not)
if (display)
myGLCD.drawBitmap(myGLCD.getDisplayXSize() - 32, 0, 32, 32, wifi); // wifi logo top right
else {
myGLCD.setColor(VGA_BLACK); // foreground colour
myGLCD.fillRect(myGLCD.getDisplayXSize() - 32, 0, 32, 32);
}
}
void displayTime() { // display date/time ISR
// Note: you cannot use Serial, or the I2C bus, in an interrupt routine
tmElements_t t; // time broken into components
char buffer[100];
interrupts(); // enable interupts so we don't slow down the time of day clock. Risky, but no choice.
breakTime(now(), t); // break current time into pieces
myGLCD.setFont(BigFont);
myGLCD.setColor(VGA_RED);
myGLCD.print(tz[timezone], CENTER, 0); // show timezone
myGLCD.setColor(VGA_YELLOW);
myGLCD.print(DaysOfWeek[t.Wday - 1], CENTER, 300); // show Day of Week
myGLCD.setFont(Grotesk32x64);
myGLCD.setColor(0, 255, 0);
snprintf(buffer, sizeof(buffer), "%02d.%02d.%04d", t.Day, t.Month, t.Year + 1970);
myGLCD.print(buffer, CENTER, 60);
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d", t.Hour, t.Minute, t.Second);
myGLCD.print(buffer, CENTER, 200);
if (t.Minute == 0 and t.Second == 0) requestNTP = true; // request NTP update once per hour.
}
ESP code
// Programming the ESP8266
// Set dip switches 1-4 and 8 OFF and 5-7 ON.
// Arduino IDE under Tools|Board: set "NodeMCU 1.0 (ESP12E Module)"
// press reset - press Mode button
// upload code
//
// to run code
// set the dip switches 1-4 and 7-8 OFF and 5-6 ON
// press reset - GPIO2 will blink and ESP8266 Serial output will appear on serial monitor
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <StringSplitter.h>
#ifndef STASSID
#define STASSID F("BigPond0277")
#define STAPSK F("********")
#endif
unsigned int localPort = 2390; // local port to listen for UDP packets
/* Don't hardwire the IP address or we won't get the benefits of the pool.
Lookup the IP address for the host name instead */
IPAddress timeServerIP; // time.nist.gov NTP server address
const char* ntpServerName = "time.nist.gov";
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
WiFiUDP udp; // A UDP instance to let us send and receive packets over UDP
#define SERIAL_BAUD 115200 //make sure this is the same in arduino.ino
String receivedCommand = "";
bool dataIn = false;
void setup(void)
{
Serial.begin(SERIAL_BAUD);
delay(500);
Serial.println(F("Entered setup"));
// We start by connecting to a WiFi network
Serial.print(F("Connecting to "));
Serial.println(STASSID);
WiFi.mode(WIFI_STA);
String newHostname = F("UTC_Clock"); // pretty host name
WiFi.hostname(newHostname.c_str());
WiFi.begin(STASSID, STAPSK);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println(F("WiFi connected"));
Serial.print(F("IP address: ")); Serial.println(WiFi.localIP());
Serial.println(F("Starting UDP"));
udp.begin(localPort);
Serial.print(F("Local port: ")); Serial.println(udp.localPort());
delay(20000); // allow time for the ATMega half to boot up
NTP(); // perform an NTP, sending result to ATMega half
}
void loop(void)
{
// listen for commands from other processor
while (Serial.available())
{
char c = Serial.read(); //read it
if (c == '[') {
//this is the start of the command string
receivedCommand = "";
dataIn = true;
}
//otherwise, we are still reading the command string:
else if (dataIn && c != ']') {
if (receivedCommand.length() < 250) // enforce maximum command length
receivedCommand += c;
else {
// maximum command length exceeded. Restart
receivedCommand = "";
dataIn = false;
Serial.println(F("[ERR]")); // unrecognised command
}
}
else if (dataIn && c == ']')
{
//finished receiving the command, process it
StringSplitter *splitter = new StringSplitter(receivedCommand, ',', 5); // parse the command
String command = splitter->getItemAtIndex(0); // process based on first word
Serial.print(command); Serial.println(F(" -> WiFi"));
if (command == "NTP") NTP();
else
Serial.println(F("[ERR]")); // unrecognised command
delete splitter; // destroy when finished
receivedCommand = "";
dataIn = false;
}
}
}
// get the time from NTP
void NTP() {
char buffer[100]; // general purpose buffer
//get a random server from the pool
WiFi.hostByName(ntpServerName, timeServerIP);
sendNTPpacket(timeServerIP); // send an NTP packet to a time server
// wait to see if a reply is available
delay(1000);
int cb = udp.parsePacket();
if (!cb) {
Serial.println(F("no packet yet"));
} else {
Serial.print(F("packet received, length="));
Serial.println(cb);
// We've received a packet, read the data from it
udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer
//the timestamp starts at byte 40 of the received packet and is four bytes,
// or two words, long. First, esxtract the two words:
unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
// combine the four bytes (two words) into a long integer
// this is NTP time (seconds since Jan 1 1900):
unsigned long secsSince1900 = highWord << 16 | lowWord;
Serial.print("Seconds since Jan 1 1900 = ");
Serial.println(secsSince1900);
// now convert NTP time into everyday time:
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
// Send data to other processor
snprintf(buffer, sizeof(buffer), "[NTP,%d]", epoch);
Serial.println(buffer);
}
// wait ten seconds before asking for the time again
delay(10000);
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress & address) {
Serial.println(F("sending NTP packet..."));
// set all bytes in the buffer to 0
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// Initialize values needed to form NTP request
// (see URL above for details on the packets)
packetBuffer[0] = 0b11100011; // LI, Version, Mode
packetBuffer[1] = 0; // Stratum, or type of clock
packetBuffer[2] = 6; // Polling Interval
packetBuffer[3] = 0xEC; // Peer Clock Precision
// 8 bytes of zero for Root Delay & Root Dispersion
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
// all NTP fields have been given values, now
// you can send a packet requesting a timestamp:
udp.beginPacket(address, 123); //NTP requests are to port 123
udp.write(packetBuffer, NTP_PACKET_SIZE);
udp.endPacket();
}