#include #include #include // 创建 M5SwitchC6 实例,最大节点数量为16,可自定义大小 / Create M5SwitchC6 instance with max peers set to 16, customizable size M5SwitchC6 switchC6(16); //填入你的SwitchC6设备MAC地址 / Fill in your SwitchC6 device MAC address const String DEVICE_MAC = "E4B3-2386-18C4"; //是否启用wifi连接 / Enable Wi-Fi connection // #define USE_WIFI #ifdef USE_WIFI const char *ssid = "your_ssid"; const char *password = "your_password"; #endif // 等待动画控制变量 / Control flag for waiting animation volatile bool showAnimation = false; TaskHandle_t animationTaskHandle = NULL; SemaphoreHandle_t displayMutex = NULL; // 等待动画任务 / Waiting animation task void animationTask(void *parameter) { while (true) { if (showAnimation) { // 获取互斥锁 / Acquire mutex if (xSemaphoreTake(displayMutex, pdMS_TO_TICKS(50)) == pdTRUE) { int centerX = M5.Display.width() / 2; int centerY = M5.Display.height() / 2; static int dots = 1; // 清除点号区域 - 只清除动画区域 / Clear dots area (only the animation region) M5.Display.fillRect(centerX - 30, centerY, 60, 20, BLACK); // 显示当前数量的点号 / Draw current number of dots M5.Display.setCursor(centerX - dots * 6, centerY); M5.Display.setTextColor(WHITE); for (int i = 0; i < dots; i++) { M5.Display.print("."); } // 释放互斥锁 / Release mutex xSemaphoreGive(displayMutex); dots++; if (dots > 5) { dots = 1; } } vTaskDelay(200 / portTICK_PERIOD_MS); // 200ms延迟 / 200 ms delay } else { // 清除动画时也需要获取锁 / Also need the lock when clearing animation if (xSemaphoreTake(displayMutex, pdMS_TO_TICKS(50)) == pdTRUE) { int centerX = M5.Display.width() / 2; int centerY = M5.Display.height() / 2 + 20; // 清除动画区域 / Clear animation region M5.Display.fillRect(centerX - 30, centerY - 10, 60, 30, BLACK); xSemaphoreGive(displayMutex); } vTaskDelay(100 / portTICK_PERIOD_MS); } } } // 开始等待动画 / Start waiting animation void startWaitingAnimation() { showAnimation = true; } // 停止等待动画 / Stop waiting animation void stopWaitingAnimation() { showAnimation = false; } void setup() { Serial.begin(115200); M5.begin(); // M5.Display.setRotation(1); M5.Display.setTextSize(2); WiFi.mode(WIFI_STA); #ifdef USE_WIFI // 连接到Wi-Fi网络 / Connect to Wi-Fi network WiFi.begin(ssid, password); if (WiFi.status() != WL_CONNECTED) { Serial.print("Connecting to Wi-Fi"); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(500); } Serial.println("\nConnected to Wi-Fi!"); // 串口打印连接信息 / Print connection info to serial Serial.printf("SSID: %s\r\n", WiFi.SSID().c_str()); Serial.printf("IP Address: %s\r\n", WiFi.localIP().toString().c_str()); Serial.printf("MAC Address: %s\r\n", WiFi.macAddress().c_str()); Serial.printf("Channel: %d\r\n", WiFi.channel()); } else { Serial.println("Already connected to Wi-Fi."); } #else WiFi.begin(); switchC6.setChannel(11); // 设置信道为11 / Set channel to 11 #endif M5.Display.clear(); // 创建互斥锁 / Create mutex displayMutex = xSemaphoreCreateMutex(); // 创建canvas,使用正确的屏幕尺寸 / Prepare display with proper size M5.Display.fillScreen(BLACK); M5.Display.setTextSize(2); M5.Display.setTextColor(WHITE); M5.Display.setCursor(10, 10); M5.Display.println("M5Switchc6 Controller"); if (switchC6.begin()) { M5.Display.setCursor(10, 60); M5.Display.println("A:ON B:OFF C:STATUS"); } // 创建等待动画任务 / Create waiting animation task xTaskCreate( animationTask, // 任务函数 / Task function "AnimationTask", // 任务名称 / Task name 4096, // 增加堆栈大小 / Stack size NULL, // 参数 / Parameter 1, // 优先级 / Priority &animationTaskHandle // 任务句柄 / Task handle ); } void loop() { M5.update(); // 先定义按钮变量,在互斥锁外部 / Define button layout variables outside the mutex int screenWidth = M5.Display.width(); int screenHeight = M5.Display.height(); int buttonWidth = screenWidth / 3; int buttonHeight = 40; int buttonY = screenHeight - buttonHeight; int button3Width = screenWidth - buttonWidth * 2; // 剩余宽度 / Remaining width // 获取互斥锁后再操作canvas / Lock before drawing on the display if (xSemaphoreTake(displayMutex, pdMS_TO_TICKS(100)) == pdTRUE) { // 绘制3个按钮 / Draw 3 buttons // 按钮A - ON / Button A - ON M5.Display.fillRect(0, buttonY, buttonWidth, buttonHeight, GREEN); M5.Display.drawRect(0, buttonY, buttonWidth, buttonHeight, WHITE); M5.Display.setTextColor(BLACK); M5.Display.setCursor(buttonWidth/2 - 10, buttonY + 15); M5.Display.println("ON"); // 按钮B - OFF / Button B - OFF M5.Display.fillRect(buttonWidth, buttonY, buttonWidth, buttonHeight, RED); M5.Display.drawRect(buttonWidth, buttonY, buttonWidth, buttonHeight, WHITE); M5.Display.setTextColor(WHITE); M5.Display.setCursor(buttonWidth + buttonWidth/2 - 15, buttonY + 15); M5.Display.println("OFF"); // 按钮C - STATUS(使用计算后的宽度避免溢出) / Button C - STATUS (use computed width to avoid overflow) M5.Display.fillRect(buttonWidth * 2, buttonY, button3Width, buttonHeight, BLUE); M5.Display.drawRect(buttonWidth * 2, buttonY, button3Width, buttonHeight, WHITE); M5.Display.setTextColor(WHITE); M5.Display.setCursor(buttonWidth * 2 + button3Width/2 - 25, buttonY + 15); M5.Display.println("STATUS"); M5.Display.setTextColor(WHITE); // 恢复默认文本颜色 / Restore default text color // 释放互斥锁 / Release mutex xSemaphoreGive(displayMutex); } bool deviceOn = false; bool deviceOff = false; bool deviceStatus = false; // 检测物理按键(适用于有按键的设备) / Handle physical buttons (if available) if (M5.BtnA.wasPressed()) { deviceOn = true; } if (M5.BtnB.wasPressed()) { deviceOff = true; } if (M5.BtnC.wasPressed()) { deviceStatus = true; } // 检测触摸事件(适用于有触摸屏的设备) / Handle touch events (if touch screen) auto touch = M5.Touch.getDetail(); if (touch.wasPressed()) { int touchX = touch.x; int touchY = touch.y; // 检查是否点击了按钮区域 / Check whether a button area is tapped if (touchY >= buttonY) { if (touchX < buttonWidth) { deviceOn = true; } else if (touchX < buttonWidth * 2) { deviceOff = true; } else { deviceStatus = true; } } } // 执行相应的设备控制逻辑 / Execute device control logic if (deviceOn) { // 开始等待动画 / Start waiting animation startWaitingAnimation(); // 开启设备 / Turn device ON if (switchC6.sendSwitchCommandWithResponse(DEVICE_MAC, true, 10000)) { Serial.println("开启命令发送成功"); // ON command sent successfully stopWaitingAnimation(); // 获取互斥锁后更新状态显示 / Update status display with mutex if (xSemaphoreTake(displayMutex, pdMS_TO_TICKS(100)) == pdTRUE) { M5.Display.fillRect(10, 160, 300, 30, BLACK); M5.Display.setCursor(10, 160); M5.Display.setTextColor(WHITE); M5.Display.println("Device ON sent"); xSemaphoreGive(displayMutex); } } else { Serial.println("开启命令发送失败"); // Failed to send ON command stopWaitingAnimation(); if (xSemaphoreTake(displayMutex, pdMS_TO_TICKS(100)) == pdTRUE) { M5.Display.fillRect(10, 160, 300, 30, BLACK); M5.Display.setCursor(10, 160); M5.Display.setTextColor(WHITE); M5.Display.println("Device ON failed"); xSemaphoreGive(displayMutex); } } } if (deviceOff) { // 开始等待动画 / Start waiting animation startWaitingAnimation(); // 关闭设备 / Turn device OFF if (switchC6.sendSwitchCommandWithResponse(DEVICE_MAC, false, 10000)) { Serial.println("关闭命令发送成功"); // OFF command sent successfully stopWaitingAnimation(); if (xSemaphoreTake(displayMutex, pdMS_TO_TICKS(100)) == pdTRUE) { M5.Display.fillRect(10, 160, 300, 30, BLACK); M5.Display.setCursor(10, 160); M5.Display.setTextColor(WHITE); M5.Display.println("Device OFF sent"); xSemaphoreGive(displayMutex); } } else { Serial.println("关闭命令发送失败"); // Failed to send OFF command stopWaitingAnimation(); if (xSemaphoreTake(displayMutex, pdMS_TO_TICKS(100)) == pdTRUE) { M5.Display.fillRect(10, 160, 300, 30, BLACK); M5.Display.setCursor(10, 160); M5.Display.setTextColor(WHITE); M5.Display.println("Device OFF failed"); xSemaphoreGive(displayMutex); } } } if (deviceStatus) { // 开始等待动画 / Start waiting animation startWaitingAnimation(); // 查询状态 / Query status SwitchC6ParsedData_t response; if (switchC6.sendStatusQueryWithResponse(DEVICE_MAC, 10000, &response)) { Serial.printf("设备状态: %s, 电压: %.2fV\n", response.switchState ? "开启" : "关闭", response.voltage); // Device status and voltage stopWaitingAnimation(); if (xSemaphoreTake(displayMutex, pdMS_TO_TICKS(100)) == pdTRUE) { M5.Display.fillRect(10, 160, 300, 30, BLACK); M5.Display.setCursor(10, 160); M5.Display.setTextColor(WHITE); M5.Display.printf("S:%s V:%.2fV", response.switchState ? "ON" : "OFF", response.voltage); xSemaphoreGive(displayMutex); } } else { stopWaitingAnimation(); if (xSemaphoreTake(displayMutex, pdMS_TO_TICKS(100)) == pdTRUE) { M5.Display.fillRect(10, 160, 300, 30, BLACK); M5.Display.setCursor(10, 160); M5.Display.setTextColor(WHITE); M5.Display.println("Query failed"); xSemaphoreGive(displayMutex); } } } delay(20); // 减少循环频率,降低系统负载 / Slow down the loop to reduce load }