Di cosa si tratta?

Parliamo di una piccola scheda di sviluppo molto diffusa alla comparsa della versione ESP12 dell’8266. E’ di produzione cinese ed era inizialmente essere prodotta da AI-THINKER (produttore cinese il cui sito sembra essere down da tempo ormai). 

Ancora oggi è possibile trovare  documentazione e acquistarne una, anche se non si tratta certo dello stato dell’arte in questo senso. Quando è stata commercializzata aveva destato scalpore perché veniva fornita con un firmware pre caricato che, abbinato ad una app sviluppata ad-hoc, ne consentiva il pieno controllo.

Benché in rete siano disponibili diversi video che ne mostrano il funzionamento dalla mia esperienza risulta che non funziona! L’ho provata a fine 2016 con Android 5 e 6 e di recente anche su smartphone con Android 8 ma niente da fare! Pensavo questo dipendesse dalla mia inettitudine ma se cercate in rete troverete diverse persone che si lamentano di questo.

Tralasciando questi aspetti abbiamo una scheda di sviluppo equipaggiata da:

  • un ESP8266 ESP-12
  • un pacco batterie (3 batterie stilo tipo AA) 
  • un regolatore di tensione
  • un led RGB
  • 6 micro led 
  • un ADC a 10bit collegato ad un foto resistore
  • un jumper per impostare comodamente la modalità di aggiornamento del firmware

La mappa dei pin disponibili è la seguente:

Attenzione : sembra esistano diverse versioni di questa scheda pertanto potreste trovare documentazioni che riportano mappature diverse. Quella che vede qui sopra è relativa al modello in mio possesso e si distingue per la presenza di un microled (il primo a sinistra del jumper giallo visibile in foto) di colore rosso usato per indicare che la scheda è alimentata.  Sembra che nell’altra versione disponibile questo microled sia blu e che la mappatura dei pin sia diversa.

Cosa andremo a realizzare?

Visto che l’app fornita non funziona mi divertirò a realizzare una webapp che fornisca più o meno le medesime funzionalità. Con l’occasione realizzerò un piccolo webserver sperimentando una libreria più potente di quella usata tipicamente e introducendo l’uso dello SPI Flash File System.

Lo  SPI Flash File System è sostanzialmente un chip di memoria flash (in genere di dimensione pari  a 1, 2 o 4 MB) che consente di disporre di un filesystem virtuale da usare per caricare dati o altro. 

Nel mio caso userò lo SPIFFS per “stivare” le pagine HTML che userò nel progetto anziché mandarle in output come stringa di volta in volta. Questo modo di gestire gli assets risulta essere molto comodo perché è possibile “disegnare” in locale le pagine web da utilizzare e simularne in funzionamento.

Per caricare files  nello SPIFFS usando Arduino IDE è necessario usare la funzione ESP8266 Sketch data upload disponibile nel menu strumenti dopo aver copiato i files nella cartella data da creare nella stessa cartella che ospita lo sketch.

ATTENZIONE : quando si effettua l’upload dei files nello SPIFFS il Serial Monitor di Arduino IDE deve essere chiuso altrimenti la procedura andrà sistematicamente in errore!

Il codice

Come prima cosa colleghiamo la scheda al PC usando una qualsiasi interfaccia USB/TTL : basta collegare TX, RX e GND in quanto per l’alimentazione si può utilizzare il pacco batterie. Se preferite potete dissaldare il pacco batterie e usare l’alimentazione fornita dall’interfaccia USB/TTL: usando i pin dai quali avete dissaldato il pacco batterie potete fornire sia 5 V che 3,3V (che sarebbe in realtà la tensione corretta di lavoro tollerata dall’ESP8266) in quanto interviene il regolatore di tensione a sistemare il tutto.

Il codice usato è il seguente:

include <ESP8266WiFi.h>
include <ESP8266WebServer.h>
include <FS.h>

// put your service values here
const char* ssid = "Your SSID";
const char* password = "Your Password";
define DEBUG 1 // Debug on serial monitor [0|1]
//
const int LDR = 99;
// RGB Led
const int GREEN = 12;
const int BLUE = 13;
const int RED = 15;
// sm LED
const int LED1 = 2;
const int LED2 = 0;
const int LED3 = 4;
const int LED4 = 5;
const int LED5 = 14;
const int LED6 = 16;
//WiFiServer server(80);
ESP8266WebServer server(80);
void setLed() {
String led_num = server.arg("led");
String enable = server.arg("enable");
int pin = LED1; // Default
if (led_num == "led1") pin = LED1;
if (led_num == "led2") pin = LED2;
if (led_num == "led3") pin = LED3;
if (led_num == "led4") pin = LED4;
if (led_num == "led5") pin = LED5;
if (led_num == "led6") pin = LED6;
// all Leds are active on LOW state!
digitalWrite(pin,(enable == "1")?LOW:HIGH);
if(DEBUG) {
Serial.print("led N. ");
Serial.print(led_num);
Serial.println((enable == "1")?" ON":" OFF");
}
server.send(200, "text/html", "ok");
}
void setRGBLed() {
String red = server.arg("red");
String green = server.arg("green");
String blue = server.arg("blue");
int r = red.toInt();
int g = green.toInt();
int b = blue.toInt();
r = (r * 1023) / 255;
g = (g * 1023) / 255;
b = (b * 1023) / 255;
if(DEBUG) {
Serial.print("RGB values - R: ");
Serial.print(r);
Serial.print(" G: ");
Serial.print(g);
Serial.print(" B: ");
Serial.println(b);
}
analogWrite(RED, r);
analogWrite(GREEN, g);
analogWrite(BLUE, b);
server.send(200, "text/html", "ok");
}
void setup() {
if(DEBUG){
Serial.begin(115200);
}
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(LED3, OUTPUT);
pinMode(LED4, OUTPUT);
pinMode(LED5, OUTPUT);
pinMode(LED6, OUTPUT);
pinMode(GREEN, OUTPUT);
pinMode(BLUE, OUTPUT);
pinMode(RED, OUTPUT);
// set all OFF
// the sm leds
digitalWrite(LED1, HIGH);
digitalWrite(LED2, HIGH);
digitalWrite(LED3, HIGH);
digitalWrite(LED4, HIGH);
digitalWrite(LED5, HIGH);
digitalWrite(LED6, HIGH);
// the RGB LED
analogWrite(GREEN,0);
analogWrite(BLUE,0);
analogWrite(RED,0);
// set a low frequency to help stabilize the PWM
analogWriteFreq(200);
delay(5000);
Serial.println("working…");
// Connect to WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
if(DEBUG){
Serial.print(".");
}
}
if(DEBUG){
Serial.println("");
Serial.println("WiFi connected");
}
// Start the server
if (!SPIFFS.begin()) {
if(DEBUG) {
// Serious problem
Serial.println("SPIFFS Mount failed");
}
} else {
if(DEBUG) {
Serial.println("SPIFFS Mount succesfull");
}
}
server.on("/setLed", setLed);
server.on("/setRGBLed", setRGBLed);
server.serveStatic("/", SPIFFS, "/index.html");
server.begin();
Serial.println ( "HTTP server started" );
if(DEBUG){
Serial.println("Server started");
Serial.println(WiFi.localIP());
}
}
void loop() {
server.handleClient();
}

Il codice è abbastanza semplice da comprendere, l’unica particolarità di rilievo è l’uso della libreria ESP8266WebServer.h. In sostanza la libreria consente di creare degli handler che associano a delle URL precise delle funzioni da richiamare. All’interno delle funzioni è possibile leggere in maniera molto semplice i parametri passati tramite url. (es. server.arg(“led”);)

L’HTML usato è invece il seguente:

<!DOCTYPE HTML>
<html>
<head><meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<link href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.2.0/css/bootstrap.min.css' rel='stylesheet'>
<link href='https://www.eyecon.ro/bootstrap-slider/css/slider.css' rel='stylesheet'>
<link href='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap2/bootstrap-switch.min.css' rel='stylesheet'>
<style>
p { font-size: large; text-align: center; }
p#title { font-size: x-large; font-weight: bold; padding:10px;}
#RGB { height: 20px; background: rgb(128, 128, 128); }
#RC .slider-selection { background: #FF8282; }
#RC .slider-handle { background: red; }
#GC .slider-selection { background: #428041; }
#GC .slider-handle { background: green; }
#BC .slider-selection { background: #8283FF; }
#BC .slider-handle { border-bottom-color: blue; }
#R, #G, #B { width: 300px; }
.container { width: 90%;left: 0;right: 0;margin: auto; margin-bottom: 15px; }
div.container div { text-align: center; }
</style>

</head><body>
<p id='title'><span>ESP8266 Yellow board</p>
<div class='container'>
<p>RGB Led control</p>
<p><b>R</b> <input type='text' class='span2' value='' data-slider-min='0' data-slider-max='255' data-slider-step='1' data-slider-value='128' data-slider-id='RC' id='R' data-slider-tooltip='hide' data-slider-handle='square' ></p>
<p><b>G</b> <input type='text' class='span2' value='' data-slider-min='0' data-slider-max='255' data-slider-step='1' data-slider-value='128' data-slider-id='GC' id='G' data-slider-tooltip='hide' data-slider-handle='round' ></p>
<p><b>B</b> <input type='text' class='span2' value='' data-slider-min='0' data-slider-max='255' data-slider-step='1' data-slider-value='128' data-slider-id='BC' id='B' data-slider-tooltip='hide' data-slider-handle='triangle' ></p>
<div id='RGB'></div>
</div>

<div class='container'>
<p>sm Led control</p>
<div>Led 1 <input class="activate_led" type='checkbox' id='led1' ></div>
<div>Led 2 <input class="activate_led" type='checkbox' id='led2' ></div>
<div>Led 3 <input class="activate_led" type='checkbox' id='led3' ></div>
<div>Led 4 <input class="activate_led" type='checkbox' id='led4' ></div>
<div>Led 5 <input class="activate_led" type='checkbox' id='led5' ></div>
<div>Led 6 <input class="activate_led" type='checkbox' id='led6' ></div>
</div>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.2.0/js/bootstrap.min.js'></script>
<script src='https://www.eyecon.ro/bootstrap-slider/js/bootstrap-slider.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/js/bootstrap-switch.min.js'></script>
<script>

$( document ).ready(function() {
var RGBChange = debounce (function() {
$('#RGB').css('background', 'rgb('+r.getValue()+','+g.getValue()+','+b.getValue()+')');
$.post( "setRGBLed?red="+r.getValue()+"&green="+g.getValue()+"&blue="+b.getValue(), function( data ) {
// nothing to do
});

}, 300);

var r = $('#R').slider().on('slide', RGBChange).data('slider');
var g = $('#G').slider().on('slide', RGBChange).data('slider');
var b = $('#B').slider().on('slide', RGBChange).data('slider');
$('.activate_led').bootstrapSwitch();
$('.activate_led').on("switchChange.bootstrapSwitch",function(e, data) {
var enable = this.checked?1:0;
$.post( "setLed?led="+this.id+"&enable="+enable, function( data ) {
// nothing to do
});
});

// https://davidwalsh.name/javascript-debounce-function
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.

function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};

var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
});
</script></body></html>

Nell’HTML ovviamente sono presenti anche gli stili (viceversa avremmo bisogno di rendere disponibili i files dei css online salvandoli in qualche dove). Per le librerie javascript il problema non si pone perché sono già disponibili diversi CDN che le ospitano.

Anche l’HTML non è nulla di che, probabilmente l’unica particolarità è l’uso di una funzione “debouncer” il cui scopo è quello di effettuare chiamate al server ad intervalli regolari evitando che il client tempesti di richieste il server.

Caricate tutto sulla yellow board, identificate l’ip assegnato alla scheda e tramite il browser del vostro smartphone richiamate l’ip.

Buon divertimento 😉

ESP8266 “Yellow dev board”

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *