在ESP32上实现WEB交互界面

本文主要介绍在未联网(AP热点)情况下实现WEB交互界面的CSS和javascript库。
在ESP32上,有时需要借助WebServer以及网页对外提供交互界面,如WiFi配置,GPIO显示和操作等,如何实现好看和易操作的界面就需要CSS和javascript了,如果是联网的情况可以直接访问外部资源,本文主要介绍在未联网(AP热点)情况下实现WEB交互界面的CSS和javascript库。

mini.css

mini.css是一个极简、响应式的CSS框架,许多组件和功能在资源受限的环境内可以达到与bootstrap类似的效果。
网格系统
mini.css 的网格系统利用 Flexbox 布局为您的 Web 应用程序提供简单、现代、响应式的布局系统。与大多数现代 CSS 框架的网格系统一样,它由三个主要组件组成——容器、行和列。

每个列类都是通过指定屏幕大小(小 - sm、中 - md 或大 - lg)和列宽(值介于 1 到 12 之间的值,或者对于流体列可以省略它),用破折号分隔(例如,.col-sm-6 表示小屏幕上的 6 宽列)。使用这些,您可以通过使用多个类更改列的宽度来为不同的屏幕尺寸应用不同的布局。

【小屏幕】

在ESP32上实现WEB交互界面

【中、大屏幕】

在ESP32上实现WEB交互界面

卡片

mini.css 提供了卡片 (.card),这是帮助你组织 Web 应用内容的常规用途容器。卡片应与网格系统结合使用,这意味着它们需要放置在网格的行内才能正常工作。使用卡片创建的布局是响应式的,会根据屏幕上的可用大小重新对齐。

在ESP32上实现WEB交互界面

表单和输入

表单、标签和常见的 HTML5 输入元素已使用干净、现代的规则进行样式设置,从而提高了 Web 应用程序表单的可访问性和可用性。

在ESP32上实现WEB交互界面

更多特性请访问:https://minicss.us/

Zepto.js

Zepto.js 是一个轻量级的 JavaScript 库,专为移动端网页设计。它提供了类似于 jQuery 的 API,但体积更小,性能更高,特别适合在移动设备上运行。
优势:
1.轻量级:Zepto.js 的压缩版只有几KB大小,非常适合移动端应用。
2.兼容性:它支持大多数现代移动浏览器,包括 iOS 和 Android 设备。

3.丰富的API:提供了丰富的 DOM 操作、事件处理、动画效果等功能。

示例代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Zepto.js 示例</title>
    <script src="https://cdn.jsdelivr.net/npm/zepto@1.2.0/dist/zepto.min.js"></script>
</head>
<body>
    <button id="myButton">点击我</button>

    <script>
        $(document).ready(function() {
            $('#myButton').on('click', function() {
                alert('按钮被点击了!');
            });
        });
    </script>
</body>
</html>

更多特性请访问:https://zeptojs.com/

实例

本实例是一个配置WiFi的例子

初始化程序先检查preferences里有无wifi配置,若没有,则开启AP热点,启动配置服务,等待配置,用手机连接热点并访问配置,设置ssid和密码,若成功连接则保存信息到preferences内。

/***************************************************
 **  ESP32 开启热点配置WIFI信息
 * 
 **  author: 老张
 **  homesite: www.xpstem.com
 ****************************************************/
#include <Arduino.h>
#include <WiFi.h>         
#include <WebServer.h>    
#include <Preferences.h> 

//************** 常数数据 开始 **********************
// AP模式下信息
const IPAddress wifi_ap_ip(192,168,5,1);
const IPAddress wifi_ap_gateway(192,168,6,0);
const IPAddress wifi_ap_subnet(255,255,255,0);
String wifi_ap_ssid = "ssid-";
const char *wifi_ap_password = "abcd1234";

// mini.css v3.0.1(替代bootstrap) https://minicss.us/
const char *minicss = "代码过多,此处省略...";

// Zepto v1.2.0(替代jQuery)https://zeptojs.com/
const char *zeptojs = "代码过多,此处省略...";
//*************** 常数数据 结束 **********************

//************* 配置变量 开始 ************************
// WiFi Credentials
String ssid;                            // wifi账号
String password;                        // wifi秘密
//************* 配置变量 结束 *********************

//************* 运行时变量 开始 ********************
WebServer webServer(80);
Preferences prefs;
String chipId;
//************* 运行时变量 结束 *********************

const char *configHtml = "<html>\
  <head>\
    <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>\
    <meta name='viewport' content='width=device-width, initial-scale=1'>\
    <link rel='stylesheet' href='/mini.css'>\
    <script src='/zepto.js'></script>\
    <script src='/layer.js'></script>\
    <title>xxx配置</title>\
  </head>\
  <body>\
    <h2>xxx配置</h2>\
    <p></p>\
    <form action='' method='post'>\
    <fieldset>\
      <legend>WiFi配置</legend>\
      <div class='row responsive-label'>\
        <div class='col-sm-12 col-md-3'><label for='ssid'>WiFi名称:</label></div>\
        <div class='col-sm-12 col-md'><input type='text' style='width:90%' name='ssid'></div>\
      </div>\
      <div class='row responsive-label'>\
        <div class='col-sm-12 col-md-3'><label for='password'>WiFi密码:</label></div>\
        <div class='col-sm-12 col-md'><input type='text' style='width:90%' name='password'></div>\
      </div>\
    </fieldset>\
    <p>&nbsp;</p>\
      <input type='button' id='submit' class='primary' value=' 提交 '>\
    </form>\
    <script type='text/javascript'>\
      $(document).ready(function() { \
        $('#submit').on('click', function() { \
          $.ajax({ \
            url: 'config', \
            type: 'post', \
            data: $('#form').serialize(), \
            success: function(resp) { \
              alert(resp.msg); \
            },\
            error: function(xhr, status, error) { \
              console.error('Error', error); \
            } \
          });\
        });\
      }); \
    </script>\
    </body>\
</html>";

/***********************************************************
 ***  检查是否保存了配置
 **********************************************************/ 
boolean comm_haveConfig() {
  prefs.begin("config", true);
  bool exist = prefs.isKey("ssid");
  prefs.end();
  return exist;
}

/***********************************************************
 ***  保存配置
 **********************************************************/ 
void conf_saveConfig() {
  prefs.begin("config", false);
  prefs.clear();

  prefs.putString("ssid", ssid);
  prefs.putString("password", password);
  
  prefs.end();
  
  Serial.println("配置已保存。");
}

/***********************************************************
 ***  配置页提交处理
 ***  1.连接WiFI
 ***  2.硬件信息设定
 **********************************************************/ 
void conf_handleConfig() {
  if (webServer.method() != HTTP_POST) {
    conf_handleNotFound();
    return;
  }
  
  String staSsid = webServer.arg("ssid");
  String staPassword = webServer.arg("password");

  if (staSsid=="" || staPassword=="") {
    // 未连接上。
    webServer.send(200, "application/json", "{\"code\":-1,\"msg\":\"内容不能为空!\"}");
    return;
  }

  // if ok
  Serial.printf("connect wifi %s testing.", ssid.c_str());
  WiFi.begin(staSsid, staPassword);
  int n = 0;
  // 最多等待60s
  while (++n < 600) {
      delay(100);
      if (n % 30 == 0) { Serial.print("."); }
      if (WiFi.status() == WL_CONNECTED) {
        break; //已连接,中止等待
      }
  }
  Serial.println("");
  
  if (WiFi.status() != WL_CONNECTED) {
    // 未连接上。
    webServer.send(200, "application/json", "{\"code\":-2,\"msg\":\"无法连接WiFi,请检查配置!\"}");
    return;
  }
  
  ssid = staSsid;
  password = staPassword;
    
  conf_saveConfig();

  // 配置成功
  webServer.send(200, "application/json", "{\"code\":0,\"msg\":\"产品配置成功,设备重启中...\"}");

  delay(3000);
  Serial.println("restarting...");
  ESP.restart();
}

/***********************************************************
 ***  404处理
 **********************************************************/ 
void conf_handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += webServer.uri();
  message += "\nMethod: ";
  message += (webServer.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += webServer.args();
  message += "\n";

  for (uint8_t i = 0; i < webServer.args(); i++) {
    message += " " + webServer.argName(i) + ": " + webServer.arg(i) + "\n";
  }

  webServer.send(404, "text/plain", message);
}

/********************************************************
 ***  启动配置服务
 ********************************************************/
void conf_startConfigServer() {

    wifi_ap_ssid = "ssid-" + chipId.substring(0, 3);
    WiFi.softAPConfig(wifi_ap_ip, wifi_ap_gateway, wifi_ap_subnet);
    if (!WiFi.softAP(wifi_ap_ssid.c_str(), wifi_ap_password)) {
      Serial.println("Soft AP creation failed.");
      while(1);
    }
    Serial.printf("Wifi AP %s on 192.168.6.1\n", wifi_ap_ssid.c_str());
    
    webServer.on("/", [](){ webServer.send(200, "text/html", configHtml); });
    webServer.on("/mini.css", [](){ webServer.send(200, "text/css", minicss); });
    webServer.on("/zepto.js", [](){ webServer.send(200, "text/javascript", zeptojs); });
    webServer.on("/config", conf_handleConfig);
    webServer.onNotFound(conf_handleNotFound);
    webServer.begin();
    Serial.println("HTTP server started");
}

/***********************************************************
 ***  获取芯片ID
 **********************************************************/
String comm_getChipId() {
  uint64_t number = ESP.getEfuseMac();
  unsigned long long1 = (unsigned long)((number & 0xFFFF0000) >> 16);
  unsigned long long2 = (unsigned long)((number & 0x0000FFFF));

  return String(long1, HEX) + String(long2, HEX);
}

/***********************************************************
 ***  读取配置
 **********************************************************/ 
void main_loadConfig() {
  prefs.begin("config", true);
  
  ssid = prefs.getString("ssid");
  password = prefs.getString("password");
  
  prefs.end();
}

/***********************************************************
 ***  连接到WiFi
 **********************************************************/ 
void main_connectToWiFi() {

    WiFi.begin(ssid, password);
    Serial.print("正在连接 WiFi");
    while (WiFi.status() != WL_CONNECTED) {
        delay(100);
        Serial.print(".");
    }
    Serial.print("\n连接到 WiFi, localIP: ");
    Serial.println(WiFi.localIP());
}

/***********************************************************
 ***  初始化设置
 **********************************************************/
void setup() {
  Serial.begin(115200);
  Serial.println();

  chipId = comm_getChipId();
  Serial.printf("ChipId:%s\n", chipId);

  if (!comm_haveConfig()) {
    Serial.println("未发现配置,启动配置程序。");
    WiFi.mode(WIFI_AP);
    conf_startConfigServer();
    Serial.println("等待配置...");
    return;
  }

  Serial.println("发现配置,启动主程序。");
  
  main_loadConfig();
 
  main_connectToWiFi();
}

/***********************************************************
 ***  主循环
 **********************************************************/
void loop() {
  
  if (!comm_haveConfig()) {
    webServer.handleClient();
    delay(10);
    return;
  }

  Serial.println("主代码开始执行...");
  // your main code.
  
  delay(1000); //1s
} 

代码中minicss和zeptojs字符数组内容请替换成对应文件内容。

运行效果

在ESP32上实现WEB交互界面

- 本文由用户 老张 发布,文中观点仅代表作者本人,不代表本站立场。
- 如需转载,请联系作者;如有侵权,请联系本站处理。

07-29   阅读(110)   评论(0)
 标签: 创客 ESP32

涨知识
欧姆定律

欧姆定律是指在同一电路中,通过某段导体的电流跟这段导体两端的电压成正比,跟这段导体的电阻成反比。该定律是由德国物理学家乔治·西蒙·欧姆1826年4月发表的《金属导电定律的测定》论文提出的。

评论:
相关文章
物联网项目开发实战-第3章-自动浇花项目迭代3

本节我们在迭代二的基础上使用四位数码管和OLED显示屏显示相关交互信息。


物联网项目开发实战-第3章-自动浇花项目迭代2

本节我们在迭代一的基础上增加采集土壤湿度数据,并根据湿度数据来决定是否自动进行浇水动作。


物联网项目开发实战-第3章-自动浇花项目迭代1

本节我们实现一个基本能工作的手动浇水装置,即通过按下按键来闭合继发器让小水泵进行浇水。


物联网项目开发实战-第2章-开发环境

本小节通过点亮LED和串口输出两个程序,来初步掌握ArduinoIDE、了解GPIO和串口使用、同时把开发环境与开发板的连接,上传程序的各环节跑通,


小鹏浇花套件单机版程序V1.0.0

本程序是小鹏物联网智能浇花套件的单机版程序(不连接物联网),供同学们参考。


ESP32 的中断机制和处理

本文介绍ESP32中的中断机制,以及如何通过GPIO中断实现按钮控制。重点讲解了如何设置中断服务例程、处理中断抖动问题,并提供了消除中断抖动的示例代码。


Arduino ESP32获取芯片、RAM信息

本文介绍如何使用Arduino-ESP32库中的API函数获取ESP32的芯片、RAM信息等,并提供了一个示例程序代码。


ESP32 FreeRTOS 双核使用

ESP32系列(包括ESP32-S3)搭载Xtensa双核处理器,默认情况下Arduino框架仅使用单核运行用户代码,通过多核编程,可以充分利用硬件资源来提升系统响应和性能。


ESP32 GPIO 矩阵和引脚多路复用

ESP32 芯片有34个物理GPIO管脚。每个GPIO管脚都可用作一个通用IO,或连接一个内部的外设信号。IO_MUX ¹、RTC IO MUX 和GPIO交换矩阵用于将信号从外设传输至GPIO管脚。


ESP32Encoder:高效的ESP32旋转编码器库

ESP32Encoder库是一个利用ESP32脉冲计数器硬件外设实现高效旋转编码器读取的软件库。