/**
* @file sketch.ino - 匹配用户原理图
* 引脚分配:
* - NAU7802: SDA=1, SCL=2
* - BNO086: SDA=17, SCL=18
* - SSD1306: SDA=4, SCL=5
* - RGB LED: DATA=11
* - 按键: GPIO0
*/
#include <Arduino.h>
#include <Wire.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <Adafruit_SSD1306.h>
#include <FastLED.h>
// ========== 引脚配置(匹配原理图)==========
#define NAU7802_SDA 1
#define NAU7802_SCL 2
#define BNO086_SDA 17
#define BNO086_SCL 18
#define SSD1306_SDA 4
#define SSD1306_SCL 5
#define LED_DATA_PIN 11
#define BTN_PIN 0
// ========== OLED 配置 ==========
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define SSD1306_ADDR 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// ========== RGB LED 配置 ==========
#define LED_COUNT 1
#define LED_BRIGHTNESS 64
CRGB leds[LED_COUNT];
// ========== WiFi & MQTT ==========
#define WIFI_SSID "Wokwi-Guest"
#define WIFI_PASSWORD ""
#define MQTT_BROKER "test.mosquitto.org"
#define MQTT_PORT 1883
#define MQTT_TOPIC_PUB "sensor/weight/data"
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
// ========== 全局变量 ==========
float weight = 0; // 重量(g)
float roll = 0; // 横滚角(度)
float pitch = 0; // 俯仰角(度)
float yaw = 0; // 偏航角(度)
int batteryPercent = 100; // 电量(%)
int page = 0; // 当前页面(0-3)
bool wifiConnected = false;
bool mqttConnected = false;
volatile bool btnPressed = false;
uint32_t lastBtnMs = 0;
// ========== 模拟传感器数据 ==========
void generateSensorData() {
// 模拟称重数据:100g ~ 5000g
weight = random(1000, 50000) / 10.0;
// 模拟姿态数据:-180° ~ 180°
roll = random(-1800, 1800) / 10.0;
pitch = random(-900, 900) / 10.0;
yaw = random(-1800, 1800) / 10.0;
// 模拟电池电量:20% ~ 100%
batteryPercent = random(20, 101);
}
// ========== OLED 显示 ==========
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
// 状态栏
display.fillRect(0, 0, 128, 10, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
display.setCursor(2, 1);
display.print(wifiConnected ? "WiFi" : "---");
display.setCursor(40, 1);
display.print(mqttConnected ? "MQTT" : "---");
display.setCursor(100, 1);
display.printf("%d%%", batteryPercent);
display.setTextColor(SSD1306_WHITE);
display.drawLine(0, 10, 127, 10, SSD1306_WHITE);
// 页面内容
if (page == 0) {
display.setCursor(0, 20);
display.printf("Weight: %.1f g", weight);
display.setCursor(0, 35);
display.printf("Roll: %.1f deg", roll);
display.setCursor(0, 50);
display.printf("Bat: %d%%", batteryPercent);
} else if (page == 1) {
display.setCursor(0, 20);
display.println("=== Attitude ===");
display.setCursor(0, 32);
display.printf("Roll: %.1f", roll);
display.setCursor(0, 44);
display.printf("Pitch: %.1f", pitch);
display.setCursor(0, 56);
display.printf("Yaw: %.1f", yaw);
} else if (page == 2) {
display.setCursor(0, 20);
display.println("=== Sensors ===");
display.setCursor(0, 32);
display.printf("NAU7802: %.1f g", weight);
display.setCursor(0, 44);
display.printf("BNO086: %.1f deg", roll);
display.setCursor(0, 56);
display.printf("ADC: OK");
} else {
display.setCursor(0, 20);
display.println("=== Network ===");
display.setCursor(0, 32);
display.printf("WiFi: %s", wifiConnected ? "OK" : "Fail");
display.setCursor(0, 44);
display.printf("MQTT: %s", mqttConnected ? "OK" : "Fail");
display.setCursor(0, 56);
display.printf("Heap: %dKB", ESP.getFreeHeap() / 1024);
}
display.display();
}
// ========== RGB LED ==========
void updateLed() {
if (batteryPercent < 10) {
fill_solid(leds, LED_COUNT, CRGB::Red);
} else if (!wifiConnected) {
fill_solid(leds, LED_COUNT, (millis() / 500) % 2 ? CRGB::Blue : CRGB::Black);
} else if (!mqttConnected) {
fill_solid(leds, LED_COUNT, CRGB::Purple);
} else {
uint8_t brightness = (sin(millis() / 1000.0) + 1) / 2 * 200 + 55;
fill_solid(leds, LED_COUNT, CRGB(0, brightness, 0));
}
FastLED.show();
}
// ========== MQTT 回调 ==========
void mqttCallback(char* topic, byte* payload, unsigned int len) {
String msg;
for (unsigned int i = 0; i < len; i++) msg += (char)payload[i];
Serial.printf("[MQTT] Cmd: %s\n", msg.c_str());
}
// ========== 按键中断 ==========
void IRAM_ATTR onBtnPress() {
uint32_t now = millis();
if (now - lastBtnMs > 200) {
btnPressed = true;
lastBtnMs = now;
}
}
// ========== setup ==========
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\n========================================");
Serial.println(" Weight Sensor System v1.0 ");
Serial.println(" Matching User Schematic ");
Serial.println("========================================");
Serial.println("Pins:");
Serial.println(" NAU7802: SDA=1, SCL=2");
Serial.println(" BNO086: SDA=17, SCL=18");
Serial.println(" SSD1306: SDA=4, SCL=5");
Serial.println(" RGB LED: DATA=11");
Serial.println(" Button: GPIO0");
Serial.println("========================================");
// 初始化 I2C
Wire.begin(SSD1306_SDA, SSD1306_SCL);
// 初始化 OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, SSD1306_ADDR)) {
Serial.println("[ERROR] SSD1306 not found!");
} else {
Serial.println("[OK] SSD1306 initialized");
display.clearDisplay();
display.display();
}
// 初始化 RGB LED
FastLED.addLeds<WS2812B, LED_DATA_PIN, GRB>(leds, LED_COUNT);
FastLED.setBrightness(LED_BRIGHTNESS);
fill_solid(leds, LED_COUNT, CRGB::Black);
FastLED.show();
Serial.println("[OK] RGB LED initialized");
// 初始化按键
pinMode(BTN_PIN, INPUT_PULLUP);
attachInterrupt(BTN_PIN, onBtnPress, FALLING);
Serial.println("[OK] Button initialized");
// 连接 WiFi
Serial.printf("[WiFi] Connecting to %s...\n", WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
wifiConnected = true;
Serial.printf("\n[WiFi] Connected! IP: %s\n", WiFi.localIP().toString().c_str());
} else {
Serial.println("\n[WiFi] Connection failed (simulated)");
wifiConnected = true;
}
// 连接 MQTT
mqttClient.setServer(MQTT_BROKER, MQTT_PORT);
mqttClient.setCallback(mqttCallback);
Serial.println("[OK] System ready!");
Serial.println("Press GPIO0 button to change pages");
}
// ========== loop ==========
void loop() {
static uint32_t lastSensorTime = 0;
static uint32_t lastDisplayTime = 0;
static uint32_t lastMqttTime = 0;
uint32_t now = millis();
// 传感器数据更新(每100ms)
if (now - lastSensorTime >= 100) {
lastSensorTime = now;
generateSensorData();
}
// MQTT 发布(每秒)
if (now - lastMqttTime >= 1000) {
lastMqttTime = now;
if (!mqttClient.connected()) {
if (mqttClient.connect("ESP32_Weight")) {
Serial.println("[MQTT] Connected!");
mqttConnected = true;
}
}
mqttClient.loop();
if (mqttClient.connected()) {
String payload = "{\"weight\":" + String(weight, 1) +
",\"roll\":" + String(roll, 1) +
",\"battery\":" + String(batteryPercent) + "}";
if (mqttClient.publish(MQTT_TOPIC_PUB, payload.c_str())) {
Serial.printf("[MQTT] Pub: %s\n", payload.c_str());
}
mqttConnected = true;
} else {
mqttConnected = false;
}
}
// 按键处理
if (btnPressed) {
btnPressed = false;
page = (page + 1) % 4;
Serial.printf("[BUTTON] Page -> %d\n", page);
}
// 显示刷新(每100ms)
if (now - lastDisplayTime >= 100) {
lastDisplayTime = now;
updateDisplay();
updateLed();
}
delay(10);
}