0

ESP8266 - two buttons - one example
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

ESP8266 - two buttons - one example

by smcculley on Tue Jun 28, 2016 12:54 pm

I have had some requests from people to share my code to talk to Adafruit IO using MQTT and being able to trigger two LED's.

Note that I am using the latest AdafruitIO MQTT code from GitHub, as it has the latest fixes available. --> https://github.com/adafruit/Adafruit_MQTT_Library

In Adafruit IO, I created two feeds, called ledone and ledtwo, and added them to a group called leds.
I then created a dashboard with two toggle switches, and tied them to the feeds (one to each, obviously) using the default ON and OFF button text.

My hardware looks like this:
IMG_2230.jpg
IMG_2230.jpg (224.55 KiB) Viewed 866 times

(ignore the diodes -- I used to have a 2n2222 transistor in there to drive a relay so I could turn on/off high voltages)

Here is the circuit diagram:
AIO-two-leds.png
AIO-two-leds.png (60.05 KiB) Viewed 866 times


And, the code:
Code: Select all | TOGGLE FULL SIZE
// Adafruit IO with two LEDS
// and two buttons
// and no_frills
// June 2016 SDM

// Load esp8266 modules
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <WiFiUdp.h>

// Load Adafruit.io modules
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

// Configure WiFi access point details.
#define WLAN_SSID  "---SSID---"
#define WLAN_PASS  "---PASSWORD---"

// Configure Adafruit IO access.
#define AIO_SERVER      "io.adafruit.com"
#define AIO_SERVERPORT  1883
#define AIO_USERNAME    "---USERNAME---"
#define AIO_KEY         "---AIO-KEY---"

// Create an ESP8266 WiFiClient class to connect to the AIO server.
WiFiClient client;

const char MQTT_SERVER[] PROGMEM    = AIO_SERVER;
const char MQTT_USERNAME[] PROGMEM  = AIO_USERNAME;
const char MQTT_PASSWORD[] PROGMEM  = AIO_KEY;

Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, AIO_SERVERPORT, MQTT_USERNAME, MQTT_PASSWORD);

const char BUTONE_FEED[] PROGMEM = AIO_USERNAME "/feeds/ledone";
Adafruit_MQTT_Publish butone = Adafruit_MQTT_Publish(&mqtt, BUTONE_FEED);

const char BUTTWO_FEED[] PROGMEM = AIO_USERNAME "/feeds/ledtwo";
Adafruit_MQTT_Publish buttwo = Adafruit_MQTT_Publish(&mqtt, BUTTWO_FEED);

const char LEDONE_FEED[] PROGMEM = AIO_USERNAME "/feeds/ledone";
Adafruit_MQTT_Subscribe ledone = Adafruit_MQTT_Subscribe(&mqtt, LEDONE_FEED);

const char LEDTWO_FEED[] PROGMEM = AIO_USERNAME "/feeds/ledtwo";
Adafruit_MQTT_Subscribe ledtwo = Adafruit_MQTT_Subscribe(&mqtt, LEDTWO_FEED);

void MQTT_connect();

// Mmount of time in milliseconds between uploads to adafruit.io
int long interval = 30000;

// Define hardware connections
int button01 = 0;
int button02 = 2;
int led01 = 14;
int led02 = 12;

// Define variables
int buttonState01 = HIGH;
int buttonState02 = HIGH;
int ledState01 = LOW;
int ledState02 = LOW;
int lastButtonState01 = HIGH;
int lastButtonState02 = HIGH;
int lastButtonTime01 = 0;
int lastButtonTime02 = 0;
int debounceDelay = 50;
long cts = 0;
long lts = 0;
long lats = 0;
String utc = "";

void setup() {
  // Setup buttons and leds
  pinMode(button01, INPUT_PULLUP);
  pinMode(button02, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(button01), btn1, FALLING);
  attachInterrupt(digitalPinToInterrupt(button02), btn2, FALLING);
  pinMode(led01, OUTPUT);
  pinMode(led02, OUTPUT);
 
  // Setup serial port.
  Serial.begin(115200);
  delay(10);
  Serial.println(); Serial.println();
  Serial.println(F("AIO Buttons"));
  Serial.println(); Serial.println();
 
  // Connect to WiFi access point.
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);
  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
 
  Serial.println("WiFi connected"); 
  Serial.println("IP address: "); Serial.println(WiFi.localIP());

  // SUBSCRIBE TO AIO FEEDS HERE
  mqtt.subscribe(&ledone);
  mqtt.subscribe(&ledtwo);

  Serial.println(F("Ready!"));
}

void loop() {
  MQTT_connect();
 
  // get time sample
  unsigned long currMillis = millis();

  // check the button status
  btn1();
  btn2();

  // get current time stamp
  cts = millis();

  // if enough time has passed, read the AIO status
  if (cts - 10000 > lats) {
    readAio();
    // set last timestamp to current
    lats = cts;
  }
}

void readAio() {
  //Serial.println("READING FROM MQTT");
  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(1000))) {

    // LED ONE AIO ON/OFF BLOCK
    if (subscription == &ledone) {
      String ledonedata = ((char *)ledone.lastread);
      //Serial.print(F("Got LEDONE: "));
      //Serial.println(ledonedata);
      if (ledonedata == "ON") {
        Serial.println("Turn on LED 1");
        digitalWrite(led01, HIGH);
        ledState01 = HIGH;
      }
      if (ledonedata == "OFF") {
        Serial.println("Turn off LED 1");
        digitalWrite(led01, LOW);
        ledState01 = LOW;
      }
    }

    // LED TWO AIO ON/OFF BLOCK
    if (subscription == &ledtwo) {
      String ledtwodata = ((char *)ledtwo.lastread);
      //Serial.print(F("Got LEDTWO: "));
      //Serial.println(ledtwodata);
      if (ledtwodata == "ON") {
        Serial.println("Turn on LED 2");
        digitalWrite(led02, HIGH);
        ledState02 = HIGH;
      }
      if (ledtwodata == "OFF") {
        Serial.println("Turn off LED 2");
        digitalWrite(led02, LOW);
        ledState02 = LOW;
      }
    }
   
  }
}

// LED ONE BUTTON ON/OFF BLOCK
void btn1() {
  int readButton01 = digitalRead(button01);
  if (readButton01 != lastButtonState01) {
    lastButtonTime01 = millis();
  }
  if ((millis() - lastButtonTime01) > debounceDelay) {
    if (readButton01 != buttonState01) {
      buttonState01 = readButton01;
      if (buttonState01 == LOW) {
        Serial.print("BUTTON 1 PRESS : "); Serial.println(ledState01);
        ledState01 = !ledState01;
        Serial.println("start send"); int startmillis = millis();
        if (ledState01 == LOW) { butone.publish("OFF"); };
        if (ledState01 == HIGH) { butone.publish("ON"); };
        Serial.println("stop send"); int stopmillis = millis();
        Serial.print("  wait time : "); Serial.println(stopmillis - startmillis);
      }
    }
  }
  digitalWrite(led01, ledState01);
  lastButtonState01 = readButton01;
}

// LED TWO BUTTON ON/OFF BLOCK
void btn2() {
  int readButton02 = digitalRead(button02);
  if (readButton02 != lastButtonState02) {
    lastButtonTime02 = millis();
  }
  if ((millis() - lastButtonTime02) > debounceDelay) {
    if (readButton02 != buttonState02) {
      buttonState02 = readButton02;
      if (buttonState02 == LOW) {
        Serial.print("BUTTON 2 PRESS : "); Serial.println(ledState01);
        ledState02 = !ledState02;
        Serial.println("start send"); int startmillis = millis();
        if (ledState02 == LOW) { buttwo.publish("OFF"); };
        if (ledState02 == HIGH) { buttwo.publish("ON");; };
        Serial.println("stop send"); int stopmillis = millis();
        Serial.print("  wait time : "); Serial.println(stopmillis - startmillis);
      }
    }
  }
  digitalWrite(led02, ledState02);
  lastButtonState02 = readButton02;
}

// CONNECT TO MQTT BLOCK
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
       Serial.println(mqtt.connectErrorString(ret));
       Serial.println("Retrying MQTT connection in 5 seconds...");
       mqtt.disconnect();
       delay(5000);  // wait 5 seconds
  }
  Serial.println("MQTT Connected!");
}



I used a more advanced timing technique (counting millis) instead of using delay, so that I was not holding up the CPU when I wanted to do other things. I also used interrupts on the physical buttons, so the human could press it and get a response.

Remember to add you SSID, PASSWORD, AIO Username, and AIO Key to your code before you upload to your 8266.

Enjoy!
Last edited by smcculley on Tue Jun 28, 2016 2:49 pm, edited 1 time in total.

smcculley
 
Posts: 166
Joined: Mon Sep 03, 2012 10:40 pm

Re: ESP8266 - two buttons - example

by sadams260 on Tue Jun 28, 2016 1:29 pm

Nothing short of great in, cant wait to try it !

sadams260
 
Posts: 9
Joined: Thu Jun 23, 2016 9:04 pm

Re: ESP8266 - two buttons - one example

by jwcooper on Wed Jun 29, 2016 10:57 am

Thank you for posting your example code for others to learn from!

jwcooper
 
Posts: 538
Joined: Tue May 01, 2012 9:08 pm

Re: ESP8266 - two buttons - one example

by kennethm4 on Wed Jun 29, 2016 2:02 pm

Cant wait to try this looks good!

kennethm4
 
Posts: 42
Joined: Mon Dec 02, 2013 5:02 pm

Re: ESP8266 - two buttons - one example

by mjpcarbon on Wed Jun 29, 2016 2:27 pm

I have a question that may be premature
Does this sketch allow the iO dashboard to control the LEDs or does the sketch report the physical button presses ?
Or both instances

mjpcarbon
 
Posts: 397
Joined: Fri Nov 29, 2013 8:57 pm

Re: ESP8266 - two buttons - one example

by uniontownlabs on Wed Jun 29, 2016 3:26 pm

mjpcarbon both should be possible with this example

uniontownlabs
 
Posts: 110
Joined: Wed Dec 17, 2014 10:56 pm

Re: ESP8266 - two buttons - one example

by smcculley on Wed Jun 29, 2016 4:13 pm

My sketch takes input from the physical buttons on the 8266 and the buttons in AIO to control the output.

Pressing the physical button gets an immediate response, since I am using the interrupt interface. Response from AIO depends on the delay time in reading the MQTT queue. The example sketch has a 10 second delay. If you find the line in the code, you can adjust it to more frequent queries. But be mindful of the limits put in place by AIO. If you query too frequently, you may get banned. So, pick an interval that is good for you, and good for them.

Code: Select all | TOGGLE FULL SIZE
  // if enough time has passed, read the AIO status
  if (cts - 10000 > lats) {
    readAio();
    // set last timestamp to current
    lats = cts;
  }


Change the "10000" to adjust your interval in millis

smcculley
 
Posts: 166
Joined: Mon Sep 03, 2012 10:40 pm

Re: ESP8266 - two buttons - one example

by mjpcarbon on Wed Jun 29, 2016 4:24 pm

Excellent information thank you for keeping up with this

mjpcarbon
 
Posts: 397
Joined: Fri Nov 29, 2013 8:57 pm

Re: ESP8266 - two buttons - one example

by uniontownlabs on Wed Jun 29, 2016 4:29 pm

@smcculley MQTT is a pub/sub protocol, so you aren't actually querying the IO servers for data in this example. it's pushed to you from the servers after the messages are processed and saved to the database.

there is a rate limit of 120 publishes every 60 seconds, but in this example with MQTT you can check for new arrived messages locally as fast as you would like, because you are only asking if your client received data from the server.

uniontownlabs
 
Posts: 110
Joined: Wed Dec 17, 2014 10:56 pm

Re: ESP8266 - two buttons - one example

by smcculley on Wed Jun 29, 2016 4:54 pm

That is good info to know. I'm going to go back and adjust my sample rate to see what difference it makes.

smcculley
 
Posts: 166
Joined: Mon Sep 03, 2012 10:40 pm

Re: ESP8266 - two buttons - one example

by uniontownlabs on Wed Jun 29, 2016 5:14 pm

cool. let us know how the lag is between the button press and the LED light once you make the adjustment. it should be close to realtime if you are checking for new arrived packets as fast as possible.

thanks for posting the example!

uniontownlabs
 
Posts: 110
Joined: Wed Dec 17, 2014 10:56 pm

Re: ESP8266 - two buttons - one example

by smcculley on Wed Jun 29, 2016 5:20 pm

More frequent queries to read the subscription subroutin gives me less overall accuracy. I don't know if this is due to my updating too much or if there is some other limitation. But, when I reduce my query time to 1000ms, I get less accurate responses from the AIO buttons. For example, when I toggle button 1 then toggle button 2 (within about a second of each other) I'll get the first one but not the second one. The state changes in the GUI, but the change never makes it to me. I'll have to turn on the debugger to see if I am receiving the data or not.

Secondly, I think the interrupts may be causing issues with some internal processes during the more frequent queries. I use to get an occasional stack trace when I'd click a physical button, like one a day, or once every other day. Now, I get one every time, and the ESP8266 resets.

So, there is still some work to be done for stability/accuracy.

smcculley
 
Posts: 166
Joined: Mon Sep 03, 2012 10:40 pm

Re: ESP8266 - two buttons - one example

by lucascastorina on Wed Aug 10, 2016 10:39 am

Hello everybody... thanks for your web... it is great.
I am using your web and libraries to monitoring the (A0) analog pin from the esp8266-12E.
I am also, using an SD card to save the reading data.
As you know we need 4 parameters to start communications:
The wifi LAN NAME, the wifi password, the username, and the AIO KEY.

As seen in mqtt_esp8266.ino example. All these variables are called in the examples like:
#define WLAN_SSID "...your SSID..."
#define WLAN_PASS "...your password..."
#define AIO_USERNAME "...your AIO username (see https://accounts.adafruit.com)..."
#define AIO_KEY "...your AIO key..."

I have this four variables on the SD card in a file.txt, in order to use them in my program.
After reading the SD i have four string variables that contains the SSID, password, AIOusername, and AIO KEY.

My question is: how can I replace these strings with the #define variables??

Thanks in advance and regards from Spain.

If you want to check my code:
https://www.dropbox.com/s/ly5p7yk63mc7l ... s.ino?dl=0

lucascastorina
 
Posts: 3
Joined: Mon Jul 25, 2016 10:03 am

Re: ESP8266 - two buttons - one example

by mjpcarbon on Wed Aug 10, 2016 11:16 am

Those four must be in your code within the " " I am unable to open your code

mjpcarbon
 
Posts: 397
Joined: Fri Nov 29, 2013 8:57 pm

Re: ESP8266 - two buttons - one example

by lucascastorina on Thu Aug 11, 2016 6:38 am

What I mean is HOW CAN I ENTER THE WLAN_SSID PARAMETER (that are read from SD and is saved in a char[] format) AFTER BEING PROGRAMED THE ESP???
The goal of the program is that a normal user be able to save the important parameter in a file.txt in the SD and the program use them to connect to the adafruit broker.
THANK YOU IN ADVANCE

The code is:
/***************************************************
Adafruit MQTT Library ESP8266 Example
Must use ESP8266 Arduino from:
https://github.com/esp8266/Arduino

Works great with Adafruit's Huzzah ESP board & Feather
----> https://www.adafruit.com/product/2471
----> https://www.adafruit.com/products/2821

Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!

Written by Tony DiCola for Adafruit Industries.
MIT license, all text above must be included in any redistribution
****************************************************/
#include <ESP8266WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include <SPI.h>
#include <SD.h>

/************************* WiFi Access Point *********************************/
int ssid_sd;
// WLAN_SSID is the parameter that i want to enter from the SD
char WLAN_SSID[25];
//#define WLAN_SSID "...your SSID..."
#define WLAN_PASS "...your password..."

/************************* Adafruit.io Setup *********************************/

#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT 1883 // use 8883 for SSL
#define AIO_USERNAME "...your AIO username (see https://accounts.adafruit.com)..."
#define AIO_KEY "...your AIO key..."

/************ Global State (you don't need to change this!) ******************/

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;
// or... use WiFiFlientSecure for SSL
//WiFiClientSecure client;

// Store the MQTT server, username, and password in flash memory.
// This is required for using the Adafruit MQTT library.
const char MQTT_SERVER[] PROGMEM = AIO_SERVER;
const char MQTT_USERNAME[] PROGMEM = AIO_USERNAME;
const char MQTT_PASSWORD[] PROGMEM = AIO_KEY;



volatile int m=1;//volatile para que se copie el flanco de interrupcion sin mirar registros
volatile int mant=0;
int tiempo = 1000;
int p[8];//Array de diferencias
int sensorValueANT[8]={0,0,0,0,0,0,0,0};//Array de lecturas anteriores
int sensorValue[8]={0,0,0,0,0,0,0,0};//Array de lecturas
int s=0 ;//para maquina de estados, cambia de estado con pulsador
int t=0 ; //para maquina de estados, cambia de estado con pulsador
File myFile;
byte MCPport= 0;
int hora;
int minuto;
int segundo=0;
int any;
int mes;
int dia;
bool GRABon=1;
char parametros[80];//se guardan los caracteres del para.txt
int largoparam=0;
int n = 0;
int num2 = 0;
int largonom=25;
char parametroschar[25];//maximo largo de nombre de red
// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, MQTT_SERVER, AIO_SERVERPORT, MQTT_USERNAME, MQTT_PASSWORD);
/****************************** Feeds ***************************************/
// Setup a feed called 'photocell' for publishing.
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
const char PHOTOCELL_FEED[] PROGMEM = AIO_USERNAME "/feeds/photocell";
Adafruit_MQTT_Publish photocell = Adafruit_MQTT_Publish(&mqtt, PHOTOCELL_FEED);
// Setup a feed called 'onoff' for subscribing to changes.
const char ONOFF_FEED[] PROGMEM = AIO_USERNAME "/feeds/onoff";
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, ONOFF_FEED);

/*************************** Sketch Code ************************************/

// Bug workaround for Arduino 1.6.6, it seems to need a function declaration
// for some reason (only affects ESP8266, likely an arduino-builder bug).
void MQTT_connect();

void setup() {
static const uint8_t D1 = 5;//Asignacion de pines de ESP8266 DEV BOARD
static const uint8_t D2 = 4;
static const uint8_t D3 = 0;//PUSH BUTTON INTERRUP
static const uint8_t D4 = 2;//PUSH BUTTON INTERRUP
static const uint8_t D5 = 14;
static const uint8_t D6 = 12;
static const uint8_t D7 = 13;
static const uint8_t D8 = 15;//chp select del SPI
static const uint8_t MCP1address = 0x21; //segun se configura A0 A1 A2
Serial.begin(115200);
delay(10);





//here you read from the SD the WLAN SSID from the SD
//////////////////////////////////SAVING SD PARAMETROS de para.txt/////////////////////////
if (!SD.begin(D8)) { //Comienzo con chip select
Serial.println( ); Serial.println("ERROR en inicio de SD ");
}else{
Serial.println("Inicio de SD OK.");
Serial.println("Lectura de parametros de inicio (para.txt)");
}
myFile = SD.open("para.txt");
if (myFile) {
while (myFile.available()) { //Lee hasta que se acaba
WLAN_SSID[largoparam]=myFile.read();//Devuelve integers del codigo ASCII
//en lugar de caracteres
largoparam=largoparam+1;
}
myFile.close();
// parametros.toCharArray(parametroschar,largonom);
Serial.println("Parametros Inicio EN SETUP:");
Serial.println(WLAN_SSID);
Serial.println("Control parametros EN SETUP:");
Serial.println(largoparam);
// char WLAN_SSID[]=parametros[];
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}









Serial.println(F("Adafruit MQTT demo"));
// Connect to WiFi access point.
Serial.println(); Serial.println();
Serial.print("Connecting to ");
Serial.println(WLAN_SSID);
WiFi.begin(WLAN_SSID, WLAN_PASS);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();

Serial.println("WiFi connected");
Serial.println("IP address: "); Serial.println(WiFi.localIP());

// Setup MQTT subscription for onoff feed.
mqtt.subscribe(&onoffbutton);
}

uint32_t x=0;

void loop() {
// Ensure the connection to the MQTT server is alive (this will make the first
// connection and automatically reconnect when disconnected). See the MQTT_connect
// function definition further below.
MQTT_connect();

// this is our 'wait for incoming subscription packets' busy subloop
// try to spend your time here

Adafruit_MQTT_Subscribe *subscription;
while ((subscription = mqtt.readSubscription(5000))) {
if (subscription == &onoffbutton) {
Serial.print(F("Got: "));
Serial.println((char *)onoffbutton.lastread);
}
}

// Now we can publish stuff!
Serial.print(F("\nSending photocell val "));
Serial.print(x);
Serial.print("...");
if (! photocell.publish(x++)) {
Serial.println(F("Failed"));
} else {
Serial.println(F("OK!"));
}

// ping the server to keep the mqtt connection alive
// NOT required if you are publishing once every KEEPALIVE seconds
/*
if(! mqtt.ping()) {
mqtt.disconnect();
}
*/
}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
int8_t ret;

// Stop if already connected.
if (mqtt.connected()) {
return;
}

Serial.print("Connecting to MQTT... ");

uint8_t retries = 3;
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
Serial.println(mqtt.connectErrorString(ret));
Serial.println("Retrying MQTT connection in 5 seconds...");
mqtt.disconnect();
delay(5000); // wait 5 seconds
retries--;
if (retries == 0) {
// basically die and wait for WDT to reset me
while (1);
}
}
Serial.println("MQTT Connected!");
}

lucascastorina
 
Posts: 3
Joined: Mon Jul 25, 2016 10:03 am

Please be positive and constructive with your questions and comments.