Preamble


Classical arduino development board (Uno, genuino, ...) do not host a wifi chipset. A wifi "shield" (i.e. an additional board that plugs on the pins of the arduino board) must be used for that. Nevertheless, there are other micro-controllers that are natively capable of communicating over wifi, bluetooth, ... for example esp32 and their "little brother", the esp8266.

A lot of electronics builders propose mini-boards based on these two micro-controllers. The one that is used in this subject is from the firm Wemos and is called D1 mini. This enterprise builds and sells several types of boards, with more or less functionalities, at a very low price. For example, D1 mini costs about 5 euros. Consult https://wiki.wemos.cc/ to see their different products.

Thanks to the arduino development kit for these micro-controllers, it is very easy to setup a wifi connection and to communicate with a web server, or a classical one directly over TCP. The goal of the following exercises is to discover how.

BEWARE ! these boards may be fragile, so do not touch the surface component, at risk to burn the card

 

 

Software setup

  • To write programs for the D1 mini, there are different solutions. We are going to use the arduino development environment, which is used to code in a langage close to the Java & C++
  • Thus, arduino must be firstly installed.
  • The following assumes that your are under a liunx OS, using french language (so you have to translate the menu title of arduino used in this subject).
    • download the last version [ here ]. You obtain a .tgz archive.
    • de-compact the archive in your home directory. You obtain a new directory named arduino-2.XX (where XX depends on the version downloaded)
    • go into this folder and launch the arduino executable
    • This first execution setup the default values for configuration parameters, for example the directory where you pout your codes. (which should be ~/Arduino). Just accept the default values.
  • After that, the esp8266 dev. kit must be installed.
    • In "Fichier" menu, choose "Préférences"
    • Then in "URL de gestionnaie de cartes ...", add https://arduino.esp8266.com/stable/package_esp8266com_index.json. Clic on "Ok" to save
    • In "Outils" menu, go to item "Type de Carte", then select "Gestionnaire de carte"
    • In the up-right text field , type "D1" to filter the results. There should be a single choice.
    • Move the mouse cursor on this single choice then an "Install" button should appear. Clic it to start installation.
    • Go back to "Outils" -> "Type de carte" and check that you can select "Wemos D1 R2 et mini". If it is the case, you are ready to proceed further.

The breadboard setup
 
  • The following exercises are based on a simple electronic setup, using few electronic components and 2 "modules" : an LCD display and an ultrasonic distance sensor.
  • These small boards already integrates various electronic components and are able to communicate with the µC thanks to different protocols.
  • All you need to work with this setup is to find an adequate arduino library for the 2 modules. All is already wired.
  • In order to initialize correctly the setup, here is the connection scheme to the µC : 
    • a led is connected to pin D1,
    • the state of the microswitch can be read on pin D2,
    • the trigger pin of the ultrasonic sensor is connected to pin D5
    • the echo pin of the ultrasonic sensor is connected to pin D6
    • the clock pin of the display is connected to pin D3
    • the data (dio) pin of the display is connected to pin D4
    • other pins are connected to Vcc (+3.3V), and ground
 
Exercice #1: a simple sketch with a led and a switch
 

Working principles:

  • pin D2 is connected to the central pin of the microswitch. Depending on the switch position, the central pin will be connected to the left or right pin. If it is on the right, it leads to connect the central pin, and thus D2 to the ground, so 0V. If it is on the left, D2 will be tied to 3.3V, through a 20Ko resistor (i.e. a pull-up resistor that allows the current to flow to the µC pin). Thus, D2 is either at 0 of 3.3V,, which corresponds respectively to 0 and 1 when digitalRead() is used to read the pin state.
  • pin D1 is used to switch on/off the led. The ESP8266 must be told to put a high or low state at this pin. In the present case, the led must be on if D2 is high, and off if D2 is low.

 

 What must be done :

  • Start arduino and create a new sketch.
  • Copy the following code to obtain the described behaviour (NB : some parts are useless for this exercise but will be for the others).
byte old_state;
byte state;
 
void setup() {
 
  Serial.begin(115200);
  delay(10);
 
  // power off Wifi to save energy
  WiFi.mode(WIFI_OFF);
  WiFi.forceSleepBegin();
  delay(10); 
 
  pinMode(D2,INPUT); // switch
  testSwitch(true); // first call to determine the initial state 
  pinMode(D1,OUTPUT); // LED
  digitalWrite(D1,LOW); // switch off the led at start
  delay(10);  
}
 
void testSwitch(bool init) {
  state = digitalRead(D2);  
  if ((init) || (old_state != state)) {
    if (state == 0) {
      Serial.println("switch off => light off");
      digitalWrite(D1,LOW);
    }
    else {
      Serial.println("switch on => light on");
      digitalWrite(D1,HIGH);    
    }  
    old_state = state;
  }
}
 
void loop() {
  testSwitch(false);
  delay(10); // attendre 10ms
}
 
  • Save this sketch in the Arduino directory, giving a simple name, e.g.  simpletest.
  • Connect the esp8266 to a USB port of the computer
  • In menu "Tools", select "Type of card" : Wemos D1 R2 & mini.
  • Check that :
    • upload speed is at 921600
    • port is /dev/ttyUSB0 (NB : if you have other connected boards, it may be ttyUSB1, ttyUSB2, ...)
  • In menu "Tools", choose "Monitor". A new window appears (called the monitor in the following), that displays what is sent to the computer by the D1, thanks to the instructions  Serial.print(...). Unfortunately, it is the only way to debug a program. CAUTION ! In the bottom-right of this window, you have to set the speed to 115200 bauds. Otherwise, the computer will receive jammed data and display random characters.
  • In menu "Sketch",  choose "Upload". The code is compiled then transfered in the flash memory of the D1. It compilation fails, there is a mistake in the code. If the transfer fails, the speed transfer may be invalid, or not adapted to the board. Try a lower speed (e.g. 460800 bauds, or even less)
 

Remarks :

  • it is better to wait few millseconds between calls to testSwitch(), hence the instruction delay(10). Otherwise, the D1 may crash.
  • It is also possible to use the interrupts principle to call testSwitch(). For example, we can tell the D1 to interrupt what it is currently doing to call a particular function (i.e. a callback) as soon as the state of pin D2 changes. It avoids to manipulate two variables to know if its state canged.

Exercise #2: connect to Wifi AP + send HTTP request
 
What must be done - first part :
  • Try to connect the esp8266 to the Wifi access point in the classroom.
  • For that, you need to know the ssid and the password of the wifi network. It is :
    • ssid : testingiot
    • password : testingiot

 

  • Here is a sketch pattern that you must modify in order to establish the connection :
#include <ESP8266WiFi.h>
 
// wifi infos : modify according to the AP config
const char* ssid     = "wifi_ssid";
const char* password = "wifi_passwd";
 
void setup() {
 
  Serial.begin(115200);
  delay(10);
  // connecting to a WiFi network
  Serial.println("Trying to connecting to "+String(ssid));
  /* Explicitly set the ESP8266 to be a WiFi-client, otherwise, it by default,
     would try to act as both a client and an access-point and could cause
     network-issues with your other WiFi-devices on your WiFi-network. */
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  // wait for the connection to establish 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // log wifi config
  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}
 
void loop() {
  // put your code here (see next part of the exercise
  delay(100);
}
  •  If everything is OK you should obtain a IP address as 192.168.0.X, with a variable X, depending on the number of µC connected to the AP.

 

What must be done - second part :

  • The goal is to send HTTP GET request with the URL http://192.168.0.2/set_state.php?state=value, under the following constraints :
    • the request must be sent only if the switch state changed compared to the previous loop() execution
    • value must be 0 if the switch is off (i.e. read 0 on pin D2) and 1 if it is on.
  • To solve these constraints, you have to merge some parts of the exercise 1 with this one, notably to read the switch state.
  • You also need to send http request. For that, you can start from the following sketch pattern, that gives an example on how to send the request and to get the answer status :
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
// variables used for wifi+http
WiFiClient client;
HTTPClient http;

// put setup here + wifi init + ...
 
void loop() {
   Serial.print("[HTTP] begin...\n");
   http.begin(client, "http://jigsaw.w3.org/HTTP/connection.html"); //HTTP
   Serial.print("[HTTP] GET...\n");
   // start connection and send HTTP header
   int httpCode = http.GET();
 
   // httpCode will be negative on error
   if(httpCode > 0) {
     // HTTP header has been send and Server response header has been handled
     Serial.printf("[HTTP] GET... code: %d\n", httpCode);
 
     // file found at server
     if(httpCode == HTTP_CODE_OK) {
       String payload = http.getString();
       Serial.println(payload);
     }
   } 
   else {
     Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
   }
   http.end();
   sleep(1000);
}
 
 
Exercise #3: get distance via ultrasonic sensor + send data over TCP

 

For that exercise, we need to install a dedicated library that handles the ultrasonic sensor. There are two ways to do taht :

  • using the library manager provided by Arduino,
  • downloading the sources and install it manually.

This is the second option that is taken in this exercise, in order you know where are going the installed libraries.

  • Firstly, locate the directory where your sketch are put. For that, go in "Fichiers" then "Préférences" menu. A dialog shows.
  • Normally, the first field gives you the location of your sketches. For example, under linux, you may have something like /home/mylogin/sketchbook
  • In this directory, there should be a subdirectory named libraries. If it is not the case, create it.
  • Now download the library for the HCSR04 ultrasonic sensor : [ download ]
  • Move the archive in the libraries directory mentionned above, and decompact it.
  • There should be now a new directory named HCSR04Ultrasonic.
  • In order to use this library, here is a sketch pattern, that log the distance every 500ms :
#include <Ultrasonic.h>
#define TRIGGER_PIN  D5
#define ECHO_PIN     D6

Ultrasonic ultrasonic(TRIGGER_PIN, ECHO_PIN);

void setup() {
  Serial.begin(115200); 
  pinMode(D1, OUTPUT);
  digitalWrite(D1, HIGH); // switch on the led
  pinMode(D2, INPUT);
  Serial.println("!!! started !!!");  
}

void loop() {
  long microsec = ultrasonic.timing();
  float dist = ultrasonic.convert(microsec, Ultrasonic::CM);  
  Serial.println(dist);  
  delay(500);
}

 

What must be done - first part :

  • Create a new sketch and copy/paste the code of the exercise #1 in it.
  • Every second, get the distance measured by the ultrasonic sensor and prints it on Serial.

 

What must be done - second part : 

  • The measured distance must be sent to a server, BUT only if the distance goes over or down 80cm during the current loop() execution, compared to the previous.
  • It means that you have to declare variables to store the current distance AND the previous one, as for the switch state.

The server can be reached at IP = 192.168.0.2, port = 12345. It is a multi-threaded server based on requests, as the time server implemented during practicial exercises. When a client successfully connects to the server, its first request must be to obtain its unique id. For that, the client just sends a line containing "0". The the server sends back a line containing its id.

Recall that sending lines of text can be done using println() but reading lines is not possible directly. You have to implement a function for that (see readLineServer() in the template of sketch below).

Remark: in order to establish a TCP connection, you have to connect to the Wifi AP firstly.

  • Here is a sketch pattern, that can be used to make the connection to the server, and to get the id :
#include <ESP8266WiFi.h>
WiFiClient client;
const char* server = "192.168.0.2";
const int port = 12345;
int stateConn = 0; // state of connection to server
String idClient; // my id given by server.

// readLineServer() blocks until a line is read, or 1023 char have been read.
String readLineServer() {
  char buffer[1024];
  memset(buffer,0,1024);    // reset buffer
  int index = 0; 
  char c;  
  while(true) {
    while (!client.available()) {} // wait for smthg to read
    while ((index < 1023) && (client.available())) {
      c = client.read();
      if (c == '\n') return String(buffer); // end-of-line reached => return
      buffer[index++] = c; // store the char
    }
    if (index == 1023) return String(buffer); 
  }  
}

void serverConnection() {
  Serial.print("connecting to "+String(server));
  // Use WiFiClient class to create TCP connections
  if (!client.connect(server, port)) {
    Serial.println("connection failed");
    stateConn = 0;
  }
  else {   
    stateConn = 1;  
    Serial.println("connected to server. Asking my id");
    client.println("0"); // envoi requête GETID
    idClient = readLineServer();
    Serial.println("my id is "+idClient);    
  }
}

// add other function: setup(), setupWifi(), ...

void loop() {
  if (stateConn == 0) {    
    serverConnection();
    if (stateConn == 0) delay(2000);    
  }
  else {
    // fulfill the code
  }
}

 

  • You have to merge this pattern with the first part to obtain  the main basis of what is asked.
  • After, you need to add a function that sends a request to the server to store the distance.
  • The communication protocol for this request is:
    • sending a line containing 1,
    • sending a line containing the client's id,
    • sending a line containing three coordinates, separated by ,
  • For that exercise, the last line can be formatted as : dist,0,0. where dist is obviously the distance measured with the ultrasonic sensor.
  • After that, you just need to call that function each time the switch state changes, and to use readLineServer() to get the server's answer.

Exercise #4: use the 4-digits display.
 
For that exercise, a new library must be installed, that must handle the TM1637 chipset. This time, the library is intalled via the Library manager :
  • Go into "Outils" then "Gérer les bibliothèques" menu.
  • In the search filed, type TM1637.
  • In the list of proposed libraries, choose "Grove 4-Digit Display". It is quite simple and works well. But you can try another one if you have the taste of adventure :-).
 
Here is a sketch pattern to use the chipset, with the "Grove 4-digit" library :
#include "TM1637.h"
#define CLK D3
#define DIO D4
TM1637 tm1637(CLK,DIO);
void setup() {
  ...
  tm1637.init();
  tm1637.set(BRIGHT_TYPICAL);
  ...
}
void loop() {
  ...
  tm1367.display(0,15); // display F on digit 0
  tm1367.display(1,2); // display 2 on digit 1
  ...
}
 
What must be done :
  • Create a new sketch and copy/paste the code of the exercise #3 in it.
  • Each time the distance is measured, this distance must be displayed, in centimeters (but rounded to an integer value) on the 3 first digits of the 4-digit display
  • The fourth digit must display the state of the led, i.e. 0 if it is off, and 1 if it is on.

 Exercise #5: using PWM (for those who go fast)
 
What must be done:
  • use PWM principles on pin D1 to make the led blinking at a speed that depends on the measured distance.
  • What is important is to make the frequency varying between values that make a blinking that a human can perceive.
  • It implies that the duty cycle can be fixed, and set to 50% for example.
  • To set the frequency, one can follow the rules :
    • below 20cm, the frequency is 1Hz,
    • beyond 100cm, the frequency is 50Hz,
    • between, the frequency varies from 1 to 50Hz proportionally to the distance.