So far, BME-280 sensor data has been exposed as a JSON API and as a web page. There are two problems with the second approach:
- There must be a wifi router to which the ESP8266 can connect
- The IP address the router assigns must be somehow displayed before we visit the page
In an earlier blog post, the IP address was displayed in the Arduino IDE's serial monitor. Of course, we could connect a display and output the IP address there. Wouldn't it be nice if our browser were automatically redirected to that page, sort of like what happens when you connect to a public network in your favorite coffee shop?
That's exactly what we'll do.
We establish our own access point such that whenever the user connects to that network, his browser will be automatically directed to a specific landing page. This landing page is called a "captive portal", and is typically used for user authentication, gather information, plant tracking cookies, etc.
Captive portals are not just for coffee shops any more - ours will display temperature, humidity, and air pressure from the BME-280 sensor!
Here's the 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 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 | /** * BME280-Captive-Portal * * By: Mike Klepper * Date: 23 September 2017 * * This program exposes BME-280 data as a captive portal. * * See patriot-geek.blogspot.com * for explanation. */ #include "ESP8266WiFi.h" #include "DNSServer.h" #include "ESP8266WebServer.h" #include "Adafruit_Sensor.h" #include "Adafruit_BME280.h" const int STARTUP_DELAY = 500; const char* MESSAGE_503 = "503 Service Unavailable"; const char* AP_NAME = "BME-280 Data"; const byte DNS_PORT = 53; const IPAddress SUBNET_MASK(255, 255, 255, 0); const IPAddress AP_IP(192, 168, 1, 1); const byte WEB_SERVER_PORT = 80; boolean sensorAvailable; DNSServer dnsServer; ESP8266WebServer webServer(WEB_SERVER_PORT); Adafruit_BME280 bme; void setup(void) { // Start the BME280 sensor if(!bme.begin()) { sensorAvailable = false; } else { sensorAvailable = true; delay(STARTUP_DELAY); } WiFi.mode(WIFI_AP); WiFi.softAPConfig(AP_IP, AP_IP, SUBNET_MASK); WiFi.softAP(AP_NAME); dnsServer.start(DNS_PORT, "*", AP_IP); webServer.onNotFound([]() { returnHtml(); }); webServer.begin(); } void loop(void) { dnsServer.processNextRequest(); webServer.handleClient(); yield(); } void returnHtml() { if(sensorAvailable) { // Get values float tempC = bme.readTemperature(); float humidity = bme.readHumidity(); float pressurePascals = bme.readPressure(); // Convert to British units float tempF = 9.0/5.0 * tempC + 32.0; float pressureInchesOfMercury = 0.000295299830714 * pressurePascals; // Build HTML response String responseHtml = ""; responseHtml += "<!DOCTYPE html>"; responseHtml += "<html>"; responseHtml += " <head>"; responseHtml += " <meta charset=\"UTF-8\">"; responseHtml += " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"; responseHtml += " <title>BME-280 Sensor Data</title>"; responseHtml += " <style>"; responseHtml += " body {font-family: sans-serif}"; responseHtml += " h1 {font-size: 1.0cm}"; responseHtml += " p {font-size: 0.50cm}"; responseHtml += " button {font-size: 1.0cm}"; responseHtml += " </style>"; responseHtml += " <script type=\"text/javascript\">"; responseHtml += " function refreshPage()"; responseHtml += " {"; responseHtml += " location.reload(true);"; responseHtml += " }"; responseHtml += " </script>"; responseHtml += " </head>"; responseHtml += " <body>"; responseHtml += " <h1>BME-280 Sensor Data</h1>"; responseHtml += " <p>Temperature: " + String(tempF) + "° F</p>"; responseHtml += " <p>Humidity: " + String(humidity) + "%</p>"; responseHtml += " <p>Pressure: " + String(pressureInchesOfMercury) + " inHg</p>"; responseHtml += " <button type=\"button\" onclick=\"refreshPage()\">Refresh</button>"; responseHtml += " </body>"; responseHtml += "</html>"; // Return HTML with correct MIME type webServer.send(200, "text/html; charset=utf-8", responseHtml); } else { webServer.send(500, "text/plain", MESSAGE_503); } } |
Once it is running, you will see a new wireless network, called "BME-280 Data".
Connect to it, and you're immediately presented with the sensor data!
The only new part of the code are on lines 49 through 58:
49 | Set the ESP8266 into access point mode |
50 | Set the AP's local IP address, gateway IP, and subnet mask |
51 | Specify the AP's name |
53 | Start a domain name server |
55-58 | Return the same HTML regardless of the address the user tries to visit |
This technique - creating a captive portal - has many applications beyond displaying sensor data. In particular, it can be used to view and edit configuration values. This will be explored in a future post.
Why bother having the user connect to that AP - why not just make the AP name to be the sensor data? One problem with this is that the AP name will usually be truncated by the network manager of the computer or smart phone. Further, most computers or smart phones cache the names of the networks it finds.
No comments:
Post a Comment