Skip to content

Instantly share code, notes, and snippets.

@tbagro
Last active January 10, 2018 13:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tbagro/f568288a7ccaf6707d2f8e8d58fa5942 to your computer and use it in GitHub Desktop.
Save tbagro/f568288a7ccaf6707d2f8e8d58fa5942 to your computer and use it in GitHub Desktop.
Client Webserver for aquarium fish feeder - Esp8266/Wemos D1 mini, Arduino IDE
//Wifi Ntp esp8266
#include <ESP8266WiFi.h> //https://github.com/ekstrand/ESP8266wifi
#include <Ticker.h> // https://github.com#include "eeprom.h"/esp8266/Arduino/blob/master/libraries/Ticker/Ticker.h
#include "OTA.h" // executa a aba OTA
#include "server.h"// executa aba com webserver
//******************** Configurações Gerais *******************//
IPAddress ip(192, 168, 1, 100); //IP estatico para a plaquinha
IPAddress gateway(192, 168, 1, 1); // IP do roteador
IPAddress subnet(255, 255, 255, 0);
const char* ssid = "SSID"; //nome do roteador
const char* password = "senha"; // senha do roteador
Ticker secondtick;
volatile int watchdogCount = 0;
void restart(){
ESP.restart();//
}
void reset_config(void) {
// Serial.println("*WifiRTC: O ESP ira resetar agora");
delay(1500);
ESP.reset();
}
//******************** Watchdog *******************//
void ISRWatchdog() {// função que reinicia a plaquinha para evitar os travamentos
watchdogCount++;
if (watchdogCount > 70) {
// Serial.println("*WifiRTC: Watchdog bite! Reiniciando");
ESP.reset();// reinicia automaticamente
}
}
void loop() {
watchdogCount = 0; //Zera Watchdog
webpage(); //carrega a pagina do webserver
TimedAction(); // verifica que o o botão "auto" esta ativado para acionar nos horarios definidos
ArduinoOTA.handle(); // recupera função da aba OTA, permite fazer upload do código via wifi
}
void setup() {
WiFi.persistent(false);
Serial.begin(115200); // define a velocidade de processamento
secondtick.attach(1, ISRWatchdog);// aciona o watchdog
pinMode(BUILTIN_LED, OUTPUT); // led da placa
pinMode(D5, OUTPUT); //pino de saida de acionamento // all outputs for LEDs
pinMode(D7, OUTPUT); //pino de saida de acionamento
pinMode(D8, OUTPUT); //pino de saida de acionamento
digitalWrite(BUILTIN_LED, 1); // desliga led da placa ligado ao reiniciar = 1 o led da placa funciona ao contrario dos pinos
digitalWrite(D5, 0); // pino desligado ao reiniciar = 0
digitalWrite(D7, 0); // pino desligado ao reiniciar = 0
digitalWrite(D8, 0); // pino desligado ao reiniciar = 0
//Define conexão direta
WiFi.config(ip, gateway, subnet);// define a conexão da placa com IP fixo
WiFi.begin(ssid, password); // faz a conexão com o roteador
while (WiFi.status() != WL_CONNECTED) {//faz debug no monitor serial da conexão
digitalWrite(BUILTIN_LED, 0);
delay(500);
digitalWrite(BUILTIN_LED, 1);
Serial.print(".");
}
while (WiFi.waitForConnectResult() != WL_CONNECTED) { //faz debug no monitor serial da conexão
Serial.println(F("*WifiRTC: Falha na conexão! Reiniciando..."));
}
Serial.println(F("*WifiRTC: Conectado")); //faz debug no monitor serial da conexão
server.begin();
OTA(); // executa a função OTA na setup
Eeprom();//recupera os dados da eeprom
ntp(); //recupera valores do horaNTP
Serial.print("IP: "); // imprime o IP no monitor serial
Serial.println(WiFi.localIP());// imprime o IP no monitor serial
}
/* salva os dado na memoria armazenando o ultimo status
*/
#include <EEPROM.h> //
//******************** EEPROM *******************//
// Endereços reservados na memória
uint8_t addr1 = 7; // status_auto
uint8_t eeAddress;
// Funções para gerenciamento
void Save_Data() {
EEPROM.put(addr1, status_auto);
eeAddress = 9; //
EEPROM.put(eeAddress , tempoD1); // Grava a variavel se o valor for diferente
eeAddress += sizeof(int); // Incrementa o endereco com valor do tamanho da variavel gravada (int).
EEPROM.put(eeAddress , tempoD2); // Grava a variavel se o valor for diferente
eeAddress += sizeof(int); // Incrementa o endereco com valor do tamanho da variavel gravada (int).
EEPROM.put(eeAddress , tempoD3); // Grava a variavel se o valor for diferente
eeAddress += sizeof(int);
//EEPROM.put(eeAddress , horaLiga);
// EEPROM.put(eeAddress , minutoLiga);
EEPROM.commit();
Serial.println("*WifiRTC: Dados salvos na EEPROM");
}
void Read_Data() {
eeAddress = 9;
EEPROM.get(addr1, status_auto);//
EEPROM.get(eeAddress , tempoD1); // Le a EEPROM e salva na variavel
eeAddress += sizeof(int); // Incrementa o endereco com valor do tamanho da variavel gravada (int).
EEPROM.get(eeAddress , tempoD2); // Le a EEPROM e salva na variavel
eeAddress += sizeof(int); // Incrementa o endereco com valor do tamanho da variavel gravada (int).
EEPROM.get(eeAddress , tempoD3); // Le a EEPROM e salva na variavel
eeAddress += sizeof(int);
// EEPROM.get(eeAddress , horaLiga);
// EEPROM.get(eeAddress , minutoLiga);
Serial.println("*WifiRTC: Dados lidos da EEPROM");
}
////
void Eeprom() { //colocar no setup
EEPROM.begin(512);
Read_Data();
}
/* Obtem a hora via internet e sincroniza com a hora da placa
* mesmo que falte internet o relogio continua funcionando
*/
#include <TimeLib.h> //https://github.com/PaulStoffregen/Time
#include <WiFiUdp.h>
static const char ntpServerName[] = "a.ntp.br"; //Servidor (pode ser a.ntp.br / b.ntp.br / c.ntp.br )
const int timeZone = -4; // Fuso horario (-3 Padrão / -2 Horário de Verão)
WiFiUDP Udp;
unsigned int localPort = 8888;
time_t getNtpTime();
void sendNTPpacket(IPAddress &address);
const int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];
time_t getNtpTime()
{
IPAddress ntpServerIP;
while (Udp.parsePacket() > 0) ;
Serial.println(F("Transmitindo NTP Request"));
WiFi.hostByName(ntpServerName, ntpServerIP);
Serial.print(ntpServerName);
Serial.print(": ");
Serial.println(ntpServerIP);
sendNTPpacket(ntpServerIP);
uint32_t beginWait = millis();
while (millis() - beginWait < 3000) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println(F("Resposta recebida do NTP"));
Udp.read(packetBuffer, NTP_PACKET_SIZE);
unsigned long secsSince1900;
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("Sem resposta do NTP");
return 0;
}
void sendNTPpacket(IPAddress &address)
{
memset(packetBuffer, 0, NTP_PACKET_SIZE);
packetBuffer[0] = 0b11100011;
packetBuffer[1] = 0;
packetBuffer[2] = 6;
packetBuffer[3] = 0xEC;
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
Udp.beginPacket(address, 123);
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
}
time_t prevDisplay = 0;
String hora () {//faz a leitura do RTC
char hora[30];
sprintf( hora, "%02d:%02d", hour(), minute()); //
//Serial.println (hora);
return hora;
}
//******************** Verificação das horas *******************//
void ntp() { /// colocar no setup
Serial.println(F("Iniciando UDP"));
Udp.begin(localPort);
Serial.print(F("Porta local: "));
Serial.println(Udp.localPort());
Serial.println(F("Aguardando sincronia do NTP"));
setSyncProvider(getNtpTime);
setSyncInterval(300);
}
/* Faz a configuração da placa para permitir atualização via WIFI
*/
#include <ArduinoOTA.h>
#include <ESP8266mDNS.h>
void OTA(){ // executa a função OTA na setup
ArduinoOTA.setHostname("esp1");
// No authentication by default
// ArduinoOTA.setPassword("admin");
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH)
type = "sketch";
else // U_SPIFFS
type = "filesystem";
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
});
ArduinoOTA.begin();
Serial.println("Ready OTA ESP8266");
}
/* Aba para adição de funções repetitivas padrões
*/
//aciona os pinos definidos na função
void runAndWait(int pin, int Status, int interval) // pino, status, invervalo
{
digitalWrite(pin, Status); // pino(gpio) e status do pino (ligado/desligado)
delay(interval); // intervalo de acionamento
digitalWrite(pin, !Status);// pino(gpio) e status do pino ao contrario do anterior
delay(150); // aguarda X milli segundos antes do proximo acionamento para evitar ligar mais de um pino ao mesmo tempo
// break;
}
/* Aba para execução do webserver e demais funções
*/
#include "horaNTP.h" // busca hora NTP
#include "runanyway.h"
//******************** botões comandos *******************//
uint8_t status_gpio = 0; // Define condição para GPIO
uint8_t status_auto; // Define status do botão auto
//******************** Função Temporizada *******************//
int horaLiga = 7, horaLiga1 = 12, horaLiga2 = 18, minutoLiga = 10;// coloque aqui os horarios de acionamento
int tempoD1, tempoD2, tempoD3; // agreda os tempo de acionamento de cada pino utilizado
String time1() {//converte o valor int(inteiro) para string para exibir na pagina do webserver
char time1[10];
sprintf( time1, "%d", (tempoD1 ));// converte em string
return time1; // retorna a string
}
String time2() {//converte o valor int(inteiro) para string para exibir na pagina do webserver
char time2[10];
sprintf( time2, "%d", (tempoD2)); // converte em string
return time2; // retorna a string
}
String time3() {//converte o valor int(inteiro) para string para exibir na pagina do webserver
char time3[10];
sprintf( time3, "%d", (tempoD3));// converte em string
return time3;// retorna a string
}
String time4() {//converte o valor int(inteiro) para string para exibir na pagina do webserver
char time1[10];
sprintf( time1, "%d", minutoLiga);// converte em string
return time1; // retorna a string
}
String time5() {//converte o valor int(inteiro) para string para exibir na pagina do webserver
char time1[10];
sprintf( time1, "%d", horaLiga);// converte em string
return time1;// retorna a string
}
String horaligar () {//converte o valor int(inteiro) para string para exibir na pagina do webserver
char horaligar[30];
sprintf( horaligar, "%02d:%02d ", horaLiga, minutoLiga); // concatena horaliga com minutoliga e converte em string
return horaligar; // retorna a string
}
String horaligar1 () {//converte o valor int(inteiro) para string para exibir na pagina do webserver
char horaligar1[30];
sprintf( horaligar1, ",%02d:%02d ", horaLiga1, minutoLiga); // concatena horaliga com minutoliga e converte em string
return horaligar1; // retorna a string
}
String horaligar2 () {//converte o valor int(inteiro) para string para exibir na pagina do webserver
char horaligar2[30];
sprintf( horaligar2, ",%02d:%02d ", horaLiga2, minutoLiga); // concatena horaliga com minutoliga e converte em string
return horaligar2; // retorna a string
}
// ------------------------------ - HTML------------------------ -
void TimedAction() { // função que define os horarios de acionamento no modo automatico
if (status_auto == true) {
if (((int(hour()) == (int)horaLiga) || (int(hour()) == (int)horaLiga1) || (int(hour()) == (int)horaLiga2)) && int(minute()) == (int)minutoLiga && (second() <= 2)) {
status_gpio = 1;// status = ligado
digitalWrite(BUILTIN_LED, 1); // all LEDs off to start
runAndWait(D5, status_gpio, tempoD1); //recupera função da aba runanyway.h (pino, status, interval)
runAndWait(D7, status_gpio, tempoD2); //recupera função da aba runanyway.h (pino, status, interval)
runAndWait(D8, status_gpio, tempoD3); //recupera função da aba runanyway.h (pino, status, interval)
status_gpio = 0; // status = desligado
delay(500);
}
}
if (status_auto == true) {
if ((int(hour()) == 23) && int(minute()) == (int)minutoLiga && (second() <= 2)) {
status_gpio = 1; // status = ligado
digitalWrite(BUILTIN_LED, 1); // all LEDs off to start
runAndWait(D5, status_gpio, tempoD1 / 3); //recupera função da aba runanyway.h (pino, status, interval) e divide intervalo por 3 para reduzir a dose de ração
runAndWait(D7, status_gpio, tempoD2 / 3); //recupera função da aba runanyway.h (pino, status, interval) e divide intervalo por 3 para reduzir a dose de ração
runAndWait(D8, status_gpio, tempoD3 / 3); //recupera função da aba runanyway.h (pino, status, interval) e divide intervalo por 3 para reduzir a dose de ração
status_gpio = 0; // status = desligado
delay(700);
}
}
}
#include "eeprom.h" // adiciona a aba eeprom para salvar os dados
// ------------------------------ - HTML------------------------ -
WiFiServer server(80); // define a porta do servidor local
String HTMLHeader() { // monta o cabeçalho em HTML
String h = (F("HTTP/1.1 200 OK\r\n"));
h += (F("Content-Type: text/html\r\n\r\n"));
h += (F("<!DOCTYPE HTML><html><head><META HTTP-EQUIV='Refresh' CONTENT='10;URL=/'><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\"/>"));
h += (F("<link href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css' rel='stylesheet'></link><link rel=\"stylesheet\" href=\"http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css\"><center><body>"));
h += (F("<link rel=\"stylesheet\" href=\"http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css\">"));
h += (F("<title>WebServer ESP8266</title>"));
h += (F("<style>.c{text-align: center;} div,input{padding:5px;font-size:1em;} input{width:80%;} body{text-align: center;font-family:verdana;}</style>"));
h += (F("</head>"));
h += (F("<body><div class=\"panel panel-primary\">"));
h += (F("<div class=\"panel-heading\"><h3>Simple Webserver</h3></div>"));
h += (F("<div class=\"panel-body\">"));
h += (F("<div id=\"txt\" style=\"font-weight:bold;\"></div>"));
return h;
}
String HTMLFooter() { // monta o rodapé em HTML
String f = (F("<BR><table width=\"100%\" bgcolor=\"black\" cellpadding=\"12\" border=\"0\">"));
f += (F("<tr><td><p style = \"color: white; background: black;font-size: 0.8em; font-weight: bold; text-align: center; margin: 0px 10px 0px 10px;\">"));
f += (F("Tiago Batista &copy; 2017</p></td></tr>"));
f += (F("</table></body></center></html>"));
return f;
}
String HTMLPage() {// monta o corpo da pagina em HTML
String p = (F("</p><div class='container'>"));
p += (F("<h4>Wemos D1mini</h4>"));
p += (F("<div class='btn-group'>"));
p += (F("<BR><table><tr>"));// abre tabela
// adiciona o botão auto
p += (status_auto) ? "<td><a href=\"/Auto/off\" class='btn btn-primary'>Auto ON <i class=\"fa fa-toggle-on\" aria-hidden=\"true\"></i></a>" : "<td><a href=\"/Auto/on\" class='btn btn-success'>Auto OFF <i class=\"fa fa-toggle-off\" aria-hidden=\"true\"></i></a>";
//De acordo com o status da GPIO
TimedAction(); // recupera a função que confere a hora de acionamento
// adiciona o botão manual
p += (status_gpio) ? "<a href=\"/rele/off\" class='btn btn-danger'><i class=\"fa fa-power-off\" aria-hidden=\"true\"></i> Desligar</a></td>" : "<a href=\"/rele/on\" class='btn btn-success'><i class=\"fa fa-power-off\" aria-hidden=\"true\"></i> Ligar</a></td>";
p += (F("</tr></table></div><BR>"));// fecha tabela
p += (F("<div class='btn-group'><p>"));
p += (F("<BR><table><tr>"));// abre tabela
//botões de escolha do tempo de acionamento
p += (F("<td><span><a href=\"/tempoD1/u\"><button type='button' class='btn btn-info' style='margin: 5px'>D1 Up</button></a>"));
p += "<strong style='font-size:16px;'>" + time1() + " ms</strong>";//armazena na eeprom;// exibe o delay
p += (F("</span><a href=\"/tempoD1/d\"; URL=/'><button type='button' class='btn btn-info' style='margin: 5px'>D1 down</button></a></td>"));
p += "</tr><tr>";
p += (F("<td><span><a href=\"/tempoD2/u\"><button type='button' class='btn btn-info' style='margin: 5px'>D2 Up</button></a>"));
p += "<strong style='font-size:16px;'>" + time2() + " ms</strong>";// exibe o delay
p += (F("</span><a href=\"/tempoD2/d\"><button type='button' class='btn btn-info' style='margin: 5px'>D2 down</button></a></td>"));
p += "</tr><tr>";
p += (F("<td><span><a href=\"/tempoD3/u\"><button type='button' class='btn btn-info' style='margin: 5px'>D3 Up</button></a>"));
p += "<strong style='font-size:16px;'>" + time3() + " ms</strong>";// exibe o delay
p += (F("</span><a href=\"/tempoD3/d\"><button type='button' class='btn btn-info' style='margin: 5px'>D3 down</button></a></td>"));
p += (F("</tr></table>"));// fecha tabela
// //horaliga
// p += (F("<td><a href = \"/hora/d\" class='btn btn-default'>Down<i class=\"fa fa-angle-down\" aria-hidden=\"true\"></i></a>"));
// p += "<strong style='font-size:16px;'>hora " + time5() + "</strong>"; // exibe o delay
// p += (F("<a href = \"/hora/u\" class='btn btn-default'>Up<i class=\"fa fa-angle-up\" aria-hidden=\"true\"></i></a></td>"));
// //minutoliga
// p += (F("<td><a href = \"/min/d\" class='btn btn-default'>Down<i class=\"fa fa-angle-down\" aria-hidden=\"true\"></i></a>"));
// p += "<strong style='font-size:16px;'>min" + time4() + "</strong>"; // exibe o delay
// p += (F("<a href = \"/min/u\" class='btn btn-default'>Up<i class=\"fa fa-angle-up\" aria-hidden=\"true\"></i></a></td>"));
p += (F("</p></div>"));//container
p += (F("</div> "));
//reset
p += (F("<a href=\"/reset\"><button type='button' class='btn btn-info' style='margin: 5px'>reset</button></a><BR>"));
//************************************
p += (F("<p>last update ")); // DIV para hora
p += String(hora ());
p += (F("<p>Programado para ligar &#224;s <span class=\"label label-success\">"));
p += horaligar();
p += horaligar1();
p += horaligar2();
p += (F(" horas</span></p><BR>"));
return p;
}
void webpage() { // função que monta o servidor
// ------------------------------ - server ------------------------ -
WiFiClient client = server.available();
if (!client) {
return;
}
Serial.println(F("*WifiRTC: Nova conexao requisitada..."));
while (!client.available()) {
delay(1);
}
Serial.println (F("*WifiRTC: Nova conexao OK..."));
String req = client.readStringUntil('\r'); //Le a string enviada pelo cliente
Serial.println(req); //Mostra a string enviada
client.flush(); //Limpa dados/pfer
TimedAction(); //verifica as condições da eeprom
// unsigned long currentMillis = millis();
if (req.indexOf(F("Auto/on")) != -1) {//liga o botão automatico no servidor
status_auto = true;
} else if (req.indexOf(F("Auto/off")) != -1) {//desliga o botão automatico no servidor
status_auto = false;
} else if (req.indexOf(F("rele/on")) != -1) { // liga os pinos no servidor
status_gpio = 1;// Muda botão ligar para ON
digitalWrite(BUILTIN_LED, 1); // all LEDs off to start
runAndWait(D5, status_gpio, tempoD1);
runAndWait(D7, status_gpio, tempoD2);
runAndWait(D8, status_gpio, tempoD3);
status_gpio = 0;
} else if (req.indexOf(F("rele/off")) != -1) { //desliga os pinos no servidor
// stateRelay = false;
status_gpio = 0; //Muda botão ligar para OFF
} else if (req.indexOf(F("tempoD1/u")) != -1) {// aumenta o intervalo na pagina do servidor
tempoD1 = tempoD1 + 20;
if (tempoD1 > 1000) {
tempoD1 = 0;
}
} else if (req.indexOf(F("tempoD1/d")) != -1) {// diminui o intervalo na pagina do servidor
tempoD1 = tempoD1 - 20;
if (tempoD1 < 0) {
tempoD1 = 1000;
}
} else if (req.indexOf(F("tempoD2/u")) != -1) {// aumenta o intervalo na pagina do servidor
tempoD2 = tempoD2 + 20;
if (tempoD2 > 1000) {
tempoD2 = 0;
}
} else if (req.indexOf(F("tempoD2/d")) != -1) {// diminui o intervalo na pagina do servidor
tempoD2 = tempoD2 - 20;
if (tempoD2 < 0) {
tempoD2 = 1000;
}
} else if (req.indexOf(F("tempoD3/u")) != -1) {// aumenta o intervalo na pagina do servidor
tempoD3 = tempoD3 + 20;
if (tempoD3 > 1000) {
tempoD3 = 0;
}
} else if (req.indexOf(F("tempoD3/d")) != -1) {// diminui o intervalo na pagina do servidor
tempoD3 = tempoD3 - 20;
if (tempoD3 < 0) {
tempoD3 = 1000;
}
} else if (req.indexOf(F("/reset")) != -1) {
ESP.restart();//
}
else {
// Serial.println(F("*WifiRTC: Requisicao invalida"));
}
Save_Data();//armazena na eeprom
//Prepara a resposta para o cliente e carrega a pagina
//Monta a pagina HTML
String r = HTMLHeader(); // monta o cabeçalho
r += HTMLPage(); //monta o corpo da pagina
r += HTMLFooter(); // monta rodapé
client.print(r); // envia a pagina para o navegador
digitalWrite(BUILTIN_LED, 0); //
client.flush();
client.stop();
//Serial.println(F("*WifiRTC: Cliente desconectado!"));
}
@tbagro
Copy link
Author

tbagro commented Jan 10, 2018

This Webserver Client code with Bootstrap controls 3 pin motors D5, D7, D8, the time is obtained via NTPserver, and saves the time of the motors in eeprom.
With Watchdog and upload via OTA.

Code in C arduino IDE, to use it you need to put all the files in the same folder.

simplewebserver

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment