Built-in project · ESP32-C3-DevKitM-1

WiFi: fetch the weather, on an ESP32-C3

HTTPClient against a simulated origin, with the numbers on a dial in the Cloud panel, and a request to the real internet that is refused, in as many words, while Real internet is off. The whole build, an ESP32-C3, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 10 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $13. On Mokxi they're free.

The circuit runs here once the page has loaded.

What you'll learn

  • Connecting to WiFi and checking the status
  • Making an HTTP GET request with HTTPClient
  • Reading a status code and a response body
  • Pulling a value out of JSON text
  • Difficulty: Intermediate
  • Time: About 10 minutes
  • Board: ESP32-C3-DevKitM-1
  • Parts: 2

How it works

This is all simulated. The ESP32-C3 joins a simulated WiFi network called Mokxi-Lab, and the only websites that exist are the .mokxi names. Nothing here reaches the real internet. The Cloud panel shows requests and lets you change the numbers the weather server sends back.

In setup the board connects and prints its IP address. It also tries example.com to show that a name outside .mokxi fails. Then loop calls fetchWeather every 5 seconds. It asks weather.mokxi for JSON and prints the temperature and humidity.

Parts list

2 parts, plus the jumper wires. They retail for about $13; on Mokxi they're free.

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · ESP32-C3-DevKitM-1
/*
 * WiFi: fetch the weather.
 *
 * SIMULATED, all of it. The radio is simulated, and so is the internet: the
 * only names that resolve are time.mokxi, weather.mokxi, echo.mokxi, api.mokxi
 * and the board's own address. Nothing here reaches the network your browser
 * is on, ever. Open the Cloud panel to watch the requests go out and to
 * change the numbers weather.mokxi answers with.
 *
 * The code is the code you would write for a real board.
 */
#include <HTTPClient.h>
#include <WiFi.h>
#include <WiFiClient.h>

const char *SSID = "Mokxi-Lab";
const char *PASSWORD = "breadboard";

/* Pull one number out of a JSON body without a JSON library: find the key,
   step over the colon, copy what follows. Good enough for a lesson, and it
   keeps the sketch to the one thing it is about. */
static bool jsonValue(const char *body, const char *key, char *out, int size) {
  String text(body);
  int at = text.indexOf(key);
  if (at < 0) return false;
  at += (int)strlen(key);
  while (text[(size_t)at] == '"' || text[(size_t)at] == ':' || text[(size_t)at] == ' ') at++;
  int used = 0;
  while (used + 1 < size) {
    char c = text[(size_t)at + (size_t)used];
    if (c == ',' || c == '}' || c == '\0') break;
    out[used++] = c;
  }
  out[used] = '\0';
  return used > 0;
}

void fetchWeather() {
  WiFiClient client;
  HTTPClient http;
  http.begin(client, "http://weather.mokxi/");
  int code = http.GET();
  if (code <= 0) {
    Serial.print("request failed: ");
    Serial.println(HTTPClient::errorToString(code));
    http.end();
    return;
  }
  Serial.print("HTTP ");
  Serial.println(code);
  String body = http.getString();
  Serial.print("body: ");
  Serial.println(body);

  char value[24];
  if (jsonValue(body.c_str(), "temperatureC", value, sizeof value)) {
    Serial.print("temperature: ");
    Serial.print(value);
    Serial.println(" C");
  }
  if (jsonValue(body.c_str(), "humidity", value, sizeof value)) {
    Serial.print("humidity: ");
    Serial.print(value);
    Serial.println(" %");
  }
  http.end();
}

void setup() {
  Serial.begin(115200);
  Serial.println("Simulated WiFi and a simulated internet: .mokxi names only.");
  WiFi.begin(SSID, PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(250);
  }
  Serial.println();
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  /* A name that is not under .mokxi does not resolve, and says so. */
  HTTPClient outside;
  outside.begin("http://example.com/");
  int code = outside.GET();
  Serial.print("example.com -> ");
  Serial.println(HTTPClient::errorToString(code));
  outside.end();
}

void loop() {
  fetchWeather();
  delay(5000);
}

Code walkthrough

The key functions of the sketch, and what each one does.

jsonValue sketch.ino

Finds a key in the body text, steps over quotes, colons and spaces, and copies characters up to the next comma or closing brace. It returns false if the key is missing. It is a tiny shortcut that avoids a JSON library.

static bool jsonValue(const char *body, const char *key, char *out, int size) {
  String text(body);
  int at = text.indexOf(key);
  if (at < 0) return false;
  at += (int)strlen(key);
  while (text[(size_t)at] == '"' || text[(size_t)at] == ':' || text[(size_t)at] == ' ') at++;
  int used = 0;
  while (used + 1 < size) {
    char c = text[(size_t)at + (size_t)used];
    if (c == ',' || c == '}' || c == '\0') break;
    out[used++] = c;
  }
  out[used] = '\0';
  return used > 0;
}

fetchWeather sketch.ino

Sends a GET to weather.mokxi. If the code is not positive, it prints the error and stops. Otherwise it prints the HTTP code and the body, then uses jsonValue to find temperatureC and humidity and print them.

void fetchWeather() {
  WiFiClient client;
  HTTPClient http;
  http.begin(client, "http://weather.mokxi/");
  int code = http.GET();
  if (code <= 0) {
    Serial.print("request failed: ");
    Serial.println(HTTPClient::errorToString(code));
    http.end();
    return;
  }
  Serial.print("HTTP ");
  Serial.println(code);
  String body = http.getString();
  Serial.print("body: ");
  Serial.println(body);

  char value[24];
  if (jsonValue(body.c_str(), "temperatureC", value, sizeof value)) {
    Serial.print("temperature: ");
    Serial.print(value);
    Serial.println(" C");
  }
  if (jsonValue(body.c_str(), "humidity", value, sizeof value)) {
    Serial.print("humidity: ");
    Serial.print(value);
    Serial.println(" %");
  }
  // ... 2 more lines in the editor

setup sketch.ino

Opens serial, calls WiFi.begin with the network name and password, and prints dots until connected. It prints the IP address, then tries http://example.com/ to show that it fails to resolve.

void setup() {
  Serial.begin(115200);
  Serial.println("Simulated WiFi and a simulated internet: .mokxi names only.");
  WiFi.begin(SSID, PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(250);
  }
  Serial.println();
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  /* A name that is not under .mokxi does not resolve, and says so. */
  HTTPClient outside;
  outside.begin("http://example.com/");
  int code = outside.GET();
  Serial.print("example.com -> ");
  Serial.println(HTTPClient::errorToString(code));
  outside.end();
}

loop sketch.ino

Calls fetchWeather, then waits 5 seconds, over and over.

void loop() {
  fetchWeather();
  delay(5000);
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • It prints dots forever This project
    The sketch must use the simulated network name and password in SSID and PASSWORD.
  • A request fails with an error This project
    Only .mokxi names resolve here. Use weather.mokxi, time.mokxi, echo.mokxi or api.mokxi.
  • The temperature never changes This project
    Change the numbers in the Cloud panel. The server answers with whatever is set there.
  • Upload fails or the board is not found by the computer. ESP32-C3-DevKitM-1
    Pick the ESP32-C3 board in your tools and the right port. If it still fails, hold BOOT, tap RESET, then release BOOT to enter download mode and try again.
  • GPIO pins act strangely or the board resets. ESP32-C3-DevKitM-1
    GPIO is 3.3 V only, so never feed it 5 V. GPIO 18 and 19 are USB, and GPIO 2, 8 and 9 are boot strapping pins. Avoid loading them at reset.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.