电子网首页 > 开源与设计

【ArduinoGIGAR1WiFi】基于stcc4的网络环境监测系统

2026-02-07 01:10 | 来源:电子世界报

【前言】

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(" &deg;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模组,获取环境信息如下:

image.png

同时用电脑打开网页:

image.png

【总结】

此次实现,基于arduino的生态,可以快速的实现环境的网络监测!。

阅读全文

推荐技术

返回顶部