【前言】
Arduino GIGA R1 WiFi开发板获取家里的环境信息,可以通过互联网获取。
【硬件】
1、ArduinoGIGAR1WiFi开发板
2、OLED模组
3、STCC4二氧化碳监测模组
【硬件连接】
OLED采用IIC接口。
STCC4采用IIC接口。
ArduinoGIGAR1WiFi 开发板上有三组IIC接口,如下:
SDA - D20
SCL - D21
SDA1 - also available on the camera connector.
SCL1 - also available on the camera connector.
SDA2 - D9
SCL2 - D8
根据开发板的资料使用如下:
Wire.begin() //SDA & SDL Wire1.begin(); //SDA1 & SDL1 Wire2.begin(); //SDA2 & SDL2
我采用SDA/SCL接口。
【软件安装思路】
使用开发板上的wifi模组连上互联网,开启web服务。当网络向开发板提起数据访问时,把数据反馈回去。
【代码实现】
1、ArduinoGIGAR1WiFi端
/**
* @file stcc4_ssd1306_web_station_mode.ino
* @brief Combines STCC4 CO2 sensor reading with SSD1306 OLED display and a WiFi web server.
* Reads CO2, Temperature, and Humidity, displays them on OLED and serves them via a webpage.
* Connects to an existing WiFi network (Station Mode).
*
* Connect STCC4 to Wire1 (SDA1, SCL1) on Arduino Giga R1.
* Connect SSD1306 to default Wire (SDA, SCL) on Arduino Giga R1.
*/
#include <WiFi.h>
#include "arduino_secrets.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_STCC4.h>
// --- WiFi Configuration ---
char ssid[] = "HUAWEI-H10R9U"; // Your network SSID (name)
char pass[] = "18977381885@"; // Your network password
int keyIndex = 0; // Your network key index number (needed only for WEP)
WiFiServer server(80); // Web server on port 80
// --- OLED Configuration ---
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET_PIN -1 // Use -1 if not connected to a pin
#define SCREEN_ADDRESS 0x3C // Default address for 128x64 display
// --- Object Declarations ---
// Create STCC4 object
Adafruit_STCC4 stcc4;
// Create SSD1306 object
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET_PIN);
// Variables to store sensor readings
uint16_t last_co2 = 0;
float last_temp = 0.0;
float last_humid = 0.0;
uint16_t last_status = 0;
bool last_read_success = false;
// Timing variables
unsigned long previousMillis = 0;
const long interval = 2000; // Read sensor every 2 seconds
void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
Serial.println(F("Initializing STCC4, SSD1306, and WiFi..."));
// Initialize Wire1 for STCC4 (SDA1, SCL1)
Wire.begin(); // Corrected initialization for Wire1
delay(10);
// Initialize STCC4 sensor on Wire1
if (!stcc4.begin()) { // Pass Wire1 pointer to begin()
Serial.println(F("Failed to find STCC4 chip on Wire1!"));
Serial.println(F("Check wiring and restart."));
errorLoop("STCC4 Init Failed");
}
Serial.println(F("STCC4 found!"));
if (!stcc4.reset()) {
Serial.println(F("Failed to reset STCC4!"));
errorLoop("STCC4 Reset Failed");
}
Serial.println(F("STCC4 reset successful."));
if (!stcc4.enableContinuousMeasurement(true)) {
Serial.println(F("Failed to start continuous measurement on STCC4!"));
errorLoop("Start Measurement Failed");
}
Serial.println(F("STCC4 continuous measurement started."));
// Initialize SSD1306 display
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed!"));
Serial.println(F("Check wiring and restart."));
errorLoop("OLED Init Failed");
}
Serial.println(F("SSD1306 initialized successfully."));
// Clear the buffer and show a startup message on the OLED
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 20);
display.println(F("System Ready"));
display.setCursor(0, 30);
display.println(F("Reading Sensors..."));
display.display();
delay(2000); // Briefly show startup message
// Initialize WiFi in Station Mode
Serial.println("Connecting to WiFi Network");
// Check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("Communication with WiFi module failed!");
errorLoop("WiFi Module Failed");
}
// Remove static IP configuration to use DHCP
// WiFi.config(IPAddress(10, 0, 0, 1));
// Print the network name (SSID) we are trying to connect to
Serial.print("Connecting to: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network
WiFi.begin(ssid, pass);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected!");
// Start the web server on port 80
server.begin();
// Print WiFi status (now includes actual assigned IP)
printWiFiStatus();
}
void loop() {
unsigned long currentMillis = millis();
// --- Sensor Reading Logic ---
// Only read the sensor every 'interval' milliseconds
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
Serial.println("Attempting to read STCC4...");
if (stcc4.readMeasurement(&last_co2, &last_temp, &last_humid, &last_status)) {
last_read_success = true;
Serial.print(F("Read OK - CO2: ")); Serial.print(last_co2);
Serial.print(F(" ppm, Temp: ")); Serial.print(last_temp);
Serial.print(F(" C, Humidity: ")); Serial.print(last_humid);
Serial.print(F(" %, Status: 0x")); Serial.println(last_status, HEX);
} else {
last_read_success = false;
Serial.println(F("Failed to read measurement from STCC4."));
}
}
// --- Display Update Logic ---
// Update the display on every loop iteration to maintain responsiveness
updateDisplay();
// --- Web Server Logic ---
handleWebRequests();
// Small delay to prevent overwhelming the CPU
delay(50);
}
void updateDisplay() {
// Clear the display buffer
display.clearDisplay();
// --- Layout Information ---
// Title
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE, SSD1306_BLACK); // White text on black background
display.setCursor(0, 0);
display.println(F("ENVIRONMENTAL DATA"));
// Divider Line
display.drawLine(0, 12, SCREEN_WIDTH - 1, 12, SSD1306_WHITE);
if (last_read_success) {
// --- Define row heights based on font size ---
// Font Size 1: ~8 pixels high
// Font Size 2: ~16 pixels high
int row1_y = 16; // For CO2
int row2_y = 34; // For Temp & Humidity
int row3_y = 52; // For Status or other info if needed later
// --- CO2 Section ---
display.setTextSize(1); // Use smaller font for label
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, row1_y);
display.print(F("CO2:"));
display.setTextSize(2); // Larger font for value
display.setCursor(30, row1_y); // Position value further right
display.print(last_co2);
display.print(F("p")); // Abbreviated unit to save space
// --- Temperature & Humidity Section (Single Row) ---
display.setTextSize(1);
display.setCursor(10, 36);
display.print(F("TEMP HUMI")); // Short label for Temperature
display.setTextSize(2);
display.setCursor(0, 48);
display.print(last_temp, 1); // Print with 1 decimal place
display.print(F("C")); // Unit
display.setTextSize(2);
// Position Humidity value after its label
display.setCursor(64, 48);
display.print(last_humid, 1); // Print with 1 decimal place
display.print(F("%")); // Percentage sign
} else {
// Display error message if reading failed
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 25);
display.println(F("Sensor Read Error!"));
display.setCursor(0, 35);
display.println(F("Check Connections."));
}
// Actually write the buffer to the screen
display.display();
}
// New function to handle incoming web requests
// New function to handle incoming web requests
void handleWebRequests() {
WiFiClient client = server.accept(); // listen for incoming clients
if (client) { // if you get a client,
Serial.println("New web client"); // print a message out the serial port
String currentLine = ""; // make a String to hold incoming data from the client
bool requestHandled = false; // Flag to ensure we only respond once per request
while (client.connected()) { // loop while the client's connected
// Move delayMicroseconds here, after checking for available data
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
// Serial.write(c); // Commenting out serial output for clarity during web requests
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0 && !requestHandled) {
Serial.println("Sending response headers..."); // Add this debug line
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Access-Control-Allow-Origin: *"); // 允许所有来源的请求
client.println();
// --- Generate HTML Response with Sensor Data ---
// Start of HTML
client.println("<!DOCTYPE html><html>");
client.println("<head><meta charset=\"utf-8\"><title>Environmental Data</title></head>");
client.println("<body>");
// Page Title
client.println("<h1>Environmental Monitoring Station</h1>");
// Data Table
client.println("<table id='dataTable' border='1'>");
client.println("<tr><th>Parameter</th><th>Value</th></tr>");
if (last_read_success) {
client.print("<tr><td>CO2</td><td>"); client.print(last_co2); client.println(" ppm</td></tr>");
client.print("<tr><td>Temperature</td><td>"); client.print(last_temp, 1); client.println(" °C</td></tr>");
client.print("<tr><td>Humidity</td><td>"); client.print(last_humid, 1); client.println(" %</td></tr>");
client.print("<tr><td>Status</td><td>0x"); client.print(last_status, HEX); client.println("</td></tr>");
} else {
client.println("<tr><td colspan='2'>Error reading sensor!</td></tr>");
}
client.println("</table>");
// End of HTML
client.println("</body>");
client.println("</html>");
requestHandled = true;
Serial.println("Response sent, breaking inner loop.");
break; // Exit the inner while loop
}
else { // if you got a newline, then clear currentLine:
currentLine = "";
}
}
else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
// Optional: Handle LED control commands like original example
// Check to see if the client request was "GET /H" or "GET /L":
if (currentLine.endsWith("GET /H")) {
digitalWrite(LED_BUILTIN, HIGH); // Turn LED on
}
if (currentLine.endsWith("GET /L")) {
digitalWrite(LED_BUILTIN, LOW); // Turn LED off
}
}
// Delay is now at the end of the loop, ensuring it runs even when no data is available
// This prevents tight looping and gives other processes time.
delayMicroseconds(10);
}
// Close the connection:
client.stop();
Serial.println("Web client disconnected");
}
}
// Helper function to handle initialization errors gracefully
void errorLoop(const char* errorMsg) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(F("ERROR:"));
display.println(errorMsg);
display.println(F("Restart device."));
display.display();
while (true) {
delay(1000); // Flash or do nothing, indefinitely
}
}
void printWiFiStatus() {
// print the SSID of the network you're attached to:
Serial.print("Connected to SSID: ");
Serial.println(WiFi.SSID());
// print your WiFi shield's IP address assigned by DHCP:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address (DHCP): ");
Serial.println(ip);
// print where to go in a browser:
Serial.print("To see this page in action, open a browser to http://");
Serial.println(ip);
}2、在PC上写一个html文件,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Environmental Monitoring Station</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f0f0f0;
}
.container {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
text-align: center;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th,
td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
th {
background-color: #4CAF50;
color: white;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
#status {
text-align: center;
font-style: italic;
color: #666;
margin-top: 20px;
}
#ipInputContainer {
text-align: center;
margin-bottom: 20px;
}
input[type="text"] {
padding: 8px;
width: 200px;
margin-right: 10px;
}
button {
padding: 8px 15px;
background-color: #008CBA;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #007B9A;
}
</style>
</head>
<body>
<div class="container">
<h1>Environmental Monitoring Station</h1>
<div id="ipInputContainer">
<label for="arduinoIP">Enter Arduino IP Address:</label>
<input type="text" id="arduinoIP" placeholder="e.g., 192.168.3.159">
<button onclick="setArduinoIP()">Connect</button>
</div>
<table id="dataTable">
<thead>
<tr>
<th>Parameter</th>
<th>Value</th>
</tr>
</thead>
<tbody id="dataBody">
<!-- Data rows will be inserted here -->
<tr>
<td colspan="2">Please enter the Arduino IP address and click Connect.</td>
</tr>
</tbody>
</table>
<div id="status">Status: Waiting for connection...</div>
</div>
<script>
let arduinoIP = '';
let refreshIntervalId = null;
// Function to set the Arduino IP and start fetching data
function setArduinoIP() {
const inputField = document.getElementById('arduinoIP');
arduinoIP = inputField.value.trim();
if (!arduinoIP) {
alert('Please enter a valid IP address.');
return;
}
// Clear any existing interval
if (refreshIntervalId) {
clearInterval(refreshIntervalId);
}
// Update status and start fetching
document.getElementById('status').textContent = 'Status: Fetching data...';
fetchDataAndPopulateTable(); // Initial fetch
// Set up periodic fetching every 2 seconds (2000 milliseconds)
refreshIntervalId = setInterval(fetchDataAndPopulateTable, 2000);
}
// Function to fetch data from the Arduino server and update the table
async function fetchDataAndPopulateTable() {
if (!arduinoIP) {
document.getElementById('status').textContent = 'Status: No IP address set.';
return;
}
const url = `http://${arduinoIP}`; // Construct URL using entered IP
const statusElement = document.getElementById('status');
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const htmlText = await response.text();
// Parse the received HTML to extract table data
const parser = new DOMParser();
const doc = parser.parseFromString(htmlText, 'text/html');
const tableRows = doc.querySelectorAll('#dataTable tbody tr'); // Select rows inside the data table body
if (tableRows.length === 0) {
throw new Error('Could not parse data from the response.');
}
const dataBody = document.getElementById('dataBody');
// Clear the existing table body content
dataBody.innerHTML = '';
// Iterate through the parsed rows and append them to our local table
tableRows.forEach(row => {
dataBody.appendChild(row.cloneNode(true));
});
statusElement.textContent = 'Status: Data updated successfully.';
} catch (error) {
console.error('Error fetching data:', error);
statusElement.textContent = `Status: Error fetching data - ${error.message}`;
// Optionally, display an error row in the table
const dataBody = document.getElementById('dataBody');
dataBody.innerHTML = `<tr><td colspan="2">Error: ${error.message}</td></tr>`;
}
}
</script>
</body>
</html>【测试】
编译下载后,连接OLED以及STCC4模组,获取环境信息如下:

同时用电脑打开网页:

【总结】
此次实现,基于arduino的生态,可以快速的实现环境的网络监测!。
阅读全文