完整代码,最好修改下SERIAL_RX_BUFFER_SIZE 的大小为512。
#include
#define DHT11_PIN (5) //SCL引脚内部和ANALOG5相连,实际是PC5端口
byte dht11_dat[5]; //存储dht11的5字节数据
SoftwareSerial mySerial = SoftwareSerial(11, 12);
char ok[] = "OK";
char ipd[] = "+IPD,";
char rdy[] = "ready";
char GET[] = "GET /";
char SEND[] = ">";
int headlength;
char Header[] = "HTTP/1.1 200 OK\r\n\
Server: nginx/1.10.3 (Ubuntu)\r\n\
Date: Wed, 08 Apr 2020 09:29:22 GMT\r\n\
Content-Type: text/plain\r\n\
Content-Length: 42\r\n\
Connection: keep-alive\r\n\
Last-Modified: Tue, 18 Sep 2018 03:55:48 GMT\r\n\
Accept-Ranges: bytes\r\n\
\r\n";
void setup()
{
DDRC |= _BV(DHT11_PIN); //Data Direction Register(数据方向寄存器)
PORTC |= _BV(DHT11_PIN); //数据寄存器
mySerial.begin(9600);
mySerial.println(F("start"));
Serial.begin(115200);
Serial.print(F("AT+RST\r\n"));
if (waitForStr(rdy, 5000) == true)
mySerial.println(F("ready ok"));
Serial.println(F("ATE0")); //disable echo
if (waitForStr(ok, 1000) == true)
mySerial.println(F("ATE0 ok"));
Serial.println(F("AT+CWMODE=2")); //station, AP is 3
if (waitForStr(ok, 1000) == true)
mySerial.println(F("AT+CWMODE=2 ok"));
Serial.println(F("AT+CIPMUX=1"));
if (waitForStr(ok, 1000) == true)
mySerial.println(F("AT+CIPMUX=1 ok"));
Serial.println(F("AT+CIPSERVER=1,8080"));
if (waitForStr(ok, 1000) == true)
mySerial.println(F("AT+CIPSERVER=1,8080 ok"));
headlength = getStrLength(Header);
byte ret = read_dht11();
if (ret != 0)
{
mySerial.println("dht11 error!");
return ;
}
}
void loop()
{
static char mux; //记录连接的通道号
if (waitForStr(ipd, 1000) == true) //有新的连接接入
{
mux = Serial.read(); //ipd字符串下一个字节就是连接通道号
if (waitForStr(GET, 1000) == true) //有新的GET请求
{
//组建Body
char Body[64];
int bodylength = 0;
sprintf(Body, "Current humdity = %2d.%1d%%\r\ntemperature=%2d.%1dC", dht11_dat[0], dht11_dat[1], dht11_dat[2], dht11_dat[3]);
bodylength = getStrLength(Body);
//调试串口打印 组建的数据的内容及长度
//Header中的Content-Length必须设置成和这里的bodylength一样,否则浏览器不能正确解析,导致解析失败
mySerial.println(Body);
mySerial.print(F("Body length:"));
mySerial.println(bodylength);
//AT+CIPSEND=x,y 进入透传模式进行回复,x为通道号,y为要返回的数据长度
Serial.print(F("AT+CIPSEND="));
Serial.print(mux);
Serial.print(",");
Serial.println(headlength + bodylength);
if (waitForStr(SEND, 1000) == true) //出现'>'符号开始发送,发送头和体
{
Serial.print(Header);
Serial.println(Body);
if (waitForStr(ok, 10000) != true) mySerial.println(F("send timeout"));
//强制断开该连接
Serial.print(F("AT+CIPCLOSE="));
Serial.println(mux);
if (waitForStr(ok, 1000) != true) mySerial.println(F("close failed"));
}
else mySerial.println(F("no >"));
}
else mySerial.println(F("no GET"));
return ; //为了保证WIFI模块的响应速度,响应成功后立即退出,重新进入loop进行下次响应,没有响应时才开始读传感器数据
}
else mySerial.println(F("no connect"));
delay(1000); //最快1s读一次传感器的数据
byte ret = read_dht11();
if (ret != 0)
{
mySerial.println("dht11 error!");
return ;
}
}
/*---------------------------------------------------------------------------------------------------------------*/
//获取数据长度函数
int getStrLength(char* str) {
int i = 0;
while (str[i] != '\0') {
i++;
}
return i;
}
//从串口中等待字符串str,超时时间为timeout ms
bool waitForStr(char* str, long timeout) {
long intime = millis();
int i = 0, j = 0, cur = 0, index = 0;
int len = getStrLength(str);
bool ret = true;
char* temp = new char(len);
while (str[i] != '\0') {
while (j - cur < len) {
if (timeout > 0)
if (millis() - intime > timeout) {
ret = false;
goto OUT;
}
if (Serial.available()) {
index = j % len;
temp[index] = Serial.read();
j++;
}
}
index = (cur + i) % len;
if (str[i] == temp[index])
i++;
else
i = 0, cur++;
}
OUT:
delete temp;
return ret;
}
/*---------------------------------------------------------------------------------------------------------------*/
/*
从DHT11中读取一个byte的数据
*/
byte read_dht11_dat()
{
byte i = 0;
byte result = 0;
for (i = 0; i < 8; i++) {
while (!(PINC & _BV(DHT11_PIN))); // wait for 50us //#define _BV(bit) (1 << (bit))
delayMicroseconds(30);
if (PINC & _BV(DHT11_PIN))
result |= (1 << (7 - i));
while ((PINC & _BV(DHT11_PIN))); // wait '1' finish
}
return result;
}
/*
从DHT11中读取一次数据
*/
int read_dht11()
{
byte dht11_in; //存储当前端口读取的值
byte i;
// start condition
// 1. pull-down i/o pin from 18ms
PORTC &= ~_BV(DHT11_PIN); //拉低
delay(18);
PORTC |= _BV(DHT11_PIN); //拉高
delayMicroseconds(40);
DDRC &= ~_BV(DHT11_PIN);
delayMicroseconds(40);
dht11_in = PINC & _BV(DHT11_PIN);
if (dht11_in) {
//Serial.println("dht11 start condition 1 not met");
return 1;
}
delayMicroseconds(80);
dht11_in = PINC & _BV(DHT11_PIN);
if (!dht11_in) {
//Serial.println("dht11 start condition 2 not met");
return 2;
}
delayMicroseconds(80);
// now ready for data reception
for (i = 0; i < 5; i++)
dht11_dat[i] = read_dht11_dat();
DDRC |= _BV(DHT11_PIN);
PORTC |= _BV(DHT11_PIN);
byte dht11_check_sum = dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3];
// check check_sum
if (dht11_dat[4] != dht11_check_sum)
{
//Serial.println("DHT11 checksum error");
return 3;
}
return 0;
}
实物连线如图,IIC接口连接的是DHT11传感器,实际信号连接的是A5接口(PC5);
软件串口连接的是D11和D12;
白色USB线只用于供电,未使用板载的USB2TTL功能,实际的硬件串口接入了ESP8266的WIFI模块;
板子上的系统供电开关拨至3V3处;
手机访问显示效果:
软件串口看到的日志:
注意白色USB线的串口是COM4,用于程序的下载,软件串口使用了USB2TTL模块,显示的是串口5,打开串口监视器的时候需要使用串口5才能正确的显示。
下面是添加了oled屏幕的代码:
#include
#include "U8glib.h"
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE | U8G_I2C_OPT_DEV_0); // I2C / TWI
#define DHT11_PIN (3) //SCL引脚内部和ANALOG5相连,实际是PC5端口
byte dht11_dat[5]; //存储dht11的5字节数据
SoftwareSerial mySerial = SoftwareSerial(11, 12);
char ok[] = "OK";
char ipd[] = "+IPD,";
char rdy[] = "ready";
char GET[] = "GET /";
char SEND[] = ">";
int headlength;
char Header[] = "HTTP/1.1 200 OK\r\n\
Server: nginx/1.10.3 (Ubuntu)\r\n\
Date: Wed, 08 Apr 2020 09:29:22 GMT\r\n\
Content-Type: text/plain\r\n\
Content-Length: 42\r\n\
Connection: keep-alive\r\n\
Last-Modified: Tue, 18 Sep 2018 03:55:48 GMT\r\n\
Accept-Ranges: bytes\r\n\
\r\n";
void setup()
{
DDRC |= _BV(DHT11_PIN); //Data Direction Register(数据方向寄存器)
PORTC |= _BV(DHT11_PIN); //数据寄存器
oled_init();
mySerial.begin(9600);
mySerial.println(F("start"));
Serial.begin(115200);
Serial.print(F("AT+RST\r\n"));
if (waitForStr(rdy, 5000) == true)
mySerial.println(F("ready ok"));
Serial.println(F("ATE0")); //disable echo
if (waitForStr(ok, 1000) == true)
mySerial.println(F("ATE0 ok"));
Serial.println(F("AT+CWMODE=2")); //station, AP is 3
if (waitForStr(ok, 1000) == true)
mySerial.println(F("AT+CWMODE=2 ok"));
Serial.println(F("AT+CIPMUX=1"));
if (waitForStr(ok, 1000) == true)
mySerial.println(F("AT+CIPMUX=1 ok"));
Serial.println(F("AT+CIPSERVER=1,8080"));
if (waitForStr(ok, 1000) == true)
mySerial.println(F("AT+CIPSERVER=1,8080 ok"));
headlength = getStrLength(Header);
delay(1000); //垃圾DHT11模块开机必须等待1s才能用
byte ret = read_dht11();
if (ret != 0)
{
mySerial.println(F("dht11 error!"));
return ;
}
}
void loop()
{
oled_show();
static char mux; //记录连接的通道号
if (waitForStr(ipd, 1000) == true) //有新的连接接入
{
mux = Serial.read(); //ipd字符串下一个字节就是连接通道号
if (waitForStr(GET, 1000) == true) //有新的GET请求
{
//组建Body
char Body[64];
int bodylength = 0;
sprintf(Body, "Current humdity = %2d.%1d%%\r\ntemperature=%2d.%1dC", dht11_dat[0], dht11_dat[1], dht11_dat[2], dht11_dat[3]);
bodylength = getStrLength(Body);
//调试串口打印 组建的数据的内容及长度
//Header中的Content-Length必须设置成和这里的bodylength一样,否则浏览器不能正确解析,导致解析失败
mySerial.println(Body);
mySerial.print(F("Body length:"));
mySerial.println(bodylength);
//AT+CIPSEND=x,y 进入透传模式进行回复,x为通道号,y为要返回的数据长度
Serial.print(F("AT+CIPSEND="));
Serial.print(mux);
Serial.print(",");
Serial.println(headlength + bodylength);
if (waitForStr(SEND, 1000) == true) //出现'>'符号开始发送,发送头和体
{
Serial.print(Header);
Serial.println(Body);
if (waitForStr(ok, 10000) != true) mySerial.println(F("send timeout"));
//强制断开该连接
Serial.print(F("AT+CIPCLOSE="));
Serial.println(mux);
if (waitForStr(ok, 1000) != true) mySerial.println(F("close failed"));
}
else mySerial.println(F("no >"));
}
else mySerial.println(F("no GET"));
return ; //为了保证WIFI模块的响应速度,响应成功后立即退出,重新进入loop进行下次响应,没有响应时才开始读传感器数据
}
else mySerial.println(F("no connect"));
delay(1000); //最快1s读一次传感器的数据
byte ret = read_dht11();
if (ret != 0)
{
mySerial.println("dht11 error!");
return ;
}
}
/*---------------------------------------------------------------------------------------------------------------*/
void oled_init(void) {
// flip screen, if required
// u8g.setRot180();
// set SPI backup if required
//u8g.setHardwareBackup(u8g_backup_avr_spi);
// assign default color value
if ( u8g.getMode() == U8G_MODE_R3G3B2 ) {
u8g.setColorIndex(255); // white
}
else if ( u8g.getMode() == U8G_MODE_GRAY2BIT ) {
u8g.setColorIndex(3); // max intensity
}
else if ( u8g.getMode() == U8G_MODE_BW ) {
u8g.setColorIndex(1); // pixel on
}
else if ( u8g.getMode() == U8G_MODE_HICOLOR ) {
u8g.setHiColorByRGB(255, 255, 255);
}
}
void oled_show()
{
u8g.firstPage();
do {
draw();
} while ( u8g.nextPage() );
}
void draw(void) {
// graphic commands to redraw the complete screen should be placed here
u8g.setFont(u8g_font_unifont);
char ss[16];
sprintf(ss, "humi = %2d.%1d%%", dht11_dat[0], dht11_dat[1]);
u8g.drawStr( 0, 12, ss);
sprintf(ss, "temp = %2d.%1dC", dht11_dat[2], dht11_dat[3]);
u8g.drawStr(0, 23, ss);
}
/*---------------------------------------------------------------------------------------------------------------*/
//获取数据长度函数
int getStrLength(char* str) {
int i = 0;
while (str[i] != '\0') {
i++;
}
return i;
}
//从串口中等待字符串str,超时时间为timeout ms
bool waitForStr(char* str, long timeout) {
long intime = millis();
int i = 0, j = 0, cur = 0, index = 0;
int len = getStrLength(str);
bool ret = true;
char* temp = new char(len);
while (str[i] != '\0') {
while (j - cur < len) {
if (timeout > 0)
if (millis() - intime > timeout) {
ret = false;
goto OUT;
}
if (Serial.available()) {
index = j % len;
temp[index] = Serial.read();
j++;
}
}
index = (cur + i) % len;
if (str[i] == temp[index])
i++;
else
i = 0, cur++;
}
OUT:
delete temp;
return ret;
}
/*---------------------------------------------------------------------------------------------------------------*/
/*
从DHT11中读取一个byte的数据
*/
byte read_dht11_dat()
{
byte i = 0;
byte result = 0;
for (i = 0; i < 8; i++) {
while (!(PINC & _BV(DHT11_PIN))); // wait for 50us //#define _BV(bit) (1 << (bit))
delayMicroseconds(30);
if (PINC & _BV(DHT11_PIN))
result |= (1 << (7 - i));
while ((PINC & _BV(DHT11_PIN))); // wait '1' finish
}
return result;
}
/*
从DHT11中读取一次数据
*/
int read_dht11()
{
byte dht11_in; //存储当前端口读取的值
byte i;
// start condition
// 1. pull-down i/o pin from 18ms
PORTC &= ~_BV(DHT11_PIN); //拉低
delay(18);
PORTC |= _BV(DHT11_PIN); //拉高
delayMicroseconds(40);
DDRC &= ~_BV(DHT11_PIN);
delayMicroseconds(40);
dht11_in = PINC & _BV(DHT11_PIN);
if (dht11_in) {
//Serial.println("dht11 start condition 1 not met");
return 1;
}
delayMicroseconds(80);
dht11_in = PINC & _BV(DHT11_PIN);
if (!dht11_in) {
//Serial.println("dht11 start condition 2 not met");
return 2;
}
delayMicroseconds(80);
// now ready for data reception
for (i = 0; i < 5; i++)
dht11_dat[i] = read_dht11_dat();
DDRC |= _BV(DHT11_PIN);
PORTC |= _BV(DHT11_PIN);
byte dht11_check_sum = dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3];
// check check_sum
if (dht11_dat[4] != dht11_check_sum)
{
//Serial.println("DHT11 checksum error");
return 3;
}
return 0;
}