欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ESP8266_SDK发送温度数据到阿里云

程序员文章站 2022-05-03 09:22:13
...

陈拓 [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_SDK发送温度数据到阿里云

有关ESP8266的测试请看《树莓派 Zero W+温度传感器DS18B20》一文。

https://blog.csdn.net/chentuo2000/article/details/81051701

  • 设置nano的tab键

因为我们写的代码都不长,就用linux自带的文本编辑器nano来写。写代码时会经常用tab键产生空格缩进,nano默认按一次tab键缩进8个空格,我们设置按一次tab键缩进4个空格。

ESP8266_SDK发送温度数据到阿里云

再次打开nano的时候, 按一次tab键就是缩进4个空格了。

  • Nano显示行号

先按Ctrl+3,再按 Shift+3。

重复操作可以关闭行号显示。

2. 编写代码

注意截图中每段代码的位置!

2.1 添加操作DS18B20的头文件ds18b20.h

nano include/ds18b20.h

ESP8266_SDK发送温度数据到阿里云

#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

ESP8266_SDK发送温度数据到阿里云

 

#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

ESP8266_SDK发送温度数据到阿里云

 

/* 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

ESP8266_SDK发送温度数据到阿里云

ESP8266_SDK发送温度数据到阿里云

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

在上一篇文章中我们已经烧写过了,所以:

ESP8266_SDK发送温度数据到阿里云

3. 测试

3.1 查看ESP8266的运行情况

用minicom查看ESP8266的运行情况,关于minicom的使用请看前面两篇文章。

ESP8266_SDK发送温度数据到阿里云

3.2 查看云端的数据

有关阿里云物联网平台的操作,请看参考前面两篇文章和参考文档中的文章。

ESP8266_SDK发送温度数据到阿里云

ESP8266_SDK发送温度数据到阿里云

为了加快测试,我临时将采用时间间隔设置为10秒,上图和下表是10秒采样间隔的数据。

ESP8266_SDK发送温度数据到阿里云

 

参考文档:

  1. 电脑连接树莓派3B+
    https://blog.csdn.net/chentuo2000/article/details/103332186
  2. 树莓派安装ESP8266_SDK开发环境
    https://blog.csdn.net/chentuo2000/article/details/105296166
  3. ESP8266_SDK连接阿里云
    https://blog.csdn.net/chentuo2000/article/details/105446772
  4. esp8266控制gpio2
    https://blog.csdn.net/zakigo/article/details/103029920
  5. 树莓派 Zero W+温度传感器DS18B20
    https://blog.csdn.net/chentuo2000/article/details/81051701
  6. 自己写微信小程序MQTT模拟器https://blog.csdn.net/chentuo2000/article/details/102507560
  7. 阿里云物联网平台基本设置-物模型
    https://blog.csdn.net/chentuo2000/article/details/103559553
  8. 树莓派连接阿里云物联网平台-属性(nodejs)https://blog.csdn.net/chentuo2000/article/details/103705694
  9. 树莓派连接阿里云物联网平台-服务(nodejs)https://blog.csdn.net/chentuo2000/article/details/103754860
  10. 树莓派连接阿里云物联网平台-订阅(nodejs)https://blog.csdn.net/chentuo2000/article/details/103769449
  11. 树莓派连接阿里云物联网平台-事件(nodejs)https://blog.csdn.net/chentuo2000/article/details/103805559