ESP8266 Servo & PWM.

Jak sterować serwem i PWM na ESP8266.

Sterowanie serwem i PWM przez stronę www. Sposobów rozwiązania tego tematu może być wiele, ja zrobiłem to tak jak poniżej.

 

Tak wygląda strona www na ESP8266.  Strona jest responsywna  – dopasuje się do wielkości ekranu.
Regulację kąta wychylenia serwa możemy robić precyzyjnie co 1 lub skokowo co 10 stopni w zakresie od 0 do 180 stopni.
Regulację wypełnienia PWM możemy robić co 5 lub co 50 w zakresie 0 do 1023.
Częstotliwość możemy zmieniać co 50Hz lub co 1000Hz w zakresie od 50Hz do 20000Hz. Częstotliwość PWM nie będzie idealnie taka jak ją ustawisz, tylko będzie do niej zbliżona. Częstotliwość ta zależy od dzielników zastosowanych w ESP8266 – czym wyższa tym skok będzie większy.
To testów nie jest potrzebna precyzja.
Aby zmienić poszczególne wartości należy skorzystać z przycisków na ekranie, w których jest wpisany krok zwiększenia lub zmniejszenia kąta wychylenia serwa, szerokości impulsu lub częstotliwości PWM. Po każdej zmianie ekran jest aktualizowany.
Na ekranie jest wypisane do jakiego pinu podłączyć sterowanie serwa i sterowanie PWM.
Przycisk Zapisz zmiany umożliwia zapisanie bieżącego stanu ustawionych wartości.  Po wyłączeniu zasilania lub RESET, wszystkie ustawione dane zostaną przywrócone.

 Uruchom Arduino IDE. Utwórz nowy szkic i zapisz jako ServoPWM. Do pliku ServoPWM.ino wlkej poniższą zawartość.

// ServoPWM.ino
#include "Arduino.h"
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#include <Servo.h>
#include <EEPROM.h>

/* Program pokazuje jak sterować servo i PWM
 * W PWM mamy możliwość zmiany wypełnienia impulsu
 * oraz możliwość zmiany częstotliwości.
 * Zakres zmiany częstotliwości nie jest płynny,
 * ale skokowy i zależy od podanej wielkości.
 * Czym większa częstotliwość, tym kroki zmian są większe. 
 * val1 - kąt wychylenia serva (od 0 do 180),
 * Przycisk Zapisz zmiany zapisuje bieżący stan do EEPROM.
 * Aktualizacja programu poprzez WiFi
 * http://IP/update
 * Licencja na zasadzie open source.
 * Program przeznaczony jest tylko do testów.
 */
//#define IP_STATIC  //IP statyczne wyłączone
#define DEBUG  //wyświetlanie na monitorze szeregowym włączone

//definiowamie pinów
#define pinled0 {D4} // led wbudowany. Gaśnie po uzyskaniu połączenia z WiFi
#define pinservo {D7} // pin dla serva nr 1
#define pinPWM {D8}//pin dla PWM

int val1 = 50; // wartość domyślna dla serwa
int val2 = 250; //wartość domyślna dla PWM

// o ile zwiększany jest krok serwa1
int w1stepfine = 1;
int w1step = 10;
int w1max=180; //maksymalna wartość wychylenia serwa
// o ile zwiększany jest krok PWM
int w2stepfine = 5;
int w2step = 50;
int w2max = 1023; //maksymalna wartość wypełnienia PWM
int freq = 5000; //częstotliwość w Hz dla PWM
int freqmax = 20000; //maksymalna częstotliwość PWM
int freqmin = 50;  //minimalna częstotliwość PWM
int fstep = 1000;  //krok dla częstotliwości
int fstepfine = 50; //dokładny krok dla częstotliwośći
const char* ssid = "SSID"; // SSID twojej sieci WiFi
const char* password = "pass"; // password do WiFi
const int port = 80; // port serwera www

#ifdef IP_STATIC
IPAddress IPadr(10,110,2,103); //stały IP 
IPAddress netmask(255,255,0,0);
IPAddress gateway(10,110,0,1);
#endif
extern ESP8266WebServer server;
extern void setservers();
extern Servo servo1;
//zamienia int na char
//stosowane wartości int nie przekraczają 2 bajtów
uint8_t int2char(uint16_t liczba, uint8_t bytex){
 if (bytex == 'L'){
 return (liczba & 0xFF); //zwraca młodszy bajt
 }else if (bytex == 'H') {
 return (liczba >> 8); //zwraca starszy bajt
 }
 return 0;
}
//zamienia char na int
 uint16_t char2int(uint8_t Lbyte, uint8_t Hbyte){
return ((Hbyte << 8) + Lbyte);
}
//zmienne pomocnicze do współpracy z EEPROM
char Eval1[2];
char Eval2[2];
char Efreq[2];
//czyta wartości z EEPROM
void readEEProm(void){
 int offset = 2;
 Eval1[0] = EEPROM.read(1);
 Eval1[1] = EEPROM.read(2);
 Eval2[0] = EEPROM.read(1+offset);
 Eval2[1] = EEPROM.read(2+offset);
 Efreq[0] = EEPROM.read(1+2*offset);
 Efreq[1] = EEPROM.read(2+2*offset);
 val1 = char2int(Eval1[1],Eval1[0]);
 val2 = char2int(Eval2[1],Eval2[0]);
 freq = char2int(Efreq[1],Efreq[0]);
 return;
}
//zapisuje wartości w EEPROM
void saveEEProm(void){
 int offset = 2;
 Eval1[0] = int2char(val1,'H');
 Eval1[1] = int2char(val1,'L');
 Eval2[0] = int2char(val2,'H');
 Eval2[1] = int2char(val2,'L');
 Efreq[0] = int2char(freq,'H');
 Efreq[1] = int2char(freq,'L');
 EEPROM.write(1,Eval1[0]); //1
 EEPROM.write(2,Eval1[1]); //2
 EEPROM.write(1+offset,Eval2[0]); //3
 EEPROM.write(2+offset,Eval2[1]); //4
 EEPROM.write(1+2*offset,Efreq[0]); //5
 EEPROM.write(2+2*offset,Efreq[1]); //6
 EEPROM.commit();
 return;
}

void setup()
{

#ifdef DEBUG
 Serial.begin(115200);
#endif

 EEPROM.begin(512);
//podczas pierwszego wgrania programu usuń dwa ukośniki przed saveEEProm();
//aby do EEPROM zapisać dane domyślne.
//Jeśli tego nie zrobisz, to pamiętaj aby na stronie www
//wcisnąć przycisk Zapisz zmiany po ustawieniu poszczególnych wartości.
//Przy kolejnym wgrywaniu dodaj ponownie dwa ukośniki, bo nadpiszesz dane w EEPROM
 //saveEEProm();
 readEEProm(); //czytaj dane z EEPROM
 //gdy brak danych w EEPROM wpisz wartości średnie
 if (val1>180) val1=90;
 if (val2>1023) val2=510;
 if (freq > freqmax) freq = 1000;
 if (freq < freqmin) freq = 1000;
 //inicjalizacja serwa i PWM
 servo1.attach(pinservo);
 pinMode(pinPWM, OUTPUT);
 analogWriteFreq(freq); //ustaw częstotliwość dla PWM
 servo1.write(val1);
 analogWrite(pinPWM,val2); // PWM ustaw na wartość domyślną
 //załacz LED
 pinMode(pinled0, OUTPUT);
 digitalWrite(pinled0,LOW);
 // konfiguracja WiFi
 #ifdef IP_STATIC
 WiFi.config(IPadr,gateway,netmask); // stały IP
 #endif
 WiFi.mode(WIFI_STA); //tryb STATION
 WiFi.begin(ssid, password);
 while (WiFi.status() != WL_CONNECTED) { // czekaj na połączenie z WiFi
 delay(500);
 #ifdef DEBUG
 Serial.print(".");
 #endif
 }
 #ifdef DEBUG
 Serial.println("");
 Serial.println("WiFi połączone");
 Serial.println(WiFi.localIP());
 Serial.println(WiFi.macAddress());
 #endif
 digitalWrite(pinled0,HIGH);// wyłącz LED gdy jest połączenie z WiFi

setservers();
}

void loop()
{
 server.handleClient(); // czekaj na wywołanie strony www

 if (WiFi.status() != WL_CONNECTED){
 digitalWrite(pinled0,LOW);// załączenie LED wbudowanej gdy brak połączenia z WiFi
 }
 else{ // wyłącz LED gdy jest połączenie z WiFi
 digitalWrite(pinled0,HIGH);//
 }
}

Do katalogu ServoPWM należy wstawić jeszcze dwa poniższe plik.
Plik nagłówkowy ServoPWMWeb.h.

/*
 * ServoPWMWeb.h
 *
 * Created on: 14.02.2017
 * Author: Jan Trzciński
 */

#ifndef SERVOPWMWEB_H_
#define SERVOPWMWEB_H_

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>

String HTMLHeader();
String HTMLFooter();
String HTMLPage();
String HTMLPage1();
String HTMLPage2();
void setservers(void);

#endif /* SERVOPWMWEB_H_ */ 

Plik ServoPWMWeb.cpp zawierający dane serwera i układ strony www.

 /*
 * ServoPWMWeb.cpp
 *
 * Copyright (c) 2017. All rights reserved.
 *
 * Author: Jan Trzciński <poljant@post.pl>
 */
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#include <Servo.h>

extern int w1step;
extern int w2step;
extern int w1stepfine;
extern int w2stepfine;
extern int val1;
extern int val2;
extern int w1max;
extern int w2max;
extern int freq;
extern int freqmax;
extern int freqmin;
extern int fstep;
extern int fstepfine;
extern void saveEEProm();

#define pinservo {D7} // pin dla serva
#define pinPWM {D8}//pin dla PWM

// login i hasło do sytemu
const char* www_login = "admin";
const char* www_pass = "esp8266";

const int port = 80; // port serwera www
ESP8266WebServer server(port);
ESP8266HTTPUpdateServer httpUpdate;
Servo servo1;

unsigned int ilM=10;
unsigned long fminutes( unsigned int ile) {
 return (millis()+(1000*60*ile));
}

String HTMLHeader() { // nagłówek strony
 String h = "<!DOCTYPE html>\n";
 h += "<html>";
 h += "<head>";
 h += "<title> Servo-PWM</title>";
 h += "<meta charset=\"utf-8\">";
 h += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
 h += "<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css\" >";
 h += "</head>";
 h += "<body style=\"text-align: center;color: white; background: black;font-size: 1.5em;\">\n";
 return h;
 }

String HTMLFooter() { // stopka strony www
 String f = ""; 
 f += "<p><a href = \"/\"><button class=\"btn btn-info\">Odświerz stronę</button></a></p>";
 f += "<p>Jan Trzciński &copy; 2016-2017</p></td></tr>";
 f += "</body>\n";
 f += "</html>\n";
 return f;
 }

String HTMLPage1() { // pierwsza część strony www
 String t;
 unsigned long sec = millis() / 1000;
 unsigned long min = (sec / 60);
 unsigned long hour = (min / 60);
 unsigned long days = hour / 24;
 sec = sec % 60;
 min = min % 60;
 hour = hour % 24;
 t = "<h1>Servo i PWM</h1>";
 t += "<p> Wersja oprogramowania 1.0 z dnia 30-03-2017";
 t += "</p>";
 t += "<p>Czas od uruchomienia dni: ";
 t += (days);
 t += " godz:" ;
 t += ((hour<10) ? "0" : ""); //gdy mniej niż 10 wstaw zero wiodące
 t += (hour);
 t += ":";
 t += ((min<10) ? "0" : ""); //gdy mniej niż 10 wstaw zero wiodące
 t += (min);
 t += ":";
 t += ((sec < 10) ? "0" : ""); //gdy mniej niż 10 wstaw zero wiodące
 t += (sec);
 t += "</p>";
 return t;
 }

String HTMLPage2() { // główna strona www
 String p = "";
 p += "<p> Pozycja serwa ( 0 - 180): </p>";
 p += "<p><a href = \"/servo1/sub\"><button class=\"btn btn-info\"> - " + String(w1step) + " </button> </a> \<\< ";
 p += "<a href = \"/servo1/finesub\"><button class=\"btn btn-info\"> - " + String(w1stepfine) + "</button></a> ";
 p += "<b>["+String(val1)+"]</b>";
 p += "<a href = \"/servo1/fineadd\"><button class=\"btn btn-info\"> + " + String(w1stepfine) + "</button> </a> \>\> ";
 p += "<a href = \"/servo1/add\"><button class=\"btn btn-info\"> + " + String(w1step) + "</button></a></p>\n";
 p += "<p> Wartość PWM ( 0 - 1023):</p> ";
 p += "<p><a href = \"/PWM/sub\"><button class=\"btn btn-info\"> - " + String(w2step) + "</button></a> \<\< ";
 p += "<a href = \"/PWM/finesub\"><button class=\"btn btn-info\"> - " + String(w2stepfine) + "</button></a> ";
 p += "<b>["+String(val2)+"]</b>";
 p += "<a href = \"/PWM/fineadd\"><button class=\"btn btn-info\"> + " + String(w2stepfine) + "</button> </a> \>\> ";
 p += "<a href = \"/PWM/add\"><button class=\"btn btn-info\"> + " + String(w2step) + "</button></a></p>\n";
 p += "<p> Częstotliwość PWM ( " + String(freqmin) + " - " + String(freqmax)+" )Hz:</p> ";
 p += "<p><a href = \"/freq/sub\"><button class=\"btn btn-info\"> - " + String(fstep) + "</button></a> \<\< ";
 p += "<a href = \"/freq/finesub\"><button class=\"btn btn-info\"> - " + String(fstepfine) + "</button></a>";
 p += "<b>["+String(freq)+"]</b>";
 p += "<a href = \"/freq/fineadd\"><button class=\"btn btn-info\"> + " + String(fstepfine) + "</button> </a> \>\> ";
 p += "<a href = \"/freq/add\"><button class=\"btn btn-info\"> + " + String(fstep) + "</button></a></p>\n";
 p += "<p>Połączenia: Serwo na D7, Sygnał PWM ";
 p += (freq);
 p += "Hz na D8</p>\n";
 p += "<p><a href = \"/save\"><button class=\"btn btn-info\">Zapisz zmiany</button></a></p>\n";
 return p;
 }


 String WebPage() { // połącz wszystkie części strony www
 return (HTMLHeader()+HTMLPage1()+ HTMLPage2()+HTMLFooter());
 }

// funkcja ustawia wszystkie strony www
void setservers(){
 httpUpdate.setup(&server,"/update", www_login, www_pass); // umożliwia aktualizację poprzez WiFi

 server.on("/", [](){
 server.send(200, "text/html", WebPage());
 });

 server.on("/servo1/add", []() // zwiększ wychylenie serwa
 {val1 = ((val1+w1step)>w1max) ? w1max : val1+w1step;
 servo1.write(val1);
 server.send(200, "text/html", WebPage());
 });

 server.on("/servo1/sub", []() // zmniejsz wychylenie serwa
 { val1 = ((val1-w1step)<0) ? 0 : val1-w1step;
 servo1.write(val1);
 server.send(200, "text/html", WebPage());
 });
 server.on("/servo1/fineadd", []() // zwiększ wychylenie serwa
 {val1 = ((val1+w1stepfine)>w1max) ? w1max : val1+w1stepfine;
 servo1.write(val1);
 server.send(200, "text/html", WebPage());
 });

 server.on("/servo1/finesub", []() // zmniejsz wychylenie serwa
 { val1 = ((val1-w1stepfine)<0) ? 0 : val1-w1stepfine;
 servo1.write(val1);
 server.send(200, "text/html", WebPage());
 });

 server.on("/PWM/add", []() // zwiększ wartość PWM
 {val2 = ((val2+w2step)>w2max) ? w2max : val2+w2step;
 analogWrite(pinPWM,val2);
 server.send(200, "text/html", WebPage());
 });

 server.on("/PWM/sub", []() // zmniejsz wartość PWM
 {val2 = ((val2-w2step)<0) ? 0 : val2-w2step;
 analogWrite(pinPWM,val2);
 server.send(200, "text/html", WebPage());
 });

 server.on("/PWM/fineadd", []() // zwiększ wartość PWM
 {val2 = ((val2+w2stepfine)>w2max) ? w2max : val2+w2stepfine;
 analogWrite(pinPWM,val2);
 server.send(200, "text/html", WebPage());
 });

 server.on("/PWM/finesub", []() // zmniejsz wartość PWM
 {val2 = ((val2-w2stepfine)<0) ? 0 : val2-w2stepfine;
 analogWrite(pinPWM,val2);
 server.send(200, "text/html", WebPage());
 });

 server.on("/freq/sub", []() // zmniejsz wartość PWM
 {freq = ((freq-fstep)<=0) ? freqmin : freq-fstep;
 analogWriteFreq(freq); //ustaw częstotliwość dla PWM
 server.send(200, "text/html", WebPage());
 });

 server.on("/freq/add", []() // zmniejsz wartość PWM
 {freq = ((freq+fstep)>freqmax) ? freqmax : freq+fstep;
 analogWriteFreq(freq); //ustaw częstotliwość dla PWM
 server.send(200, "text/html", WebPage());
 });

 server.on("/freq/fineadd", []() // zwiększ wartość PWM
 {freq = ((freq+fstepfine)>freqmax) ? freqmax : freq+fstepfine;
 analogWriteFreq(freq); //ustaw częstotliwość dla PWM
 server.send(200, "text/html", WebPage());
 });

 server.on("/freq/finesub", []() // zmniejsz wartość PWM
 {freq = ((freq-fstepfine)<=0) ? freqmin : freq-fstepfine;
 analogWriteFreq(freq); //ustaw częstotliwość dla PWM
 server.send(200, "text/html", WebPage());
 });

 server.on("/save", []() // zmniejsz wartość PWM
 {
 saveEEProm();
 server.send(200, "text/html", WebPage());
 });

 server.begin(); // Start serwera www
}

Szczegóły programu są opisane w komentarzach poszczególnych plików. W pliku ServoPWM.ino wpisz dane swojej sieci WiFi.
Skompiluj i wgraj program do WeMos D1 mini lub innego urządzenia ESP8266.  Do przeglądarki www wpisz IP odczytany z monitora szeregowego i już możesz sterować serwem i PWM.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *