Skip to main content Link Search Menu Expand Document (external link)

Networking & Comm – ESP32 Sensor Web Server

Table of contents
  1. Networking & Comm – ESP32 Sensor Web Server
    1. Group Assignment for Output Devices
    2. ESP32-CAM
    3. WiFi Scan with ESP32-CAM
    4. WiFi WebCam with ESP32-CAM
    5. ToF and Hall Sensor Web Server using Server-Sent Events (SSE)
    6. Results
      1. Code Snippets

Group Assignment for Output Devices

send a message between two projects.

shown in Week 10 of EECS Group Assignments.

ESP32-CAM

As mentioned in my ongoing final project WingCam, I would like to use a light-weighted camera as the image and video capturing module on the airplane. Anthony recommended me with ESP32-CAM. And I found it convinient to prepare it for my final project.
image
ESP32-CAM basic layout and GPIO pins.

I focused on two tasks related to ESP32-CAM that will be used for my final project.

WiFi Scan with ESP32-CAM

Firstly, I try to program ESP32-CAM with Arduino and start with a basic WiFi Scanner as listed in the Arduino ESP32 example.

Since this ESP32-CAM does not come with a serial port such as USB Micro / Mini connector, it requires an addition programmer to write code on it using Arduino, as shown below.
image
ESP32-CAM programming tools and instructions from here.

Here, I was using the FTDI programmer (bottom one) and followed the connection shown below. Here is an interesting combination of 3.3V and 5V power on both ESP32-CAM and FTDI programmer because both can support 3.3/5V power as input or output while program uploading (And the jumper shown in green between GND and IO0 to turn off CSI_MCLK and write code into the board). However, while functioning, the board requires 5V/2A as input, otherwise it will raise brownout issue, as shown below.

1
Brownout detector was triggered

Eventually, the minimal solution is to use 5V power on both ESP32-CAM and FTDI programmer while program uploading and functioning. The jumper between GND and IO0 is used to switch between these two modes, where program uploading requires the jumper is connected.

After figuring out the power and jumper issue, I managed to get the WiFi Scan work, as shown below. The source code is from the Arduino ESP32 example.
image
ESP32-CAM basic WiFi scanning.

As shown in the photo, I used AI Thinker ESP32-CAM as the board and pre-installed Arduino ESP32 Libarary by adding https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json to the additional board manager in Preferences.

WiFi WebCam with ESP32-CAM

Then I experimented with the WiFi WebCam using ESP32-CAM, which is essentially why I want to use this module. Everything comes exactly the same as the WiFi Scan part, I only changed to the CameraWebServer example and set the corresponding WiFi access point that ESP32-CAM can directly connect to without further certification. So I used the SSID StataCenter (instead of CSAILPrivate requiring CSAIL kerberos certification) in Stata Building and MIT (instead of MIT (Secure) requiring MIT kerberos certification) and set password to empty string.

And it worked pretty well so long as the client opening the URL in the web browser is in the same WLAN or LAN network as the ESP32-CAM.
image
ESP32-CAM as a web camera.

ToF and Hall Sensor Web Server using Server-Sent Events (SSE)

As the assignment indicates, the intuitive idea to make a cool networking and communication demo is to connect the ESP32-CAM with a sensor and have its value broadcasted on a webpage. So, I decided to show two values, one it the ToF sensor (VL53L0X), which I designed and used in Week 8 as a 2D localizer, another is the Hall magnetic sensor embedded with ESP32.

One challenge is to configure the board with I²C for the ToF sensor, because the default I²C channel is already occupied by the OV2640 camera module and there are no open connectors to that.

I followed this neat example and set IO14 and IO15 as I2C_SDA and I2C_SCL, respectively. One mistake I was making and took me almost one hour to figure it out is I put TwoWire I2CWire = TwoWire(0); in setup() instead of as a global variable and Arduino cannot correctly use I2CWire in the sensor reading stage.

Another challenge is to use asynchronous TCP for Server-Sent Events (SSE), which is crucial because the basic web server example only uses synchronous TCP and failed to update the sensor value in real-time. Following this nice example of asynchronous TCP for asynchronous web server, I managed to make the Sensor Web Server work using server-sent events (after manually installing AsyncTCP and ESPAsyncWebServer libraries by going Sketch -> Include Library -> Add .zip Library). A bonus of this example is that it offers a great template for displaying the sensor layout.

Results

Code Snippets

esp32_sensor_async_webserver.ino
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Wire.h>
#include <VL53L0X.h>

#define I2C_SDA 14
#define I2C_SCL 15

// const char* ssid = "StataCenter";
const char* ssid = "MIT";
const char* password = "";

AsyncWebServer server(80);
AsyncEventSource events("/events"); // Create an Event Source on /events

// Timer variables
unsigned long lastTime = 0;  
unsigned long timerDelay = 100; // update interval [ms]

const int led = 33; // built-in LED1

VL53L0X ToFSensor;
const int tof_shutdown  = 13; // Pin of ToF sensor SHUTDOWN
TwoWire I2CWire = TwoWire(0);

float range; // ToF range in cm [from mm]
int   hall;  // Hall magnetic field detector

void initToFSensor() {
  // setup I2C ToF Sensor
  I2CWire.begin(I2C_SDA, I2C_SCL, 100000);
  ToFSensor.setBus(&I2CWire);
  I2CWire.begin();

  pinMode(tof_shutdown, OUTPUT);
  digitalWrite(tof_shutdown, HIGH);

  ToFSensor.setTimeout(500);
  if (!ToFSensor.init())
  {
    Serial.println("Failed to detect and initialize ToFSensor!");
    while (1) {}
  }
}

void getSensorReadings() {
  // [1] read hall sensor
  hall = hallRead();

  // [2] read ToF sensor
  int rangemm = ToFSensor.readRangeSingleMillimeters();
  range = float(rangemm) / 10.0F;
  if (ToFSensor.timeoutOccurred()) { Serial.print(" ToFSensor TIMEOUT"); }
}

void initWiFi() {
  pinMode(led, OUTPUT);
  digitalWrite(led, 0);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");

  // 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());
}

String processor(const String& var){
  getSensorReadings();
  //Serial.println(var);
  if(var == "RANGE"){
    return String(range);
  }
  else if(var == "HALL"){
    return String(hall);
  }
  return String();
}

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP32-CAM Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
  <link rel="icon" href="data:,">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    p { font-size: 1.2rem;}
    body {  margin: 0;}
    .topnav { overflow: hidden; background-color: #50B8B4; color: white; font-size: 1rem; }
    .content { padding: 20px; }
    .card { background-color: white; box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5); }
    .cards { max-width: 800px; margin: 0 auto; display: grid; grid-gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
    .reading { font-size: 1.4rem; }
  </style>
</head>
<body>
  <div class="topnav">
    <h1>ToF and Hall Sensor Web Server (ESP32-CAM)</h1>
  </div>
  <div class="content">
    <div class="cards">
      <div class="card">
        <p><i class="fas fa-ruler-horizontal" style="color:#059e8a;"></i> RANGE</p><p><span class="reading"><span id="temp">%RANGE%</span> cm</span></p>
      </div>
      <div class="card">
        <p><i class="fas fa-magnet" style="color:#00add6;"></i> HALL</p><p><span class="reading"><span id="hum">%HALL%</span> </span></p>
      </div>
    </div>
  </div>
<script>
if (!!window.EventSource) {
 var source = new EventSource('/events');
 
 source.addEventListener('open', function(e) {
  console.log("Events Connected");
 }, false);
 source.addEventListener('error', function(e) {
  if (e.target.readyState != EventSource.OPEN) {
    console.log("Events Disconnected");
  }
 }, false);
 
 source.addEventListener('message', function(e) {
  console.log("message", e.data);
 }, false);
 
 source.addEventListener('range', function(e) {
  console.log("range", e.data);
  document.getElementById("temp").innerHTML = e.data;
 }, false);
 
 source.addEventListener('hall', function(e) {
  console.log("hall", e.data);
  document.getElementById("hum").innerHTML = e.data;
 }, false);
 
}
</script>
</body>
</html>)rawliteral";

void setup(void) {
  Serial.begin(115200);
  initWiFi();
  initToFSensor();

  // Handle Web Server
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Handle Web Server Events
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client reconnected! Last message ID that it got is: %u\n", client->lastId());
    }
    // send event with message "hello!", id current millis
    // and set reconnect delay to 1 second
    client->send("hello!", NULL, millis(), 10000);
  });
  server.addHandler(&events);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  if ((millis() - lastTime) > timerDelay) {
    getSensorReadings();
    Serial.printf("ToF Range = %.2f cm \n", range);
    Serial.printf("Hall = %d \n", hall);
    Serial.println();

    // Send Events to the Web Server with the Sensor Readings
    events.send("ping",NULL,millis());
    events.send(String(range).c_str(),"range",millis());
    events.send(String(hall).c_str(),"hall",millis());
    
    lastTime = millis();
  }
}