Hello everyone, I'm learning so please be gentle. I'm really in the ''cut and paste'' part of my learning journey.
I've managed to put the code together but now i want to subscribe to a MQTT topic but I just can't work it out!!
I'd like to receive a reading from an temp sensor (via mqtt) and display in on the graph / logger as well as the locally logged temperature data.
And code help would be appreciated.
#include <Wire.h>
#include <SPI.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ESP8266WiFi.h>
#include <Ticker.h>
#include <AsyncMqttClient.h>
#include <PubSubClient.h>
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include <Fonts/FreeSerif9pt7b.h> // set font - must be included above at top. https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts
#include <Fonts/FreeSans9pt7b.h>
#include <NTPClient.h>
#include <WiFiUdp.h>
//----------------------------------------------------------------------------------------------------
// For the Adafruit shield, these are the default.
#define TFT_DC D4
#define TFT_CS D8
#define WIFI_SSID "*****"
#define WIFI_PASSWORD "*********"
// Raspberri Pi Mosquitto MQTT Broker
#define MQTT_HOST IPAddress(192, 168, 1, 118)
// For a cloud MQTT broker, type the domain name
//#define MQTT_HOST "example.com"
#define MQTT_PORT 1883
// Temperature MQTT Topics
#define MQTT_PUB_TEMP "esp/ds18b20/temperature"
//https://en.wikipedia.org/wiki/List_of_UTC_time_offsets
// offset for Sydney is + 10 or 11 hours dependant of daylight saving.
// offset is seconds calculated as follows: +10hours = 10x60x60=36000 11x60x60=39600
const long utcOffsetInSeconds = 3600;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "pool.ntp.org", utcOffsetInSeconds);
// GPIO where the DS18B20 is connected to
const int oneWireBus = 5; //(GPIO5 - D1)
OneWire oneWire(oneWireBus);
DallasTemperature sensors(&oneWire);
float temp;
float xscale;
float del;
//float temp_readings;
//float temperature;
// Use hardware SPI (on Uno use #13, #12, #11) and the above for CS/DC
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
float min_temp = 100, max_temp = -100;
#define max_readings 290
#define del 599250 // 599250 for 48 hours -measurement rate in ms - take off 750ms for the time needed to update temp readings.
float history6;
float history12;
float history18;
float history24;
float history30;
float history36;
float history42;
float history48;
float historyA;
float historyB;
float historyC;
float historyD;
float historyE;
float historyF;
float historyG;
float historyH;
float temp_error_offset;
float temp_readings[max_readings+1] = {0};
int reading = 290; // starting point of graph
#define temp_error_offset 0 // calibration factor?
#define autoscale_on true
#define autoscale_off false
#define barchart_on true
#define barchart_off false
// Assign names to common 16-bit color values:
#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define MAGENTA 0xF81F
#define GREY 0x4228
//----------------------------------------------------------------------------------------------------
/* (C) D L BIRD
* This function will draw a graph on a TFT / LCD display, it requires an array that contrains the data to be graphed.
* The variable 'max_readings' determines the maximum number of data elements for each array. Call it with the following parametric data:
* x_pos - the x axis top-left position of the graph
* y_pos - the y-axis top-left position of the graph, e.g. 100, 200 would draw the graph 100 pixels along and 200 pixels down from the top-left of the screen
* width - the width of the graph in pixels
* height - height of the graph in pixels
* Y1_Max - sets the scale of plotted data, for example 5000 would scale all data to a Y-axis of 5000 maximum
* data_array1 is parsed by value, externally they can be called anything else, e.g. within the routine it is called data_array1, but externally could be temperature_readings
* auto_scale - a logical value (TRUE or FALSE) that switches the Y-axis autoscale On or Off
* barchart_on - a logical value (TRUE or FALSE) that switches the drawing mode between barhcart and line graph
* barchart_colour - a sets the title and graph plotting colour
* If called with Y!_Max value of 500 and the data never goes above 500, then autoscale will retain a 0-500 Y scale, if on, it will increase the scale to match the data to be displayed, and reduce it accordingly if required.
* auto_scale_major_tick, set to 1000 and autoscale with increment the scale in 1000 steps.
*/
void DrawGraph(int x_pos, int y_pos, int width, int height, int Y1Max, String title, float DataArray[max_readings], boolean auto_scale, boolean barchart_mode, int graph_colour) {
#define auto_scale_major_tick 5 // Sets the autoscale increment, so axis steps up in units of e.g. 5
#define yticks 15 // 5 y-axis division markers
int maxYscale = 0;
if (auto_scale) {
for (int i=1; i <= max_readings; i++ ) if (maxYscale <= DataArray[i]) maxYscale = DataArray[i];
maxYscale = ((maxYscale + auto_scale_major_tick + 2) / auto_scale_major_tick) * auto_scale_major_tick; // Auto scale the graph and round to the nearest value defined, default was Y1Max
if (maxYscale < Y1Max) Y1Max = maxYscale;
}
//Graph the received data contained in an array
// Draw the graph outline
tft.drawRect(x_pos,y_pos,width+2,height+3,WHITE);
tft.setTextSize(2);
tft.setTextColor(graph_colour);
tft.setCursor(x_pos + (width - title.length()*12)/2,y_pos-20); // 12 pixels per char assumed at size 2 (10+2 pixels)
tft.print(title);
tft.setTextSize(1);
// Draw the data
int x1,y1,x2,y2;
for(int gx = 1; gx <= max_readings; gx++){
x1 = x_pos + gx * width/max_readings;
y1 = y_pos + height;
x2 = x_pos + gx * width/max_readings; // max_readings is the global variable that sets the maximum data that can be plotted
y2 = y_pos + height - constrain(DataArray[gx],0,Y1Max) * height / Y1Max + 1;
if (barchart_mode) {
tft.drawLine(x1,y1,x2,y2,graph_colour);
} else {
tft.drawPixel(x2,y2,graph_colour);
tft.drawPixel(x2,y2-1,graph_colour); // Make the line a double pixel height to emphasise it, -1 makes the graph data go up!
}
}
//Draw the Y-axis scale
for (int spacing = 0; spacing <= yticks; spacing++) {
#define number_of_dashes 80
for (int j=0; j < number_of_dashes; j++){ // Draw dashed graph grid lines
if (spacing < yticks) tft.drawFastHLine((x_pos+1+j*width/number_of_dashes),y_pos+(height*spacing/yticks),width/(2*number_of_dashes),GREY);
}
tft.setFont();
tft.setTextSize(1);
tft.setTextColor(YELLOW);
tft.setCursor((x_pos-20),y_pos+height*spacing/yticks-4);
tft.print(Y1Max - Y1Max / yticks * spacing);
tft.setFont();
}
tft.drawRect(x_pos,y_pos,width+2,height+3,WHITE);
tft.setTextSize(2);
tft.setTextColor(graph_colour);
tft.setCursor(x_pos + (width - title.length()*12)/2,y_pos-20); // 12 pixels per char assumed at size 2 (10+2 pixels)
tft.print(title);
tft.setTextSize(1);
}
AsyncMqttClient mqttClient;
Ticker mqttReconnectTimer;
WiFiEventHandler wifiConnectHandler;
WiFiEventHandler wifiDisconnectHandler;
Ticker wifiReconnectTimer;
unsigned long previousMillis = 0; // Stores last time temperature was published
const long interval = 10000; // Interval at which to publish sensor readings
void connectToWifi() {
Serial.println("Connecting to Wi-Fi...");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
}
void onWifiConnect(const WiFiEventStationModeGotIP& event) {
Serial.println("Connected to Wi-Fi.");
tft.setTextColor(BLACK);
tft.setTextSize(1);
tft.setCursor(265,10);
tft.print("WiFi:Fail");
tft.setTextColor(GREEN);
tft.setTextSize(1);
tft.setCursor(265,10);
tft.print("WiFi:OK");
connectToMqtt();
}
void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
Serial.println("Disconnected from Wi-Fi.");
mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
wifiReconnectTimer.once(2, connectToWifi);
tft.setTextColor(BLACK);
tft.setTextSize(1);
tft.setCursor(265,10);
tft.print("WiFi:OK");
tft.setTextColor(RED);
tft.setTextSize(1);
tft.setCursor(265,10);
tft.print("WiFi:Fail");
}
void connectToMqtt() {
Serial.println("Connecting to MQTT...");
mqttClient.connect();
tft.setTextColor(BLACK);
tft.setTextSize(1);
tft.setCursor(0,233);
tft.print("Remote Database: Disconnected (Try: Restart PI)");
tft.setTextColor(YELLOW);
tft.setTextSize(1);
tft.setCursor(0,233);
tft.print("Remote Database: Connecting.....");
}
void onMqttConnect(bool sessionPresent) {
Serial.println("Connected to MQTT.");
Serial.print("Session present: ");
Serial.println(sessionPresent);
tft.setTextColor(BLACK);
tft.setTextSize(1);
tft.setCursor(0,233);
tft.print("Remote Database: Connecting.....");
tft.setTextColor(GREEN);
tft.setTextSize(1);
tft.setCursor(0,233);
tft.print("Remote Database: Conneted");
tft.setTextColor(BLACK);
tft.setTextSize(1);
tft.setCursor(270,0);
tft.print("DB:FAIL");
tft.setTextColor(GREEN);
tft.setTextSize(1);
tft.setCursor(265,0);
tft.print("DB :OK");
}
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
Serial.println("Disconnected from MQTT.");
tft.setTextColor(BLACK);
tft.setTextSize(1);
tft.setCursor(0,233);
tft.print("Remote Database: Connecting.....");
tft.setTextColor(BLACK);
tft.setTextSize(1);
tft.setCursor(0,233);
tft.print("Remote Database: Connected");
tft.setTextColor(RED);
tft.setTextSize(1);
tft.setCursor(0,233);
tft.print("Remote Database: Disconnected (Try: Restart PI)");
tft.setTextColor(BLACK);
tft.setTextSize(1);
tft.setCursor(265,0);
tft.print("DB :OK");
tft.setTextColor(RED);
tft.setTextSize(1);
tft.setCursor(270,0);
tft.print("DB:FAIL");
if (WiFi.isConnected()) {
mqttReconnectTimer.once(2, connectToMqtt);
}
}
void onMqttPublish(uint16_t packetId) {
Serial.print("Publish acknowledged.");
Serial.print(" packetId: ");
Serial.println(packetId);
tft.setTextColor(GREEN);
tft.setTextSize(1);
tft.setCursor(265,0);
tft.print("DB :OK");
//tft.print(packetId);
}
void setup(){
timeClient.begin();
Serial.begin(9600);
wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);
mqttClient.onConnect(onMqttConnect);
mqttClient.onDisconnect(onMqttDisconnect);
//mqttClient.onSubscribe(onMqttSubscribe);
//mqttClient.onUnsubscribe(onMqttUnsubscribe);
mqttClient.onPublish(onMqttPublish);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
// If your broker requires authentication (username and password), set them below
//mqttClient.setCredentials("REPlACE_WITH_YOUR_USER", "REPLACE_WITH_YOUR_PASSWORD");
connectToWifi();
tft.begin(); // Start the TFT display
tft.setRotation(3);
tft.setTextSize(2);
tft.setTextColor(BLACK);
tft.fillScreen(BLACK); // Clear the screen
sensors.begin();
sensors.requestTemperatures();
delay(1000);
temp = sensors.getTempCByIndex(0)+ temp_error_offset;
analogWriteFreq(500); // Enable TFT display brightness
analogWrite(D0, 750); // Set display brightness using D0 as driver pin, connects to TFT Backlight pin
for (int x = 0; x <= max_readings; x++){
temp_readings[x] = 0;
} // Clears the arrays
// Preset the max and min values of temperature and humidity
tft.setFont();//reset font
xscale = (290 * ((((del+750) / 1000))/60)/60);
timeClient.update();
delay(2000);
}
void loop(){
Serial.begin(9600);
timeClient.update();
if ((timeClient.getHours() >=18) && (timeClient.getHours() <=6)) { analogWrite(D0,050);} // time for screen to go off and onn
else { analogWrite(D0,750);}
xscale = (290 * ((((del+750) / 1000))/60)/60);
tft.fillScreen(BLACK);
tft.setFont(); //reset font
tft.setTextColor(YELLOW);
tft.setTextSize(1);
// tft.setCursor(15,205);
// tft.drawLine(20,202,20,204,BLACK);
// tft.setTextSize(1);
// tft.print(xscale,0); // if using a smaller scale chnage out the 0 for a 1 to give decimal points
//-48 hours
tft.setCursor(6,205);
history24 = (timeClient.getHours()-24);
history48 = (timeClient.getHours()-48);
if (history48 <0) {tft.setTextColor(MAGENTA);}
else {tft.setTextColor(YELLOW);}
if (history48 < -24) {tft.setTextColor(CYAN);}
if (history24 <0){historyD = history24 + 24;}
if (history24 <0){
if (historyD<10) {tft.print("0");}
tft.print(historyD,0);
}
else {
if (history24<10) {tft.print("0");}
tft.print(history24,0);
}
tft.print(":");
if (timeClient.getMinutes() <10)tft.print("0");
tft.print(timeClient.getMinutes(),1);
tft.drawLine(20,20,20,204,GREY);
// past 42 hours
tft.setCursor(42,205);
history18 = (timeClient.getHours()-18);
history42 = (timeClient.getHours()-42);
if (history42 <0) {tft.setTextColor(MAGENTA);}
else {tft.setTextColor(YELLOW);}
if (history42 < -24) {tft.setTextColor(CYAN);}
if (history18 <0){historyC = history18 + 24;}
if (history18 <0){
if (historyC<10) {tft.print("0");}
tft.print(historyC,0);
}
else {
if (history18<10) {tft.print("0");}
tft.print(history18,0);
}
tft.print(":");
if (timeClient.getMinutes() <10)tft.print("0");
tft.print(timeClient.getMinutes(),1);
tft.drawLine(56,20,56,204,GREY);
//- 36 hours (based on 48 hours)
tft.setCursor(78,205);
history12 = (timeClient.getHours()-12);
history42 = (timeClient.getHours()-36);
if (history42 <0) {tft.setTextColor(MAGENTA);}
else {tft.setTextColor(YELLOW);}
if (history42 < -24) {tft.setTextColor(CYAN);}
if (history12 <0){historyB = history12 + 24;}
if (history12 <0){
if (historyB<10) {tft.print("0");}
tft.print(historyB,0);
}
else {
if (history12<10) {tft.print("0");}
tft.print(history12,0);
}
tft.print(":");
if (timeClient.getMinutes() <10)tft.print("0");
tft.print(timeClient.getMinutes(),1);
tft.drawLine(92,20,92,204,GREY);
// -30 hours (based on 48 hours)
tft.setCursor(114,205);
history6 = (timeClient.getHours()-30);
if (history6 < 0) {tft.setTextColor(MAGENTA);}
else {tft.setTextColor(YELLOW);}
if (history6 <-24) {tft.setTextColor(CYAN);}
if (history6 <0){historyA = history6 + 24;}
if (history6 <0){
if (historyA<10) {tft.print("0");}
tft.print(historyA,0);
}
else {
if (history6<10) {tft.print("0");}
tft.print(history6,0);
}
tft.print(":");
if (timeClient.getMinutes() <10)tft.print("0");
tft.print(timeClient.getMinutes(),1);
tft.drawLine(128,20,128,204,GREY);
// -24 hours (based on 48hour trend)
tft.setCursor(150,205);
history24 = (timeClient.getHours()-24);
if (history24 <0) {tft.setTextColor(MAGENTA);}
else {tft.setTextColor(YELLOW);}
if (history24 <-24) {tft.setTextColor(CYAN);}
if (history24 <0){historyD = history24 + 24;}
if (history24 <0){
if (historyD<10) {tft.print("0");}
tft.print(historyD,0);
}
else {
if (history24<10) {tft.print("0");}
tft.print(history24,0);
}
tft.print(":");
if (timeClient.getMinutes() <10)tft.print("0");
tft.print(timeClient.getMinutes(),1);
tft.drawLine(164,20,164,204,GREY);
// -18 hours (based on 48hour trend)
tft.setCursor(186,205);
history18 = (timeClient.getHours()-18);
if (history18 <0) {tft.setTextColor(MAGENTA);}
else {tft.setTextColor(YELLOW);}
if (history18 <0){historyC = history18 + 24;}
if (history18 <0){
if (historyC<10) {tft.print("0");}
tft.print(historyC,0);
}
else {
if (history18<10) {tft.print("0");}
tft.print(history18,0);
}
tft.print(":");
if (timeClient.getMinutes() <10)tft.print("0");
tft.print(timeClient.getMinutes(),1);
tft.drawLine(200,20,200,204,GREY);
tft.setCursor(222,205);
history12 = (timeClient.getHours()-12);
if (history12 <0) {tft.setTextColor(MAGENTA);}
else {tft.setTextColor(YELLOW);}
if (history12 <0){historyB = history12 + 24;}
if (history12 <0){
if (historyB<10) {tft.print("0");}
tft.print(historyB,0);
}
else {
if (history12<10) {tft.print("0");}
tft.print(history12,0);
}
tft.print(":");
if (timeClient.getMinutes() <10)tft.print("0");
tft.print(timeClient.getMinutes(),1);
tft.drawLine(236,20,236,204,GREY);
Serial.print("his12");
Serial.print (history12);
Serial.print("hisB");
Serial.print (historyB);
// -6 hours (based on 48hour trend) xscale = (290 * ((((del+750) / 1000))/60)/60);
tft.setCursor(258,205);
history6 = (timeClient.getHours()-6);
if (history6 <0) {tft.setTextColor(MAGENTA);}
else {tft.setTextColor(YELLOW);}
if (history6 <0){historyA = history6 + 24;}
if (history6 <0){
if (historyA<10) {tft.print("0");}
tft.print(historyA,0);
}
else {
if (history6<10) {tft.print("0");}
tft.print(history6,0);
}
tft.print(":");
if (timeClient.getMinutes() <10)tft.print("0");
tft.print(timeClient.getMinutes(),1);
tft.drawLine(272,20,272,204,GREY);
//NOW
tft.setCursor(300,205);
tft.drawLine(309,202,309,204,BLACK);
tft.print("NOW");
tft.setFont(&FreeSans9pt7b); // set font - must be included above at top. https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts
tft.setTextColor(YELLOW);
tft.setTextSize(1);
tft.setCursor(200,230);
tft.print("NOW ");
tft.print(temp,1);
tft.print(char(247)); //Deg-C symbol
tft.print("C");
temp_readings[reading] = temp;
if (temp > max_temp) max_temp = temp;
if (temp < min_temp) min_temp = temp;
tft.setTextSize(1);
// Now display max and min temperature readings
tft.setTextColor(RED);
tft.setCursor(10,230);
tft.print(max_temp,1);
tft.print(char(247)); // Deg-C symbol
tft.print("C max");
tft.setTextColor(CYAN);
tft.setCursor(100,230);
tft.print(min_temp,1);
tft.print(char(251)); // Deg-C symbol
tft.print("C min");
tft.setFont(); //reset font
tft.setTextColor(YELLOW);
tft.setFont(&FreeSans9pt7b);
tft.setCursor(120,13);
tft.setTextSize(1);
tft.print("Temperature");
// tft.print(xscale,1);
// tft.print(" hrs");
tft.setFont();//reset font
unsigned long currentMillis = millis();
// Every X number of seconds (interval = 10 seconds)
// it publishes a new MQTT message
if (currentMillis - previousMillis >= interval) {
// Save the last time a new reading was published
previousMillis = currentMillis;
// New temperature readings
sensors.requestTemperatures();
// Temperature in Celsius degrees
temp = sensors.getTempCByIndex(0)+ temp_error_offset;
// Temperature in Fahrenheit degrees
//temp = sensors.getTempFByIndex(0);
// Publish an MQTT message on topic esp/ds18b20/temperature
uint16_t packetIdPub1 = mqttClient.publish(MQTT_PUB_TEMP, 1, true, String(temp).c_str());
Serial.printf("Publishing on topic %s at QoS 1, packetId: %i ", MQTT_PUB_TEMP, packetIdPub1);
Serial.printf("Message: %.2f \n", temp);
}
// Display temperature readings on graph
// DrawGraph(int x_pos, int y_pos, int width, int height, int Y1_Max, String title, float data_array1[max_readings], boolean auto_scale, boolean barchart_mode, int colour)
DrawGraph(20,20,290,180,30,"",temp_readings, autoscale_off, barchart_off, RED); //YI_Max is the Y scale max figure.
reading = reading + 1;
if (reading > max_readings) { // if number of readings exceeds max_readings (e.g. 100) then shift all array data to the left to effectively scroll the display left
reading = max_readings;
for (int i = 1; i < max_readings; i++) {
temp_readings[i] = temp_readings[i+1];
}
temp_readings[reading] = temp;
}
if (WiFi.status() == 3) {
tft.setTextColor(BLACK);
tft.setTextSize(1);
tft.setCursor(265,10);
tft.print("WiFi:Fail");
tft.setTextColor(GREEN);
tft.setTextSize(1);
tft.setCursor(265,10);
tft.print("WiFi:OK");
}
if (WiFi.status() == 6) {
tft.setTextColor(BLACK);
tft.setTextSize(1);
tft.setCursor(265,10);
tft.print("WiFi:OK");
tft.setTextColor(RED);
tft.setTextSize(1);
tft.setCursor(265,10);
tft.print("WiFi:Fail");
}
// Prints time in TOP LEFT corner.
tft.setTextColor(YELLOW);
tft.setTextSize(1);
tft.setCursor(1,1);
tft.print("Last Update:");
tft.print(timeClient.getHours());
tft.print(":");
if (timeClient.getMinutes() <10)tft.print("0");
tft.print(timeClient.getMinutes());
delay(del); //Data logging rate. (allowing 750ms for temp updtate)
//tft.fillScreen(BLACK);
}
/* Examples of various display commands
tft.setTextSize(2);
tft.fillScreen(colour);
tft.setTextColor(txt_colour);
tft.setCursor(x, y);
tft.println("text...");
tft.drawCircle(x,y,diameter,colour);
tft.fillCircle(x,y,diameter,colour);
tft.drawRoundRect(x1,y1,x2,y2,curve_diameter,colour);
tft.drawLine(x1, y1, x2, y2,colour);
tft.drawChar(x,y,char,bgcolour,color,size);
*/