ESP8266 TCP 调试问题 MALLOC问题

一、TCP卡死(无法下发,无法上传)

  1. 采用非阻塞socket时,发送多次数据后,发现tcp发送是成功的,读取是成功的,服务器下发却是失败的,经过排查,是因为heap过低导致的,这里会出现一种假死状态,所以为了维持tcp的正常(除非用没收到心跳包回复来做死机判断),必须限定tcp的heap足够

二、内存溢出排查

  1. 排查内存溢出时,发现是 采用 malloc ,free没有释放,排查原因,发现是malloc的buf的地址指针被改变了!
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
esp8266 语音播放 //Priorities of the reader and the decoder thread. Higher = higher prio. #define PRIO_READER 11 #define PRIO_MAD 1 //The mp3 read buffer size. 2106 bytes should be enough for up to 48KHz mp3s according to the sox sources. Used by libmad. #define READBUFSZ (2106) static char readBuf[READBUFSZ]; static long bufUnderrunCt; //Reformat the 16-bit mono sample to a format we can send to I2S. static int sampToI2s(short s) { //We can send a 32-bit sample to the I2S subsystem and the DAC will neatly split it up in 2 //16-bit analog values, one for left and one for right. //Duplicate 16-bit sample to both the L and R channel int samp=s; samp=(samp)&0xffff; samp=(samp<65535) samp=65535; if (samp>11]; err=(samp&0x7ff); //Save rounding error. return samp; } //2nd order delta-sigma DAC //See http://www.beis.de/Elektronik/DeltaSigma/DeltaSigma.html for a nice explanation static int sampToI2sDeltaSigma(short s) { int x; int val=0; int w; static int i1v=0, i2v=0; static int outReg=0; for (x=0; x<32; x++) { val<0) w-=32767; else w+=32767; //Difference 1 w+=i1v; i1v=w; //Integrator 1 if (outReg>0) w-=32767; else w+=32767; //Difference 2 w+=i2v; i2v=w; //Integrator 2 outReg=w; //register if (w>0) val|=1; //comparator } return val; } //Calculate the number of samples that we add or delete. Added samples means a slightly lower //playback rate, deleted samples means we increase playout speed a bit. This returns an //8.24 fixed-point number int recalcAddDelSamp(int oldVal) { int ret; long prevUdr=0; static int cnt; int i; static int minFifoFill=0; i=spiRamFifoFill(); if (i<minFifoFill) minFifoFill=i; //Do the rest of the calculations plusminus every 100mS (assuming a sample rate of 44KHz) cnt++; if (cnt<1500) return oldVal; cnt=0; if (spiRamFifoLen()<10*1024) { //The FIFO is very small. We can't do calculations on how much it's filled on average, so another //algorithm is called for. int tgt=1600; //we want an average of this amount of bytes as the average minimum buffer fill //Calculate underruns this cycle int udr=spiRamGetUnderrunCt()-prevUdr; //If we have underruns, the minimum buffer fill has been lower than 0. if (udr!=0) minFifoFill=-1; //If we're below our target decrease playback speed, and vice-versa. ret=oldVal+((minFifoFill-tgt)*ADD_DEL_BUFFPERSAMP_NOSPIRAM); prevUdr+=udr; minFifoFill=9999; } else { //We have a larger FIFO; we can adjust according to the FIFO fill rate. int tgt=spiRamFifoLen()/2; ret=(spiRamFifoFill()-tgt)*ADD_DEL_BUFFPERSAMP; } return ret; } //This routine is called by the NXP modifications of libmad. It passes us (for the mono synth) //32 16-bit samples. void render_sample_block(short *short_sample_buff, int no_samples) { //Signed 16.16 fixed point number: the amount of samples we need to add or delete //in every 32-sample static int sampAddDel=0; //Remainder of sampAddDel cumulatives static int sampErr=0; int i; int samp; #ifdef ADD_DEL_SAMPLES sampAddDel=recalcAddDelSamp(sampAddDel); #endif sampErr+=sampAddDel; for (i=0; i(1<<24)) { sampErr-=(1<<24); //...and don't output an i2s sample } else if (sampErr<-(1<<24)) { sampErr+=(1<bufend-stream->next_frame; memmove(readBuf, stream->next_frame, rem); while (rem<sizeof(readBuf)) { n=(sizeof(readBuf)-rem); //Calculate amount of bytes we need to fill buffer. i=spiRamFifoFill(); if (i<n) n=i; //If the fifo can give us less, only take that amount if (n==0) { //Can't take anything? //Wait until there is enough data in the buffer. This only happens when the data feed //rate is too low, and shouldn't normally be needed! // printf("Buf uflow, need %d bytes.\n", sizeof(readBuf)-rem); bufUnderrunCt++; //We both silence the output as well as wait a while by pushing silent samples into the i2s system. //This waits for about 200mS for (n=0; nerror, mad_stream_errorstr(stream)); return MAD_FLOW_CONTINUE; } //This is the main mp3 decoding task. It will grab data from the input buffer FIFO in the SPI ram and //output it to the I2S port. void ICACHE_FLASH_ATTR tskmad(void *pvParameters) { int r; struct mad_stream *stream; struct mad_frame *frame; struct mad_synth *synth; //Allocate structs needed for mp3 decoding stream=malloc(sizeof(struct mad_stream)); frame=malloc(sizeof(struct mad_frame)); synth=malloc(sizeof(struct mad_synth)); if (stream==NULL) { printf("MAD: malloc(stream) failed\n"); return; } if (synth==NULL) { printf("MAD: malloc(synth) failed\n"); return; } if (frame==NULL) { printf("MAD: malloc(frame) failed\n"); return; } //Initialize I2S i2sInit(); bufUnderrunCt=0; printf("MAD: Decoder start.\n"); //Initialize mp3 parts mad_stream_init(stream); mad_frame_init(frame); mad_synth_init(synth); while(1) { input(stream); //calls mad_stream_buffer internally while(1) { r=mad_frame_decode(frame, stream); if (r==-1) { if (!MAD_RECOVERABLE(stream->error)) { //We're most likely out of buffer and need to call input() again break; } error(NULL, stream, frame); continue; } mad_synth_frame(synth, frame); } } } int getIpForHost(const char *host, struct sockaddr_in *ip) { struct hostent *he; struct in_addr **addr_list; he=gethostbyname(host); if (he==NULL) return 0; addr_list=(struct in_addr **)he->h_addr_list; if (addr_list[0]==NULL) return 0; ip->sin_family=AF_INET; memcpy(&ip->sin_addr, addr_list[0], sizeof(ip->sin_addr)); return 1; } //Open a connection to a webserver and request an URL. Yes, this possibly is one of the worst ways to do this, //but RAM is at a premium here, and this works for most of the cases. int ICACHE_FLASH_ATTR openConn(const char *streamHost, const char *streamPath) { int n, i; while(1) { struct sockaddr_in remote_ip; bzero(&remote_ip, sizeof(struct sockaddr_in)); if (!getIpForHost(streamHost, &remote_ip)) { vTaskDelay(1000/portTICK_RATE_MS); continue; } int sock=socket(PF_INET, SOCK_STREAM, 0); if (sock==-1) { continue; } remote_ip.sin_port = htons(streamPort); printf("Connecting to server %s...\n", ipaddr_ntoa((const ip_addr_t*)&remote_ip.sin_addr.s_addr)); if (connect(sock, (struct sockaddr *)(&remote_ip), sizeof(struct sockaddr))!=00) { close(sock); printf("Conn err.\n"); vTaskDelay(1000/portTICK_RATE_MS); continue; } //Cobble together HTTP request write(sock, "GET ", 4); write(sock, streamPath, strlen(streamPath)); write(sock, " HTTP/1.0\r\nHost: ", 17); write(sock, streamHost, strlen(streamHost)); write(sock, "\r\n\r\n", 4); //We ignore the headers that the server sends back... it's pretty dirty in general to do that, //but it works here because the MP3 decoder skips it because it isn't valid MP3 data. return sock; } } //Reader task. This will try to read data from a TCP socket into the SPI fifo buffer. void ICACHE_FLASH_ATTR tskreader(void *pvParameters) { int madRunning=0; char wbuf[64]; int n, l, inBuf; int t; int fd; int c=0; while(1) { fd=openConn(streamHost, streamPath); printf("Reading into SPI RAM FIFO...\n"); do { n=read(fd, wbuf, sizeof(wbuf)); if (n>0) spiRamFifoWrite(wbuf, n); c+=n; if ((!madRunning) && (spiRamFifoFree()0); close(fd); printf("Connection closed.\n"); } } //Simple task to connect to an access point, initialize i2s and fire up the reader task. void ICACHE_FLASH_ATTR tskconnect(void *pvParameters) { //Wait a few secs for the stack to settle down vTaskDelay(3000/portTICK_RATE_MS); //Go to station mode wifi_station_disconnect(); if (wifi_get_opmode() != STATION_MODE) { wifi_set_opmode(STATION_MODE); } //Connect to the defined access point. struct station_config *config=malloc(sizeof(struct station_config)); memset(config, 0x00, sizeof(struct station_config)); sprintf(config->ssid, AP_NAME); sprintf(config->password, AP_PASS); wifi_station_set_config(config); wifi_station_connect(); free(config); //Fire up the reader task. The reader task will fire up the MP3 decoder as soon //as it has read enough MP3 data. if (xTaskCreate(tskreader, "tskreader", 230, NULL, PRIO_READER, NULL)!=pdPASS) printf("Error creating reader task!\n"); //We're done. Delete this task. vTaskDelete(NULL); } //We need this to tell the OS we're running at a higher clock frequency. extern void os_update_cpu_frequency(int mhz); void ICACHE_FLASH_ATTR user_init(void) { //Tell hardware to run at 160MHz instead of 80MHz //This actually is not needed in normal situations... the hardware is quick enough to do //MP3 decoding at 80MHz. It, however, seems to help with receiving data over long and/or unstable //links, so you may want to turn it on. Also, the delta-sigma code seems to need a bit more speed //than the other solutions to keep up with the output samples, so it's also enabled there. #if defined(DELTA_SIGMA_HACK) SET_PERI_REG_MASK(0x3ff00014, BIT(0)); os_update_cpu_frequency(160); #endif //Set the UART to 115200 baud UART_SetBaudrate(0, 115200); //Initialize the SPI RAM chip communications and see if it actually retains some bytes. If it //doesn't, warn user. if (!spiRamFifoInit()) { printf("\n\nSPI RAM chip fail!\n"); while(1); } printf("\n\nHardware initialized. Waiting for network.\n"); xTaskCreate(tskconnect, "tskconnect", 200, NULL, 3, NULL); }
要实现微信小程序和STM32+ESP8266之间的TCP通信,需要在STM32上使用TCP协议栈来实现TCP连接和数据传输。在这里,我以STM32F103为例,使用标准库进行开发。 首先,需要在STM32上初始化ESP8266模块并建立TCP连接。下面是一个简单的示例代码: ```c #include "stm32f10x.h" #include "stdio.h" #include "string.h" // ESP8266连接信息 #define WIFI_SSID "your_ssid" #define WIFI_PASSWORD "your_password" #define TCP_SERVER_IP "192.168.1.100" #define TCP_SERVER_PORT 1234 // 接收缓冲区大小 #define RX_BUF_SIZE 1024 // 接收缓冲区 uint8_t rx_buf[RX_BUF_SIZE]; uint16_t rx_index = 0; // USART1初始化函数 void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; // 使能USART1和GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置PA9为推挽输出,TXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置PA10为浮空输入,RXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // USART1初始化 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART1, &USART_InitStructure); // 使能USART1 USART_Cmd(USART1, ENABLE); } // 发送数据到USART1 void USART1_SendData(uint8_t *data, uint16_t len) { for(uint16_t i = 0; i < len; i++) { USART_SendData(USART1, data[i]); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } } // 接收数据 void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { rx_buf[rx_index++] = USART_ReceiveData(USART1); } } // 初始化ESP8266 void ESP8266_Init(void) { // 等待模块启动完成 DelayMs(500); // 发送AT指令,检查模块是否正常 ESP8266_SendCmd("AT\r\n"); if(!ESP8266_WaitResponse("OK", 1000)) { printf("ESP8266 init failed!\r\n"); while(1); } // 设置WIFI模式为STA ESP8266_SendCmd("AT+CWMODE=1\r\n"); if(!ESP8266_WaitResponse("OK", 1000)) { printf("ESP8266 set mode failed!\r\n"); while(1); } // 连接WIFI ESP8266_SendCmd("AT+CWJAP=\"" WIFI_SSID "\",\"" WIFI_PASSWORD "\"\r\n"); if(!ESP8266_WaitResponse("OK", 5000)) { printf("ESP8266 connect wifi failed!\r\n"); while(1); } // 获取本地IP地址 ESP8266_SendCmd("AT+CIFSR\r\n"); if(!ESP8266_WaitResponse("+CIFSR:STAIP,\"", 1000)) { printf("ESP8266 get ip failed!\r\n"); while(1); } // 解析IP地址 uint8_t *ip_str = strstr((char *)rx_buf, "+CIFSR:STAIP,\""); uint8_t *ip_end = strstr((char *)ip_str + 15, "\""); if(ip_str && ip_end) { *ip_end = 0; printf("ESP8266 IP: %s\r\n", ip_str + 15); } else { printf("ESP8266 parse ip failed!\r\n"); while(1); } } // 建立TCP连接 void TCP_Connect(void) { // 发送AT指令,建立TCP连接 char cmd[128]; sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", TCP_SERVER_IP, TCP_SERVER_PORT); ESP8266_SendCmd(cmd); if(!ESP8266_WaitResponse("CONNECT", 5000)) { printf("TCP connect failed!\r\n"); while(1); } } int main(void) { // 初始化USART1 USART1_Init(); // 初始化ESP8266 ESP8266_Init(); // 建立TCP连接 TCP_Connect(); while(1) { // 发送数据到服务器 uint8_t data[] = "Hello, world!"; uint16_t len = sizeof(data) - 1; char cmd[128]; sprintf(cmd, "AT+CIPSEND=%d\r\n", len); ESP8266_SendCmd(cmd); if(!ESP8266_WaitResponse(">", 1000)) { printf("TCP send data failed 1!\r\n"); continue; } USART1_SendData(data, len); if(!ESP8266_WaitResponse("SEND OK", 1000)) { printf("TCP send data failed 2!\r\n"); continue; } } } ``` 在上面的代码中,我们使用USART1与ESP8266模块进行通信,并在USART1的中断服务函数中接收数据。在初始化ESP8266模块后,我们首先连接WIFI并获取本地IP地址,然后通过发送AT指令来建立TCP连接。在建立连接后,我们可以通过发送AT指令和USART1将数据发送到服务器。 接下来,我们需要通过TCP协议栈来实现TCP连接和数据传输。使用TCP协议栈可以使我们更方便地控制TCP连接,并提供更高效的数据传输。下面是一个简单的TCP客户端示例代码: ```c #include "stm32f10x.h" #include "stdio.h" #include "string.h" #include "lwip/opt.h" #include "lwip/arch.h" #include "lwip/api.h" #include "lwip/tcp.h" #include "lwip/tcpip.h" #include "netif/etharp.h" // ESP8266连接信息 #define WIFI_SSID "your_ssid" #define WIFI_PASSWORD "your_password" #define TCP_SERVER_IP "192.168.1.100" #define TCP_SERVER_PORT 1234 // 接收缓冲区大小 #define RX_BUF_SIZE 1024 // 接收缓冲区 uint8_t rx_buf[RX_BUF_SIZE]; uint16_t rx_index = 0; // TCP协议栈任务 void tcp_task(void *arg) { struct netconn *conn = NULL; err_t err; struct netbuf *buf; // 创建TCP连接 conn = netconn_new(NETCONN_TCP); if(conn == NULL) { printf("TCP connect failed!\r\n"); return; } err = netconn_connect(conn, IP_ADDR_ANY, TCP_SERVER_PORT); if(err != ERR_OK) { printf("TCP connect failed!\r\n"); return; } while(1) { // 接收数据 err = netconn_recv(conn, &buf); if(err != ERR_OK) { continue; } // 处理数据 uint16_t len = buf->p->tot_len; uint8_t *data = malloc(len); if(data != NULL) { uint16_t index = 0; struct pbuf *q; for(q = buf->p; q != NULL; q = q->next) { memcpy(data + index, q->payload, q->len); index += q->len; } printf("TCP recv data: %s\r\n", data); free(data); } // 释放缓冲区 netbuf_delete(buf); } } // USART1初始化函数 void USART1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; // 使能USART1和GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置PA9为推挽输出,TXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置PA10为浮空输入,RXD GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // USART1初始化 USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; USART_Init(USART1, &USART_InitStructure); // 使能USART1 USART_Cmd(USART1, ENABLE); // USART1中断配置 NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 使能USART1接收中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); } // 发送数据到USART1 void USART1_SendData(uint8_t *data, uint16_t len) { for(uint16_t i = 0; i < len; i++) { USART_SendData(USART1, data[i]); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } } // 接收数据 void USART1_IRQHandler(void) { if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { rx_buf[rx_index++] = USART_ReceiveData(USART1); } } // 初始化ESP8266 void ESP8266_Init(void) { // 等待模块启动完成 DelayMs(500); // 发送AT指令,检查模块是否正常 ESP8266_SendCmd("AT\r\n"); if(!ESP8266_WaitResponse("OK", 1000)) { printf("ESP8266 init failed!\r\n"); while(1); } // 设置WIFI模式为STA ESP8266_SendCmd("AT+CWMODE=1\r\n"); if(!ESP8266_WaitResponse("OK", 1000)) { printf("ESP8266 set mode failed!\r\n"); while(1); } // 连接WIFI ESP8266_SendCmd("AT+CWJAP=\"" WIFI_SSID "\",\"" WIFI_PASSWORD "\"\r\n"); if(!ESP8266_WaitResponse("OK", 5000)) { printf("ESP8266 connect wifi failed!\r\n"); while(1); } // 获取本地IP地址 ESP8266_SendCmd("AT+CIFSR\r\n"); if(!ESP8266_WaitResponse("+CIFSR:STAIP,\"", 1000)) { printf("ESP8266 get ip failed!\r\n"); while(1); } // 解析IP地址 uint8_t *ip_str = strstr((char *)rx_buf, "+CIFSR:STAIP,\""); uint8_t *ip_end = strstr((char *)ip_str + 15, "\""); if(ip_str && ip_end) { *ip_end = 0; printf("ESP8266 IP: %s\r\n", ip_str + 15); } else { printf("ESP8266 parse ip failed!\r\n"); while(1); } } int main(void) { // 初始化USART1 USART1_Init(); // 初始化ESP8266 ESP8266_Init(); // 初始化TCP协议栈 tcpip_init(NULL, NULL); // 创建TCP协议栈任务 sys_thread_new("tcp_task", tcp_task, NULL, 512, 2); while(1) { // 发送数据到服务器 uint8_t data[] = "Hello, world!"; uint16_t len = sizeof(data) - 1; char cmd[128]; sprintf(cmd, "AT+CIPSEND=%d\r\n", len); ESP8266_SendCmd(cmd); if(!ESP8266_WaitResponse(">", 1000)) { printf("TCP send data failed 1!\r\n"); continue; } USART1_SendData(data, len); if(!ESP8266_WaitResponse("SEND OK", 1000)) { printf("TCP send data failed 2!\r\n"); continue; } } } ``` 在上面的代码中,我们使用lwIP协议栈来实现TCP连接和数据传输。首先,我们需要在主函数中调用tcpip_init函数来初始化lwIP协议栈。然后,我们创建一个TCP协议栈任务,并在任务中使用netconn_new函数创建一个TCP连接。在循环中,我们使用netconn_recv函数接收数据,并使用netbuf_delete函数释放缓冲区。 在主函数中,我们使用USART1与ESP8266模块进行通信,并在USART1的中断服务函数中接收数据。在初始化ESP8266模块后,我们首先连接WIFI并获取本地IP地址,然后通过发送AT指令和USART1将数据发送到服务器

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值