ESP8266 NodeMCU 无法发送 TCP 命令 - 相同的代码适用于 Arduino Uno?
ESP8266 NodeMCU can't send TCP commands - same code works on Arduino Uno?
我对此一窍不通,所以希望这是一个非常明显的问题。
我正在尝试创建一个简单的 temperature/humidity 传感器,它从 DHT22 获取读数并使用 ESP8266 将它们 ping 到 Thingspeak 的 API,然后绘制图表 out/store 等等
我在下面粘贴了代码 - 它适用于 Arduino Uno,我正试图将它缩小到 ESP8266 上,这样我就可以为房子周围生产很多小温度传感器。
症状
- wifi 连接正常
- 它正在生成一个正确的 API 字符串(我已经通过手动剪切并粘贴到浏览器中进行了测试)
- 温度传感器也生成正确的读数
- 它在串口监视器中返回 'Data Fail!',表明这是代码中发生错误的地方
我不知道是不是从 Arduino Uno 转移到 ESP8266 导致了问题(即需要不同的库,TCP 命令不同等)
非常感谢经验丰富的老手的任何帮助!
这是串行监视器输出和代码的片段(只是 passwords/apis 等)
22:16:50.266 -> **************
22:16:57.579 -> Wifi Connection Successful
22:16:57.579 -> The IP Address of the Sensor is:192.168.1.211
22:16:57.579 -> Humidity: 41.50
22:16:57.579 -> Temperature: 21.70
22:16:57.579 -> AT+CIPSTART="TCP","api.thingspeak.com",80
22:17:00.574 -> AT+CIPSEND=63
22:17:01.561 -> AT+CIPCLOSE
22:17:02.577 -> Data Fail!
22:17:02.577 -> GET /update?apikey=<REMOVED>&field1=21.70&field2=41.50
#include<stdlib.h>
#include "DHT.h"
#include <ESP8266WiFi.h>
#define SSID "<REMOVED>" //your network name
#define PASS "<REMOVED>" //your network password
#define API "<REMOVED>" //api string
#define IP "api.thingspeak.com" // thingspeak.com
#define DHTPIN 4 // what pin the DHT sensor is connected to
#define DHTTYPE DHT22 // Change to DHT22 if that's what you have
#define Baud_Rate 115200 //Another common value is 9600
#define DELAY_TIME 300000 //time in ms between posting data to ThingSpeak
//Can use a post also
String GET = String("GET /update?apikey=") + API + "&field1=";
String FIELD2 = "&field2=";
//if you want to add more fields this is how
//String FIELD3 = "&field3=";
bool updated;
DHT dht(DHTPIN, DHTTYPE);
//this runs once
void setup()
{
delay(5000);
Serial.begin(Baud_Rate);
// Connect to WIFI
WiFi.begin(SSID, PASS);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print("*");
}
Serial.println("");
Serial.println("Wifi Connection Successful");
Serial.print("The IP Address of the Sensor is:");
Serial.println(WiFi.localIP()); //Print the IP Address
//initalize DHT sensor
dht.begin();
}
//this runs over and over
void loop() {
float h = dht.readHumidity();
Serial.print("Humidity: ");
Serial.println(h);
// Read temperature as Fahrenheit (isFahrenheit = true)
float c = dht.readTemperature();
Serial.print("Temperature: ");
Serial.println(c);
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(c)) {
Serial.println("Reading DHT22 Failed, exiting");
return;
}
//update ThingSpeak channel with new values
updated = updateTemp(String(c), String(h));
//wait for delay time before attempting to post again
delay(DELAY_TIME);
}
bool updateTemp(String tempC, String humid) {
//initialize your AT command string
String cmd = "AT+CIPSTART=\"TCP\",\"";
//add IP address and port
cmd += IP;
cmd += "\",80";
//connect
Serial.println(cmd);
delay(2000);
if (Serial.find("Error")) {
return false;
}
//build GET command, ThingSpeak takes Post or Get commands for updates, I use a Get
cmd = GET;
cmd += tempC;
cmd += FIELD2;
cmd += humid;
cmd += "\r\n";
//continue to add data here if you have more fields such as a light sensor
//cmd += FIELD3;
//cmd += <field 3 value>
//Serial.println(cmd);
//Use AT commands to send data
Serial.print("AT+CIPSEND=");
Serial.println(cmd.length());
if (Serial.find(">")) {
//send through command to update values
Serial.print(cmd);
} else {
Serial.println("AT+CIPCLOSE");
}
if (Serial.find("OK")) {
//success! Your most recent values should be online.
Serial.println("Data Sent!");
return true;
} else {
Serial.println("Data Fail!");
Serial.println(cmd);
return false;
}
}
boolean connectWiFi() {
//set ESP8266 mode with AT commands
Serial.println("AT+CWMODE=1");
delay(2000);
//build connection command
String cmd = "AT+CWJAP=\"";
cmd += SSID;
cmd += "\",\"";
cmd += PASS;
cmd += "\"";
//connect to WiFi network and wait 5 seconds
Serial.println(cmd);
delay(5000);
//if connected return true, else false
if (Serial.find("OK")) {
Serial.println("WIFI connected");
return true;
} else {
Serial.println("WIFI not connected");
return false;
}
}
您必须解决三个问题:
去掉 delay() 我的意思不是说你不应该时不时地调用例程以免破坏你的服务器。原因:delay() 函数在指定的时间内停止 CPU。
在等待某些硬件初始化的安装程序中确定,并确定快速 test/debug 简单的例程。 server/client 场景中的坏主意(其中通信任务 运行 与用户任务并行)并且致命带 2 个 Cpus 的 ESP32。简短的程序指南。
unsigned long previousMillis = 0;
setup() {
....
}
loop(){
if (millis() - previousMillis > DELAY_TIME) { // we check for elapsed time
// What ever needs to be done all DELAY_TIME ms
previousMillis = millis(); // we "reset" the timer
}
// other code which needs no delayed execution
}
同时更改 Arduino 的代码并将其提升到一个新的水平-
第二点:摆脱字符串 class 并改用固定字符。
原因是字符串 class 内存管理不善并破坏了您的堆,这导致 ESP 迟早会崩溃,字符都编译成flash,没有这个问题。一个简短的例子,如何使用你的 commands/functions:
// instead of #define API "<REMOVED>" //api String use
const char* API = "<REMOVED>" // pointer to the char API
//Can use a post also
char fixPartGET[128] = '[=11=]'; // takes 255 chars and the terminator, if needed enlarge
char cmd[256] = '[=11=]'; // takes 127 chars and the terminator, if needed enlarge
char numBuffer [16] = '[=11=]'; // we use for float conversions
strcpy (fixPartGET, "GET /update?apikey="); // Copy empties
strcat (fixPartGET, API);
strcat (fixPartGET, "&field1=");
const char* FIELD2 = "&field2=";
setup(){
....
}
loop(){
....
}
bool updateTemp( float c, float h) {
if (WiFi.status() != WL_CONNECTED) {
return false;
}
//build GET command, ThingSpeak takes Post or Get commands for updates, I use a Get
strcpy(cmd, fixPartGET); // start building the GET command
// The dtostrf() function converts the double value passed in into an ASCII representation
dtostrf(c, 6, 2, numBuffer); // change to what you need as precision
strcat(cmd, numBuffer);
strcat(cmd, FIELD2);
dtostrf(h, 6, 2, numBuffer); // change to what you need as precision
strcat(cmd, numBuffer);
strcat(cmd, "\r\n");
//continue to add data here if you have more fields such as a light sensor
//strcat(cmd, FIELD3);
//Some other code to discuss
return true;
}
最后一点
与 AP 的 WiFi 通信 - 查看 WiFi 库的示例并使用串行 AT 命令避免麻烦。目前这些命令 运行 通过 USB 电缆而不是 WiFi。与 Arduino 相比,nodeMCU 是一台功能强大的机器,但由于技术概念,它更容易出现不稳定。
我提议的更改 1 和 2 可以而且应该在 Arduino 上实施,作为第一次习惯这种方法。如果这 运行s 用于将 AT 更改为 NodeMCU 上的 WiFi 客户端功能。 ESP8266WiFi.h lib 示例 WiFiClientBasic 和 WiFiClient 可能有助于快速获得结果(查找 client.print(...) )。
首先在我们讨论您面临的问题之前,您需要了解使用 ESP8266 的两种不同方式:
- 将其用作 WiFi 调制解调器;
- 将其用作独立的 MCU。
将它用作 WiFi 调制解调器时,您使用 AT 命令与它通信,默认情况下这是大多数 ESP8266 模块所做的,它附带了 AT 命令固件。
当您将 ESP8266 用作 MCU 时,您使用 Arduino sketch 烧写 ESP8266,它会覆盖 AT-Command 固件并使用 Arduino 引导加载程序上传它,并允许 ESP8266 用作独立的 MCU,例如一个 Arduino。
您提到您在使用 Arduino Uno 时可以毫无问题地与 ESP8266 通信,这表明您正在使用 AT 命令与 ESP8266 通信(并且您的部分代码表明是这种情况)。
当您将 Arduino 草图上传到 ESP8266 以将其用作独立 MCU 时,您需要使用 Arduino 库(例如 ESP8266WiFi
与 ESP8266 进行通信,该库将负责与 ESP8266 的底层通信ESP8266芯片。这就是为什么您可以使用 ESP8266WiFi
class.
提供的功能建立 WiFi 的原因
但是,您 updateTemp()
中的代码仍在使用不再有效的 AT 命令。您将需要使用 ESP8266WebClient
或 ESP8266HTTPClient
(这个更简单、更容易)库来建立 http 连接。这是我为使用 ESP8266HTTClient
库处理 HTTP 请求而修改的代码。顺便说一句,代码编译没问题,但我没有用 thinkspeak 服务器测试它,因为我不使用 thinkspeak 也没有 api-key。
#include <DHT.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#define SSID "<REMOVED>" //your network name
#define PASS "<REMOVED>" //your network password
#define API "<REMOVED>" //api string
#define IP "api.thingspeak.com" // thingspeak.com
#define PORT 80
#define DHTPIN 4 // what pin the DHT sensor is connected to
#define DHTTYPE DHT22 // Change to DHT22 if that's what you have
#define BAUD_RATE 115200 //Another common value is 9600
#define DELAY_TIME 300000 //time in ms between posting data to ThingSpeak
DHT dht(DHTPIN, DHTTYPE);
//this runs once
void setup()
{
Serial.begin(BAUD_RATE);
// Connect to WIFI
WiFi.begin(SSID, PASS);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print("*");
}
Serial.println("");
Serial.println("Wifi Connection Successful");
Serial.print("The IP Address of the Sensor is:");
Serial.println(WiFi.localIP());
//initalize DHT sensor
dht.begin();
}
//this runs over and over
void loop() {
float h = dht.readHumidity();
Serial.print("Humidity: ");
Serial.println(h);
// Read temperature as Fahrenheit (isFahrenheit = true)
float c = dht.readTemperature();
Serial.print("Temperature: ");
Serial.println(c);
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(c)) {
Serial.println("Reading DHT22 Failed, exiting");
return;
}
//update ThingSpeak channel with new values
updateTemp(c, h);
//wait for delay time before attempting to post again
delay(DELAY_TIME);
}
bool updateTemp(float tempC, float humid) {
WiFiClient client; // Create a WiFiClient to for TCP connection
if (!client.connect(IP, PORT)) {
Serial.println("HTTP connection failed");
return false;
}
Serial.println("Sending data to server");
if (client.connected()) {
client.print("GET /update?api_key="); client.print(API);
client.print("&field1="); client.print(String(tempC));
client.print("&field2="); client.print(String(humid));
client.println(" HTTP/1.1");
client.print("Host: "); client.println(IP);
client.println("Connection: close");
client.println(); //extra "\r\n" as per HTTP protocol
}
// wait for data to be available
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println("HTTP Client Timeout !");
client.stop();
return false;
}
}
Serial.println("Receiving HTTP response");
while (client.available()) {
char ch = static_cast<char>(client.read());
Serial.print(ch);
}
Serial.println();
Serial.println("Closing TCP connection");
client.stop();
return true;
}
我对此一窍不通,所以希望这是一个非常明显的问题。
我正在尝试创建一个简单的 temperature/humidity 传感器,它从 DHT22 获取读数并使用 ESP8266 将它们 ping 到 Thingspeak 的 API,然后绘制图表 out/store 等等
我在下面粘贴了代码 - 它适用于 Arduino Uno,我正试图将它缩小到 ESP8266 上,这样我就可以为房子周围生产很多小温度传感器。
症状
- wifi 连接正常
- 它正在生成一个正确的 API 字符串(我已经通过手动剪切并粘贴到浏览器中进行了测试)
- 温度传感器也生成正确的读数
- 它在串口监视器中返回 'Data Fail!',表明这是代码中发生错误的地方
我不知道是不是从 Arduino Uno 转移到 ESP8266 导致了问题(即需要不同的库,TCP 命令不同等)
非常感谢经验丰富的老手的任何帮助!
这是串行监视器输出和代码的片段(只是 passwords/apis 等)
22:16:50.266 -> **************
22:16:57.579 -> Wifi Connection Successful
22:16:57.579 -> The IP Address of the Sensor is:192.168.1.211
22:16:57.579 -> Humidity: 41.50
22:16:57.579 -> Temperature: 21.70
22:16:57.579 -> AT+CIPSTART="TCP","api.thingspeak.com",80
22:17:00.574 -> AT+CIPSEND=63
22:17:01.561 -> AT+CIPCLOSE
22:17:02.577 -> Data Fail!
22:17:02.577 -> GET /update?apikey=<REMOVED>&field1=21.70&field2=41.50
#include<stdlib.h>
#include "DHT.h"
#include <ESP8266WiFi.h>
#define SSID "<REMOVED>" //your network name
#define PASS "<REMOVED>" //your network password
#define API "<REMOVED>" //api string
#define IP "api.thingspeak.com" // thingspeak.com
#define DHTPIN 4 // what pin the DHT sensor is connected to
#define DHTTYPE DHT22 // Change to DHT22 if that's what you have
#define Baud_Rate 115200 //Another common value is 9600
#define DELAY_TIME 300000 //time in ms between posting data to ThingSpeak
//Can use a post also
String GET = String("GET /update?apikey=") + API + "&field1=";
String FIELD2 = "&field2=";
//if you want to add more fields this is how
//String FIELD3 = "&field3=";
bool updated;
DHT dht(DHTPIN, DHTTYPE);
//this runs once
void setup()
{
delay(5000);
Serial.begin(Baud_Rate);
// Connect to WIFI
WiFi.begin(SSID, PASS);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print("*");
}
Serial.println("");
Serial.println("Wifi Connection Successful");
Serial.print("The IP Address of the Sensor is:");
Serial.println(WiFi.localIP()); //Print the IP Address
//initalize DHT sensor
dht.begin();
}
//this runs over and over
void loop() {
float h = dht.readHumidity();
Serial.print("Humidity: ");
Serial.println(h);
// Read temperature as Fahrenheit (isFahrenheit = true)
float c = dht.readTemperature();
Serial.print("Temperature: ");
Serial.println(c);
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(c)) {
Serial.println("Reading DHT22 Failed, exiting");
return;
}
//update ThingSpeak channel with new values
updated = updateTemp(String(c), String(h));
//wait for delay time before attempting to post again
delay(DELAY_TIME);
}
bool updateTemp(String tempC, String humid) {
//initialize your AT command string
String cmd = "AT+CIPSTART=\"TCP\",\"";
//add IP address and port
cmd += IP;
cmd += "\",80";
//connect
Serial.println(cmd);
delay(2000);
if (Serial.find("Error")) {
return false;
}
//build GET command, ThingSpeak takes Post or Get commands for updates, I use a Get
cmd = GET;
cmd += tempC;
cmd += FIELD2;
cmd += humid;
cmd += "\r\n";
//continue to add data here if you have more fields such as a light sensor
//cmd += FIELD3;
//cmd += <field 3 value>
//Serial.println(cmd);
//Use AT commands to send data
Serial.print("AT+CIPSEND=");
Serial.println(cmd.length());
if (Serial.find(">")) {
//send through command to update values
Serial.print(cmd);
} else {
Serial.println("AT+CIPCLOSE");
}
if (Serial.find("OK")) {
//success! Your most recent values should be online.
Serial.println("Data Sent!");
return true;
} else {
Serial.println("Data Fail!");
Serial.println(cmd);
return false;
}
}
boolean connectWiFi() {
//set ESP8266 mode with AT commands
Serial.println("AT+CWMODE=1");
delay(2000);
//build connection command
String cmd = "AT+CWJAP=\"";
cmd += SSID;
cmd += "\",\"";
cmd += PASS;
cmd += "\"";
//connect to WiFi network and wait 5 seconds
Serial.println(cmd);
delay(5000);
//if connected return true, else false
if (Serial.find("OK")) {
Serial.println("WIFI connected");
return true;
} else {
Serial.println("WIFI not connected");
return false;
}
}
您必须解决三个问题:
去掉 delay() 我的意思不是说你不应该时不时地调用例程以免破坏你的服务器。原因:delay() 函数在指定的时间内停止 CPU。
在等待某些硬件初始化的安装程序中确定,并确定快速 test/debug 简单的例程。 server/client 场景中的坏主意(其中通信任务 运行 与用户任务并行)并且致命带 2 个 Cpus 的 ESP32。简短的程序指南。
unsigned long previousMillis = 0;
setup() {
....
}
loop(){
if (millis() - previousMillis > DELAY_TIME) { // we check for elapsed time
// What ever needs to be done all DELAY_TIME ms
previousMillis = millis(); // we "reset" the timer
}
// other code which needs no delayed execution
}
同时更改 Arduino 的代码并将其提升到一个新的水平-
第二点:摆脱字符串 class 并改用固定字符。
原因是字符串 class 内存管理不善并破坏了您的堆,这导致 ESP 迟早会崩溃,字符都编译成flash,没有这个问题。一个简短的例子,如何使用你的 commands/functions:
// instead of #define API "<REMOVED>" //api String use
const char* API = "<REMOVED>" // pointer to the char API
//Can use a post also
char fixPartGET[128] = '[=11=]'; // takes 255 chars and the terminator, if needed enlarge
char cmd[256] = '[=11=]'; // takes 127 chars and the terminator, if needed enlarge
char numBuffer [16] = '[=11=]'; // we use for float conversions
strcpy (fixPartGET, "GET /update?apikey="); // Copy empties
strcat (fixPartGET, API);
strcat (fixPartGET, "&field1=");
const char* FIELD2 = "&field2=";
setup(){
....
}
loop(){
....
}
bool updateTemp( float c, float h) {
if (WiFi.status() != WL_CONNECTED) {
return false;
}
//build GET command, ThingSpeak takes Post or Get commands for updates, I use a Get
strcpy(cmd, fixPartGET); // start building the GET command
// The dtostrf() function converts the double value passed in into an ASCII representation
dtostrf(c, 6, 2, numBuffer); // change to what you need as precision
strcat(cmd, numBuffer);
strcat(cmd, FIELD2);
dtostrf(h, 6, 2, numBuffer); // change to what you need as precision
strcat(cmd, numBuffer);
strcat(cmd, "\r\n");
//continue to add data here if you have more fields such as a light sensor
//strcat(cmd, FIELD3);
//Some other code to discuss
return true;
}
最后一点
与 AP 的 WiFi 通信 - 查看 WiFi 库的示例并使用串行 AT 命令避免麻烦。目前这些命令 运行 通过 USB 电缆而不是 WiFi。与 Arduino 相比,nodeMCU 是一台功能强大的机器,但由于技术概念,它更容易出现不稳定。
我提议的更改 1 和 2 可以而且应该在 Arduino 上实施,作为第一次习惯这种方法。如果这 运行s 用于将 AT 更改为 NodeMCU 上的 WiFi 客户端功能。 ESP8266WiFi.h lib 示例 WiFiClientBasic 和 WiFiClient 可能有助于快速获得结果(查找 client.print(...) )。
首先在我们讨论您面临的问题之前,您需要了解使用 ESP8266 的两种不同方式:
- 将其用作 WiFi 调制解调器;
- 将其用作独立的 MCU。
将它用作 WiFi 调制解调器时,您使用 AT 命令与它通信,默认情况下这是大多数 ESP8266 模块所做的,它附带了 AT 命令固件。
当您将 ESP8266 用作 MCU 时,您使用 Arduino sketch 烧写 ESP8266,它会覆盖 AT-Command 固件并使用 Arduino 引导加载程序上传它,并允许 ESP8266 用作独立的 MCU,例如一个 Arduino。
您提到您在使用 Arduino Uno 时可以毫无问题地与 ESP8266 通信,这表明您正在使用 AT 命令与 ESP8266 通信(并且您的部分代码表明是这种情况)。
当您将 Arduino 草图上传到 ESP8266 以将其用作独立 MCU 时,您需要使用 Arduino 库(例如 ESP8266WiFi
与 ESP8266 进行通信,该库将负责与 ESP8266 的底层通信ESP8266芯片。这就是为什么您可以使用 ESP8266WiFi
class.
但是,您 updateTemp()
中的代码仍在使用不再有效的 AT 命令。您将需要使用 ESP8266WebClient
或 ESP8266HTTPClient
(这个更简单、更容易)库来建立 http 连接。这是我为使用 ESP8266HTTClient
库处理 HTTP 请求而修改的代码。顺便说一句,代码编译没问题,但我没有用 thinkspeak 服务器测试它,因为我不使用 thinkspeak 也没有 api-key。
#include <DHT.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#define SSID "<REMOVED>" //your network name
#define PASS "<REMOVED>" //your network password
#define API "<REMOVED>" //api string
#define IP "api.thingspeak.com" // thingspeak.com
#define PORT 80
#define DHTPIN 4 // what pin the DHT sensor is connected to
#define DHTTYPE DHT22 // Change to DHT22 if that's what you have
#define BAUD_RATE 115200 //Another common value is 9600
#define DELAY_TIME 300000 //time in ms between posting data to ThingSpeak
DHT dht(DHTPIN, DHTTYPE);
//this runs once
void setup()
{
Serial.begin(BAUD_RATE);
// Connect to WIFI
WiFi.begin(SSID, PASS);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print("*");
}
Serial.println("");
Serial.println("Wifi Connection Successful");
Serial.print("The IP Address of the Sensor is:");
Serial.println(WiFi.localIP());
//initalize DHT sensor
dht.begin();
}
//this runs over and over
void loop() {
float h = dht.readHumidity();
Serial.print("Humidity: ");
Serial.println(h);
// Read temperature as Fahrenheit (isFahrenheit = true)
float c = dht.readTemperature();
Serial.print("Temperature: ");
Serial.println(c);
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(c)) {
Serial.println("Reading DHT22 Failed, exiting");
return;
}
//update ThingSpeak channel with new values
updateTemp(c, h);
//wait for delay time before attempting to post again
delay(DELAY_TIME);
}
bool updateTemp(float tempC, float humid) {
WiFiClient client; // Create a WiFiClient to for TCP connection
if (!client.connect(IP, PORT)) {
Serial.println("HTTP connection failed");
return false;
}
Serial.println("Sending data to server");
if (client.connected()) {
client.print("GET /update?api_key="); client.print(API);
client.print("&field1="); client.print(String(tempC));
client.print("&field2="); client.print(String(humid));
client.println(" HTTP/1.1");
client.print("Host: "); client.println(IP);
client.println("Connection: close");
client.println(); //extra "\r\n" as per HTTP protocol
}
// wait for data to be available
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println("HTTP Client Timeout !");
client.stop();
return false;
}
}
Serial.println("Receiving HTTP response");
while (client.available()) {
char ch = static_cast<char>(client.read());
Serial.print(ch);
}
Serial.println();
Serial.println("Closing TCP connection");
client.stop();
return true;
}