Featured image of post week13

week13

Embedded Networking and Communications

Embedded Networking and Communications

Recitation: https://vimeo.com/821473847

Review: https://vimeo.com/823525835

Slides: http://academy.cba.mit.edu/classes/networking_communications/index.html

Task: Embedded Networking and Communications

  • Group assignment:

    • Send a message between two projects
    • Document your work to the group work page and reflect on your individual page what you learned
  • Individual assignment:

    • design, build, and connect wired or wireless node(s) with network or bus addresse

image

Implement a web server on esp32c3

Connect to wifi

Hardcoded version

A quick way to connect esp to wifi is to write on arduino code ssid and wifi password. Here is a piece of code based on ESP32Webserver library example esp32webserver1. This shortened code simply connect the esp to wifi network “myWifi”. It display dots on Serial monitor until concction, then display a message aknolewedging the connection and displaying the IP adress given to the esp by the router.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <WiFiClient.h>
#include <ESP32WebServer.h>
#include <WiFi.h>

const char* ssid = "myWifi";
const char* password = "myPassword";

void setup(void){
  Serial.begin(9600);
  WiFi.begin(ssid, password);

 // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}

loop(){
}

WifiManager utility

If found that it can be more versatile to use the “WiFiManager” library (https://github.com/tzapu/WiFiManager). This librarie is very handy to connect to different wifi networks. On the first launch, or when the board has no known Wifi networks to connect, it act as a wifi Access Point. We connect to this access point with a computer or mobile phone. We now have access to a webpage where we can choose our network and set it password.

image

If given informations are correct, the esp is now connected to wifi and doesn’t act anymore as an Access Point. We can like this save automatically into esp memory the connection information of several Wifi networks.

Bellow is the code from WifiManager library, example Basic. All comments describing options are already included in the example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <WiFiManager.h>

void setup() {
    // WiFi.mode(WIFI_STA); // explicitly set mode, esp defaults to STA+AP
    // it is a good practice to make sure your code sets wifi mode how you want it.
    // put your setup code here, to run once:
    Serial.begin(115200);
    //WiFiManager, Local intialization. Once its business is done, there is no need to keep it around
    WiFiManager wm;

    // reset settings - wipe stored credentials for testing
    // these are stored by the esp library
    // wm.resetSettings();

    // Automatically connect using saved credentials,
    // if connection fails, it starts an access point with the specified name ( "AutoConnectAP"),

    // if empty will auto generate SSID, if password is blank it will be anonymous AP (wm.autoConnect())

    // then goes into a blocking loop awaiting configuration and will return success result

    bool res;
    // res = wm.autoConnect(); // auto generated AP name from chipid
    res = wm.autoConnect("AutoConnectAP"); // anonymous ap
    //res = wm.autoConnect("AutoConnectAP","password"); // password protected ap

    if(!res) {
        Serial.println("Failed to connect");
        // ESP.restart();
    }
    else {
        //if you get here you have connected to the WiFi    
        Serial.println("connected...yeey :)");
    }
}

void loop() {
    // put your main code here, to run repeatedly:  
}

Run a webserver

ESP32 can be used as a web server to serve web pages and handle HTTP requests. Used as a webserver, the esp can perform tasks such as:

  • Serve web pages: the internal ESP file system can store and serve files as HTML, CSS, Javascript…
  • Handle HTTP requests: It can respond to requests such as GET and POST from client devices (typically a computer, tablet, mobile phone or another web enabled microcontroler)
  • WebSocket support: with webSocket connections, we can enable real-time, two-way communication beetween the server and the clients.

Now let’s combine the previous wifi connection code with webserver setup.

In the code bellow, esp act as a webserver . If we load the adress http://ESP_IP_ADRESS/message on the web navigator of a device connected to the same network, it will call the method handleMessageReceived.

We can even add some parameters as follow:

http://ESP_IP_ADRESS/message?arg1=value1&arg2=value2&arg3=value3

Those value can be retrieve on esp side with server.arg(“arg_name”)

Base code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <WiFiClient.h>
#include <DNSServer.h>
#include <WebServer.h>
#include <WebSocketsServer.h>
#include <WiFiManager.h>
#include <ESPmDNS.h>

#include <time.h>
#include <Math.h>

//=============================================================
// DEFINITIONS
//=============================================================
WebServer server(80);

//=============================================================
// HTTP HANDLERS
//=============================================================
void handleMessageReceived(){
  int arg1 = server.arg("arg1").toInt();
  int arg2 = server.arg("arg2").toInt();
  int arg3 = server.arg("arg3").toInt();
  Serial.println("message received");
  server.send(200, "text/plain","Message received OK");
}

//=============================================================
// SETUPS
//=============================================================
void setup_serial(){
  Serial.begin(9600);
  while (!Serial) { ; }
  Serial.println("Serial initialisation");
}  

void setup_wifi(){
  //WiFiManager
  //Local intialization. Once its business is done, there is no need to keep it around
  WiFiManager wifiManager;
  //Uncomment to reset saved settings
  //wifiManager.resetSettings();
  //set custom ip for portal
  //wifiManager.setAPStaticIPConfig(IPAddress(10,0,1,1), IPAddress(10,0,1,1), IPAddress(255,255,255,0));

  //fetches ssid and pass from eeprom and tries to connect
  //if it does not connect it starts an access point with the specified name
  //and goes into a blocking loop awaiting configuration
  wifiManager.autoConnect("MY_ESP_BOARD");
  //or use this for auto generated name ESP + ChipID
  //wifiManager.autoConnect();
  
  //if you get here you have connected to the WiFi
  Serial.println("connected to WiFi!");  
}

void handleIndex(){
    server.send(200, "text/plain","Hello from ESP!");
}

void setup_server(){
  //=============================================================
  //Pages declaration//=============================================================
  server.on("/", handleIndex);
  server.on("/message", handleMessageReceived);
  
  //=============================================================
  // Launch server  //=============================================================
  server.begin();
  Serial.println("HTTP server started");
}


void setup() {
  setup_serial();
  setup_wifi();
}

  
void loop() {
  server.handleClient();
  yield();
}

When we ask for root page on a web client (server.on("/", handleIndex); line of the code), handleIndex is called and the Hello message is send to client. image

Controling LEDs through network

I will connect the esp on my pebble board to the same wifi network as my computer. Requesting url http://esp_ip/activate_leds?led_id=XX&state=0or1, I will switch on or off led X remotely of the esp board.

On esp setup, I add the following parameters. Note that server will send back a response to client that will be displayed on web browser with server.send(200, “text/plain”,“Message send back to client”);.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16

void setup(){
[...]
server.on("/activate_leds", handleActivateLedsMessageReceived);
[...]
}

//Create a callback method, called when http://esp_ip//activate_leds is called
void handleActivateLedsMessageReceived(){
  int led_id = server.arg("led_id").toInt();
  int state = server.arg("state").toInt();
  int color_id = server.arg("color_id").toInt();
  switch_led(led_id,state);
  Serial.println("message received");
  server.send(200, "text/plain","Led activation message received!");
}

As we use ws2812b leds, I choose to perform Led activation with the help of NeoPixelBus Library. I usualy use FastLed library but it appear to be incompatible with Wifi on esp32c3, causing all kind of problem including leds flickering.

Bellow is the code relative to leds only. I hardcoded few preset colors for testing but we could send r,g,b values directly to switch_leds().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <NeoPixelBus.h>

#define LED_COUNT 31
#define LED_PIN 6

NeoPixelBus<NeoGrbFeature, NeoWs2812xMethod> strip(LED_COUNT, LED_PIN);

void setup() {
    FastLED.addLeds<NEOPIXEL, LED_PIN>(leds, NUM_LEDS);
 }

void switch_led(int led_id, bool state, int color_id){

  RgbColor color = RgbColor(255);

  if(color_id==-1)     color = RgbColor(0);
  else if(color_id==0) color = RgbColor(255, 255, 255);
  else if(color_id==1) color = RgbColor(255, 0, 0);
  else if(color_id==2) color = RgbColor(0, 255, 0);
  else if(color_id==3) color = RgbColor(0, 0, 255);
  else if(color_id==4) color = RgbColor(255, 255, 0);
  else if(color_id==5) color = RgbColor(0, 255, 255);
  else if(color_id==6) color = RgbColor(255, 0, 255);

  if(led_id >= 0 && led_id < LED_COUNT-1){

      if(state == 0)  strip.SetPixelColor(led_id, RgbColor(0));
      else            strip.SetPixelColor(led_id, color);
  }

  if(led_id==-1){
      RgbColor output_color;
      if(state == 0)  output_color = RgbColor(0);
      else            output_color = color;

      for (int i=0;i<LED_COUNT;i++) strip.SetPixelColor(i, output_color);
  }

  strip.Show();
}

void blink_all(int color, int count, int freq){
  for(int i=0;i<count;i++){
    switch_led(-1,1, color);
    delay(500/freq);
    switch_led(-1,0,color);
    delay(500/freq);
  }
}

On this video change the url parameters in the web client to switch ON or OFF specific leds.

http://ESP_IP/activate_leds?led_id=XXX&state=YYY&color_id=ZZZ

Serve an HTML page

Serve files from the ESP32

Check “ESP32 as a file server” section on week 4 to get the process of file upload into esp32c3

Web page template

I made a template containing a html page, css file and images for design customisation, js file for interactivity and jquery library.

Template files: template zip

image

Here is a snapshot of the webpage: image

First, we need to set the esp ip adress and click on Connect button. Actions performed when slider moves or at button press are defined on “esp_template.js” file. For example:

Slider action:

1
2
3
4
5
6
7
8
9
$( "#var1_slider" ).slider({
        value:50,
        min:1,
        max:100,
        change: function(event,ui){
                                    $("#var1").html(ui.value);
                                    $.get( "http://" + esp_adress + "/message",{arg1:var1}, function( data ) {});
                                  }
    });

When the slider is moved, value is displayed at the right of “variable1:”" and url http://esp_adress/message?arg1=XXX is called.

Button action:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$('.button1').on("click",function(){
    button1Clicked();
});

function button1Clicked(){
        console.log("button1 clicked");
        var var1 = $("#var1").text();
        var var2 = $("#var2").text();
        var var3 = $("#var3").text();
        $.get( "http://" + esp_adress + "/send_values",{r:var1,g:var2,b:var3}, function( data ) {});
    };

When button1 is clicked, all slider values are send to url http://esp_adress/send_values?r=XXX&g=YYY&b=ZZZ

Control of a custom board

Now let’s adapt this template to our previous code:

  • On button 1 click, switch all leds on red:

http://ESP_IP/activate_leds?led_id=-1&state=1&color_id=1

  • On button 2 click, switch all leds on green:

http://ESP_IP/activate_leds?led_id=-1&state=1&color_id=2

  • On button 3 click, switch all leds on blue:

http://ESP_IP/activate_leds?led_id=-1&state=1&color_id=3

  • On button 4 click, switch all leds off:

http://ESP_IP/activate_leds?led_id=-1&state=0&color_id=1

Change in one slider will send the value of all 3 sliders to:

http://ESP_IP/blink?color_id=SLIDER1_VALUE&count=SLIDER2_VALUE&freq=SLIDER3_VALUE

  • Slider 1 will control led_color. I change limits from -1 to 6 to match my colors preset defined in switch_led()
  • Slider 2 will control blink count
  • Slider 3 will control blink frequency

The files of the modified template can be found here: network control

Note that the html/js/css code can be opened locally on the computer and doesn’t necessary have to be served by the esp. We just have to specify the board adress on the bottom text line.

image

Control another project through network

Now that we know how to control a board from an url, we just need to create a client on another board to call this url.

Part of the whole code, related to http connection is below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void send_order_to_other_board(int leds_state, int board_id){

  String board_ip = board1_ip;
  
  if(board_id==0)     {board_ip = board1_ip}
  else if(board_id==1){board_ip = board2_ip})
  
  String url_to_call = "http://" + board_ip + "/activate_leds?led_id=-1&color_id=4&state=" + String(leds_state);
  
  Serial.print("sending order to ");
  Serial.println(url_to_call);

  http.begin(url_to_call);

  // Send HTTP GET request
  int httpResponseCode = http.GET();

  if (httpResponseCode > 0) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    String payload = http.getString();
    Serial.println(payload);
  }

  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();
}

Now we simply need to call this code when user press on a button of the board. We call send_order_to_other_board(led_state, 0) from board 2 and send_order_to_other_board(led_state, 1) from board 1.

Here is the code related to button push. send_order_to_other_board is called when button state change.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
  #define BUTTON_1_PIN D9
  bool prev_button_state_1 = false;
  bool button_state_1 = false;

void loop(){
  [...]
  //=================================// 
  //         ACTION BUTTON 1 
  //=================================//
  button_state_1 = !digitalRead(BUTTON_1_PIN);

  if(prev_button_state_1 != button_state_1){
    send_order_to_other_board(0,button_state_1);
  }

  prev_button_state_1 = button_state_1;
  
  [...]
}

In the video below we can see the communication beetween 2 boards. First two button press on a board make it light up, the third send the order to light-up the other board.

Sketch slow down

I found a strange behaviour of my boards on esp32c3 xiao. When serial communication is established, everything works as expected, but when it is not, the program seems to works at least 10 times slower (led blinking and reaction to button press).

TODO: find why and/or disable serial output

mDNS

We see that we can access our device through network thank to their IP adresses. Those adresses are given by our wifi router, we can assign an IP to a device giving it MAC adresse inside router configuration. We can Know the IP given to the device by checking the output of WifiManager in serial monitor. Those solution are not ideal, hopefully, with mDNS we can access our device by names like http://DEVICE_NAME.local

To implement such fonctionality we can add the following code. We only have to change mDNSname before uploading on the different boards.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <ESPmDNS.h>

String mDNSname = "ESP_BOARD_1";

void setup_mDNS(){
  if (MDNS.begin(mDNSname)) {  
    Serial.printf("mDNS OK : %s.local\r\n", mDNSname);
  } else {
    Serial.println("mDNS error, service doesn't start!");
  }
  MDNS.addService("http", "tcp", 80);
}

void loop(){
    [...]
    mDNS.update();
    [...]
}

Serial output of the board, it seems OK!

image

Our code became:

  • On board 1
1
2
String mDNSname        = "ESP_BOARD_2";
String target_adress   = "ESP_BOARD_1.local";
  • On board 2
1
2
String mDNSname        = "ESP_BOARD_1";
String target_adress   = "ESP_BOARD_2.local";

and inside the loop:

1
2
3
4
5
6
7
8
9
  //======= ACTION BUTTON1 ================//
  button_state_1 = !digitalRead(BUTTON_1_PIN);

  if(prev_button_state_1 != button_state_1){

    switch_led(-1, button_state_1, 3);
    send_order_to_other_board(button_state_1,target_adress);
  }
  prev_button_state_1 = button_state_1;

A little change on send_order function.

1
2
3
4
5
void send_order_to_other_board(int leds_state, String board_adress){

  String url_to_call = board_adress + "/activate_leds?led_id=-1&color_id=4&state=" + String(leds_state);
  [...]
}

Here is the console output of the 2 boards on initialisation, we can see the different mDNS names:

Board 1 Board2
image image

Problems

I found that boards can be called from a computer or phone web browser using BOARD_NAME.local, unfortunatly I was not able to make it works when a board talk to another board. I can’t find a solution for now, so I have to stay with the IP solution.

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy