目录
前言:
硬件:ESP8266主板、数据线
编译器:Arduino IDE
需要的库:<ESP8266WiFi.h> <ArduinoJson.h>
TinyWebDB
TinyWebDB是一个非关系型数据库,它的数据结构是键值对(Key-Value)结构。在TinyWebDB中,每个数据项都有一个键和一个对应的值,键值对之间没有固定的关系,可以随时添加、删除和修改。TinyWebDB的数据库结构可以简单地表示为:
{
"key1": "value1",
"key2": "value2",
"key3": "value3",
...
}
其中,"key1"、"key2"、"key3"等表示键名,"value1"、"value2"、"value3"等表示对应的值。这种数据结构非常简单,但也非常灵活,可以存储各种不同类型的数据,包括字符串、数字、布尔值等。
在TinyWebDB中,每个数据项的键名是唯一的,如果添加了重复的键名,后面的键值会覆盖前面的键值。这种设计可以保证数据的唯一性和正确性。同时,由于TinyWebDB的数据结构非常简单,因此可以很方便地进行数据的查询、修改和删除操作。
总的来说,TinyWebDB的数据库结构非常简单,由键值对组成,这种设计具有灵活性和高效性,非常适合存储小规模的数据。TinyWebDB本是用于APP Inventor中的网络微数据库,但由于其简洁、轻量且免费,所以在此项目中被用来作为POST请求的操作对象。
以下是TinyWebDB服务器的网址,可以自行注册登录使用:
TinyWebDB服务器 - APPInventor网络微数据库http://tinywebdb.appinventor.space/
注册并登录后记住以下信息:
API地址:http://tinywebdb.appinventor.space/api
用户名(user):your_username
密钥(secret):your_secret
以及各种操作与各种参数要求。
代码讲解
首先是库的导入:
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
该段代码使用了ESP8266WiFi和ArduinoJson两个库。
其中ESP8266WiFi库用于连接Wi-Fi网络,ArduinoJson库用于解析JSON格式的数据。
以及一些字段的定义:
const char* ssid = "Wifi_ID";
const char* password = "Wifi_password";
const char* host = "tinywebdb.appinventor.space"; // TinyWebDB的API地址
const int httpPort = 80;
String user = "your_username";
String secret = "your_secret";
其中的 "Wifi_ID"; "Wifi_password"; "your_username"; "your_secret";要填入对应的字段。
分别对应的是你的Wifi名称、Wifi密码、TinyWebDB用户名(user)以及密钥(secret)
/*应该不会看不懂吧*/
接着是setup函数的内容
void setup() {
Serial.begin(9600);
delay(1000);
WiFi.begin(ssid, password);
delay(2000);
Serial.print("Connecting to WiFi ");
Serial.print(ssid);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println(" ");
Serial.print("Connected to WiFi: ");
Serial.println(ssid);
Serial.println(" ");
Serial.print("Select your action (update/ get/ delate/ count/ search/ show): ");
}
这里除了一些必要的代码外,还做了一些串口端的文字提示内容,如果不需要可以直接删除。
实际效果如下图:

然后是loop函数的内容
void loop() {
if (Serial.available()) {
String input = Serial.readStringUntil('\n');
Serial.println(input);
input.trim();
if (input == "update"){updateValue();} //添加或更新
else if (input == "get"){getValue();} //读取
else if (input == "delete"){deleteValue();} //删除
else if (input == "count"){countValues();} //计数
else if (input == "search"){searchValues();} //查询
else if (input == "show"){showDB();} //显示全部(100条为上限)
else {
Serial.println("Invalid command."); //非法指令
}
Serial.print("\nSelect your action (update/ get/ delate/ count/ search/ show): ");
}
}
通过六种不同的串口输入会有六种对应的操作,总体来说就是增删改查,我们会通过六个不同的函数来实现各自的功能。
这里还会涉及到一些关于串口字符串读取与判断的语句,在此不多讲,关于串口通信如有必要会专门写一篇。
在六种函数中,updateValue、deleteValue大多数情况下是不需要返回值的,而其他的函数都会有JSON格式的返回值,其中有countValues这种我们需要提取一个字符串+一个整型值的,也有searchValues、getValue以及showDB这种我们需要提取一(多)个字符串+一(多)个对应字符串的情况,所以我们将从updateValue()、searchValues()、countValues()三个函数的角度进行讲解。
⭐updateValue操作函数
void updateValue(){
String tag, value;
Serial.print("Enter tag: ");
while (!Serial.available()) {}
tag = Serial.readStringUntil('\n');
Serial.println(tag);
tag.trim();
Serial.print("Enter value: ");
while (!Serial.available()) {}
value = Serial.readStringUntil('\n');
Serial.println(value);
value.trim();
if (tag.length() > 0 && value.length() > 0) {
// 发送POST请求
WiFiClient client;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
String url = "/api?user=" + user + "&secret=" + secret + "&action=update&tag=" + tag + "&value=" + value;
Serial.print("Sending HTTP request to TinyWebDB: ");
Serial.println(url);
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
Serial.println("Server response:");
Serial.println(" ");
while (client.connected()) {
String line = client.readStringUntil('\n');
Serial.println(line);
if (line == "\r") {
break;
}
}
Serial.println("Update Done.");
}
}
通过询问需要添加条目的tag以及value(也就是键名和对应的值),发送对应的POST请求。
在这一段中,该代码首先创建了一个WiFiClient对象client,然后使用client.connect()方法连接到指定的主机和端口。如果连接失败,则输出“connection failed”并返回。
接下来,该代码构造了一个URL,并使用client.print()方法发送HTTP请求。
其中,HTTP请求的第一行为“POST URL HTTP/1.1”,表示使用POST方法发送请求。请求头中包含了主机名、连接方式和空行。
发送完请求后,该代码使用client.readStringUntil()方法读取响应数据,并在读到空行时停止。
最终,响应数据被存储在line字符串变量中。
需要注意的是,输出的内容是可以通过注释掉对应的Serial.println()进行筛选的,例如我自己在使用时是不会显示line中的响应数据的,此处为了展示所以保留,但在后续的函数中将不再显示。
实际效果如下图:
而此时我的数据库中就多了这么一条数据:
⭐searchValues操作函数
void searchValues(){
String no, count;
Serial.print("Search from NO: ");
while (!Serial.available()) {}
no = Serial.readStringUntil('\n');
Serial.println(no);
no.trim();
Serial.print("Total count: ");
while (!Serial.available()) {}
count = Serial.readStringUntil('\n');
Serial.println(count);
count.trim();
if (no.length() > 0 && count.length() > 0) {
// 发送POST请求
WiFiClient client;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
String url = "/api?user=" + user + "&secret=" + secret + "&action=search&no=" + no + "&count=" + count;
Serial.print("Sending HTTP request to TinyWebDB: ");
Serial.println(url);
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
// 等待服务器响应
while (!client.available()) {
delay(1);
}
// 读取服务器响应
String response = "";
while (client.available()) {
String line = client.readStringUntil('\r');
response += line;
}
//Serial.println(response);
int start = response.indexOf("{");
int end = response.lastIndexOf("}") + 1;
// Extract JSON data
String json = response.substring(start, end);
// 解析JSON数据
DynamicJsonDocument doc(1024);
deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>();
Serial.println("Search Result:");
for (JsonPair pair : obj) {
String tag = pair.key().c_str();
//float value = pair.value().as<float>();
String value = pair.value().as<String>();
Serial.print("Tag: ");
Serial.print(tag);
Serial.print(" Content: ");
Serial.println(value);
}
}
}
其中与updateValue函数结构重复的部分就不再进行说明。
需要说明的是对返回值中JSON数据的提取和处理。
先定义了起始和结束位置的变量start和end,然后通过response字符串中的indexOf()和lastIndexOf()函数找到JSON数据的起始和结束位置。
接着,通过substring()函数获取JSON数据的字符串形式,存储在json变量中。
然后,使用deserializeJson()函数将json解析成DynamicJsonDocument类型的对象doc。
接下来,将doc转化成JsonObject类型的对象obj,通过遍历obj中的每一个元素,获取每个元素的key和value并输出。
需要注意的是这里,如果你的数据库中所有的value值都是数字,那么你可以通过float value = pair.value().as<float>();直接提取出浮点型或者通过int value = pair.value().as<int>();直接提取出整型,但大多数情况下我不推荐这么做,因为数据在上传时并没有对value的类型做出任何要求,那么在读取时应当认为它有可能是字符串类型,否则会有意外的读取错误的情况发生。
//float value = pair.value().as<float>();
String value = pair.value().as<String>();
这一点在countValues函数中有所体现。
⭐countValues操作函数
void countValues(){
// 发送POST请求
WiFiClient client;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
String url = "/api?user=" + user + "&secret=" + secret + "&action=count";
Serial.print("Sending HTTP request to TinyWebDB: ");
Serial.println(url);
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
// 等待服务器响应
while (!client.available()) {
delay(1);
}
// 读取服务器响应
String response = "";
while (client.available()) {
String line = client.readStringUntil('\r');
response += line;
}
//Serial.println(response);
int start = response.indexOf("{");
int end = response.lastIndexOf("}") + 1;
// Extract JSON data
String json = response.substring(start, end);
// 解析JSON数据
DynamicJsonDocument doc(1024);
deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>();
Serial.print("Count Result: ");
for (JsonPair pair : obj) {
String count = pair.key().c_str();
int value = pair.value().as<int>();
//Serial.print("Count: ");
//Serial.print(count);
//Serial.print(" Value: ");
Serial.println(value);
}
}
与前两个函数类似的部分就不再赘述。
可以看到,由于countValues操作返回的value是数据的总条目数,而数据的总条目数必然是一个整数,所以我在提取元素时便直接将这里的value作为整型变量(int)进行操作了。
这种对于JSON数据的处理方式也可以灵活运用于Arduino IDE编程中的其他地方。
整体代码
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
const char* ssid = "Wifi_ID";
const char* password = "Wifi_password";
const char* host = "tinywebdb.appinventor.space"; // TinyWebDB的API地址
const int httpPort = 80;
String user = "your_username";
String secret = "your_secret";
void setup() {
Serial.begin(9600);
delay(1000);
WiFi.begin(ssid, password);
delay(2000);
Serial.print("Connecting to WiFi ");
Serial.print(ssid);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println(" ");
Serial.print("Connected to WiFi: ");
Serial.println(ssid);
Serial.println(" ");
Serial.print("Select your action (update/ get/ delate/ count/ search/ show): ");
}
void loop() {
if (Serial.available()) {
String input = Serial.readStringUntil('\n');
Serial.println(input);
input.trim();
if (input == "update") {
updateValue();
}
else if (input == "get") {
getValue();
}
else if (input == "delete") {
deleteValue();
}
else if (input == "count") {
countValues();
}
else if (input == "search") {
searchValues();
}
else if (input == "show") {
showDB();
}
else {
Serial.println("Invalid command.");
}
Serial.print("\nSelect your action (update/ get/ delate/ count/ search/ show): ");
}
}
void updateValue(){
String tag, value;
Serial.print("Enter tag: ");
while (!Serial.available()) {}
tag = Serial.readStringUntil('\n');
Serial.println(tag);
tag.trim();
Serial.print("Enter value: ");
while (!Serial.available()) {}
value = Serial.readStringUntil('\n');
Serial.println(value);
value.trim();
if (tag.length() > 0 && value.length() > 0) {
// 发送POST请求
WiFiClient client;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
String url = "/api?user=" + user + "&secret=" + secret + "&action=update&tag=" + tag + "&value=" + value;
Serial.print("Sending HTTP request to TinyWebDB: ");
Serial.println(url);
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
//Serial.println("Server response:");
//Serial.println(" ");
while (client.connected()) {
String line = client.readStringUntil('\n');
//Serial.println(line);
if (line == "\r") {
break;
}
}
Serial.println("Update Done.");
}
}
void getValue(){
String tag;
Serial.print("Enter tag: ");
while (!Serial.available()) {}
tag = Serial.readStringUntil('\n');
Serial.println(tag);
tag.trim();
if (tag.length() > 0) {
// 发送POST请求
WiFiClient client;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
String url = "/api?user=" + user + "&secret=" + secret + "&action=get&tag=" + tag;
Serial.print("Sending HTTP request to TinyWebDB: ");
Serial.println(url);
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
// 等待服务器响应
while (!client.available()) {
delay(1);
}
// 读取服务器响应
String response = "";
while (client.available()) {
String line = client.readStringUntil('\r');
response += line;
}
//Serial.println(response);
int start = response.indexOf("{");
int end = response.lastIndexOf("}") + 1;
// Extract JSON data
String json = response.substring(start, end);
// 解析JSON数据
DynamicJsonDocument doc(1024);
deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>();
Serial.println("Get Result:");
for (JsonPair pair : obj) {
String tag = pair.key().c_str();
//float value = pair.value().as<float>();
String value = pair.value().as<String>();
Serial.print("Tag: ");
Serial.print(tag);
Serial.print(" Content: ");
Serial.println(value);
}
}
}
void deleteValue(){
String tag;
Serial.print("Enter tag: ");
while (!Serial.available()) {}
tag = Serial.readStringUntil('\n');
Serial.println(tag);
tag.trim();
if (tag.length()) {
// 发送POST请求
WiFiClient client;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
String url = "/api?user=" + user + "&secret=" + secret + "&action=delete&tag=" + tag;
Serial.println("Sending HTTP request to TinyWebDB: ");
Serial.println(url);
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
//Serial.println("Server response:");
//Serial.println(" ");
while (client.connected()) {
String line = client.readStringUntil('\n');
//Serial.println(line);
if (line == "\r") {
break;
}
}
Serial.println("Delate Done.");
}
}
void countValues(){
// 发送POST请求
WiFiClient client;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
String url = "/api?user=" + user + "&secret=" + secret + "&action=count";
Serial.print("Sending HTTP request to TinyWebDB: ");
Serial.println(url);
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
// 等待服务器响应
while (!client.available()) {
delay(1);
}
// 读取服务器响应
String response = "";
while (client.available()) {
String line = client.readStringUntil('\r');
response += line;
}
//Serial.println(response);
int start = response.indexOf("{");
int end = response.lastIndexOf("}") + 1;
// Extract JSON data
String json = response.substring(start, end);
// 解析JSON数据
DynamicJsonDocument doc(1024);
deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>();
Serial.print("Count Result: ");
for (JsonPair pair : obj) {
String count = pair.key().c_str();
int value = pair.value().as<int>();
//Serial.print("Count: ");
//Serial.print(count);
//Serial.print(" Value: ");
Serial.println(value);
}
}
void searchValues(){
String no, count;
Serial.print("Search from NO: ");
while (!Serial.available()) {}
no = Serial.readStringUntil('\n');
Serial.println(no);
no.trim();
Serial.print("Total count: ");
while (!Serial.available()) {}
count = Serial.readStringUntil('\n');
Serial.println(count);
count.trim();
if (no.length() > 0 && count.length() > 0) {
// 发送POST请求
WiFiClient client;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
String url = "/api?user=" + user + "&secret=" + secret + "&action=search&no=" + no + "&count=" + count;
Serial.print("Sending HTTP request to TinyWebDB: ");
Serial.println(url);
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
// 等待服务器响应
while (!client.available()) {
delay(1);
}
// 读取服务器响应
String response = "";
while (client.available()) {
String line = client.readStringUntil('\r');
response += line;
}
//Serial.println(response);
int start = response.indexOf("{");
int end = response.lastIndexOf("}") + 1;
// Extract JSON data
String json = response.substring(start, end);
// 解析JSON数据
DynamicJsonDocument doc(1024);
deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>();
Serial.println("Search Result:");
for (JsonPair pair : obj) {
String tag = pair.key().c_str();
//float value = pair.value().as<float>();
String value = pair.value().as<String>();
Serial.print("Tag: ");
Serial.print(tag);
Serial.print(" Content: ");
Serial.println(value);
}
}
}
void showDB(){
// 发送POST请求
WiFiClient client;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed");
return;
}
String url = "/api?user=" + user + "&secret=" + secret + "&action=search&no=1&count=100";
//Serial.print("Sending HTTP request to TinyWebDB: ");
//Serial.println(url);
Serial.println("Searching...");
client.print(String("POST ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Connection: close\r\n\r\n");
// 等待服务器响应
while (!client.available()) {
delay(1);
}
// 读取服务器响应
String response = "";
while (client.available()) {
String line = client.readStringUntil('\r');
response += line;
}
//Serial.println(response);
int start = response.indexOf("{");
int end = response.lastIndexOf("}") + 1;
// Extract JSON data
String json = response.substring(start, end);
// 解析JSON数据
DynamicJsonDocument doc(1024);
deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>();
Serial.println("Search Result:");
for (JsonPair pair : obj) {
String tag = pair.key().c_str();
//float value = pair.value().as<float>();
String value = pair.value().as<String>();
Serial.print("Tag: ");
Serial.print(tag);
Serial.print(" Content: ");
Serial.println(value);
}
}
连续操作演示