#include <PubSubClient.h>
#include <ESP8266WiFi.h>
#include "SSD1306Wire.h"
#include "OLEDDisplayUi.h"
#include "fonts.h"
#include <ArduinoJson.h>
#define PIN 2
// Display Settings
const int I2C_DISPLAY_ADDRESS = 0x3c;
const int SDA_PIN = D2;
const int SDC_PIN = D3;
//button stuff
int oldChan=0;
long startTime=millis();
const int interrupt1Pin=12;
const int interrupt2Pin=14;
char ssid[] = "ssid"; // Change this to your network SSID (name).
char pass[] = "PWD"; // Change this your network password.
#define THING_SPEAK_ADDRESS "api.thingspeak.com"
char mqttUserName[] = "Channel_Display"; // Can be any name.
char mqttPass[] = "XXXXXXXXXXXXXXXXX"; // Change to your MQTT API Key from Account > MyProfile.
char userAPIKey[]="YYYYYYYYYYYYYYYYY"; // Change to your User API Key from Account > MyProfile.
const char* server = "mqtt.thingspeak.com";
const char* fieldChar[9];
int subField=0; //zero for subscribe to all fields
int channelCount=0;
long channelList[100];
String aPIKey[100];
int currentChannel=1; // Swhich channel number is shown, starts at one, not channelID
int updateScreen=0; // Boolean to drive screen refresh
long channelNumber; // Keep track of the ThingSpeak channel number
int fieldNum=1; // Loop number for fields on the display
int numFields=8; // In case I want to show a subset or seperset of the available fields.
long updateInterval=2000; // How often to I switch to a new field
long lastUpdate=0; // Keep track of when I last updated the field
bool chChange=true; // Boolean for tracking need to update MQTT subscriptions
int lastChannel=0; // Placeholder for unsubscribe command.
SSD1306Wire display(I2C_DISPLAY_ADDRESS, SDA_PIN, SDC_PIN);
OLEDDisplayUi ui ( &display );
WiFiClient client; // Initialize the Wifi client library.
PubSubClient mqttClient(client); // Initialize the PuBSubClient library.
//prototypes
int callback(char* topic, byte* payload, unsigned int length);
void mqttConnect();
int mqttSubscribe(long subChannelID,int field,char* readKey,int unsubscribeFlag);
int mqttUnSubscribe(long subChannelID,int field,char* readKey);
int connectWifi();
void newData(OLEDDisplay *display);
void updateData(OLEDDisplay *display,int fieldLoop);
void getChannelList(char* API_Key);
bool getChannel();
void upButton();
void downButton();
//functions
int callback(char* topic, byte* payload, unsigned int length) {
StaticJsonBuffer<512> jsonBuffer;
char jsonObject[512];
char p[length + 1];
Serial.print("Callback");
updateScreen=1;
memcpy(p, payload, length);
p[length] = NULL;
Serial.print("Answer: ");
Serial.println(String(p));
JsonObject& root=jsonBuffer.parseObject(p);
if (!root.success()) {
Serial.println( "parseObject() failed");
return 0;
}
channelNumber=root["channel_id"];
fieldChar[1]=root["field1"];
fieldChar[2]=root["field2"];
fieldChar[3]=root["field3"];
fieldChar[4]=root["field4"];
fieldChar[5]=root["field5"];
fieldChar[6]=root["field6"];
fieldChar[7]=root["field7"];
fieldChar[8]=root["field8"];
}
void setup() {
Serial.begin( 115200 );
Serial.println( "Start" );
int status = WL_IDLE_STATUS; // Set a temporary WiFi status.
display.init();
display.flipScreenVertically();
display.clear();
display.display();
display.setFont( Crushed_Plain_36 );
display.drawString( 1, 5, "Thing" );
display.display();
delay( 1000 );
display.clear();
display.drawString( 38, 5, "Speak" );
display.display();
delay( 1000 );
display.clear();
display.drawString( 1, 15, "TV!" );
display.display();
delay( 1000 );
// Attempt to connect to WiFi network
connectWifi();
Serial.println( "Connected to wifi" );
mqttClient.setServer( server, 1883 ); // Set the MQTT broker details.
mqttClient.setCallback( callback );
getChannelList( userAPIKey );
pinMode( interrupt1Pin, INPUT_PULLUP );
pinMode( interrupt2Pin, INPUT_PULLUP );
attachInterrupt( interrupt1Pin, upButton, FALLING );
attachInterrupt( interrupt2Pin, downButton, FALLING );
}
void loop() {
long channelsList[140];
// Maintain WiFi connection.
if ( WiFi.status() != WL_CONNECTED ) {
connectWifi();
}
// Maintain MQTT connection.
if ( !mqttClient.connected() ){
delay( 1000 );
mqttConnect();// Reconnect if MQTT client is not connected.
}
mqttClient.loop(); // Call the loop continuously to establish connection to the server.
// Update screen upon changes
if ( updateScreen == 1 ){
updateScreen = 0;
newData( &display );
fieldNum = 1;
}
long elapsedTime = millis() - lastUpdate;
if ( elapsedTime > updateInterval ){
lastUpdate=millis();
if (( fieldChar[ fieldNum ]) != '\0' ){
updateData( &display, fieldNum ); //moop
}
else{
lastUpdate = 0;
delay( 25 ); // Prevent WDT from triggering from looping on channels with no data
}
fieldNum++;
if ( fieldNum > numFields ){
fieldNum = 1;
}
}
delay( 20 );
if ( chChange ){
char localKey[ 10 ];
// Unsubscribe from last channel.
aPIKey[ lastChannel ].toCharArray( localKey,17 );
mqttSubscribe( channelList[ lastChannel ], subField, localKey, 1);
if ( currentChannel < 0 ){
currentChannel = channelCount - 2;
}
if ( currentChannel > channelCount-1 ){
currentChannel = 1;
}
lastChannel = currentChannel;
// Subscribe to the new channel.
aPIKey[ currentChannel ].toCharArray( localKey, 17 );
mqttSubscribe( channelList[ currentChannel ], subField, localKey, 0 );
chChange = false;
}
}
/** void mqttConnect()
* Connect to the MQTT broker
* clientID - make sure to terminate appropriately.
*/
void mqttConnect()
{
char clientID[ 9 ];
// Loop until connected.
while ( !mqttClient.connected() )
{
getID( clientID,8 ); // Createa random client ID
// Connect to the MQTT broker.
Serial.print( "Attempting MQTT connection..." );
if ( mqttClient.connect( clientID, mqttUserName, mqttPass ) )
{
Serial.println( "Connected with Client ID: " + String( clientID ) + " User "+ String( mqttUserName ) + " Pwd "+String( mqttPass ) );
} else
{
Serial.print( "failed, rc = " );
// See http://pubsubclient.knolleary.net/api.html#state for the failure code explanation.
Serial.print( mqttClient.state() );
Serial.println( " Will try again in 5 seconds" );
delay( 5000 );
}
}
}
/** int mqttSubscribe(long subChannelID,int field,char* readKey,int unsubscribeFlag)
* Subscribe to the MQTT topic
* String myTopic - string to hold the topic as its built
*/
int mqttSubscribe( long subChannelID,int field,char* readKey,int unsubscribeFlag ){
String myTopic;
// If unsubscribeFlag == 1 then unsubscribe.
if (field==0){
myTopic="channels/"+String(subChannelID)+"/subscribe/json/"+String(readKey);
}
else{
myTopic = "channels/"+String( subChannelID )+"/subscribe/fields/field"+String( field )+"/"+String( readKey );
}
if (unsubscribeFlag == 0 ){
Serial.println( "Subscribing to " +myTopic );
Serial.println( "State= " + String(mqttClient.state()) );
}
char charBuf[ myTopic.length()+1 ];
myTopic.toCharArray( charBuf, myTopic.length()+1 );
// Serial.println(charBuf);
if (unsubscribeFlag == 1){
Serial.println( "UnSubscribing " + myTopic );
return !mqttClient.unsubscribe( charBuf );
}
return mqttClient.subscribe( charBuf,0 );
}
/** int mqttUnSubscribe(long subChannelID,int field,char* readKey)
* Before subscribing to the next channel, you must unsubscribe from the last.
* String myTopic - string to hold the topic as its built
*/
int mqttUnSubscribe(long subChannelID,int field,char* readKey){
String myTopic;
if ( field == 0 ){
myTopic = "channels/" + String( subChannelID ) + "/subscribe/json/" + String( readKey );
}
else{
myTopic = "channels/"+String( subChannelID ) + "/subscribe/fields/field"+String( field ) + "/"+String( readKey );
}
char charBuf[ myTopic.length() + 1 ];
myTopic.toCharArray( charBuf, myTopic.length() + 1 );
return !mqttClient.unsubscribe( charBuf );
}
int connectWifi()
{
while (WiFi.status() != WL_CONNECTED) {
WiFi.begin( ssid, pass );
delay( 2500 );
Serial.println( "Connecting to WiFi" ); // Inform the serial output
Serial.print( "." );
display.clear();
display.setFont( DejaVu_Sans_16 );
display.drawString( 1, 5, "Connecting" );
display.drawString( 1, 20, " to" );
display.drawString( 1, 35, "WiFi" );
display.display();
}
Serial.println( "Connected" );
display.clear();
display.drawString( 34, 5, "Connected!" );
display.display();
}
void newData( OLEDDisplay *display ){
display -> clear();
}
/**
* void updateData(OLEDDisplay *display,int fieldLoop)
* Update the display
*/
void updateData( OLEDDisplay *display,int fieldLoop ) {
display -> clear();
display -> display();
display -> setFont(DejaVu_Sans_16);
String buffer1 = "Channel:" +String( channelNumber );
display -> drawString( 1,1,buffer1 );
delay( 10 );
display -> display();
buffer1 = "Field# " + String( fieldLoop );
display -> drawString( 1,16,buffer1 );
delay( 20 );
display -> display();
display -> drawString( 1, 39, fieldChar[ fieldLoop ] );
delay( 50 );
display -> display();
}
/**
* void getChannelList(char* API_Key)
* Make API call to channels list
* startTime - keep track of how long we have been readung
* TIMOUT - as the name implies
*/
void getChannelList( char* API_Key ){
long startTime = millis();
long TIMEOUT = 4000;
display.clear();
display.setFont( DejaVu_Sans_16 );
display.drawString( 4, 1, "Getting" );
display.drawString( 4, 15, "Channel" );
display.drawString( 4, 30, "List" );
display.display();
channelCount = 0;
if ( client.connect( THING_SPEAK_ADDRESS , 80 )){
// GET data via HTTP
Serial.println( "Connecting to ThingSpeak for channel list..." );
Serial.println();
client.print( "GET /channels.json?api_key=" );
client.println(userAPIKey);
client.println();
while ( client.available() < 1 && (( millis() - startTime ) < TIMEOUT ) ){
delay( 1 );
}
while(getChannel()){
channelCount++;
}
}
else
{
Serial.println ( "Connection Failed" );
}
client.stop();
}
/**
* getChannel()
* Processes one line of channel list and adds to array.
* charCount - keep track of the size of the labels
* channelResponse - data from serial input
*/
bool getChannel(){
int braces = 0;
int charCount = 0;
if( client.available() > 0 ){
// Get response from server
char charIn;
String channelResponse;
channelResponse = client.readStringUntil( ']' );
channelResponse += client.readStringUntil( ']' );
// Parse before returning.
int spot= channelResponse.indexOf( "id" );
Serial.print( String( channelCount )+ " " );
channelList[ channelCount ] = channelResponse.substring( spot+4,spot+10 ).toInt();
Serial.print( String( channelList[channelCount])+ " ");
spot=channelResponse.indexOf( "\"api_key\":\"" );
aPIKey[ channelCount ] = channelResponse.substring( spot+11, spot+27 );
Serial.println( aPIKey[ channelCount ] );
if ( spot == 0 ){
return false; // If there is no length to the data read.
}
return true; // When reading is complete.
}
return false; // If the connection failed.
}
void upButton(){
// noInterrupts();
currentChannel++;
long Milliseconds = millis() + 250; //debounce delay
while ( Milliseconds > millis() ) ;
chChange = true; // Trigger MQTT subscription change.
}
void downButton(){
currentChannel--;
long Milliseconds = millis() + 250; //debounce delay
while ( Milliseconds > millis() ) ;
chChange=true; // Trigger MQTT subscription change
}
/**
* Build a random client ID
* clientID - char array for output
* idLength - length of clientID (actual length will be one longer for NULL)
*/
void getID( char clientID[], int idLength ){
static const char alphanum[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"; // For random generation of client ID.
// Generate ClientID
for (int i = 0; i < idLength ; i++) {
clientID[ i ] = alphanum[ random( 51 ) ];
}
clientID[ idLength ] = '\0';
}
Comments