我的最终目标是设置一个带有 ESP-01S 的 Raspberry Pi Pico 来启用 wifi。Pico 将定期检查服务器并在不使用时让 ESP 进入睡眠状态。两者之间的通信是通过UART。我想同时支持来自 ESP 的 GET 和 POST HTTP 请求。
这是我在两个设备之间使用的消息结构。
| Always start with MSG | Request Type | Next message Size
| | Next message size | | | URL
V V V V V
|-----|-------------------|-----|-------------------|-----------|
[M|S|G|\x00\|x00\|x00\|x03|G|E|T|\x00|\x00|\x00|\x1f|h|t|t|p|...]
[M|S|G|\x00\|x00\|x00\|x04|P|O|S|T|URL SIZE...|URL...|\x00|\x00|\x006|POST Data...]
|-----|-------------------|-------|-----------|------|---------------|------------|
^ ^
| | Post Data
| Post data size
出于测试目的,我在 python 中生成字符串,打印它们并将它们直接粘贴到我正在闪烁到 ESP 的 .cpp 文件中。
这是我在我的电脑上用来生成消息的代码片段。
import struct
import json
url = "http://192.168.X.X:8090/korok"
size_of_url = struct.pack('!I', len(url))
data = json.dumps({
"serial": "12345",
"sensor_data": {"0": 75, "1": 67}
})
size_of_data = struct.pack('!I', len(data))
print(f"{struct.pack('!I', len('GET'))}GET{size_of_url}{url}")
print(f"{struct.pack('!I', len('POST'))}POST{size_of_url}{url}{size_of_data}{data}")
>>>> ...
b'\x00\x00\x00\x03'GETb'\x00\x00\x00\x1f'http://192.168.X.X:8090/korok
b'\x00\x00\x00\x03'GETb'\x00\x00\x00\x1f'http://192.168.X.X:8090/korokb'\x00\x00\x006'{"serial": "12345", "sensor_data": {"0": 75, "1": 67}}
这是在 ESP 上运行的代码。我对我正在经历的一些行为发表了评论。
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#define GPIO_STATUS 2
#define BUFFER_SIZE 256
#define DATA_SIZE 4
char buf[BUFFER_SIZE];
void setup() {...}
...
// Struct is used so I can also get the size to use as an offset
// and read out part of the message in the buffer.
struct Message
{
char *value;
unsigned int size;
unsigned int totalMessageSize;
};
Message readMessage(char *data)
{
struct Message message;
message.size = ntohl(*(unsigned int *)(data)); // Unable to read shorthand hex
Serial.println(message.size);
message.totalMessageSize = message.size + DATA_SIZE; // Shorthand is only 3 char
message.value = (char *)malloc(message.size);
Serial.println(message.size);
int idx = DATA_SIZE;
int jdx = 0;
while (idx < message.size + DATA_SIZE && idx < BUFFER_SIZE)
{
message.value[jdx++] = data[idx++];
}
return message;
}
void loop()
{
delay(3000);
char * msg = "\x00\x00\x00\x03GET\x00\x00\x00\x1fhttp://192.168.X.X:8090/korok";
// char *msg = read_message();
if (msg)
{
Serial.print("\n");
int bufferIdx = 0;
struct Message request = readMessage(msg);
bufferIdx = request.totalMessageSize;
// This is odd and doing it due to odd behavior with ntohl and a variable
// offset. See below.
memcpy(msg, msg + bufferIdx, BUFFER_SIZE - bufferIdx);
struct Message url = readMessage(msg);
struct Message data;
if (memcmp(request.value, "GET", 3) == 0)
{
}
else if (memcmp(request.value, "POST", 4) == 0)
{
bufferIdx = url.totalMessageSize;
memcpy(msg, msg + bufferIdx, BUFFER_SIZE - bufferIdx);
struct Message data = readMessage(msg);
Serial.println(data.value);
}
free(request.value);
free(url.value);
if (memcmp(request.value, "POST", 4) == 0)
{
free(data.value);
}
}
}
这是两个问题之一,尽管我通过对原始 char* 进行 memcpy 来偏移起始索引找到了一种解决方法。下面的第一行有效,但第二行引发 LoadStoreAlignmentCause 异常。
理想情况下,我想了解这里发生了什么,并在没有 memcpy 的情况下让它工作。
ntohl(*(unsigned int *)(msg + 7)); // Works
int offset = 7;
ntohl(*(unsigned int *)(msg + offset)); // Throws Exception (9) LoadStoreAlignmentCause
我遇到的主要问题是当我在 python 中打包大小时,一些十六进制值被简写。例如struct.pack('!I', 54) == \x00\x00\x006
当这种情况发生时ntohl()
,它似乎读取了一个不应该读取的地址并输出 635。此外,由于我期望四个字符,消息的其余部分被一个索引关闭。
关于这个问题的几个问题。这种速记十六进制语法的名称是什么?反正有没有让python不输出这个速记?或者有什么关于如何让这个在 ESP 上工作的建议吗?