目的
MCU 采集到的数据能够直接渲染出来,不需要与前端、后端进行对接,而是直接渲染,效果图如下
效果

准备
单片机一块,AT89C52RC 即可
WIFi 通讯模块,Esp8266
数据存储,InfluxDB(建议 Docker 版本)
数据渲染,Grafana(建议 Docker 版本)
链路
链路如下,其中整个服务端仅需要部署 InfluxDB + Grafana 即可,无需任何代码,相关参考文档
InfluxDB 中文文档
Grafana 教程 - 构建你的第一个仪表盘

相关细节
Esp8266 操作比较简单,连接上单片机后基本就是对串口的操作,网上也有很多资料这里仅列举几项关键说明
// WiFi ssid 和 password
#define WIFI_CMD_CONNECT_ROUTER "AT+CWJAP_DEF=\"SSID\",\"PASSWORD\"\r\n"
// InfluxDB 所在的 TCP 服务,默认是 8086端口
#define WIFI_CMD_TCP_SERVER "AT+CIPSTART=\"TCP\",\"192.168.50.11\",8086\r\n"
// InfluxDB HTTP 请求里面的 Host 字段
#define WIFI_HTTP_INFLUXDB_HOST "192.168.50.11:8086"/**
* 通过定时器 2 来初始化 esp8266,波特率 115200
*/
void wifi_init_timer2()
{
SCON = 0x50;
TH2 = 0xFF;
TL2 = 0xFD;
RCAP2H = 0xFF;
RCAP2L = 0xFD;
TCLK = 1;
RCLK = 1;
C_T2 = 0;
EXEN2 = 0;
TR2 = 1;
ES = 1;
}POST /write?db=telegraf HTTP/1.1
Host: 192.168.50.11:8086
Content-Length: 42
embedded,mcu=c51,name=temperature value=10/**
* 模拟采集,并上传数据
*/
void mock_data_post(u8 event_type, u8 key_code)
{
WifiPost xdata mock_post;
char xdata mock_value_str[4];
char xdata mock_name_str[5] = "btn_";
int xdata mock_value = 0;
mock_name_str[4] = '0' + key_code;
// 0-key_code*100 的随机数
// random_seeds 调度器每扫描一次就 +1
mock_value = random_seeds % ((key_code + 1) * 100);
sprintf(mock_value_str, "%d", mock_value);
mock_post.host = WIFI_HTTP_INFLUXDB_HOST;
mock_post.path = WIFI_HTTP_INFLUXDB_WRITE_PATH;
mock_post.content_type = NULL_STR;
mock_post.body = NULL_STR;
mock_post.influx_db_measure_name = mock_name_str;
mock_post.influx_db_measure_value = mock_value_str;
wifi_exec_post(&mock_post);
}/**
* 连接指定的服务器,会自动进入透传模式
* @param server 服务器地址
*/
bool wifi_connect_tcp_server(char code *server)
{
bool is_ok = false;
is_ok = wifi_exec_cmd(server, WIFI_RESP_OK, 11000);
if (!is_ok)
{
// wifi 连接 tcp 服务器失败
led_light_one(0);
}
// 进入透传模式
is_ok &= wifi_enter_auto_trans();
return is_ok;
}
/**
* wifi 执行命令
* @param cmd AT 命令
* @param resp_ok AT 命令响应 ok 的字符串
* @param delay_ms 执行命令等待多久延迟才获取响应数据
*/
bool wifi_exec_cmd(char *cmd, char *resp_ok, u16 delay_ms)
{
bool is_ok = false;
wifi_response_clear();
wifi_send_cmd(cmd);
soft_delay_ms(delay_ms);
is_ok = string_contains(wifi_response.bytes, resp_ok);
wifi_response_clear();
return is_ok;
}/**
* wifi 首次初始化配置,仅需调用一次
*/
void wifi_first_init()
{
// 1. 测试响应
if (!wifi_exec_cmd(WIFI_CMD_TEST, WIFI_RESP_OK, 2000))
{
led_light_one(0);
return;
}
// 2. 设置工作模式 STA+AP
if (!wifi_exec_cmd(WIFI_CMD_MODE_STA, WIFI_RESP_OK, 2000))
{
led_light_one(3);
return;
}
// 3. 连接路由器
if (!wifi_exec_cmd(WIFI_CMD_CONNECT_ROUTER, WIFI_RESP_OK, 10000))
{
led_light_one(5);
return;
}
// 4. 设置自动连接路由器
if (!wifi_exec_cmd(WIFI_CMD_AUTO_CONN, WIFI_RESP_OK, 2000))
{
led_light_one(7);
return;
}
// 5. 配置成功点亮所有 LED
P1 = 0x00;
}/**
* Post 请求结构体
*/
typedef xdata struct
{
/**
* 请求的服务器地址,比如 192.168.50.11:8086
*/
char *host;
/**
* 请求路由
*/
char *path;
/**
* body 内容,body 和 influx_xxx 二选一,优先 body
*/
char *body;
/**
* 往 influxdb 写入的 name
*/
char *influx_db_measure_name;
/**
* 往 influxDB 写入的 value
*/
char *influx_db_measure_value;
/**
* text/plain application/json 等等
*/
char *content_type;
} WifiPost;/**
* 透传模式下发送数据
*/
void wifi_auto_trans(char *package)
{
wifi_send_cmd(package);
}
/**
* 操作 influx post api
*
* POST /write?db=mcu HTTP/1.1
* Host: 192.168.50.11:8086
* Content-Type:text/plain
* Content-Length:59
*
* weather,mcu=c51,measure_name=temperature,region=hz value=10
*
* @param post 请求结构体
*/
void wifi_exec_post(void xdata *param)
{
// 1. 优先取 post->body
WifiPost xdata *post = (WifiPost *)param;
bool body_clear = false;
char *body = post->body;
// sprintf 仅支持 int
int content_len = 0;
char content_len_str[3];
//2. 其次取 influxdb 写入
if (string_len(body) == 0)
{
body = wifi_influx_write_body(post->influx_db_measure_name, post->influx_db_measure_value);
body_clear = true;
}
//3. 得出最终的 content_len
content_len = string_len(body);
sprintf(content_len_str, "%d", content_len);
//拼接写入字符串
wifi_auto_trans("POST ");
wifi_auto_trans(post->path);
wifi_auto_trans(" HTTP/1.1\r\n");
wifi_auto_trans("Host: ");
wifi_auto_trans(post->host);
wifi_auto_trans("\r\n");
// 可以忽略 content_type
if (string_len(post->content_type) != 0)
{
wifi_auto_trans("Content-Type: ");
wifi_auto_trans(post->content_type);
wifi_auto_trans("\r\n");
}
wifi_auto_trans("Content-Length: ");
wifi_auto_trans(content_len_str);
wifi_auto_trans("\r\n\r\n");
wifi_auto_trans(body);
if (body_clear)
{
free(body);
}
}
/**
* 获取 influx 写入的 body
*/
char *wifi_influx_write_body(char *measure_name, char *value)
{
char xdata body[WIFI_HTTP_BODY_MAX_LENGTH] = "embedded,mcu=c51,name=";
string_append(body, measure_name);
string_append(body, " value=");
string_append(body, value);
return body;
}
源码
仓库
https://github.com/ychost/HybSCH
核心代码
https://github.com/ychost/HybSCH/blob/master/Sources/Esp8266Wifi.c
https://github.com/ychost/HybSCH/blob/master/Sources/Main.c
引用链接
[1]
InfluxDB中文文档: https://jasper-zhang1.gitbooks.io/influxdb/content/[2]
Grafana 教程 - 构建你的第一个仪表盘: https://kalasearch.cn/blog/grafana-with-prometheus-tutorial/[3]
https://github.com/ychost/HybSCH[4]
https://github.com/ychost/HybSCH/blob/master/Sources/Esp8266Wifi.c[5]
https://github.com/ychost/HybSCH/blob/master/Sources/Main.c




