ESP8266_SDK发送温度数据到阿里云
陈拓 [email protected] 2020/04/10-2020/04/16
1. 准备工作
- 开发环境
看《树莓派安装ESP8266_SDK开发环境》一文。
https://zhuanlan.zhihu.com/p/122246166
https://blog.csdn.net/chentuo2000/article/details/105296166
熟悉项目的编译和烧写过程。
- ESP8266接入阿里云
看《ESP8266_SDK连接阿里云》一文。
https://zhuanlan.zhihu.com/p/128673448
https://blog.csdn.net/chentuo2000/article/details/105446772
- 硬件连接
■ 树莓派和ESP8266的连接
请看上面两篇文章中的详细描述。
■ ESP8266和DS18B20的连接
DS18B20的数据总线DQ连接ESP8266的GPIO2,DS18B20的电源VCC可以用树莓派上的3.3V电源引脚,如果另接3.3V电源要共地。
还有DQ到VCC的4.7k上拉电阻一定要接。
有关ESP8266的测试请看《树莓派 Zero W+温度传感器DS18B20》一文。 |
- 设置nano的tab键
因为我们写的代码都不长,就用linux自带的文本编辑器nano来写。写代码时会经常用tab键产生空格缩进,nano默认按一次tab键缩进8个空格,我们设置按一次tab键缩进4个空格。
再次打开nano的时候, 按一次tab键就是缩进4个空格了。
- Nano显示行号
先按Ctrl+3,再按 Shift+3。
重复操作可以关闭行号显示。
2. 编写代码
注意截图中每段代码的位置!
2.1 添加操作DS18B20的头文件ds18b20.h
nano include/ds18b20.h
#ifndef __DS18B20_H__
#define __DS18B20_H__
#define DSPORT GPIO_ID_PIN(2) //(DS18B20连接ESP8266-01的GPIO2)
uint8_t ds18b20_reset(void);
int ds18b20_read_temp(void);
#endif
2.2 添加操作DS18B20的C程序ds18b20.c
nano user/ds18b20.c
#include "ets_sys.h"
#include "os_type.h"
#include "osapi.h"
#include "mem.h"
#include "user_interface.h"
#include "gpio.h"
#include "ds18b20.h"
void delay_1ms(uint16 y) {
os_delay_us(y * 1000);
}
/* DS18B20复位 */
uint8_t ICACHE_FLASH_ATTR ds18b20_reset(void) {
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO2); // ESP8266-01的GPIO2连接DS18B20的数据线
GPIO_OUTPUT_SET(DSPORT, 0); // 将总线拉低480us-960us ,总线上所有器件都将被复位
os_delay_us(720); // 保持低电平720us
GPIO_OUTPUT_SET(DSPORT, 1); // 释放总线,恢复高电平
os_delay_us(150); // 等待DS18B20拉低总线
uint8_t ack = GPIO_INPUT_GET(DSPORT); // 获取总线状态
return(ack); // 成功返回0,失败返回1
}
/* 写一位数据 */
void ICACHE_FLASH_ATTR write_bit(uint8_t bit) {
if (bit) { // 写1时序:主机输出低电平,延时2us,主机输出高电平,延时60us。
GPIO_OUTPUT_SET(DSPORT, 0); // 拉低总线
os_delay_us(2); // 延时>1us
GPIO_OUTPUT_SET(DSPORT, 1); // 主机输出高电平
os_delay_us(60); // 延时60us
} else { // 写0时序:主机输出低电平,延时60us,然后释放总线,延时2us。
GPIO_OUTPUT_SET(DSPORT, 0); // 拉低总线
os_delay_us(60); // 延时60us
GPIO_OUTPUT_SET(DSPORT, 1); // 释放总线,恢复高电平
os_delay_us(2);
}
}
/* 写一字节数据 */
void ICACHE_FLASH_ATTR write_byte(uint8_t val) {
uint8_t i, bit;
for (i=0;i<8;i++) { // 写入一个字节的数据,一个时序中写一次
bit=val>>i; // 右移i位
bit&=0x01; // 复制那位数据到temp
write_bit(bit); // 调用write_bit
}
}
/* 读一位数据 */
uint8_t ICACHE_FLASH_ATTR read_bit(void) {
uint8_t bit;
GPIO_OUTPUT_SET(DSPORT, 0); // 拉低总线,开始读时序
os_delay_us(2); // 读数据之前先把总线拉低>1us
GPIO_OUTPUT_SET(DSPORT, 1); // 释放总线
os_delay_us(2); // 延时
bit = GPIO_INPUT_GET(DSPORT); // 获取总线值
os_delay_us(10); // 等待数据稳定
return(bit); // 返回总线值
}
/* 读一字节数据 */
uint8_t ICACHE_FLASH_ATTR read_byte(void) {
uint8_t i,value=0;
for(i=0;i<8;i++) {
if(read_bit()) // 读一字节数据,一个时序中读一次,并作移位处理
value|=0x01<<i;
os_delay_us(46); // 延时以完成此次读时序60us,之后再读下一数据
}
return(value);
}
/* 18B20内部温度转换 */
void ICACHE_FLASH_ATTR temp_convert(void) {
ds18b20_reset(); // 复位,每一次读写之前都要对DS18B20进行复位操作。
os_delay_us(200);
write_byte(0xcc); // 仅一个DS18b20 ,跳过ROM
write_byte(0x44); // 温度变换
delay_1ms(700); // 延时>600ms
}
/* 读温度 */
int ICACHE_FLASH_ATTR ds18b20_read_temp(void) {
int temp = 0;
uint8_t msb, lsb;
temp_convert(); // 18B20进行温度转换
ds18b20_reset(); // 复位
write_byte(0xcc); // 写指令,跳过ROM,仅一个DS18b20
write_byte(0xbe); // 写指令,读暂存存储器
lsb = read_byte(); // 读LSB
msb = read_byte(); // 读MSB
temp = msb;
temp <<= 8;
temp |= lsb;
return temp;
}
程序说明:
- 使用ESP8266的GPIO2
ESP8266 有两个UART。UART0有TX、RX,UART1只有TX。ESP8266的引脚是复用的,默认情况下UART1的TX使用了GPIO2,我们要用GPIO2来对DS18B20进行操作就要将其用途改变成GPIO。
gpio_init(); // init gpio subsytem
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U,FUNC_GPIO2); // 设置GPIO2功能为GPIO
2.3 修改主程序user_main.c
nano user/user_main.c
/* main.c -- MQTT client example */
#include "ets_sys.h"
#include "driver/uart.h"
#include "osapi.h"
#include "mqtt.h"
#include "wifi.h"
#include "config.h"
#include "debug.h"
#include "gpio.h"
#include "user_interface.h"
#include "mem.h"
#include "ds18b20.h"
#if ((SPI_FLASH_SIZE_MAP == 0) || (SPI_FLASH_SIZE_MAP == 1))
#error "The flash map is not supported"
#elif (SPI_FLASH_SIZE_MAP == 2)
#define SYSTEM_PARTITION_OTA_SIZE 0x6A000
#define SYSTEM_PARTITION_OTA_2_ADDR 0x81000
#define SYSTEM_PARTITION_RF_CAL_ADDR 0xfb000
#define SYSTEM_PARTITION_PHY_DATA_ADDR 0xfc000
#define SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR 0xfd000
#elif (SPI_FLASH_SIZE_MAP == 3)
#define SYSTEM_PARTITION_OTA_SIZE 0x6A000
#define SYSTEM_PARTITION_OTA_2_ADDR 0x81000
#define SYSTEM_PARTITION_RF_CAL_ADDR 0x1fb000
#define SYSTEM_PARTITION_PHY_DATA_ADDR 0x1fc000
#define SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR 0x1fd000
#elif (SPI_FLASH_SIZE_MAP == 4)
#define SYSTEM_PARTITION_OTA_SIZE 0x6A000
#define SYSTEM_PARTITION_OTA_2_ADDR 0x81000
#define SYSTEM_PARTITION_RF_CAL_ADDR 0x3fb000
#define SYSTEM_PARTITION_PHY_DATA_ADDR 0x3fc000
#define SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR 0x3fd000
#elif (SPI_FLASH_SIZE_MAP == 5)
#define SYSTEM_PARTITION_OTA_SIZE 0x6A000
#define SYSTEM_PARTITION_OTA_2_ADDR 0x101000
#define SYSTEM_PARTITION_RF_CAL_ADDR 0x1fb000
#define SYSTEM_PARTITION_PHY_DATA_ADDR 0x1fc000
#define SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR 0x1fd000
#elif (SPI_FLASH_SIZE_MAP == 6)
#define SYSTEM_PARTITION_OTA_SIZE 0x6A000
#define SYSTEM_PARTITION_OTA_2_ADDR 0x101000
#define SYSTEM_PARTITION_RF_CAL_ADDR 0x3fb000
#define SYSTEM_PARTITION_PHY_DATA_ADDR 0x3fc000
#define SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR 0x3fd000
#else
#error "The flash map is not supported"
#endif
MQTT_Client mqttClient;
#define GET_TOPIC "/a10hJ4nAdAz/temperature001/user/get"
#define POST_TOPIC "/sys/a10hJ4nAdAz/temperature001/thing/event/property/post"
void wifiConnectCb(uint8_t status)
{
if(status == STATION_GOT_IP){
MQTT_Connect(&mqttClient);
} else {
MQTT_Disconnect(&mqttClient);
}
}
long int id = 1;
char jsonData[256]={0};
void ICACHE_FLASH_ATTR getJsonData(long int id, uint8_t temp[]) {
os_sprintf(jsonData, "{'id':'%d','params':{'temperature':%s},'method':'thing.event.property.post'}", id, temp);
os_printf(" jsonData -> %s\r\n", jsonData);
}
void mqttConnectedCb(uint32_t *args)
{
MQTT_Client* client = (MQTT_Client*)args;
INFO("MQTT: Connected\r\n");
MQTT_Subscribe(client, GET_TOPIC, 0);
char temp[6] = {'2', '3', '.', '4', '5', '\0'};
getJsonData(id, temp);
// 函数原型 BOOL ICACHE_FLASH_ATTR MQTT_Publish(MQTT_Client *client, const char* topic, const char* data, int data_length, int qos, int retain);
MQTT_Publish(client, POST_TOPIC, jsonData, os_strlen(jsonData), 0, 0);
}
void mqttDisconnectedCb(uint32_t *args)
{
MQTT_Client* client = (MQTT_Client*)args;
INFO("MQTT: Disconnected\r\n");
}
void mqttPublishedCb(uint32_t *args)
{
MQTT_Client* client = (MQTT_Client*)args;
INFO("MQTT: Published\r\n");
}
void mqttDataCb(uint32_t *args, const char* topic, uint32_t topic_len, const char *data, uint32_t data_len)
{
char *topicBuf = (char*)os_zalloc(topic_len+1),
*dataBuf = (char*)os_zalloc(data_len+1);
MQTT_Client* client = (MQTT_Client*)args;
os_memcpy(topicBuf, topic, topic_len);
topicBuf[topic_len] = 0;
os_memcpy(dataBuf, data, data_len);
dataBuf[data_len] = 0;
INFO("Receive topic: %s, data: %s \r\n", topicBuf, dataBuf);
INFO("Topic_len = %d, data_len = %d \r\n", topic_len, data_len);
os_free(topicBuf);
os_free(dataBuf);
}
static const partition_item_t at_partition_table[] = {
{ SYSTEM_PARTITION_BOOTLOADER, 0x0, 0x1000},
{ SYSTEM_PARTITION_OTA_1, 0x1000, SYSTEM_PARTITION_OTA_SIZE},
{ SYSTEM_PARTITION_OTA_2, SYSTEM_PARTITION_OTA_2_ADDR, SYSTEM_PARTITION_OTA_SIZE},
{ SYSTEM_PARTITION_RF_CAL, SYSTEM_PARTITION_RF_CAL_ADDR, 0x1000},
{ SYSTEM_PARTITION_PHY_DATA, SYSTEM_PARTITION_PHY_DATA_ADDR, 0x1000},
{ SYSTEM_PARTITION_SYSTEM_PARAMETER, SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR, 0x3000},
};
void ICACHE_FLASH_ATTR user_pre_init(void)
{
if(!system_partition_table_regist(at_partition_table, sizeof(at_partition_table)/sizeof(at_partition_table[0]),SPI_FLASH_SIZE_MAP)) {
os_printf("system_partition_table_regist fail\r\n");
while(1);
}
}
#define BUF_SIZE 128
// mqtt struct
typedef struct {
uint8_t mqtt_client_id[BUF_SIZE];
uint8_t mqtt_host[BUF_SIZE];
uint8_t mqtt_username[BUF_SIZE];
uint8_t mqtt_password[BUF_SIZE];
uint8_t mqtt_key[BUF_SIZE];
} i_mqtt_Cfg;
uint8_t temp_s[8] = {' ','9','9','9','9','9','9','\0'};
// 定时器回调函数
void timer_cb(void) {
uint8_t msb, lsb;
uint8_t temp0,temp1; // temp0温度的小数位,temp1温度的整数位
uint8_t sign; // sign判断温度符号
uint16_t temp_d;
int temp = ds18b20_read_temp();
lsb = (uint8_t)temp;
msb = (uint8_t)(temp >> 8);
// 转换18B20格式到十进制数
temp0 = lsb & 0x0f; // 取4位小数部分存放到temp0
temp1 = ((msb & 0x07)<<4)|((lsb>>4) & 0x0f); // 取7位整数部分存放到存放temp1
sign=(msb>>4==0X0F);
if(sign) { // MS Byte 的前面5位是符号位,同时为 0 或 1,这里只看高4位。如果值为负
temp0 = 16 - temp0; // 求补码(4位 1111=15)
temp1 = 128 - temp1; // 求补码(7位 1111111=127)
}
temp_d = temp1 * 16 + temp0; // 十六进制转换为10进制
temp_d = temp_d * 100 / 16; //12位精度,最小分辨率为0.0625=1/16,乘100保留2位小数
os_printf("ds18b20 temp: %d \n\n", temp);
os_printf("ds18b20 temp_d: %d \n\n", temp_d);
// 转换温度的每个位到ASCII
temp_s[1] = temp_d/10000+'0'; // 百位
temp_s[2] = (temp_d%10000)/1000+'0'; // 十位
temp_s[3] = (temp_d%1000)/100+'0'; // 个位
temp_s[4] = 0x2e; // 小数点
temp_s[5] = (temp_d%100)/10+'0'; // 小数点后第一位
temp_s[6] = temp_d%10+'0'; // 小数点后第二位
// 删除前导0
uint8_t i = 0, j = 0;
for (i=0; i<2 ;i++ ) {
if (temp_s[1] == '0') {
for (j = 0; j < 6; j++) {temp_s[j+1] = temp_s[j+2];}
}
}
// 添加符号
if(sign) {
temp_s[0] = '-';
} else {
temp_s[0] = '+';
}
os_printf(" temp value -> %s\r\n", temp_s);
// 发送温度数据到阿里云
id++;
getJsonData(id, temp_s);
MQTT_Publish(&mqttClient, POST_TOPIC, jsonData, os_strlen(jsonData), 0, 0);
os_printf("-------------------------------------- \r\n");
}
os_timer_t os_timer;
void user_init(void)
{
uart_init(BIT_RATE_115200, BIT_RATE_115200);
os_delay_us(60000);
CFG_Load();
i_mqtt_Cfg i_mqtt_cfg;
os_strncpy(i_mqtt_cfg.mqtt_client_id, MQTT_CLIENT_ID, BUF_SIZE);
os_strncpy(i_mqtt_cfg.mqtt_host, MQTT_HOST, BUF_SIZE);
os_strncpy(i_mqtt_cfg.mqtt_username, MQTT_USER, BUF_SIZE);
os_strncpy(i_mqtt_cfg.mqtt_password, MQTT_PASS, BUF_SIZE);
os_strncpy(i_mqtt_cfg.mqtt_key, DEVICE_SECRET, BUF_SIZE);
// 加密函数原型 void ssl_hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, int key_len, uint8_t *digest);
// 待加密的信息 msg,** key,加密之后的信息 digest
uint8_t mydigest[20], str[40], dst[41];
ssl_hmac_sha1(i_mqtt_cfg.mqtt_password, os_strlen(i_mqtt_cfg.mqtt_password), i_mqtt_cfg.mqtt_key, os_strlen(i_mqtt_cfg.mqtt_key), mydigest);
// 十六进制数转字符串
int i;
for (i = 0; i < 20; i++) {
str[2 * i] = mydigest[i] >> 4;
str[2 * i + 1] = mydigest[i] & 0xf;
}
for (i = 0; i < 40; i++) {
os_sprintf(&dst[i], "%x", str[i]);
}
dst[40] = '\0';
os_printf(" mqtt_pass -> %s\r\n", dst);
MQTT_InitConnection(&mqttClient, i_mqtt_cfg.mqtt_host, sysCfg.mqtt_port, sysCfg.security);
MQTT_InitClient(&mqttClient, i_mqtt_cfg.mqtt_client_id, i_mqtt_cfg.mqtt_username, dst, sysCfg.mqtt_keepalive, 1);
MQTT_InitLWT(&mqttClient, "/lwt", "offline", 0, 0);
MQTT_OnConnected(&mqttClient, mqttConnectedCb);
MQTT_OnDisconnected(&mqttClient, mqttDisconnectedCb);
MQTT_OnPublished(&mqttClient, mqttPublishedCb);
MQTT_OnData(&mqttClient, mqttDataCb);
WIFI_Connect(sysCfg.sta_ssid, sysCfg.sta_pwd, wifiConnectCb);
INFO("\r\nSystem started ...\r\n");
gpio_init(); // init gpio subsytem
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U,FUNC_GPIO2); // 设置GPIO2功能为GPIO
uint8_t ack = ds18b20_reset();
os_printf(" \n\nds18b20_ack -> %d\n\n", ack);
os_timer_disarm(&os_timer); // 关闭该定时器
os_timer_setfn(&os_timer, (os_timer_func_t *) (timer_cb), NULL); // 设置定时器回调函数
os_timer_arm(&os_timer, 600000, true); // 启动该定时器,设置定时时间为10分钟
}
程序说明:
- 对于一般的温度监测10分钟采样一次就可以了。
- 对照前面两篇文章查看代码的修改情况。
2.4 编译
./gen_misc.sh < esp01
2.5 烧写
- 烧写Flash
■ 第一次烧写命令
esptool.py --port /dev/ttyAMA0 write_flash 0x00000 eagle.flash.bin 0x10000 eagle.irom0text.bin 0xfb000 blank.bin 0xfc000 esp_init_data_default_v08.bin 0xfe000 blank.bin
■ 之后的烧写命令
esptool.py --port /dev/ttyAMA0 write_flash 0x00000 eagle.flash.bin 0x10000 eagle.irom0text.bin
在上一篇文章中我们已经烧写过了,所以:
3. 测试
3.1 查看ESP8266的运行情况
用minicom查看ESP8266的运行情况,关于minicom的使用请看前面两篇文章。
3.2 查看云端的数据
有关阿里云物联网平台的操作,请看参考前面两篇文章和参考文档中的文章。
为了加快测试,我临时将采用时间间隔设置为10秒,上图和下表是10秒采样间隔的数据。
参考文档:
- 电脑连接树莓派3B+
https://blog.csdn.net/chentuo2000/article/details/103332186 - 树莓派安装ESP8266_SDK开发环境
https://blog.csdn.net/chentuo2000/article/details/105296166 - ESP8266_SDK连接阿里云
https://blog.csdn.net/chentuo2000/article/details/105446772 - esp8266控制gpio2
https://blog.csdn.net/zakigo/article/details/103029920 - 树莓派 Zero W+温度传感器DS18B20
https://blog.csdn.net/chentuo2000/article/details/81051701 - 自己写微信小程序MQTT模拟器https://blog.csdn.net/chentuo2000/article/details/102507560
- 阿里云物联网平台基本设置-物模型
https://blog.csdn.net/chentuo2000/article/details/103559553 - 树莓派连接阿里云物联网平台-属性(nodejs)https://blog.csdn.net/chentuo2000/article/details/103705694
- 树莓派连接阿里云物联网平台-服务(nodejs)https://blog.csdn.net/chentuo2000/article/details/103754860
- 树莓派连接阿里云物联网平台-订阅(nodejs)https://blog.csdn.net/chentuo2000/article/details/103769449
- 树莓派连接阿里云物联网平台-事件(nodejs)https://blog.csdn.net/chentuo2000/article/details/103805559
推荐阅读
-
Linux(CentOS7)设置自动备份数据库到阿里云OSS
-
如何将阿里云mysql RDS备份文件恢复到自建数据库
-
Arduino Esp8266开发(上传数据到阿里云MQTT服务器)
-
Delphi阿里云邮件推送【支持单一发信、邮件批量发送和获取指定条件下的发送数据】
-
阿里云服务器怎样将本地数据库数据导入到服务器数据库
-
阿里云服务器怎样将本地数据库数据导入到服务器数据库
-
arduino通过esp8266模块发送数据到云服务器
-
Linux(CentOS7)设置自动备份数据库到阿里云OSS
-
ESP8266_SDK发送温度数据到阿里云
-
阿里云服务器怎样将本地数据库数据导入到服务器数据库,该怎么处理