Lav en webserver med din NodeMCU

På DTU åbent Hus var du måske så heldig på standen eller til rundvisningen for Bachelor i Cyberteknologi at få en NodeMCU som er en slags Arduino-klon. Her giver vi dig lidt instruktioner til at komme i gang med at bruge den.

Benyt gerne vor Facebook side (https://www.facebook.com/cyberdtu/) om der skulle være komplikationer. Herudover vil vi bruge den side til at komme med nogle ideer til brug.

1 Værktøjer og biblioteker der skal installeres

1.1 Installation af Arduino software

Installer Arduino software from www.arduino.cc. Software er tilgængelig for både MAC, Linux og Windows brugere.

 

Figur 1: Download software. Seneste version er 2.0.4 (03.03.23)

1.2 Installation af USB drivere

1.2.1 Windows brugere

Check at Windows automatisk installerer CP2112 USB-UART driver. Ellers kan dette link måske hjælpe

1.2.2 MAC brugere

Det skal indledningsvis nævnes at det erfaringsmæssigt ikke er lykkedes at få alle MAC maskiner til at fungere med Arduino software og det anvendte NodeMCU board. Det er specielt at få USB portene til at fungere, som kan skabe problemer.

Følg linket https://tinyurl.com/CP2112driver og download en passende OSX driver for dit styresystem. Denne unzuippes og installeres, hvorefter der dobbeltklikkes på SiLabs USB Driver Disk for at installere USB driveren.

 

Figur 2: Installation af MAC Silicon Labs Driver

1.3 Installation af Board Manager og biblioteker for NodeMCU

Boardet der er udleveret er af typen NodeMCU0.9 med en såkaldt ESP8266 mikrocontroller. Det er et yderst prisbilligt board, som dog indeholder bl.a. WiFi, 8 digitale samt 1 analog ind/udgang.

For at konfigurere Arduino software til dette board angives følgende link i Fil -> Egenskaber->Additional board manager URL: http://arduino.esp8266.com/stable/package_esp8266com_index.json

 

Figur 3: Link i Additional Board Manager URL

Linket kan med fordel efterprøves i en browser (f.eks. Firefox) som er istand til at dekode JSON formatet. Herved kan ses at linket er en konfigurationsfil, som peger videre til en lang række biblioteker, som skal benyttes senere i forløbet..

Inkluder nu de nødvendige bibilioteker under Værktøjer -> Board -> Board Manager. Skriv ESP8266 og installer det foreslåede ESP modul. Se Figur 5.

Figur 4: Installation af board ESP8266 bibliotek

Nu har programmet tilføjet en lang række nye boards. Vælg nu boardet NodeMCU 0.9 via Værktøjer -> Board.

Hvis alt er gået vel burde du nu være klar til at forbinde din computer til dit NodeMCU board.

1.4 Forbind til NodeMCU

Undersøg først hvilke COM porte, som Arduino softwaren giver adgang til under Værktøjer -> Port. Det er muligt der ikke er porte til rådighed og ”porte” ikke er tilgængelig.

Forbind nu NodeMCU boardet til en USB port og vælg den COM port som nu er blevet tilgængelig.

Vælg upload hastighed på 115200 bits per second.

1.5 Få lysdiode til at blinke

Hvis du hurtigt vil se om der er hul til boardet kan det være hensigtsmæssigt at benytte eksempelprogrammet Blink under Filer -> Eksempler -> Basics -> Blink.

Kompiler programmet og upload til boardet

Kompiler og upload er de to ikoner til øverst til venstre i skærmbilledet

 

2 Webserver

På standen for Cyberteknologi prøvede du måske en lampe som kunne tændes og slukkes via en hjemmeside på netop en NodeMCU.

Her vil lave en næsten tilsvarende hjemmeside hvor et klik dog tænder eller slukker den digitale udgang D2. Du kan selv lave dette om til andre porte, udvide serveren eller finde noget at styre med denne digitale port. Du skal have adgang til et WiFi netværk, som skal indarbejdes i koden. Dette kan du evt. selv lave som mobil hotspot via din smartphone.

2.1 Kodning af webserver

Inden der fortsættes åbnes nyt vindue i Arduino IDE eller al kode slettes i blinkeprogrammet.

WebSeverkoden er medtaget i slutningen af dette dokument. Klip den ind i Arduino IDE

2.1.1 Konfigurer og kør server

I koden skal foretages en række justeringer. F.eks. skal detaljerne for det trådløse accesspunkt indtastes. Det er valgfrit at indtaste mere end eet WiFi, hvilket blot kan gøre enheden mere fleksibel.

wifiMulti.addAP("ssid1", "password");  // add Wi-Fi networks
wifiMulti.addAP("ssid2", "password");

Det er vigtigt at NodeMCU og den PC/mobil som skal tilgå den er på samme netværk.

Nu kan koden kompileres og uploades, som beskrevet tidligere

NodeMCUen får tildelt en IP-adresse dynamisk fra det trådløse net. For at identificere hvilken IP- adresse den har fået tildelt benyttes Serial Monitor. Gå til Værktøjer – Serial Monitor og observer hvad NodeMCU’en udsender af information når den starter op. Det kan evt. være nødvendigt at genstarte ved hjælp af den lille knap til venstre for usb-stikket på NodeMCU. Bemærk at Baud-raten i Serial Monitor til sættes til 115200. Den står default til 9600.

I koden er det alle linjerne med Serial.print() der udskriver til Serial Monitor.

2.1.2 Kodegennemgang

Du har nu oprettet og implementeret en web server på NodeMCUen. Den kører altså helt autonomt og den eneste kommunikation med værts-PCen er beskederne gennem Serial Monitor.

I første del af koden (før setup()) inkluderes en række nødvendige biblioteker, som samlet gør at vi hurtigt kommer videre i processen. Baseret på blandet andet disse biblioteker instantieres en wifi-forbindelse og en webserver, som er sat til at køre på port 80 (TCP). Yderligere deklareres en række funktioner, som siden skal fungere som handles i forskellige situationer.

I setup() forbindes til et af flere mulige WiFi punkter og så snart denne er forbundet klargøres webserveres således at diverse indkommende http beskeder kan behandles af de rette handles.

server.on("/", HTTP_GET, handleRoot);
server.on("/LED", HTTP_POST, handleLED);server.onNotFound(handleNotFound);

Herefter starts web-serveren med server.begin().

I loop() delen af koden sker der intet andet end af serveren venter og lytter om der er nogle indgående beskeder. Når der er indkommende forepørgsler vil serveren undersøge om der findes handles for disse og om nødvendigt eksekvere den rette handle.

Som eksempel kan handleRoot() iagttages, som eksekveres ved indkommende GET request af rod-objektet (intet specifikt objekt angivet).

HandleRoot() specificerer en streng (char *), som indeholder den ønskede HTML kode. Denne streng sendes tilbage til klienten sammen med statuskoden 200, som angiver en succesfuld handling. Ligeledes angives at objektet er af typen text/html.

HandleLED() er lidt speciel da den ikke returnerer et objekt, men derimod et redirect til rod-objektet. Herudover toggler den det digitale output til LEDen.

Denne webserver tænder og slukker blot et output, men det kunne i princippet være et styringssignal til et varmeapparat, aircondition, alarm og meget andet.

2.2 Jeg vil lave mere

Herligt! Der er masser af muligheder og kun fantasien sætter grænser. På markedet findes et hav a sensorer så du kan måle stort set hvad som helst og integrere dette med din WebServer.

Du kan også koble webserveren på en powerbank og så vil du se at den stadig fungerer om end der ikke skrives noget til den serielle forbindelse til computeren.

Kig evt. forbi https://www.facebook.com/cyberdtu/ og skriv om dit IoT projekt eller ide hertil og mon ikke der er nogen der kan hjælpe. Her kan vi også finde på at lægge yderligere projekter op. Hvis du vil arbejde videre med hvordan alle tingene fungerer i detaljer kan det være en uddannelse i Cyberteknologi er noget for dig. Se mere på www.dtu.dk/cyber.

Det kan også være du vil smage mere på uddannelsen via ”Studerende for en dag” eller Studiepraktik. Se mere på https://www.dtu.dk/uddannelse/moed-dtu/moed-uddannelserne

Vi håber du har haft sjov med din NodeMCU!

3 Kode til webserver

Koden findes også som en arduino ide fil på


// Kode som er en simplificeret udgave af hvad der var på Åbent Hus standen
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WiFiMulti.h> // Include the Wi-Fi-Multi library
#include <ESP8266WebServer.h> // Include the WebServer library
#include <ESP8266mDNS.h> // Include the mDNS library
// Includefilerne ovenfor benytter kode skrevet i andre filer. Det er meget brugt i C og svarer lidt til import i f.eks. Python
// Define i C er makroer, der sådan bare er placeholdere for værdier, således at de kun beskrives een gang
// Definition af hvilken udgang som skal benyttes. Vi benytter her BUILTIN for at komme nemt igang.
#define led LED_BUILTIN
// Deklaration og evt. instantiering af globale variable
ESP8266WiFiMulti wifiMulti;
WiFiClient client;
// Server, som lytter til port 80 - benyttes som default af din browser til HTTP
ESP8266WebServer server(80);

// Deklaration af funktioner, som benyttes for hver side/route, som webserven kan vise
void handleRoot();
void handleLED();
void handleNotFound();

// Setup er kode, der eksekveres een gang
void setup() {
  Serial.begin(115200); // Serial er kommunikationen over kabel mellem enhed og din PC
  delay(10);
  pinMode(LED_BUILTIN, OUTPUT); //led alias LED_BUILTIN skal være output

  // Forbind til WiFi - udskift "ssidx" og "password" med givne oplysninger eller mobil hotspot ... eller begge 
  Serial.println();
  wifiMulti.addAP("ssid1", "password");  // Primær WiFi, som søges først
  wifiMulti.addAP("ssid2", "password");  // Sekundær WiFi, som søges hvis primært ikke fundet ... osv
  Serial.println();
  Serial.print("Forbinder til WiFi ...");
  //WiFi.begin(ssid, password);
  // Skriv prikker indtil der er forbundet til WiFi 
  while (wifiMulti.run() != WL_CONNECTED) {
    delay(500);  // Vent halvt sekund
    Serial.print(".");
  }
  // Når der er forbundet, udskriv da den modtagne IP adresse
  Serial.println("");
  Serial.println("WiFi connected to ");
  Serial.println(WiFi.SSID());
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  
  // Definition af hvilke "routes" serveren skal lytte til med reference til funktion/handle
  server.on("/", HTTP_GET, handleRoot);
  server.on("/LED", HTTP_POST, handleLED);
  server.onNotFound(handleNotFound);
 
  // Start serveren
  server.begin();
  Serial.println("Server startet"); 
}

// Funktionen "loop" køres kontinuerligt
void loop() {
server.handleClient(); // Lytter til indkommende forbindelser
}

// Funktion, som eksekveres hver gang roden "/" tilgås
void handleRoot() {         
  char* buffer = "<html><title>Internet of Things - Demonstration</title><meta charset=\"utf-8\"> \
      </head><body><h1>Velkommen til BSc Cyberteknologi</h1> \
      <p>Dette er din egen hjemmeside og du kan modificere den som du ønsker.</p> \
      <p>Du kan benytte knappen til at ændre den indbyggede lysdiode.</p> \
      <form action=\"/LED\" method=\"POST\" ><input type=\"submit\" value=\"Skift tilstand på dioden\" style=\"width:500px; height:100px; font-size:24px\"></form> \
      </body></html>";  
  server.send(200, "text/html", buffer);
}

// Funktion som ekseveres når broswer tilgår "/LED" med et POST request. Dette sker efter tyek på knappen
void handleLED() {                          
  digitalWrite(led,!digitalRead(led));  // Switcher LED
  server.sendHeader("Location","/");          // Redirecter tilbage til rodobjektet "/"
  server.send(303);                           // Benytter en 303 redirect som svar
}

void handleNotFound(){                        // Denne benyttes, hvis ingen andre
  // Svaret 404 bentyder "ikke fundet". Bemærk at svaret her er angivet i ren tekst uden html 
  server.send(404, "text/plain", "404: Siden ikke fundet"); 
}