ESP32 M5 超级问卷星:轻松收集数据

b7f76c86fa762887f9f81b94a4b54e3d.png

1cfc11574ee4187e8b7fc69829511ce9.png

作者:默

不知不觉疫情已持续 3 年了,最近门卫王大爷很苦恼,因为领导要求所有进入单位的人员与车辆按照防疫要求进行登记,登记的内容有来访人员的车牌号、姓名、性别、体温、联系电话、被访人及其进入单位的时间 等。大爷年纪大了,有些时候记性也不太好,登记时经常会卡壳,每次都要向来访人员询问老半天,后面来访的人员看着不知所措的王大爷也有些无奈,毕竟大爷就是你大爷,又不能催促,只能老老实实排队等待了。大爷自己也发现了这个问题,于是就找到我,想让我给他出出主意,想一个好办法来解决他现在的问题,提高他的工作效率。

在一些小区和医院等场合,笔者也发现了和王大爷相似的情况:

  • 小区保安需要登记来访人员与车辆;

  • 医院服务的大白与志愿者们需要登记来院所有人员的家庭住址、身份证号、近期外出情况等信息;

  • 临近假期,某些大学辅导员们也要登记学生假期安排情况(留校或回家)。

这些问题都有一个共同的特点:那就是都属于表格统计,仅有简单的文本类信息,传统的这类信息录入都是靠打印纸质表格由相关人员逐一填写,费时费力,遇到问题需要查找某个人员相关信息时,都要从大量的纸质表格中去检索信息,这种古老而传统的方式在数字化的今天无疑是十分落后的。作为实用型 Maker 的我,针对这些痛点问题,我开发了“M5 超级问卷星”这个项目,通过在线的方式将数据进行录入,并且支持在线搜索与查看,以及将数据导出为 Excel 表格。

下面让我们先来看看这个项目的演示视频。该视频中,我们以王大爷的需求为例:

M5 超级问卷星名称的由来

M5 来源于基于 ESP32 的 M5core2 开发板,问卷星代表它是一个问卷调查表格统计类设备,正如前几期分享的 M5 Server X 一样,它们同属于网络服务器的范畴,故名 M5 超级问卷星。运用 M5 超级问卷星,我们能够将任何文本类数据进行在线收集,并在线查看与导出 Excel 表格,例如防疫登记表、离校意向表、商品出售清单等,农林或生物系学生可以将它用来记录一些对照试验的表格,当然 M5 超级问卷星能做的还有很多,只要有表格的地方都会有它的用武之地。

预期目标及功能

  • 在线提交表单数据

  • 数据提交反馈

  • 在线数据查看

  • 在线数据检索

  • 在线数据导出为 Excel 表格

  • SD 卡配置文件加载

  • 二维码显示服务网址

所用硬件

  • M5 Core2

aa825802c38e32eb8a845ba62cbd68ff.png

M5Core2特点

  • 基于 ESP32 开发,支持 WiFi、蓝牙;

  • 16 M 闪存,8 M PSRAM;

  • 内置扬声器,电源指示灯,震动马达,RTC,I2S 功放,电容式触摸屏,电源键,复位键;

  • TF 卡插槽(支持最大 16GB);

  • 内置锂电池,配备电源管理芯片;

  • 独立小板内置 6 轴 IMU,PDM 麦克风;

  • M-Bus 总线插座。

程序设计

下面开始详细讲解程序设计过程。

开发环境

我们使用 Aduino IDE 软件来编写本项目的程序,开发板选择 M5Stack-Core2。至于如何在 Arduino 中配置 ESP32 的开发环境,不在本文的介绍范围,请自行查阅相关资料。

程序思路

为了达到我们的预期目标,我们先绘制功能的思维导图,再根据思维导图逐步实现 M5 超级问卷星的程序设计。

ad647e8792f93ff9dd6f08c8cc933cdc.png

下面我们将具体讨论 M5 超级问卷星各个子功能是如何实现的。

获取提交参数

在前期 M5 Server  X 项目中我们采用构造路径参数的方式来区分每个服务,本期我们将采用请求参数的方式获取来自网页表单的数据,示例如下。

#include <WiFi.h>
#include <FS.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

const char* ssid = "xxxxxx";
const char* password = "xxxxxx";

AsyncWebServer server(80);

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  Serial.println(WiFi.localIP());
  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    int paramsNr = request->params();//获取提交参数的个数
    Serial.println(paramsNr);
    for (int i = 0; i < paramsNr; i++) {//循环打印所有提交参数
      AsyncWebParameter* p = request->getParam(i);
      Serial.print("Param name: ");
      Serial.println(p->name());
      Serial.print("Param value: ");
      Serial.println(p->value());
      Serial.println("------");
    }
    request->send(200, "text/plain", "message received");//反馈网页
  });
  server.begin();
}

void loop() {

}

上传程序,打开串口监视器,我们观察到路由器给 M5 Core2 分配的 IP 地址是 192.168.1.24,记住该地址。

a320450734f27a3172f6d5cff2e0e078.png

我们通过浏览器访问 http://192.168.1.24/?name=小明&gender=男, 发现浏览器返回数据如下

4942b21cd2926bbb2c65fb747aabd9fc.png

此时串口监视器返回数据如下:

2e460c85823416a158605892aed6c054.png

这里我们有两个参数,分别是 name(姓名)与 gender(性别),它们的值分别是“小明”与“男”。在这里你可以添加其他参数,例如加上年龄等参数,浏览器访问链接再次观察串口输出,寻找规律,理解访问参数的意义。

表单输入

上面的示例,我们通过参数请求的方式获取了提交的数据,那么我们如何更加简单方便的去提交不同参数呢?在网页中有一个叫表单提交的方法可以让我们去提交这个数据,下面我们来以王大爷的需求为例通过编写 HTML 脚本来进行数据的输入。示例如下:

#include <WiFi.h>
#include <FS.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

const char* ssid = "xxxxxx";
const char* password = "xxxxxx";

const char index_home[] PROGMEM = R"rawliteral(
<html>
 <head>
  <meta charset="utf-8">
  <title>来访登记</title>
  <meta name="Generator" content="vsCode">
  <meta name="Author" content="x">
  <meta name="Keywords" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
  <meta http-equiv="X-UA-Compatible" content="IE=edge chrome=1">
  <meta name="referrer" content="never">
  <meta name="format-detection" content="telephone=no,email=no,address=no">
  <meta name="renderer" content="webkit">
  <meta name="Description" content="">

  <style type="text/css">
  *{
    margin: 0;
    padding: 0;
    }
  ul,li{
    list-style:none;
    }
  a{
    text-decoration:none;
    }
  input{
    outline-style:none;
    border: 0px;
  }
  #form1{
    
    margin-top:20px;
    width:80%;
    margin:2px auto;
  }
  #form1 form span{
    color:#999;
  }
  .row { 
    margin-top: 25px;
    height: 50px;
    line-height: 26px;
    border-bottom: 1px solid #c7c6c6;
    width:100%; 
  }
  .input { 
    position: relative;
    width: 100%;
    height: 26px; 
  }
  .input input { 
    display: block;
    width: 100%;
    height: 26px;
    border:none;
    background: none;
    outline:none; 
  }
  .input label { 
    position: absolute;
    top:0;
    left:0;
    height: 26px;
    line-height: 26px;
    font-size: 14px;
    color: #999; 
  }
  .select select{
    display:block;
    width:100%;
    border:none;
    outline:none;
  }
  input[type=date]::-webkit-inner-spin-button { visibility: hidden; }
  .button{
    width:100%;
    height:40px;
    background: #ff961e;
    outline: none;
    margin:20px 0 30px 0;
  }
  .button button{
    border:0px;
    background-color:transparent;
    color:white;
    width: 100%;
    height:40px;
    margin:0 auto;
    font-size:17px;
}

  </style>
 </head>

 <body>
    <div id="form1">
        <form action="">
            <br>
            <div class="row ">
                <div class="input">
                    <span>车牌号码</span>
                    <input type="text" placeholder="例如:苏N·OQN64" name="number_plate" value="">
                </div>
            </div>
            <div class="row ">
                <div class="input">
                    <span>访客姓名</span>
                    <input type="text" placeholder="例如:蓟芊" name="name" value="">
                </div>
            </div>
            <div class="row">
                <div class="select">
                    <span>性别</span><br>
                    <select name="gender">
                        <option value="男">男</option>
                        <option value="女">女</option>
                    </select><br>
                </div>
            </div>            
            <div class="row ">
                <div class="input">
                    <span>体温</span>
                    <input type="text" placeholder="例如:37.5" name="body_temperature" value="">
                </div>
            </div>            
            <div class="row ">
                <div class="input">
                    <span>联系电话</span><br>
                    <input type="text" placeholder="例如:18831015911" name="phone_number" value="">
                </div>
            </div>
            <div class="row">
                <div class="input">
                    <span>被访人</span><br>
                    <input type="text" placeholder="例如:白允" name="interviewee" value="">
                </div>
            </div>
            <div class="row">
                <div class="input">
                    <span>进入时间</span><br>
                    <input type="text" placeholder="例如:2022/4/28 14:56" name="Entry_time" value="">
                </div>
            </div>           
            <div class="button">
                <button type="submit">提交信息</button>
            </div>
         </form>
    </div>
 </body>
</html>)rawliteral";

AsyncWebServer server(80);

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  Serial.println(WiFi.localIP());
  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    int paramsNr = request->params();
    Serial.println(paramsNr);
    for (int i = 0; i < paramsNr; i++) {
      AsyncWebParameter* p = request->getParam(i);
      Serial.print("Param name: ");
      Serial.println(p->name());
      Serial.print("Param value: ");
      Serial.println(p->value());
      Serial.println("------");
    }
    request->send(200, "text/html", index_home);
  });
  server.begin();
}

void loop() {

}

上传程序,访问 M5core2 对应的 IP 得到如下图所示网页:

ee0a3908769d770c02f4032499c51a69.png

打开串口监视器,随机输入一些信息观察串口的数据输出,在这里我们仅需要关注几个地方,网页标题通过 title 标签指定,某一参数输入由 input 标签决定,例如我们要实现年龄输入,只需替换为下列程序:

<div class="row ">
    <div class="input">
        <span>年龄</span>
        <input type="text" placeholder="例如:18" name="age" value="">
    </div>
</div>

这里我们可以通过 w3school在线网页编辑器 实时修改并预览,修改后效果如下:

ff9cdef343c7ad8f20488247993a753e.png

对于固定格式的数据,如性别、年级等可参考上面的性别例子在网页编辑器中编辑并预览,相信你一定能轻松搞定。

数据可视化

现在我们实现了任意数据表单的输入,那么我们要如何显示数据呢,下面我直接给出网页代码:

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>来访者记录</title>
  <style type="text/css">
    #myInput {
        background-image: url('https://static.runoob.com/images/mix/searchicon.png'); /* 搜索按钮 */
        background-position: 10px 12px; /* 定位搜索按钮 */
        background-repeat: no-repeat; /* 不重复图片 */
        width: 100%;
        font-size: 16px;
        padding: 12px 20px 12px 40px;
        border: 1px solid #ddd;
        margin-bottom: 12px; 
      }
    #output {
        width: 100%;
        height: 100vh;
        margin-top: 20px;
      }

      #table {
        border: solid 1px #565656;
        border-collapse: collapse;
        font-family: helvetica,serif;
        font-size: 10pt;
        width: 180mm;
      }

      #table th {
        border: solid 1px #565656;
        background-color: #9a7f5b;
        color: #eee;
        text-align: center;
        padding: 5px;
      }

      #table td {
        border: solid 1px #565656;
        text-align: center;
        padding: 2px 5px;
      }
  </style>
</head>
<body>
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="搜索...">
    <!-- 设置border="1"以显示表格框线 -->
<table align="center" id="table" border="1">
  <caption><span style="font-size:20px;">来访者记录</span></caption>
  <thead>
    <tr>
      <th>序号</th>
      <th>车牌号码</th>
      <th>访客姓名</th>
      <th>性别</th>
      <th>体温</th>
      <th>联系电话</th>
      <th>被访人</th>
      <th>进入时间</th>
    </tr>
  </thead>
  <tbody>
     <tr>
     <td>1</td>
     <td>湘P·RBBZG</td>
     <td>邱勤</td>
     <td>男</td>
     <td>36</td>
     <td>1845646545</td>
     <td>小宝</td>
     <td>2022/4/28 14:56</td>
    </td>
    <tr>
     <td>2</td>
     <td>晋B·MIVYF</td>
     <td>简馨</td>
     <td>女</td>
     <td>36</td>
     <td>1845646548</td>
     <td>马克</td>
     <td>12022/4/28 15:56</td>
    </td>
  </tbody>
</table>
<div style="text-align:center">
    <a>导出表格</a>                     
 </div>
    <script>
        // 使用outerHTML属性获取整个table元素的HTML代码(包括<table>标签),然后包装成一个完整的HTML文档,设置charset为urf-8以防止中文乱码
        var html = "<html><head><meta charset='utf-8' /></head><body>" + document.getElementsByTagName("table")[0].outerHTML + "</body></html>";
        // 实例化一个Blob对象,其构造函数的第一个参数是包含文件内容的数组,第二个参数是包含文件类型属性的对象
        var blob = new Blob([html], { type: "application/vnd.ms-excel" });
        var a = document.getElementsByTagName("a")[0];
        // 利用URL.createObjectURL()方法为a元素生成blob URL
        a.href = URL.createObjectURL(blob);
        // 设置文件名
        a.download = "来访者记录表格.xls";
    </script>
        <script>
function myFunction() {
  // 声明变量
  var input, filter, table, tr, td, i;
  input = document.getElementById("myInput");
  filter = input.value.toUpperCase();
  table = document.getElementById("table");
  tr = table.getElementsByTagName("tr");

  // 循环表格每一行,查找匹配项
  for (i = 0; i < tr.length; i++) {
    td = tr[i].getElementsByTagName("td")[1];//("td")[1]这里的1指表头的第2列数据,可根据自己需求改为其他列,例如学号或者车牌号等列,该列一般选择为唯一的数据列
    if (td) {
      if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
        tr[i].style.display = "";
      } else {
        tr[i].style.display = "none";
      }
    } 
  }
}
    </script>
</body>
</html>

将上面的网页代码复制到w3school在线网页编辑器我们得到如下的表格:

3b99437ec936a4c31cfa0823b6ae66f9.png

在这个网页中我们点击搜索,填写关键字可以在一定范围内搜索数据,例如我们输入 P 效果如下:

025cc8224406e40e29db622854aa2fd1.png

点击导出表格将会下载文件“来访者记录表格.xls”,我们打开该 Excel 文件效果如下:

2f14760d40bfc4390c50caa2df55ff4c.png

对比 Excel表格与在线表格,可知两者信息以及格式一致。值得注意的是,即使我们输入错误的信息也会被录入,如上图中的访客 简馨 输入的进入时间有误。在这里篇幅有限我们并没有验证,对于有格式要求的信息如邮箱,电话号码等,有需要的读者可以自行学习前端知识改进上面的网页代码以达到自己的要求。

对于上面的HTML文件进行分析,我们可以知道关键的几项数据如下:

<table align="center" id="table" border="1">
  <caption><span style="font-size:20px;">来访者记录</span></caption>
  <thead>
    <tr>
      <th>序号</th>
      <th>车牌号码</th>
      <th>访客姓名</th>
      <th>性别</th>
      <th>体温</th>
      <th>联系电话</th>
      <th>被访人</th>
      <th>进入时间</th>
    </tr>
  </thead>
  <tbody>
     <tr>
     <td>1</td>
     <td>湘P·RBBZG</td>
     <td>邱勤</td>
     <td>男</td>
     <td>36</td>
     <td>1845646545</td>
     <td>小宝</td>
     <td>2022/4/28 14:56</td>
    </td>
    <tr>
     <td>2</td>
     <td>晋B·MIVYF</td>
     <td>简馨</td>
     <td>女</td>
     <td>36</td>
     <td>1845646548</td>
     <td>马克</td>
     <td>12022/4/28 15:56</td>
    </tr>
  </tbody>
</table>

将上面的表格的数据替换或者增加数据项,并在网页编辑器中编辑并预览你发现了什么规律,想要实现下图中的表格样式又该如何改呢,这个问题留给大家进行思考。

6788f93309232113bc8e9b2ae0c7beba.png

在这个例子中,我们发现,一旦表格的名称与表头信息固定,那么整个表也就随之确定了。其中序号作为必要的数据项放到表格的第一位,为表格排序与统计数据个数,观察 html 表格可知其的最小重复单位如下:

<tr>
    <td>2</td>//固定序号
    <td>晋B·MIVYF</td>
    <td>简馨</td>
    <td>女</td>
    <td>36</td>
    <td>1845646548</td>
    <td>马克</td>
    <td>12022/4/28 15:56</td>
 </tr>

按照最小重复单元,我们增加数据项与重复单元,在网页编辑器中进行增删改,实时修改并预览,深刻理解 html 表格。

表单输入自动添加到表格

前面我们通过串口监视器观察到了表单数据的提交,那么我们要如何将表单提交的数据添加到表里呢?对于一条数据对应的就是上面的最小重复单元,那么我们可以这样做,对每一条提交的表单数据都将其构造为下面的形式

<tr>
    <td>data1</td>//固定序号
    <td>data2</td>//第1个提交参数
    <td>data3</td>//第2个提交参数
    ……
    <td>dataN</td>//第N个提交参数
 </tr>

这里我们不需要考虑每一个数据的数据类型到底是整数,小数还是字符串,我们将其构造后将其视为一个字符串即可。对于多条数据,那么我们如何保存它呢?在 C 语言中我们知道保存一个数据我们可以用变量,保存多个数据我们用数组,那么问题来了我们并不知道我们提交的数据有多少条,数组要求我们使用要先定义数组长度,但这里我们的长度显然是无法预知的。那么有没有可变长度的数组呢,答案是肯定的,我们的解决办法是用链表,至于链表是什么大家可以自己百度了解一下,下面我给出一个链表的简单使用例子。

#include <QList.h>

QList<String> myList;//声明一个字符串类型的链表

void setup(){
  Serial.begin(115200);
  Serial.println(myList.size());//第一次打印链表长度
  for (int i = 1; i <= 10; i = i++) {//逐次添加随机数到链表
    myList.push_back(String((random(1, 100))));
  }
  Serial.println(myList.size());//添加数据后再次打印链表长度
  for (int i = 1; i <= 10; i++) {//循环打印链表的每一项
    Serial.println(myList.at((i - 1)));
  }
  myList.clear();//清除链表
  Serial.println(myList.size());//再次打印链表长度
}

void loop(){

}

上传程序,打开串口监视器结果如下:

3ea83d2dd44f57eccb37d1c858047a4a.png

关于链表还有一个注意事项,那就是如果你访问了不存在的数据项,例如我们这里值添加了 10 项数据,但你访问了第 11 项数据,那就会导致程序崩溃,开发板会不断重启,你可以试试这种情况看是否如此,为了避免这种情况的发生我们要适当利用链表长度获取函数来规避这个问题。下面我直接给出表单提交数据放入链表的例子:

String table = "    <tr>\n";
for (int i = 1; i <= paramsNr + 1; i++) {
  table = String(table) + String(String("     <td>") + String("data") + String(i) + String("</td>\n"));
}
table = String(table) + String("    </tr>\n");
Serial.println(table);
table.replace("data1", String(myList.size() + 1));
for (int i = 0; i < paramsNr; i++) {
  AsyncWebParameter* p = request->getParam(i);
  table.replace(String("data") + String(i + 2), String(p->value()));
}
myList.push_back(table);
Serial.println(myList.at((myList.size() - 1)));

表格数据替换

现在我们已经能够获取表单数据并写入链表了,那么我们如何将链表的每一项数据都放入数据查看的 html 网页了呢,我们可以这样做 ,将原表格网页的数据项用一个占位符表示,链表的所有数据项用一个 for 循环全部连接到一起组成一个长字符串,再把这个字符串替换原来的占位符,那么就可以得到完整的 html 表格文件了。实现的方法如下:

String Tabular_data_variable = "";//声明一个变量用来存放所有链表数据 
for (int i = 0; i <  myList.size(); i++) {//获取链表长度巧妙利用for循环获取所有数据进行拼接
  Tabular_data_variable = String(Tabular_data_variable) + String(myList.at(i));
  delay(0);//延时函数必须要,否则当链表长度较大时可能会导致看门狗超时重启
}
html = index_data;//将数据表格html赋值为原始的数据表格字符串(含占位符)
html.replace("Tabular_data", Tabular_data_variable);//将占位符Tabular_data替换为有效数据

提交表单交互

到这里如果你提交了表单数据,你会发现没有一个交互效果 ,我们并不知道表单数据是否提交成功以及数据提交是否有缺失,当数据不完整时表单数据不应该加入到链表里,只有提交的所有数据参数都不为空字符串时证明数据有效,才能添加到链表,交互的网页代码如下,这是一个提交成功和失败都通用的网页,当然你也可以自己写一个交互网页:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>登记成功</title>
<script type="text/javascript">
  var num=6;
  function redirect(){
    num--;
    document.getElementById("num").innerHTML=num;
    if(num<0){
      document.getElementById("num").innerHTML=0;
      location.href="/";//回到根目录
      }
    }
  setInterval("redirect()", 1000);
</script>
</head>

<body onLoad="redirect();">
<div class="b">
    <p>
            <h1>信息登记成功!</h1>
            <h1><span id="num"></span>秒后回到主页</h1>
    </p>
  </div>
</body>
</html>

代码效果如下:

fa6834fe1ba0c75b37358e3917451ed4.png

域名解析

我们可以通过串口监视器获取设备的 IP 地址进行访问,但是路由器的分配的 IP 地址是变化的,这点很不方便,当然你也可以登录路由器后台给 M5Core2 分配一个静态 IP。我们这里采取域名解析的方式通过给 M5Core2 分配一个本地域名,这样我们不需要知道IP地址也能方便的 访问设备,实现代码如下:

#include <ESPmDNS.h>

void setup() {
  Serial.begin(115200);
  if (!MDNS.begin("M5Core2")) {//自定义域名
    Serial.println("Error setting up MDNS responder!");
  }
  MDNS.addService("http", "tcp", 80); //启用DNS服务
}

void loop() {

}

通过域名解析,我们只要和设备在同一局域网内,访问 http://m5core2 就能访问相应的服务了。

读取SD卡文件

本项目是一个通用型的项目,如果我们将网页文件进行固定那么就丧失了灵活性,因此我们将网络信息以及网页代码等配置文件放入 SD 卡内,从 SD 中去加载所有服务。SD 卡使用的简单示例如下:

#include "FS.h"
#include <SD.h>
#include <SD_MMC.h>

SPIClass sdSPI(VSPI); //定义SD卡软SPI管脚
#define SD_MISO     38
#define SD_MOSI     23
#define SD_SCLK     18
#define SD_CS       4

String readFile(fs::FS &fs, const char * path) { //读取SD卡指定路径文件
  File file = fs.open(path);
  if (!file) {
    Serial.println("Failed to open file for reading");
  }
  String data = "";
  while (file.available()) {
    data = String(data) + String(char(file.read()));
  }
  file.close();
  return data;
}

void setup() {
  Serial.begin(115200);
  sdSPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS); //初始化SD卡SPI
  if (!SD.begin(SD_CS, sdSPI)) {
    Serial.println("Card Mount Failed");
    return;
  }
  Serial.println(readFile(SD, "/admin.txt"));
}

void loop() {

}

在这里我们可以直接输入 TXT 或者 HTML 文件的路径,便可读取该文件,这里我们读取了 SD 卡根目录下的 admin.txt 文件,该文件作为配置文件用来保存网络信息与一个二维码的数据,该文件内容如下:

{
  "ssid": "ChinaNet-5678",
  "password": "1234567890",
  "qr": "http://m5core2/"
}

M5 超级问卷星网页逻辑

M5 超级问卷星服务框架如下:

a791cf4bbf5bc1500dcbf564d0193025.png

M5 超级问卷星通过区分根目录与 /data 路径来呈现不同的网页内容,其中访问域名或IP进入表单数据提交页,域名或 IP 加 /data 路径进入数据查看与导出页面。

服务响应逻辑

M5 超级问卷星服务响应的逻辑如下,访问根目录返回 Home_page.html 页面用于表单提交,当表单提交数据时有两种情况,当提交表单所有数据都不为空返回 success.html 页面,当提交表单某些数据为空时返回 mistake.html,当访问 /data 路径时返回 index_data.html 页面用于查看或导出数据。

acb888c77333405268b32ee8ddd00837.png

配置文件结构

SD 卡配置文件主要由 admin.txt、Home_page.html、success.html、mistake.html、index_data.html 这四个文件构成 ,配置文件详情示意图如下:

eb57653f429dbc5d5bd0292a09d0a3e8.png

细节优化

M5 超级问卷星是一个用于表格数据收集的项目,它集数据库与服务器于一身,不依赖于任何第三方服务,为了交互体验更好,还应当有些许动态提示功能,例如是否有 SD 卡,是否有网络等、我们可以显示一些图像文字和播放音效来进行提示,屏幕上应当显示一个用于外网或者本地的二维码地址,用户直接扫码浏览器打开就可以提交数据,关于这些功能的实现可以参考往期《DIY掌上POS机,或许是最小的收银POS机了!》《超便利!教你用ESP32 开发板 DIY 掌上服务器》这两篇教程,其中有功能实现详细的描述,这里就不再赘述,可通过链接进行查看。

程序下载

以上就是M5 超级问卷星的项目介绍,如果你不想下载IDE只想体验该项目,那么你可以访问 https://docs.m5stack.com/zh_CN/download 根据你自己的系统下载M5Burner烧录工具进行安装,打开软件按照下面的步骤进行烧录体验,其中SD卡网页模板与配置文件请通过本教程附件进行下载,直接解压到的SD卡修改网络信息即可体验。

a00f902059185d57c4a0aedbd526ebad.png

使用说明

  1. 烧录固件;

  2. 将附件提供的模板解压到 SD 卡;

  3. 打开 admin.txt 文件修改网络信息(不需要外网访问二维码地址可以不改,如果有使用内网穿透请填写外网地址);

  4. SD 卡放入 M5core2 并重启设备;

  5. 等待 M5core2 初始化并进入二维码显示页面(若初始化错误请按照屏幕显示与语音提示使用正确配置文件或更换 SD 卡);

  6. 访问 http://m5core2/ 按演示视频提交表单数据(体验提交成功与失败两种情况);

  7. 访问 http://m5core2/data 查看数据并导出 Excel 查看;

  8. 尝试修改模板文件自定义表单文件与数据表格重复上述步骤理解本项目。

总结

根据上面的理论基础我们便能完成 M5 超级问卷星的项目制作了,其中具体实现细节由于篇幅限制,这里就不再讨论,大家可以通过附件下载程序源代码进行查看,其中必要的程序说明已经注释,对此项目有任何建议或者疑问均可评论区留言。使用 M5 超级问卷星你能够轻松利用表单收集任意文本信息并且导出为 Excel 表格,如果你会网页前端知识你还可以定制属于自己的网页样式满足个性化,与问卷星不同这个是你个人私有的服务器且可以灵活定制各种功能(数据分类汇总,表格可视化等),如果你想要定制或者有其他项目需求请联系我,如果你有好玩的创意也可以评论区留言,或许下期你的创意就会得到实现,我也会@你来见证你的好创意。

我是默,我们下期见。

代码下载

关注本公众号“铁熊玩创客”,回复“M5问卷星”获取完整代码。

43cf922d82ad9c261b6b1d2a6b25e65f.png

欢迎转发朋友圈。如需转载,请注明出处和原作者。

b3d8e22c199f4ed0df372bfa57d33a6c.png

点个在看支持一下吧 ↓↓↓

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值