You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Flutter的水位监测APP与ESP模块数据交互方案问询

水位监测IoT APP开发求助

我正在Android Studio(Hedgehog版本)中使用Flutter开发一款水位监测APP,该项目属于IoT范畴。已完成水位指示器的UI设计,了解Blynk APP,希望打造一款匹配自定义UI、类似Blynk的APP实现水位监测。我具备ESP模块控制LED和继电器的经验,当前核心需求是实现ESP模块与移动端APP之间的数据收发。曾参考过IoT平台搭建教程,但因ESP和云技术基础薄弱难以理解,现寻求清晰的行动路线指导。

我的UI代码

import 'package:flutter/material.dart';

class RowWithTwoColumns extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          body: Container(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: [
                    Transform.translate(
                      offset: const Offset(20, 40),
                      child:
                          //Image.asset('assets/images/blue_1.png',width: 30,),),
                          Image.asset(
                            'assets/images/blue_1.png',
                            width: 30,
                          ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 80),
                      child: Image.asset(
                        'assets/images/green.png',
                        width: 30,
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 120),
                      child: Image.asset(
                        'assets/images/green.png',
                        width: 30,
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 160),
                      child: Image.asset(
                        'assets/images/green.png',
                        width: 30,
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 200),
                      child: Image.asset(
                        'assets/images/red_1.png',
                        width: 30,
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 240),
                      child: Image.asset(
                        'assets/images/red_1.png',
                        width: 30,
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 280),
                      child: Image.asset(
                        'assets/images/red_1.png',
                        width: 30,
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 300),
                      child: Text(
                        'WATER\nLEVEL',
                        textAlign: TextAlign.center,
                      ),
                    ),
                  ],
                ),
                Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: [
                    Transform.translate(
                      offset: const Offset(20, 40),
                      child: Text(
                        '100 %',
                        style: TextStyle(fontSize: 20),
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 80),
                      child: Text(
                        '90 %',
                        style: TextStyle(fontSize: 20),
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 123),
                      child: Text(
                        '75 %',
                        style: TextStyle(fontSize: 20),
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 163),
                      child: Text(
                        '60 %',
                        style: TextStyle(fontSize: 20),
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 204),
                      child: Text(
                        '45 %',
                        style: TextStyle(fontSize: 20),
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 243),
                      child: Text(
                        '30 %',
                        style: TextStyle(fontSize: 20),
                      ),
                    ),
                    Transform.translate(
                      offset: const Offset(20, 285),
                      child: Text(
                        '15 %',
                        style: TextStyle(fontSize: 20),
                      ),
                    ),
                  ],
                ),
                Column(mainAxisAlignment: MainAxisAlignment.start, children: [
                  Transform.translate(
                    offset: const Offset(0, 32),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 55),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 75),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 98),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 120),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 137),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 162),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 175),
                    child: Text(
                      'ON \n NOTIFY',
                      textAlign: TextAlign.center,
                    ),
                  ),
                ]),
                Column(mainAxisAlignment: MainAxisAlignment.start, children: [
                  Transform.translate(
                    offset: const Offset(0, 32),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 55),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 75),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 98),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 120),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 137),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 162),
                    child: SwitchScreen(),
                  ),
                  Transform.translate(
                    offset: const Offset(0, 175),
                    child: Text(
                      'OFF \n NOTIFY',
                      textAlign: TextAlign.center,
                    ),
                  ),
                ]),
              ],
            ),
          ),
          appBar: AppBar(
            title: Text(
              'Water Level Indicator',
              style: TextStyle(color: Colors.white),
            ),
            backgroundColor: Colors.blueAccent,
            centerTitle: true,
          )),
    );
  }
}

class SwitchScreen extends StatefulWidget {
  @override
  SwitchClass createState() => new SwitchClass();
}
class SwitchClass extends State {
  bool isSwitched = false;

  void toggleSwitch(bool value) {
    if (isSwitched == false) {
      setState(() {
        isSwitched = true;
      });
    } else {
      setState(() {
        isSwitched = false;
      });
    }
  }
  @override
  Widget build(BuildContext context) {
    return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
      Transform.scale(
          scale: 1,
          child: Switch(
            onChanged: toggleSwitch,
            value: isSwitched,
          )),
    ]);
  }
}

UI截图

水位指示器UI


行动路线指导

一、先从本地WiFi通信入手(避开复杂云平台)

因为你ESP基础薄弱,优先选局域网HTTP通信,不需要云服务器,代码简单易调试,能快速跑通数据收发:

  • ESP作为WiFi服务器,提供数据接口;Flutter APP作为客户端,调用接口获取水位、发送控制指令

二、ESP端开发步骤

  1. 配置WiFi连接
    用Arduino IDE编写代码,让ESP连接到你的家庭WiFi,获取局域网IP:

    #include <WiFi.h>
    const char* ssid = "你的WiFi名称";
    const char* password = "WiFi密码";
    
    void setup() {
      Serial.begin(115200);
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }
      Serial.println("\nWiFi连接成功,IP地址:");
      Serial.println(WiFi.localIP()); // 记录这个IP,APP要用到
    }
    
    void loop() {}
    
  2. 搭建HTTP服务器,实现数据接口
    添加WebServer库,实现两个核心接口:

    • /getWaterLevel:返回当前水位数据
    • /setSwitch:接收APP的开关控制指令
      示例代码(ESP32):
    #include <WiFi.h>
    #include <WebServer.h>
    
    const char* ssid = "你的WiFi名称";
    const char* password = "WiFi密码";
    WebServer server(80);
    
    // 模拟水位数据,实际替换为传感器读数
    int waterLevel = 50;
    
    // 处理水位请求
    void handleGetWaterLevel() {
      server.send(200, "text/plain", String(waterLevel));
    }
    
    // 处理开关指令
    void handleSetSwitch() {
      if (server.hasArg("state")) {
        String state = server.arg("state");
        // 这里根据state控制继电器/LED,比如state=="1"触发开操作
        Serial.print("收到开关指令:");
        Serial.println(state);
        server.send(200, "text/plain", "success");
      } else {
        server.send(400, "text/plain", "缺少state参数");
      }
    }
    
    void setup() {
      Serial.begin(115200);
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
      }
      Serial.println("\nWiFi连接成功,IP地址:");
      Serial.println(WiFi.localIP());
    
      // 注册接口
      server.on("/getWaterLevel", handleGetWaterLevel);
      server.on("/setSwitch", handleSetSwitch);
      server.begin();
      Serial.println("HTTP服务器已启动");
    }
    
    void loop() {
      server.handleClient();
      // 模拟水位变化,实际替换为传感器读取逻辑
      waterLevel = random(10, 100);
      delay(1000);
    }
    
  3. 接入水位传感器
    将代码中的模拟水位数据替换为实际传感器(如超声波、液位传感器)的读数,确保传感器数据能正常输出到串口。

三、Flutter APP端开发步骤

  1. 添加网络依赖
    在pubspec.yaml中添加http包:

    dependencies:
      flutter:
        sdk: flutter
      http: ^1.1.0
    

    执行flutter pub get安装依赖。

  2. 实现网络请求逻辑
    添加工具函数,用于获取水位和发送开关指令:

    import 'package:http/http.dart' as http;
    
    // 获取水位数据
    Future<int> fetchWaterLevel(String espIp) async {
      try {
        final response = await http.get(Uri.parse('http://$espIp/getWaterLevel'));
        if (response.statusCode == 200) {
          return int.parse(response.body);
        }
      } catch (e) {
        print("获取水位失败:$e");
      }
      return 0;
    }
    
    // 发送开关指令
    Future<void> sendSwitchCommand(String espIp, bool isOn) async {
      try {
        final state = isOn ? "1" : "0";
        await http.get(Uri.parse('http://$espIp/setSwitch?state=$state'));
      } catch (e) {
        print("发送指令失败:$e");
      }
    }
    
  3. 关联UI与网络逻辑

    • 将RowWithTwoColumns改为StatefulWidget,以便更新UI状态
    • 在initState中启动定时任务,每隔2-3秒获取一次水位数据,更新UI上的百分比和指示灯
    • 修改SwitchClass的toggleSwitch方法,调用sendSwitchCommand发送指令

四、测试与调试

  1. 确保手机和ESP连接到同一WiFi网络
  2. 上传ESP代码,打开串口监视器确认WiFi连接成功并记录IP
  3. 运行Flutter APP,替换代码中的ESP IP,测试水位显示和开关控制功能
  4. 若连接失败,检查IP是否正确、WiFi信号是否稳定、设备是否被防火墙拦截

五、进阶远程通信(可选)

当本地通信跑通后,如需远程监控,推荐使用现成IoT云平台(比自建简单):

  • 可选平台:阿里云IoT、腾讯云IoT、ThingSpeak
  • 核心步骤:
    1. 在云平台创建设备,获取设备证书
    2. ESP端接入云平台SDK,上报水位数据
    3. Flutter APP通过云平台API获取数据、发送控制指令

内容的提问来源于stack exchange,提问作者Xeyad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 08:24:57