作业题目5:
ESP866连接网络成功之后,实现输入一个MSH命令,创建一个动态线程,在该线程回调函数中实现一分钟上传一次DS18B20温度数据至本地TCP服务器。
大家提交作业时,直接在本贴下方跟贴即可
本次课题我使用温湿度传感器DTH11,ESP8266-01S。在之前的工程上添加论文DTH11的驱动程序,
static void DHT11_Mode_IPU(void);
static void DHT11_Mode_Out_PP(void);
static uint8_t DHT11_ReadByte(void);
/* 函数体 --------------------------------------------------------------------*/
/**
* 函数功能:
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
void DHT11_Delay(rt_uint32_t time)
{
uint8_t i;
while(time)
{
for (i = 0; i < 16; i++)
{
}
time--;
}
}
/**
* 函数功能: DHT11 初始化函数
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
void DHT11_Init ( void )
{
DHT11_Dout_GPIO_CLK_ENABLE();
DHT11_Mode_Out_PP();
DHT11_Dout_HIGH(); // 拉高GPIO
rt_thread_mdelay(3000);
}
/**
* 函数功能: 使DHT11-DATA引脚变为上拉输入模式
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void DHT11_Mode_IPU(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* 串口外设功能GPIO配置 */
GPIO_InitStruct.Pin = DHT11_Dout_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(DHT11_Dout_PORT, &GPIO_InitStruct);
}
/**
* 函数功能: 使DHT11-DATA引脚变为推挽输出模式
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static void DHT11_Mode_Out_PP(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* 串口外设功能GPIO配置 */
GPIO_InitStruct.Pin = DHT11_Dout_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT11_Dout_PORT, &GPIO_InitStruct);
}
/**
* 函数功能: 从DHT11读取一个字节,MSB先行
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
static uint8_t DHT11_ReadByte ( void )
{
uint8_t i, temp=0;
for(i=0;i<8;i++)
{
/*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/
while(DHT11_Data_IN()==GPIO_PIN_RESET);
/*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,
*通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时
*/
DHT11_Delay(40); //延时x us 这个延时需要大于数据0持续的时间即可
if(DHT11_Data_IN()==GPIO_PIN_SET)/* x us后仍为高电平表示数据“1” */
{
/* 等待数据1的高电平结束 */
while(DHT11_Data_IN()==GPIO_PIN_SET);
temp|=(uint8_t)(0x01<<(7-i)); //把第7-i位置1,MSB先行
}
else // x us后为低电平表示数据“0”
{
temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行
}
}
return temp;
}
/**
* 函数功能: 一次完整的数据传输为40bit,高位先出
* 输入参数: DHT11_Data:DHT11数据类型
* 返 回 值: ERROR: 读取出错
* SUCCESS:读取成功
* 说 明:8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和
*/
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{
uint8_t temp;
uint16_t humi_temp;
/*输出模式*/
DHT11_Mode_Out_PP();
/*主机拉低*/
DHT11_Dout_LOW();
/*延时18ms*/
rt_thread_mdelay(18);
/*总线拉高 主机延时30us*/
DHT11_Dout_HIGH();
DHT11_Delay(30); //延时30us
/*主机设为输入 判断从机响应信号*/
DHT11_Mode_IPU();
/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/
if(DHT11_Data_IN()==GPIO_PIN_RESET)
{
/*轮询直到从机发出 的80us 低电平 响应信号结束*/
while(DHT11_Data_IN()==GPIO_PIN_RESET);
/*轮询直到从机发出的 80us 高电平 标置信号结束*/
while(DHT11_Data_IN()==GPIO_PIN_SET);
/*开始接收数据*/
DHT11_Data->humi_high8bit= DHT11_ReadByte();
DHT11_Data->humi_low8bit = DHT11_ReadByte();
DHT11_Data->temp_high8bit= DHT11_ReadByte();
DHT11_Data->temp_low8bit = DHT11_ReadByte();
DHT11_Data->check_sum = DHT11_ReadByte();
/*读取结束,引脚改为输出模式*/
DHT11_Mode_Out_PP();
/*主机拉高*/
DHT11_Dout_HIGH();
/* 对数据进行处理 */
humi_temp=DHT11_Data->humi_high8bit*100+DHT11_Data->humi_low8bit;
DHT11_Data->humidity =(float)humi_temp/100;
humi_temp=DHT11_Data->temp_high8bit*100+DHT11_Data->temp_low8bit;
DHT11_Data->temperature=(float)humi_temp/100;
/*检查读取的数据是否正确*/
temp = DHT11_Data->humi_high8bit + DHT11_Data->humi_low8bit +
DHT11_Data->temp_high8bit+ DHT11_Data->temp_low8bit;
if(DHT11_Data->check_sum==temp)
{
return SUCCESS;
}
else
return ERROR;
}
else
return ERROR;
}
使用stm32L476的USART3连接WiFi模块,代码移植老师是代码框架,但是其中的macESP8266_Usart函数也就是串口格式化输出函数,我修改后如下,并不能使用。不知道是不是hal库还是我哪里出错了的原因。调试看到传过来的数据到形参Data中就不对了,但是上一级的数据都是对的。
void USART_printf ( UART_HandleTypeDef * USARTx, char * Data, ... )
{
const char *s;
uint8_t a;
int d;
char buf[16];
va_list ap;
va_start(ap, Data);
while ( * Data != 0 ) // 判断是否到达字符串结束符
{
if ( * Data == 0x5c ) //'\'
{
switch ( *++Data )
{
case 'r': //回车符
//USART_SendData(USARTx, 0x0d);
a= 0x0d;
HAL_UART_Transmit(USARTx, &a, 1, 10);
Data ++;
break;
case 'n': //换行符
//USART_SendData(USARTx, 0x0a);
a= 0x0a;
HAL_UART_Transmit(USARTx, &a, 1, 10);
Data ++;
break;
default:
Data ++;
break;
}
}
else if ( * Data == '%')
{ //
switch ( *++Data )
{
case 's': //字符串
s = va_arg(ap, const char *);
for ( ; *s; s++)
{
//USART_SendData(USARTx,*s);
HAL_UART_Transmit(USARTx, (uint8_t *)s, 1, 0xFFFF);
//while( __HAL_UART_GET_FLAG(USARTx,UART_FLAG_TXE) == RESET );
}
Data++;
break;
case 'd':
//十进制
d = va_arg(ap, int);
itoa(d, buf, 10);
for (s = buf; *s; s++)
{
//USART_SendData(USARTx,*s);
HAL_UART_Transmit(USARTx, (uint8_t *)s, 1, 0xFFFF);
//while( __HAL_UART_GET_FLAG(USARTx,UART_FLAG_TXE) == RESET );
}
Data++;
break;
default:
Data++;
break;
}
}
else HAL_UART_Transmit(USARTx, (uint8_t *)*Data++, 1, 0xFFFF);
//while ( __HAL_UART_GET_FLAG(USARTx,UART_FLAG_TXE) == RESET );
__HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE);
__HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);
}
}
没有用这个函数,我自己在ESP8266_Cmd函数中直接发送了代码如下
rt_bool_t ESP8266_Cmd ( char * cmd, char * reply1, char * reply2, uint32_t waittime )
{
volatile rt_uint8_t len = 0;
rt_uint8_t i;
char Scmd[50] = {0};
//Scmd = &cmd;
rt_memcpy(Scmd,cmd,rt_strlen(cmd));
strEsp8266_Fram_Record .InfBit .FramLength = 0; //从新开始接收新的数据包
len = rt_strlen(Scmd);
//macESP8266_Usart( "%s\r\n", cmd );
Scmd[rt_strlen(Scmd)] = 0x0D;
Scmd[rt_strlen(Scmd)] = 0x0A;
len = rt_strlen(Scmd);
for(i = 0; i < len; i++)
{
HAL_UART_Transmit( &huart3,&Scmd[i], 1,0xFFFF );
}
//rt_memset(Scmd,0,50);
//HAL_UART_Transmit( &huart3,Scmd, rt_strlen(Scmd),0xFFFF );
if ( ( reply1 == 0 ) && ( reply2 == 0 ) ) //不需要接收数据
return true;
rt_thread_mdelay(waittime);
strEsp8266_Fram_Record .Data_RX_BUF [ strEsp8266_Fram_Record .InfBit .FramLength ] = '\0';
if ( ( reply1 != 0 ) && ( reply2 != 0 ) )
return ( ( rt_bool_t ) rt_strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply1 ) || ( rt_bool_t ) rt_strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply2 ) );
else if ( reply1 != 0 )
return ( ( rt_bool_t ) rt_strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply1 ) );
else
return ( ( rt_bool_t ) rt_strstr ( strEsp8266_Fram_Record .Data_RX_BUF, reply2 ) );
}
void ATcmd(int argc,char **argv)
{
if(!rt_strcmp(argv[1],"AT"))
{
ESP8266_Cmd ( "AT", "OK", NULL, 500 );
printf("%s\r\n",strEsp8266_Fram_Record .Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"STA"))//设置为工作站模式
{
ESP8266_Net_Mode_Choose ( STA );
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"AP"))//设置为热点模式
{
ESP8266_Net_Mode_Choose ( AP );
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"STA_AP")) //设置为工作站+热点模式
{
ESP8266_Net_Mode_Choose ( STA_AP );
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"JoinAP")) //加入热点
{
ESP8266_JoinAP ( argv[2], argv[3]);
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"ipconfig")) //查询本机IP
{
ESP8266_InquireIP( );
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"Mult")) //开启多连接
{
ESP8266_Enable_MultipleId(ENABLE);
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"GetStatus"))
{
ESP8266_Get_LinkStatus();
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"LinkServer")) //加入服务器
{
if(!rt_strcmp(argv[2],"TCP"))
ESP8266_Link_Server ( enumTCP, argv[3], argv[4], Single_ID_0);
else
ESP8266_Link_Server ( enumUDP, argv[3], argv[4], Single_ID_0);
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"CloseLink")) //关闭TCP或UDP连接
{
ESP8266_Close_Link();
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"Unvarnish")) //开启透传
{
ESP8266_UnvarnishSend();
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"USendData")) //透传时发送数据,数据间不能包含空格;若需发送空格数据请加双引号
{
ESP8266_SendString ( ENABLE, argv[2], rt_strlen(argv[2]), Single_ID_0 );
printf("Send Data:%s\r\n",argv[2]);
}
else if(!rt_strcmp(argv[1],"SendData")) //透传时发送数据,数据间不能包含空格;若需发送空格数据请加双引号
{
ESP8266_SendString ( DISABLE, argv[2], rt_strlen(argv[2]), Single_ID_0 );
printf("Send Data:%s\r\n",argv[2]);
}
else if(!rt_strcmp(argv[1],"ExitUnvarnish")) //关闭透传
{
ESP8266_ExitUnvarnishSend ();
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"Rece"))
{
ESP8266_ReceiveString(DISABLE);
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
else if(!rt_strcmp(argv[1],"URece"))
{
ESP8266_ReceiveString(ENABLE);
printf("%s\n",strEsp8266_Fram_Record.Data_RX_BUF);
}
}
MSH_CMD_EXPORT(ATcmd, ESP8266 Test.);
两个驱动调试完成后,编写上传温湿度的线程函数
void UpdateDTH11(void)
{
dht_thread = rt_thread_create("updataDTH11",
dht_thread_entry,
RT_NULL,
512,
4,
10);
if(dht_thread != RT_NULL)
{
rt_thread_startup(dht_thread);
}
}
MSH_CMD_EXPORT(UpdateDTH11, UpdataDTH Test.);
void dht_thread_entry(void *parameter)
{
char cStr [50];
DHT11_Data_TypeDef DHT11_Data;
DHT11_Init();
while(1)
{
DHT11_Read_TempAndHumidity(&DHT11_Data);
//rt_kprintf("temperature = %d\n",(rt_uint32_t)DHT11_Data.temperature);
//rt_kprintf("humidity = %d\n\n",(rt_uint32_t)DHT11_Data.humidity);
sprintf ( cStr, "temperature = %d", (rt_uint32_t)DHT11_Data.temperature);
ESP8266_Cmd ( cStr, "SEND OK", 0, 100 );
rt_memset(cStr,0,50);
sprintf ( cStr, "humidity = %d\r\n", (rt_uint32_t)DHT11_Data.humidity);
ESP8266_Cmd ( cStr, "SEND OK", 0, 100 );
rt_memset(cStr,0,50);
rt_thread_mdelay(1000);
}
}
通过控制台连接WiFi和TCP服务器后,打开透传模式,在调用命令UpdateDTH11初始化线程。
[i=s] 本帖最后由 yichao 于 2020-4-5 22:59 编辑 [/i]
[md]- 创建动态线程 通过MSH命令触发
static rt_thread_t uploadtemp_thread = RT_NULL;
void uploadtemp_thread_entry(void *parameter);
int create_thread_bymsh(void)
{
uploadtemp_thread = rt_thread_create("uploadtemp_thread",
uploadtemp_thread_entry,
RT_NULL,
1024,
5,
10);
if(uploadtemp_thread != RT_NULL)
{
rt_thread_startup(uploadtemp_thread);
}
return 0;
}
MSH_CMD_EXPORT(create_thread_bymsh, create_thread_bymsh.);
while(1)
{
//假设之前模块已经连接就绪 并处于透传模式 ESP8266_UnvarnishSend();
rt_kprintf("begin to upload...");
sprintf(SendData,"\r\nTemperature: %.1f\r\n",DS18B20_GetTemp_SkipRom());
ESP8266_SendString ( ENABLE, SendData, strlen(SendData), Single_ID_0 );
//发送数据
rt_thread_mdelay(60000-100);
}
```[/md]
帖子不好排版,直接贴CSDN地址交作业啦
https://blog.csdn.net/qq_22902757/article/details/105335527
整体部分,分为三部分,以下是部分说明[list]
[*]DS18B20驱动实现
[/list] DS18B20实现最重要的就是us的延迟实现,更改主频是十分不建议的!!建议的方式为定时器,https://blog.csdn.net/zhaqonianzhu/article/details/105291715 我的实现笔记
void HAL_Delay_us(uint32_t Delay_us)
{
uint16_t wait = Delay_us;
if (wait < UINT16_MAX) //设定定时器计数器阈值
wait++;
__HAL_TIM_SET_COUNTER(&htim14,1); //将CNT值设置为0
HAL_TIM_Base_Start(&htim14); //启动定时器
while(__HAL_TIM_GET_COUNTER(&htim14) < wait) //查询计数器的计数值判断
{
}
// HAL_TIM_Base_Stop(&htim14);
}
实现代码十行左右,接着就是DS18B20的数据读取,DS18B20每次操作都是三步,
Step 1. InitializationStep 2. ROM Command (followed by any required data exchange)
Step 3. DS18B20 Function Command (followed by any required data exchange)
需要注意的是指令是从低位先发送和接受。ROM使用0xCC跳过就好了,读取温度时建议时间长一点,默认12bit的数据转换时间为750ms。
#include <rtthread.h>
#include "DS18B20.h"
#include "tim.h"
#include <rthw.h>
#include <stdio.h>
#include <usart.h>
#define DS18B20_PRIORITY 11
#define DS18B20_STACK_SIZE 1024
#define DS18B20_TIMESLICE 5
static rt_thread_t DS18B20_run_tid = RT_NULL;
void HAL_Delay_us(uint32_t Delay_us)
{
uint16_t wait = Delay_us;
if (wait < UINT16_MAX) //设定定时器计数器阈值
wait++;
__HAL_TIM_SET_COUNTER(&htim14,1); //将CNT值设置为0
HAL_TIM_Base_Start(&htim14); //启动定时器
while(__HAL_TIM_GET_COUNTER(&htim14) < wait) //查询计数器的计数值判断
{
}
// HAL_TIM_Base_Stop(&htim14);
}
/** Configure pins as
* * Input
* * Output
*/
void PIN_Mode_Set(uint32_t Mode, uint32_t Pull ,GPIO_PinState PinState)
{
static GPIO_InitTypeDef GPIO_InitStruct = {0};
if(Mode == GPIO_MODE_OUTPUT_PP)
{
HAL_GPIO_WritePin(US_PIN_GPIO_Port, US_PIN_Pin, PinState);
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
}
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = US_PIN_Pin;
GPIO_InitStruct.Mode = Mode;
GPIO_InitStruct.Pull = Pull;
HAL_GPIO_Init(US_PIN_GPIO_Port, &GPIO_InitStruct);
}
/*功能:向DS18B20发送一个复位信号*/
int DS18B20_RESET()
{
PIN_Mode_Set(GPIO_MODE_OUTPUT_PP,GPIO_PULLDOWN,GPIO_PIN_RESET);//拉低总线480~960 us ,对 DS18B20 芯片进行复位
HAL_Delay_us(500);
PIN_Mode_Set(GPIO_MODE_INPUT,GPIO_PULLUP,GPIO_PIN_RESET);//释放总线15~60 us
HAL_Delay_us(68);
/*功能:检测DS18B20存在脉冲*/
for(int Index = 0;Index < 12;Index++)
{
if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(US_PIN_GPIO_Port,US_PIN_Pin))
{
HAL_Delay_us(500);
return 0;
}
HAL_Delay_us(20);
}
return -1;
}
void DS18B20_Write_Bit(GPIO_PinState PinState)
{
PIN_Mode_Set(GPIO_MODE_OUTPUT_PP,GPIO_PULLDOWN,GPIO_PIN_RESET);
if (GPIO_PIN_RESET == PinState)
{
HAL_Delay_us(80);//写 0 主机拉低总线60-120us
PIN_Mode_Set(GPIO_MODE_OUTPUT_PP,GPIO_PULLUP,GPIO_PIN_SET);
}
else
{
HAL_Delay_us(10);
PIN_Mode_Set(GPIO_MODE_OUTPUT_PP,GPIO_PULLUP,GPIO_PIN_SET);
HAL_Delay_us(60);
}
HAL_Delay_us(50);
return;
}
GPIO_PinState DS18B20_Read_Bit()
{
GPIO_PinState sta;
PIN_Mode_Set(GPIO_MODE_OUTPUT_PP,GPIO_PULLDOWN,GPIO_PIN_RESET);
HAL_Delay_us(2);
PIN_Mode_Set(GPIO_MODE_INPUT,GPIO_PULLUP,GPIO_PIN_RESET);
HAL_Delay_us(10);
sta = HAL_GPIO_ReadPin(US_PIN_GPIO_Port,US_PIN_Pin);
HAL_Delay_us(60);
PIN_Mode_Set(GPIO_MODE_OUTPUT_PP,GPIO_PULLUP,GPIO_PIN_SET);
return sta;
}
/*功能:向DS18B20写一个字节数据(命令)*/
void DS18B20_Write_Byte(uint8_t byte)
{
for(int Index = 0;Index < 8;Index++)
{
DS18B20_Write_Bit((byte&(0x01<<Index)) == 0 ? GPIO_PIN_RESET : GPIO_PIN_SET);
}
}
uint8_t DS18B20_Read_Byte()
{
uint8_t re_data = 0;
for(int Index = 0;Index < 8;Index++)
{
re_data |= (DS18B20_Read_Bit()<<Index);
}
return re_data;
}
// 向传感器发送指令,测量温度
int DS18B20_Temperature_Conversion()
{
if(0 == DS18B20_RESET())
{
DS18B20_Write_Byte(0xcc);
DS18B20_Write_Byte(0x44);
return 0;
}
return -1;
}
float DS18B20_Get_Temperature()
{
uint8_t LS_BYTE ;
uint8_t MS_BYTE ;
if(0 == DS18B20_RESET())
{
DS18B20_Write_Byte(0xcc);
DS18B20_Write_Byte(0xbe);
LS_BYTE = DS18B20_Read_Byte();
MS_BYTE = DS18B20_Read_Byte();
return ((LS_BYTE>>4) + (MS_BYTE<<4) + ((LS_BYTE&0x0f)*0.625));
}
return -1;
}
static void DS18B20_entry(void *param)
{
rt_base_t level ;
float temp;
static uint8_t data_send[128];
while (1)
{
level = rt_hw_interrupt_disable();
if(0 == DS18B20_Temperature_Conversion())
{
rt_thread_mdelay(1000);
temp = DS18B20_Get_Temperature();
}
rt_hw_interrupt_enable(level);
printf("DS18B20_Get_Temperature:%4.1f℃\r\n",temp);
snprintf((char*)data_send,sizeof(data_send),"DS18B20:%4.1f℃",temp);
HAL_UART_Transmit(&huart2,data_send,rt_strlen((char*)data_send),3000);
rt_thread_mdelay(60*1000);
}
}
int DS18B20_create(void)
{
DS18B20_run_tid = rt_thread_create("DS18B20_run",
DS18B20_entry,
RT_NULL,
DS18B20_STACK_SIZE,
DS18B20_PRIORITY,
50);
if (DS18B20_run_tid != RT_NULL)
rt_thread_startup(DS18B20_run_tid);
return 0;
}
//INIT_DEVICE_EXPORT(DS18B20_create);
MSH_CMD_EXPORT(DS18B20_create, DS18B20 create);
[list]
[*]ESP8266联网实现
[/list]AT模块主要我实现的了简单的AT框架,建议使用os带的框架比较好,因为是透传模式,AT指令没有几条。串口数据接是直接send到消息队列中,读取数据时从消息队列取数据并进行判断数据。
int at_cmd_send(uint32_t timeout,const char *cmd, ...)
{
int len = 0;
char * p = echo.buffer;
static uint8_t at_cmd[512];
memset(at_cmd,0,sizeof(at_cmd));
va_list args;
va_start(args, cmd);
len = vsnprintf((char*)at_cmd, sizeof(at_cmd)-1, cmd, args);
va_end(args);
HAL_UART_Transmit(&huart2,at_cmd,len,3000);
rt_kprintf("\r\n>%s\r\n",at_cmd);
if (-1 == at_uart_read((uint8_t*)echo.buffer,echo.buffer_size,timeout))
{
echo.status = AT_ECHO_STATUS_NONE;
return -1;
}
rt_kprintf("\r\n>%s\r\n",echo.buffer);
for(int i = 0;i < (echo.buffer_size - 1);i++)
{
if (strncmp(echo.echo_expect,p++ ,strlen(echo.echo_expect)) == 0)
{
echo.status = AT_ECHO_STATUS_EXPECT;
return 0;
}
else if(echo.echo_expect == NULL)
{
echo.status = AT_ECHO_STATUS_OK;
return 0;
}
echo.status = AT_ECHO_STATUS_NONE;
}
return -1;
}
[attach]14724[/attach]联网成功之后就进入到透传模式,串口可以直接收发数据
[list]
[*]组合实现一分钟发送一次温度数据
[/list]见上面DS18B20代码实现,我们只需要把创建导入到msh命令中
MSH_CMD_EXPORT(DS18B20_create, DS18B20 create);
这里需要注意的是,假如有需要可以进行判断任务是否为空判断任务id是否存在
int DS18B20_create(void)
{
if (DS18B20_run_tid != RT_NULL)
return 0;
DS18B20_run_tid = rt_thread_create("DS18B20_run",
DS18B20_entry,
RT_NULL,
DS18B20_STACK_SIZE,
DS18B20_PRIORITY,
50);
if (DS18B20_run_tid != RT_NULL)
rt_thread_startup(DS18B20_run_tid);
return 0;
}
//INIT_DEVICE_EXPORT(DS18B20_create);
MSH_CMD_EXPORT(DS18B20_create, DS18B20 create);
[attach]14725[/attach]到此完成了全部工作。
一直在等待传感器到货,今天刚到就直接开始玩起来,代码早就是弄好了的,就等着调试呢!
需求:向服务器发送温度数据
需要用到:串口数据收发,传感器数据读取,网络通信
串口数据收发在第二次作业中就已经完成,直接拿过来用
传感器是DS18B20,是单线通信传感器,驱动一大堆,就不贴出来了,直接开始下一步
网络通信,由于ESP8266是使用的AT指令方式和,比较简单,直接写一个指令发送检验就可以了,代码如下:
rt_err_t esp_send_cmd(char *cmd, char *reply1, char *reply2, uint32_t waittime)
{
UART4_RX_STA = 0;
USART_printf(UART4, "%s\r\n", cmd);
if ((reply1 == 0)&&(reply2 == 0))
return RT_EOK;
while((UART4_RX_STA&0x8000)==0&&waittime)
{
rt_thread_mdelay(1);
waittime--;
}
UART4_RX_STA = 0;
if(waittime == 0)
return -RT_ETIMEOUT;
else if((reply1 != 0) && (reply2 != 0))
{
if(strstr((char *)UART4_RX_BUF, reply1) != NULL || strstr((char *)UART4_RX_BUF, reply2) != NULL)
return RT_EOK;
}
else if(reply1 != 0 && strstr((char *)UART4_RX_BUF, reply1) != NULL)
{
return RT_EOK;
}
else if(strstr((char *)UART4_RX_BUF, reply2) != NULL) return RT_EOK;
else return -RT_ERROR;
}
模块驱动有了,就是一系列的配置,例如链接AP,链接服务器,发送数据,代码如下:
rt_err_t esp_joinap(char *pSSID, char *pPassWord)
{
char cCmd[120];
sprintf(cCmd, "AT+CWJAP=\"%s\",\"%s\"", pSSID, pPassWord);
return(esp_send_cmd(cCmd, "OK", NULL, 5000));
}
rt_err_t esp_start_socket(char *mode,u8 socket, char *ip, u16 port)
{
rt_err_t result = RT_EOK;
char str[50];
result = esp_send_cmd("AT+CIPMUX=1","OK",NULL,500);
if(RT_EOK != result) return result;
sprintf(str,"AT+CIPSTART=%d,\"%s\",\"%s\",%d",socket,mode,ip,port);
rt_kprintf("%s\n",str);
result = esp_send_cmd(str, "CONNECT",NULL,2000);
return result;
}
rt_err_t esp_close_socket(u8 socket)
{
char *str;
sprintf(str, "AT+CIPCLOSE=%d",socket);
return(esp_send_cmd(str,"CLOSED",NULL,1000));
}
rt_err_t esp_send(u8 socket,char *str)
{
rt_err_t result = RT_EOK;
char cmd[1024];
u8 data_len = strlen(str);
sprintf(cmd, "AT+CIPSEND=%d,%d",socket,data_len);
result = esp_send_cmd(cmd,">",NULL,100);
if (RT_ETIMEOUT == result)
{
return result;
}
result = esp_send_cmd(str,"OK",NULL,500);
return result;
}
最后就是app服务程序了,也很简单,就是将数据向服务器发送就可以了,代码如下:
static rt_thread_t server_thread = RT_NULL;
void server_entry(void *parameter)
{
float temp = 0.0;
char data[20];
rt_err_t result = RT_EOK;
__retry:
esp_init();
rt_kprintf("start esp8266!\n");
rt_thread_delay(5000);
result = esp_start_socket("TCP",0,"192.168.31.178",1234);
if(RT_EOK != result) goto __exit;
rt_kprintf("link the server ok!\n");
while(1)
{
temp = DS18B20_GetTemp();
sprintf(data,"Temperature:%.1f\r\n",temp);
result = esp_send(0,data);
if(RT_EOK != result) goto __exit;
rt_thread_delay(5000);
}
__exit:
esp_close_socket(0);
rt_kprintf("close the socket!\n");
goto __retry;
}
void wifi_task(void)
{
/* 创建线程 */
server_thread = rt_thread_create("server", server_entry, \
RT_NULL, 512, 10, 10);
if(server_thread != RT_NULL)
{
/* 启动线程 */
rt_thread_startup(server_thread);
rt_kprintf("server thread start!\n");
}
}
INIT_APP_EXPORT(wifi_task);
得到结果如图所示:
[attach]14744[/attach][attach]14745[/attach]
另外,针对定时一分钟发送一次数据,可以使用软件定时器,然后将发送数据放在定时器回调函数中,前提是将服务器链接成功,代码如下:
char data[50];
u8 server_socket = 0;
static void Temp_callback(void* parameter)
{
sprintf(data,"Temperature:%.1f\r\n",temp);
esp_send(server_socket,data);
}
int upload_data(void)
{
Ds18B20Timer = rt_timer_create("Temp", /* 软件定时器的名称 */
Temp_callback, /* 软件定时器的回调函数 */
0, /* 定时器超时函数的入口参数 */
RT_TICK_PER_SECOND, /* 软件定时器的超时时间(周期回调时间) */
RT_TIMER_FLAG_PERIODIC );/* 软件定时器HARD_TIMER模式 周期模式 */
return 0;
}
MSH_CMD_EXPORT(upload_data);
效果和上面的一样,就不贴图了。
1创建一个任务线程,并导出到控制台
2,在任务线程里面首先初始化ESP8266 然后 切换工作模式为STA 链接WIFI网络,连接TCP服务器 进入透传模式,在任务线程的主循环里面每隔1分钟获取一下温度并封包通过ESP8266发送
观看何老师的RT-Thread Nano-DS18B20-高精度微秒延时和RT-Thread Nano-ESP82266 WIFI相关的视频之后,完成本次作业。
移植何老师TemperatureUploadLocalSer.c,WifiCmdTest.c,BspDs18b20.c和BspEsp8266.c以及相关的头文件,在这基础上进行代码修改。
移植完之后我在config.h的头文件中加入了以下头文件:
#include "stdbool.h"
#include "BspDs18b20.h"
#include "BspEsp8266.h"
在TemperatureUploadLocalSer.c中,实现输入一个MSH命令(T_Process),创建一个动态线程,在该线程回调函数中创建一个定时器并开启,该定时器实现一分钟上传一次DS18B20温度数据至本地TCP服务器。
线程的创建相关代码如下:
// 线程相关参数宏定义
#define Ds18B20_THREAD_STACK_SIZE 512
#define Ds18B20_THREAD_PRIORITY 8
static rt_timer_t Ds18B20ProcessSoftTimer;
void Ds18B20_thread_entry(void *parameter)//用户消息处理入口函数
{
Ds18B20ProcessSoftTimer = rt_timer_create("Ds18B20ProcessSoftTimer", /* 软件定时器的名称 */
Ds18B20ProcessSoftTimer_callback,/* 软件定时器的回调函数 */
0, /* 定时器超时函数的入口参数 */
60*RT_TICK_PER_SECOND, /* 软件定时器的超时时间(周期回调时间) */
RT_TIMER_FLAG_PERIODIC );
/* 软件定时器HARD_TIMER模式 周期模式 */
if (Ds18B20ProcessSoftTimer != NULL)
rt_timer_start(Ds18B20ProcessSoftTimer);//启动软件定时器
}
int T_Process(void)
{
rt_thread_t tid;
/* 创建动态线程Ds18B20Process */
tid = rt_thread_create("Ds18B20Process",
Ds18B20_thread_entry, RT_NULL,
Ds18B20_THREAD_STACK_SIZE, Ds18B20_THREAD_PRIORITY, 10);
if (tid != NULL)
{
rt_thread_startup(tid);
rt_kprintf("线程Ds18B20Process创建成功\n\n");
}
return 0;
}
MSH_CMD_EXPORT(T_Process, Temperature Data Sendto Server Init.);
其中软件定时器的回调函数代码如下:
static char SendData[30];//发往服务器的包,暂存数组
static void Ds18B20ProcessSoftTimer_callback(void* parameter)
{
sprintf(SendData,"\r\nTemperature: %.1f\r\n",DS18B20_GetTemp_SkipRom());
ESP8266_SendString ( ENABLE, SendData, strlen(SendData), Single_ID_0 );//每隔1分钟打印一次测得的温度值
}
我在WifiCmdTest.c中编写了一个直接连接网络的,代码如下:
#define WIFI_IP "MERCURY_CC72" //热点名称
#define WIFI_password "fxmfy210." //热点密码
#define TcpServer_IP "192.168.0.103" //服务器IP地址
#define TcpServer_Port "8000" //服务器连接端口
int ESP8266_Network_Connection(void)
{
ESP8266_Cmd ( "AT", "OK", NULL, 500 );//AT命令
ESP8266_Rst();
ESP8266_Net_Mode_Choose ( STA );//设置为工作站模式
while(!ESP8266_JoinAP ( WIFI_IP, WIFI_password));//加入热点
while(!ESP8266_Link_Server ( enumTCP, TcpServer_IP, TcpServer_Port, Single_ID_0));//加入服务器
while(!ESP8266_UnvarnishSend());//开启透传
printf("ESP8266 Network connection successful.\n");
return 0;
}
INIT_APP_EXPORT(ESP8266_Network_Connection);
接下来我改一下ESP8266和DS18B20的接入引脚。
ESP8266我使用PE.8为使能脚,PE.9为复位脚,串口使用串口3;
DS18B20的数据接收脚为PE.11。
硬件调试结果如下:
[attach]14792[/attach]
其中可看出网络连接成功了。
[attach]14791[/attach]
图片中可看出,MSH命令有一个叫T_Process的命令,以及输入后能成功创建动态线程Ds18B20Process,以及每隔1分钟将温度值发送给本地TCP服务器。
[i=s] 本帖最后由 yoowiwi 于 2020-4-8 14:55 编辑 [/i]
[md]## 小作业:通过ESP8266 将DS18B20温度数据传至本地TCP服务器
DS18B20介绍
只有一条总线,温度测量范围-55°至+125°,精度为±0.5°,工作电压3至5.5V,测量结果9至12位数字量
DS18B20相关协议函数网上及老师的工程文件里有,这里就不展出,直接放出读取传感器温度的函数,同时在RTThread需要实现高精度微秒延迟才能与DS18B20通信,实现函数如下:
void rt_hw_us_delay(rt_uint32_t us) // RTT中实现微秒延迟
{
rt_uint32_t delta;
rt_uint32_t current_delay;
us = us * (SysTick->LOAD/(1000000/RT_TICK_PER_SECOND));
delta = SysTick->VAL;
do
{
if ( delta > SysTick->VAL )
current_delay = delta - SysTick->VAL;
else
current_delay = SysTick->LOAD + delta - SysTick->VAL;
} while( current_delay < us );
}
int GetTemperature(void)
{
float temp = DS18B20_GetTemp_SkipRom(); // 读取温度数据
if(temp < 1.0 || temp > 80.0) // 如果数据异常则直接返回
return 1;
esp8266_upload_temp(temp); // 将温度数据上传到ONENET
printf ( "\r\温度:%.1f\r\n", temp);// 打印DS18B20 获取的温度值
return 0;
}
MSH_CMD_EXPORT(GetTemperature, Get Ds18b20 Temperature);
ESP8266使用
常用指令
AT // 测试指令
AT+RST // 复位
AT+CWMODE=1 // 1:STA 2:AP 3:AP+STA
AT+CWJAP="NAME","PAS" // NAME为wifi名称,PAS为wifi密码
AT+CIFSR // 查询模块IP地址
AT+CIPSTART="TCP","IP",PORT // 填入TCPserver的IP地址及端口号
AT+CIPMODE=1 // 开启透传模式
AT+CIPSEND // 发送数据,透传模式下使用
+++ // 结束透传,注意此处不能“发送新行”
带参数的msh命令示例
#include <rtthread.h>
static void atcmd(int argc, char**argv)
{
if (argc < 2)
{
rt_kprintf("Please input'atcmd <server|client>'\n");
return;
}
if (!rt_strcmp(argv[1], "server"))
rt_kprintf("AT server!\n");
else if (!rt_strcmp(argv[1], "client"))
rt_kprintf("AT client!\n");
else
rt_kprintf("Please input'atcmd <server|client>'\n");
}
MSH_CMD_EXPORT(atcmd, atcmd sample: atcmd <server|client>);
HTTP协议示例
POST /devices/设备ID/datapoints?type=5 HTTP/1.1
api-key:密钥
Host:api.heclouds.com
Content-Length:发送的数据包大小
,;属性,具体值
| ip | 域名 | 端口 | 功能 |
| :————————————- | :—————————— | :———— | :————— |
| 183.230.40.33 | api.heclouds.com | 80 | api |
| 183.230.40.39 | jjfaedp.hedevice.com | 876 | edp协议 |
| 183.230.40.42 | modbus.heclouds.com | 987 | modbus协议 |
| 183.230.40.42 | jtext.heclouds.com | 4362 | jtext协议 |
| 183.230.40.39 | mqtt.heclouds.com | 6002 | mqtt协议 |
| 183.230.40.40 218.201.45.3 | nbiotacc.heclouds.com | 5683(UDP) | nbiot接入机 |
| 183.230.40.39 | nbiotbt.heclouds.com | 5683(UDP) | nbiot引导机 |
| 183.230.40.40 | dtu.heclouds.com | 1811 | tcp透传协议 |
rt_kprintf接收的数据大小取决于宏:RT_CONSOLEBUF_SIZE
,默认为128
代码示例
相关宏、变量定义
#define BUFF_REC_SIZE 256 // 接收的数据大小
rt_uint8_t buff3[BUFF_REC_SIZE];
与esp8266相关的串口初始化,此处对8266的数据接收使用了DMA传输
int USART3_Config(void)
{
GPIO_InitTypeDef gpio;
USART_InitTypeDef usart;
DEBUG_USART3_GPIO_APBxClkCmd(DEBUG_USART3_GPIO_CLK, ENABLE);
DEBUG_USART3_APBxClkCmd(DEBUG_USART3_CLK, ENABLE);
gpio.GPIO_Pin = DEBUG_USART3_TX_GPIO_PIN;
gpio.GPIO_Mode = GPIO_Mode_AF_PP;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART3_TX_GPIO_PORT, &gpio);
gpio.GPIO_Pin = DEBUG_USART3_RX_GPIO_PIN;
gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART3_RX_GPIO_PORT, &gpio);
usart.USART_BaudRate = DEBUG_USART3_BAUDRATE;
usart.USART_WordLength = USART_WordLength_8b;
usart.USART_StopBits = USART_StopBits_1;
usart.USART_Parity = USART_Parity_No ;
usart.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
usart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(DEBUG_USART3, &usart);
NVIC_Configuration();
USARTx_DMA_Config();
USART_DMACmd(USART3,USART_DMAReq_Rx,ENABLE);
DMA_ClearFlag(DMA1_FLAG_TC3);
DMA_ITConfig(DMA1_Channel3, DMA_IT_TE, ENABLE);
USART_ITConfig(DEBUG_USART3, USART_IT_IDLE, ENABLE);
USART_Cmd(DEBUG_USART3, ENABLE);
return 0;
}
INIT_BOARD_EXPORT(USART3_Config);
串口空闲中断,用于数据接收
void DEBUG_USART3_IRQHandler()
{
uint8_t len = 0;
if(USART_GetFlagStatus(USART3, USART_FLAG_IDLE) != RESET)
{
USART3->SR;USART3->DR; // 清除中断标志位
DMA_Cmd(DMA1_Channel3, DISABLE);
len = BUFF_REC_SIZE - DMA_GetCurrDataCounter(DMA1_Channel3);
buff3[len]='\0';
DMA1_Channel3->CNDTR = BUFF_REC_SIZE;
DMA_Cmd(DMA1_Channel3, ENABLE);
}
}
esp8266 AT指令传输函数
void esp8266_atcmd_send(char *str)
{
if(!rt_strcmp(str,"+++")) // 结束透传,不需要换行
{
Usart_SendString(USART3,str);
return;
}
Usart_SendString(USART3,str);
Usart_SendString(USART3,"\r\n");
}
esp8266上传温度到onenet平台
int esp8266_upload_temp(float temp)
{
char buff_send[256];
sprintf(buff_send,
"POST /devices/590754126/datapoints?type=5 HTTP/1.1\n"
"api-key:pdGPsadeZ66PFPx5=lNj0MTcBqweqsarg=\n"
"Host:api.heclouds.com\n"
"Connection:close\n"
"Content-Length:12\n\n"
",;Temp,%2.1f\n",temp);
esp8266_atcmd_send("AT+CIPSTART=\"TCP\",\"183.230.40.33\",80");
rt_thread_mdelay(200);
esp8266_atcmd_send("AT+CIPMODE=1");
rt_thread_mdelay(10);
esp8266_atcmd_send("AT+CIPSEND");
rt_thread_mdelay(10);
Usart_SendString(USART3,buff_send); // 发送数据
rt_thread_mdelay(100);
esp8266_atcmd_send("+++");
rt_thread_mdelay(50);
esp8266_atcmd_send("AT+CIPCLOSE");
return 0;
}
int UploadTemprature(void)
{
float temp = DS18B20_GetTemp_SkipRom();
if(temp < 1.0 || temp > 80.0)
{
return 1;
}
esp8266_upload_temp(temp);
printf ( "\r\n温度监测上传:%.1f℃\r\n", temp);
return 0;
}
MSH_CMD_EXPORT(UploadTemprature, Get Ds18b20 Temperature);
每5秒上传一次数据到ONENET平台:
```C
static struct rt_thread tupload;
rt_uint8_t upload_thread_stack[2048];
void UploadTemp_Thread(void *paramter);
int UploadTemp_TimerInit()
{
rt_thread_init(&tupload,"UploadTemp",UploadTemp_Thread,RT_NULL, \
upload_thread_stack,sizeof(upload_thread_stack), \
5,10);
rt_thread_startup(&tupload);
return 0;
}
INIT_APP_EXPORT(UploadTemp_TimerInit);
MSH_CMD_EXPORT(UploadTemp_TimerInit, UploadTemp_TimerInit);
void UploadTemp_Thread(void *paramter)
{
while(1)
{
UploadTemprature();
rt_thread_mdelay(5000);
}
}
```[/md]
效果截图;
[attach]14794[/attach] [attach]14795[/attach]
你好~请问你有完整的代码吗
@富士山下_LJH 我都忘记这是什么时候写的代码了,可能是参加RTT的一些训练营的时候做的了,代码早就删了,而且这个代码也有很多问题的