ESP8266_SDK发送温度数据到阿里云

14 篇文章 1 订阅
13 篇文章 5 订阅

陈拓 chentuo@ms.xab.ac.cn 2020/04/10-2020/04/16

1. 准备工作

  • 开发环境

看《树莓派安装ESP8266_SDK开发环境》一文。

https://zhuanlan.zhihu.com/p/122246166

树莓派安装ESP8266 NON-OS SDK开发环境_esp-open-sdk ct-ng gmp-CSDN博客

熟悉项目的编译和烧写过程。

  • ESP8266接入阿里云

看《ESP8266_SDK连接阿里云》一文。
https://zhuanlan.zhihu.com/p/128673448

ESP8266_SDK连接阿里云_esp8266 阿里云sdk-CSDN博客

  • 硬件连接

    ■ 树莓派和ESP8266的连接

请看上面两篇文章中的详细描述。

    ■ ESP8266和DS18B20的连接

DS18B20的数据总线DQ连接ESP8266的GPIO2,DS18B20的电源VCC可以用树莓派上的3.3V电源引脚,如果另接3.3V电源要共地。

还有DQ到VCC的4.7k上拉电阻一定要接。

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

树莓派+温度传感器DS18B20_设备树dtb中 温度传感器-CSDN博客

  • 设置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秒采样间隔的数据。

参考文档:

  1. 电脑连接树莓派3B+
    电脑连接树莓派3B+_树莓派3b usb千兆网卡-CSDN博客
  2. 树莓派安装ESP8266_SDK开发环境
    树莓派安装ESP8266 NON-OS SDK开发环境_esp-open-sdk ct-ng gmp-CSDN博客
  3. ESP8266_SDK连接阿里云
    ESP8266_SDK连接阿里云_esp8266 阿里云sdk-CSDN博客
  4. esp8266控制gpio2
    esp8266控制gpio2_esp8266-01s gpio2-CSDN博客
  5. 树莓派 Zero W+温度传感器DS18B20
    树莓派+温度传感器DS18B20_设备树dtb中 温度传感器-CSDN博客
  6. 自己写微信小程序MQTT模拟器自己写微信小程序MQTT模拟器_小程序 多页面 mqtt-CSDN博客
  7. 阿里云物联网平台基本设置-物模型
    阿里云物联网平台基本设置-物模型_阿里云如何在物模型中设置触发条件?-CSDN博客
  8. 树莓派连接阿里云物联网平台-属性(nodejs)树莓派连接阿里云物联网平台-属性(nodejs)_树莓派与阿里云显示未激活-CSDN博客
  9. 树莓派连接阿里云物联网平台-服务(nodejs)树莓派连接阿里云物联网平台-服务(nodejs)_nodejs mqtt连接阿里云物联网平台-CSDN博客
  10. 树莓派连接阿里云物联网平台-订阅(nodejs)树莓派连接阿里云物联网平台-订阅(nodejs)_树莓派与阿里云显示未激活-CSDN博客
  11. 树莓派连接阿里云物联网平台-事件(nodejs)树莓派连接阿里云物联网平台-事件(nodejs)_changetemp:function(temperature){ device.on('messa-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晨之清风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值