Hey all! First post here but I've read so many similar posts to this one inside and out but no dice. So I am using an Arduino Mega 2560, a W5100 Ethernet Shield, and a 3.5in (ILI9481) TFT shield with an SD card slot (which is in use).
I've gotten this code to work before, but it's hit or miss whenever I change anything and I cant seem to get it working now. So I need it to: load an image from the SD card, display it to the screen, start the ethernet web server (preferably with DHCP), and then query DHT sensor values to display to the web page.
The current issue: For some reason, server.available() always returns 0 so I can't connect to the web page anymore. I've tried different mac addresses so it gets a new DCHP IP but no luck. Running the test scripts, everything works independent of each other, just not together. My code is very long, but I'll include it all. Like I mentioned earlier, I've gotten this to work before so I know it's possible, but it's been months since I've looked at it because of school. Am I just missing some sort of line to reset the SPI after establishing the SD card? Nothing "fails", but once in the loop(), client is always 0 and never "sees" a connection request.
Thank you!!
#include <UTFT.h>
#include <SPI.h>
#include <SdFat.h>
#include <UTFT_SdRaw.h>
#include <TimeLib.h>
#include <stdio.h>
#include "DHTStable.h"
#include <Ethernet.h>
#include <Vector.h>
/*
* TODO
* Read in each file for mac, ip, building, etc.
* Create admin web interface
* let the SD card files be written to from here?
* Verify 24hr temp/humid saving system
* Fix ip addr display on screen
* change ethernet.begin(mac) to include ip if no dhcp is set
*/
const int SD_CHIP_SELECT = 53; // SD chip select pin (Arduino Mega)
//const int SD_CHIP_SELECT = 57; // SD chip select pin (Arduino Mega)
String BUILDING = "C0";
// Initialize the SD card, screen, and create a pointer to load images onto the screen
//UTFT name(MODEL, RS, WR, CS, RST);
UTFT myGLCD(ILI9481, 38, 39, 40, 41); // Connected to main GPIO rows
UTFT_SdRaw myFiles(&myGLCD);
// Load in the fonts to use for the LCD screen
extern uint8_t BigFont[];
extern uint8_t SmallFont[];
// Define the sensor types and their pins
DHTStable DHT0;
DHTStable DHT1;
DHTStable DHT2;
// NOTE: CANNOT use analog pins for DHT22 sensor reading, must use PWM pins
#define DHT22_PIN0 8
#define DHT22_PIN1 9
#define DHT22_PIN2 10
// In order to get date and time, we must send a request to a time server via port 8888 in this case
// We will use an NTP server from https://tf.nist.gov/tf-cgi/servers.cgi
// We will use this server in colorado: 128.138.140.44 and then adjust for the timezone difference
IPAddress timeSrvr(128,138,140,44);
EthernetUDP ethernet_UDP;
unsigned int localPort = 8888; // I believe that any UDP port should work, so change as you please
// variable to store previous displayed time
time_t prevDisplay = 0;
// array to hold incoming/outgoing NTP messages
// NTP time message is 48 bytes long
byte messageBuffer[48];
// Manually define device's MAC address
// Must be 12 characters long, each with "0x" preceding them
// Can be any numbers 0-9 and letters A-F
// This is a good website to generate MAC's: https://www.browserling.com/tools/random-mac
// Example, if that site outputs 0C:AD:BE:EF:FE:ED then make sure you have a 0x before each 2 characters
// as demonstrated below
byte mac[] = { 0x12, 0x34, 0xBE, 0xEF, 0xFE, 0xED };
// Function to turn the IPAddress type into a string
String DisplayAddress(IPAddress address)
{
return String(address[0]) + "." +
String(address[1]) + "." +
String(address[2]) + "." +
String(address[3]);
}
String mac2String(byte ar[]){
String s;
for (byte i = 0; i < 6; ++i)
{
char buf[3];
sprintf(buf, "%.2X", ar[i]);
s += buf;
if (i < 5) s += ':';
}
return s;
}
String readline(File file) {
String contents = "";
while (file.available()) {
contents += (char)file.read();
}
return contents;
}
void configureNonDHCP() {
// If you do not want to use DHCP, call this function in setup() below
IPAddress ip(10, 0, 0, 192);
Serial.print("IP: ");
Serial.print(DisplayAddress(ip));
Serial.print("\n");
Ethernet.begin(mac, ip);
}
// Initialize arrays to hold the temperature and humidity values
float h[3];
float t[3];
float tf[3];
float avgTempC = 0.00;
float avgTempF = 0.00;
float avgHumid = 0.00;
// Set mininmum values to 999 so that they can be overwritten
float minTempC = 999.00;
float minHumid = 999.00;
// Set maximum values to 0 so that they can be overwritten
float maxTempC = 0.00;
float maxHumid = 0.00;
//(int CS=A3, int RS=A2, int WR=A1, int RD=A0, int RST=A4)
/*
SD_CS = 53
CS = 46
RS = 44
WR = 47
RD =
RST = 49
*/
// Start the ethernet server running on port 80
EthernetServer server(80);
void(* resetFunc) (void) = 0; //declare reset function @ address 0
void setup()
{
//pinMode(4,OUTPUT);
//digitalWrite(4,HIGH);
Serial.begin(9600);
//dht0.begin();
//dht1.begin();
//dht2.begin();
Serial.println(F("Initialising LCD."));
myGLCD.InitLCD();
myGLCD.clrScr();
myGLCD.setBackColor(VGA_WHITE);
myGLCD.setColor(VGA_BLACK);
// disable w5100 while setting up SD
// pinMode(10,OUTPUT);
// pinMode(53,OUTPUT);
// pinMode(57,OUTPUT);
// digitalWrite(10,HIGH);
// digitalWrite(53,HIGH);
// digitalWrite(57,HIGH);
//
// pinMode(4,OUTPUT);
// digitalWrite(4,HIGH);
SdFat sd;
delay(100);
bool mysd = 0;
while (!mysd)
{
if (!sd.begin(SD_CHIP_SELECT, SPI_FULL_SPEED))
{
Serial.println(F("Card failed, or not present"));
Serial.println(F("Retrying...."));
}
else
{
mysd = 1;
Serial.println(F("Card initialised."));
File file;
if (!file.open("config.cfg", FILE_READ)) {
Serial.println("Failed to read configuration file 'config.cfg'");
}
else {
// Get building letter & number (ex: config.cfg contains 'building=C4' so the var is C4)
Serial.println("CFG Opened successfully!");
String building = readline(file);
Serial.println(building);
Serial.println(building.substring(building.indexOf('=') + 1));
BUILDING = building.substring(building.indexOf('=') + 1);
}
}
}
myGLCD.fillScr(255, 255, 255);
drawLogo();
// Logo shows here but not in loop
// disable SD SPI so that we can start Ethernet
//pinMode(4,OUTPUT);
//digitalWrite(4,HIGH);
Serial.print(F("Starting ethernet..."));
// If you don't want to use DHCP, then change true to false
if (true) {
if(!Ethernet.begin(mac)) {
Serial.println(F("failed"));
// If you don't want the script to stop if ethernet connection fails, comment out the entire while(true) loop right here\
// (Look at the "///*" and "//*/" and remove the first two "//" from both)
///*
while(true)
{
delay(1);
}
//*/
}
else {
Serial.println(Ethernet.localIP());
Serial.print("DHCP in use! Gateway: ");
Serial.print(Ethernet.gatewayIP());
}
}
else {
configureNonDHCP();
}
server.begin();
Serial.println("\nserver.begin() called");
// start UDP
// [part of Ethernet Library]
ethernet_UDP.begin(localPort);
Serial.println("Ethernet UDP Start....");
// pass function getTime() to Time Library to update current time
// [part of Time Library]
setSyncProvider(getTime);
}
void loop( )
{
Ethernet.maintain();
checkTime();
drawDeviceInfo();
EthernetClient client = server.available();
if (client)
{
Serial.print("Client Created\n");
boolean currentLineIsBlank = true;
while (client.connected ( ) )
{
if (client.available ( ) )
{
char character = client.read ( );
Serial.write(character);
if (character == '\n' && currentLineIsBlank)
{
readSensors();
drawTemp(t[0], t[1], t[1], avgTempC);
drawHumid(h[0], h[1], h[1], avgHumid);
drawLastCycle(); // Right now last 24h values are zeroed out until PHP API is tested
client.println ("HTTP/1.1 200 OK");
client.println ("Content-Type: text/html");
client.println ("Connection: close");
client.println ("Refresh: 5");
client.println ( );
client.println ("<!DOCTYPE HTML>");
client.println ("<html>");
client.print ("<Title>Arduino Sensor Readings </Title>");
//client.println("<BODY>");
delay(500);
for (int i = 0; i < 3; i++) {
auto tmpTitle = "<h1>Sensor " + String(i) + " Reading</h1>";
client.print (tmpTitle);
client.print ("<p>Temperature " + String(i) + " in C: ");
client.print(t[i]); client.print(" C</p>");
client.print ("<p>Temperature " + String(i) + " in F: ");
client.print(tf[i]); client.print(" F</p>");
client.print ("<p>Humidity " + String(i) + ": ");
client.print (h[i]); client.print("%</p>"); ////
client.println ("<br />");
}
auto avgTitle = "<h1>Average of All Readings</h1>";
client.print (avgTitle);
client.print ("<p>Avg Temperature in C: ");
client.print (avgTempC); client.print(" C</p>");
client.print ("<p>Avg Temperature in F: ");
client.print (avgTempF); client.print(" F</p>");
client.print ("<p>Avg Humidity: ");
client.print (avgHumid); client.print("%</p>");
// DIY buttons
client.println("<a href=\"/?on\"\">ON</a>");
client.println("<a href=\"/?off\"\">OFF</a>");
// mousedown buttons
client.println("<input type=\"button\" value=\"ON\" onmousedown=\"location.href ('/?on');\"/>");
client.println("<input type=\"button\" value=\"OFF\" onmousedown=\"location.href ('/?off');\"/>");
// mousedown radio buttons
client.println("<input type=\"radio\" value=\"ON\" onmousedown=\"location.href ('/?on');\"\">ON</>");
client.println("<input type=\"radio\" value=\"OFF\" onmousedown=\"location.href ('/?off');\"\">OFF</>");
client.println ("<br />");
//client.println("</BODY>");
client.println ("</html>");
break;
}
if ( character == '\n')
{
currentLineIsBlank = true;
}
else if (character != '\r')
{
currentLineIsBlank = false;
}
}
}
delay(2);
client.stop();
}
else {
delay(2000);
readSensors();
drawTemp(t[0], t[1], t[1], avgTempC);
drawHumid(h[0], h[1], h[1], avgHumid);
drawLastCycle(); // Right now last 24h values are zeroed out until PHP API is tested
}
}
void computeAverage() {
// Reset avgs to zero so we get consistent results
avgTempC = 0.00;
avgTempF = 0.00;
avgHumid = 0.00;
for (int i = 0; i < 3; i++) {
avgTempC = avgTempC + t[i];
avgTempF = avgTempF + tf[i];
avgHumid = avgHumid + h[i];
}
avgTempC = avgTempC / 3;
avgTempF = avgTempF / 3;
avgHumid = avgHumid / 3;
}
void computeExtremes() {
for (int i = 0; i < 3; i++) {
if (t[i] <= minTempC) {
minTempC = t[i];
}
if (h[i] <= minHumid) {
minHumid = h[i];
}
if (t[i] >= maxTempC) {
maxTempC = t[i];
}
if (h[i] >= maxHumid) {
maxHumid = h[i];
}
}
}
void readSensors() {
delay(2000);
int chk0 = DHT0.read22(DHT22_PIN0);
int chk1 = DHT1.read22(DHT22_PIN1);
int chk2 = DHT2.read22(DHT22_PIN2);
t[0] = DHT0.getTemperature();
t[1] = DHT1.getTemperature();
t[2] = DHT2.getTemperature();
h[0] = DHT0.getHumidity();
h[1] = DHT1.getHumidity();
h[2] = DHT2.getHumidity();
tf[0] = (t[0] * 9.00 / 5.00) + 32.00;
tf[1] = (t[1] * 9.00 / 5.00) + 32.00;
tf[2] = (t[2] * 9.00 / 5.00) + 32.00;
computeAverage();
computeExtremes();
}
void drawTemp(float t1, float t2, float t3, float ta) {
myGLCD.setFont(BigFont);
// R1SX, R1SY = Rectangle 1 Start X, Rectangle 1 Start Y
// R1EX, R1EY = Rectangle 1 End X, Rectangle 1 End Y
int T1X = 15;
int T1Y = 68; //68
int R1SX = 10;
int R1SY = 60; // 60
int R1EX = 270; //275
int R1EY = 130; // 150
myGLCD.setColor(VGA_BLUE);
myGLCD.drawRoundRect(R1SX, R1SY, R1EX, R1EY);
myGLCD.setColor(VGA_BLACK);
myGLCD.print("T1- ", T1X, T1Y);
myGLCD.print(String(t1, 2) + "C", T1X + 50, T1Y);
myGLCD.print("T2- ", T1X, T1Y+20);
myGLCD.print(String(t2, 2) + "C", T1X + 50, T1Y+20);
myGLCD.print("T3- ", T1X, T1Y+40);
myGLCD.print(String(t3, 2) + "C", T1X + 50, T1Y+40);
myGLCD.setColor(VGA_BLUE);
myGLCD.drawRoundRect(R1SX + 160, R1SY, R1EX, R1EY);
myGLCD.setColor(VGA_BLACK);
myGLCD.print("AVGT", T1X + 172, T1Y + 10);
myGLCD.print(String(ta, 2)+ "C", T1X + 158, T1Y + 30);
}
void drawHumid(float h1, float h2, float h3, float ha) {
myGLCD.setFont(BigFont);
// R2SX, R2SY = Rectangle 2 Start X, Rectangle 2 Start Y
// R2EX, R2EY = Rectangle 2 End X, Rectangle 2 End Y
int R1X = 15; // 35
int R1Y = 198; //178
int R2SX = 10; //30
int R2SY = 190; //170
int R2EX = 270; //290
int R2EY = 260; //250
myGLCD.setColor(VGA_BLUE);
myGLCD.drawRoundRect(R2SX, R2SY, R2EX, R2EY);
myGLCD.setColor(VGA_BLACK);
myGLCD.print("H1- ", R1X, R1Y);
myGLCD.print(String(h1, 2) + "%", R1X + 50, R1Y);
myGLCD.print("H2- ", R1X, R1Y+20);
myGLCD.print(String(h2, 2) + "%", R1X + 50, R1Y+20);
myGLCD.print("H3- ", R1X, R1Y+40);
myGLCD.print(String(h3, 2) + "%", R1X + 50, R1Y+40);
myGLCD.setColor(VGA_BLUE);
myGLCD.drawRoundRect(R2SX + 160, R2SY, R2EX, R2EY);
myGLCD.setColor(VGA_BLACK);
myGLCD.print("AVGH", R1X + 172, R1Y + 10);
myGLCD.print(String(ha, 2) + "%", R1X + 158, R1Y + 30);
}
void drawLastCycle() {
myGLCD.setFont(BigFont);
// R3SX, R3SY = Rectangle 3 Start X, Rectangle 3 Start Y
// R3EX, R3EY = Rectangle 3 End X, Rectangle 3 End Y
int LASTX = 288;
int LASTY = 68;
int R3SX = 283;
int R3SY = 60;
int R3EX = 470;
int R3EY = 260;
myGLCD.setColor(VGA_BLUE);
myGLCD.drawRoundRect(R3SX,R3SY,R3EX,R3EY);
myGLCD.setColor(VGA_BLACK);
myGLCD.print(" 24h CYCLE", LASTX, LASTY);
String dateStr = String(day()) + "-" + monthShortStr(month()) + "-" + (year() - 2000);
myGLCD.print(dateStr, LASTX+15, LASTY+25);
// myGLCD.print(" 24-APR-21", LASTX, LASTY+25); // Hardcode date for placeholder
myGLCD.print("AVG TEMP[C]", LASTX-2, LASTY+55);
myGLCD.print(" Min | Max", LASTX, LASTY+75);
//myGLCD.print("00.0C|00.0C", LASTX, LASTY+95);
myGLCD.print(String(minTempC) + "|" + String(maxTempC), LASTX, LASTY+95);
myGLCD.print(" AVG RH [%]", LASTX, LASTY+125);
myGLCD.print(" Min | Max", LASTX, LASTY+145);
//myGLCD.print("00.0%|00.0%", LASTX, LASTY+165);
myGLCD.print(String(minHumid) + "|" + String(maxHumid), LASTX, LASTY+165);
}
void drawDeviceInfo() {
myGLCD.setFont(BigFont);
String dateStr = String(day()) + "-" + monthShortStr(month()) + "-" + (year() - 2000);
myGLCD.print(dateStr, 308, 10);
String timeStr = String(hour()) + ":" + String(minute());
myGLCD.print(timeStr, 338, 30);
myGLCD.print(BUILDING, 15, 285);
myGLCD.setFont(SmallFont);
String macStr = "MAC " + mac2String(mac);
myGLCD.print(macStr, 130, 290);
String ipStr = "IP " + DisplayAddress(Ethernet.localIP());
myGLCD.print(ipStr, 330, 290);
}
void digitalClockDisplay() {
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
/*
=========================================
helper function for digitalClockDisplay()
=========================================
*/
void printDigits(int digits) {
// add colon character and a leading zero if number < 10
Serial.print(":");
if (digits < 10)
Serial.print('0');
Serial.print(digits);
}
void checkTime() {
if (timeStatus() != timeNotSet) { // check if the time is successfully updated
if (now() != prevDisplay) { // update the display only if time has changed
prevDisplay = now();
//digitalClockDisplay(); // display the current date and time
}
}
}
void drawLogo() {
myFiles.load(0, 0, 254, 45, "logoSMALL.raw", 1, 0);
}
time_t getTime()
{
while (ethernet_UDP.parsePacket() > 0) ; // discard packets remaining to be parsed
Serial.println("Transmit NTP Request message");
// send packet to request time from NTP server
sendRequest(timeSrvr);
// wait for response
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = ethernet_UDP.parsePacket();
if (size >= 48) {
Serial.println("Receiving NTP Response");
// read data and save to messageBuffer
ethernet_UDP.read(messageBuffer, 48);
// NTP time received will be the seconds elapsed since 1 January 1900
unsigned long secsSince1900;
// convert to an unsigned long integer the reference timestamp found at byte 40 to 43
secsSince1900 = (unsigned long)messageBuffer[40] << 24;
secsSince1900 |= (unsigned long)messageBuffer[41] << 16;
secsSince1900 |= (unsigned long)messageBuffer[42] << 8;
secsSince1900 |= (unsigned long)messageBuffer[43];
// returns UTC time
return secsSince1900 - 2208988800UL - 25200UL; // - 25200UL is to adjust for the timezone difference
}
}
// error if no response
Serial.println("Error: No Response.");
resetFunc();
return 0;
}
/*
helper function for getTime()
this function sends a request packet 48 bytes long
*/
void sendRequest(IPAddress &address)
{
// set all bytes in messageBuffer to 0
memset(messageBuffer, 0, 48);
// create the NTP request message
messageBuffer[0] = 0b11100011; // LI, Version, Mode
messageBuffer[1] = 0; // Stratum, or type of clock
messageBuffer[2] = 6; // Polling Interval
messageBuffer[3] = 0xEC; // Peer Clock Precision
// array index 4 to 11 is left unchanged - 8 bytes of zero for Root Delay & Root Dispersion
messageBuffer[12] = 49;
messageBuffer[13] = 0x4E;
messageBuffer[14] = 49;
messageBuffer[15] = 52;
// send messageBuffer to NTP server via UDP at port 123
ethernet_UDP.beginPacket(address, 123);
ethernet_UDP.write(messageBuffer, 48);
ethernet_UDP.endPacket();
}