sui整个工程可以分为三大部分:WIFI_AP模式的实现,TCP协议的创建与开启ADC单次采集与传输
一、WIFI_AP的实现
ESP32支持AP、STA、AP&STA模式,这里我们通过PC连接ESP32_AP方式。
ESP32-IDF提供了各个模块部分的搭建:WIFI_AP参考于wifi目录下的sofAP
代码如下:
#ifndef WiFIAP_H_
#define WifiAP_H_
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_mac.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "sdkconfig.h"
#define EXAMPLE_ESP_WIFI_SSID "XHL"
#define EXAMPLE_ESP_WIFI_PASS "12345678"
#define EXAMPLE_ESP_WIFI_CHANNEL 1
#define EXAMPLE_MAX_STA_CONN 4
// static void wifi_event_handler(void* arg, esp_event_base_t event_base,
// int32_t event_id, void* event_data);
void wifi_init_softap(void);
#endif
#include "WiFiAP.h"
static const char *TAG = "AP";
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_id == WIFI_EVENT_AP_STACONNECTED)
{
wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data;
ESP_LOGI(TAG, "station " MACSTR " join, AID=%d",
MAC2STR(event->mac), event->aid);
}
else if (event_id == WIFI_EVENT_AP_STADISCONNECTED)
{
wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data;
ESP_LOGI(TAG, "station " MACSTR " leave, AID=%d",
MAC2STR(event->mac), event->aid);
}
}
void wifi_init_softap(void)
{
// ESP_ERROR_CHECK(esp_netif_init()); //初始化底层TCP/IP协议栈,底层创建任务task
// ESP_ERROR_CHECK(esp_event_loop_create_default()); //创建event_task
esp_err_t ret = nvs_flash_init(); // 存储空间
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
ESP_ERROR_CHECK(esp_netif_init()); // 建立lwip协议栈
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); // 初始化WIFI Driver
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL)); // 注册WIFI事件处理函数,将事件注册到loop循环中
wifi_config_t wifi_config = {
// 配置WIFI Driver
.ap = {
.ssid = EXAMPLE_ESP_WIFI_SSID,
.ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
.channel = EXAMPLE_ESP_WIFI_CHANNEL,
.password = EXAMPLE_ESP_WIFI_PASS,
.max_connection = EXAMPLE_MAX_STA_CONN,
#ifdef CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT
.authmode = WIFI_AUTH_WPA3_PSK,
.sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
#else /* CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT */
.authmode = WIFI_AUTH_WPA2_PSK,
#endif
.pmf_cfg = {
.required = true,
},
},
};
if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0)
{
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); // 设置配置
ESP_ERROR_CHECK(esp_wifi_start()); // 启动WIFI_Driver
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS, EXAMPLE_ESP_WIFI_CHANNEL);
}
在B站发现一个UP主讲得很好,将AP模式的流程基本理清,这里粘贴出来
大致流程是先通过 ESP_ERROR_CHECK(esp_netif_init()); 建立lwip协议栈然后esp_wifi_init()初始化WIFI_AP,随便建立回调处理的相关函数,其实基于ESP-IDF也可以理清AP模式的初始化流程
二、TCP协议的传输
参考于TCP_server,并对于官方的代码进行一定修改,主要是修改do_transmit函数,避免其因为do while而一直循环,
#ifndef WiFITCP_H_
#define WiFiTCP_H_
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "WiFiAP.h"
#define PORT CONFIG_EXAMPLE_PORT
#define KEEPALIVE_IDLE CONFIG_EXAMPLE_KEEPALIVE_IDLE
#define KEEPALIVE_INTERVAL CONFIG_EXAMPLE_KEEPALIVE_INTERVAL
#define KEEPALIVE_COUNT CONFIG_EXAMPLE_KEEPALIVE_COUNT
void TCP_TASK();
// static esp_err_t creat_listen_socket();
esp_err_t send_message(char* buf, int _size);
esp_err_t send_message_test(int buf, int _size);
#endif
#include "WiFiTCP.h"
static const char *TAG = "TCP";
static int sock = 0;
static int listen_sock;
int do_retransmit(int sock)
{
int len;
char rx_buffer[128];
int temp_sock = 0;
temp_sock = sock;
len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
if (len < 0)
{
ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno);
temp_sock = 0;
return temp_sock;
}
else if (len == 0)
{
ESP_LOGW(TAG, "Connection closed");
temp_sock = 0;
return temp_sock;
}
else
{
rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string
ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer);
// send() can return less bytes than supplied length.
// Walk-around for robust implementation.
int to_write = len;
while (to_write > 0)
{
int written = send(sock, rx_buffer + (len - to_write), to_write, 0);
if (written < 0)
{
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
// Failed to retransmit, giving up
}
to_write -= written;
}
}
return temp_sock;
}
static esp_err_t creat_listen_socket()
{
int addr_family = AF_INET;
int ip_protocol = 0;
struct sockaddr_storage dest_addr;
struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr_ip4->sin_family = AF_INET;
dest_addr_ip4->sin_port = htons(PORT);
ip_protocol = IPPROTO_IP;
listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol); // 创建监听
if (listen_sock < 0)
{
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
vTaskDelete(NULL);
goto CLEAN_UP;
}
ESP_LOGI(TAG, "listen_sock created");
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
ESP_LOGI(TAG, "Socket created");
int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); // 绑定监听sock到端口
if (err != 0)
{
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
goto CLEAN_UP;
}
ESP_LOGI(TAG, "Socket bound, port %d", PORT);
err = listen(listen_sock, 1); // 启动监听
if (err != 0)
{
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
goto CLEAN_UP;
}
ESP_LOGI(TAG, "Listen loop....");
return ESP_OK;
CLEAN_UP:
close(listen_sock);
vTaskDelete(NULL);
return -1;
}
static void tcp_server_task(void *pvParameters)
{
char addr_str[128];
// int addr_family = (int)pvParameters;
// int ip_protocol = 0;
int keepAlive = 1;
int keepIdle = KEEPALIVE_IDLE;
int keepInterval = KEEPALIVE_INTERVAL;
int keepCount = KEEPALIVE_COUNT;
// struct sockaddr_storage dest_addr;
creat_listen_socket(); // 创建listen并启动
// #ifdef CONFIG_EXAMPLE_IPV4
// if (addr_family == AF_INET)
// {
// struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
// dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
// dest_addr_ip4->sin_family = AF_INET;
// dest_addr_ip4->sin_port = htons(PORT);
// ip_protocol = IPPROTO_IP;
// }
// #endif
// int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol); // 创建监听
// if (listen_sock < 0)
// {
// ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
// vTaskDelete(NULL);
// return;
// }
// int opt = 1;
// setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
// #if defined(CONFIG_EXAMPLE_IPV4) && defined(CONFIG_EXAMPLE_IPV6)
// // Note that by default IPV6 binds to both protocols, it is must be disabled
// // if both protocols used at the same time (used in CI)
// setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
// #endif
// int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); // 绑定监听sock到端口
// if (err != 0)
// {
// ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
// ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
// goto CLEAN_UP;
// }
// ESP_LOGI(TAG, "Socket bound, port %d", PORT);
// err = listen(listen_sock, 1); // 检测端口是否发生连接
// if (err != 0)
// {
// ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
// goto CLEAN_UP;
// }
while (1)
{
if (sock == 0)
{
ESP_LOGI(TAG, "Socket listening");
struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
socklen_t addr_len = sizeof(source_addr);
sock = accept(listen_sock, (struct sockaddr *)&source_addr, &addr_len); // 接受当前的连接
if (sock < 0)
{
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
break;
}
// Set tcp keepalive option //对接受的连接进行设置
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
// Convert ip address to string
#ifdef CONFIG_EXAMPLE_IPV4
if (source_addr.ss_family == PF_INET)
{
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
}
#endif
ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
}
else if (sock > 0)
{
sock = do_retransmit(sock);
}
// do_retransmit(sock); // 在此处进行数据的接受和发送
// send(sock,"ho",sizeof("ho"),0);
}
shutdown(sock, 0);
close(sock);
}
void TCP_TASK()
{
ESP_LOGI(TAG, "TCP_TASK_Creating");
xTaskCreate(tcp_server_task, "tcp_server", 4096, (void *)AF_INET, 5, NULL);
}
esp_err_t send_message(char* buf, int _size)
{
ESP_LOGE(TAG, "Tring");
if (sock <= 0)
{
ESP_LOGI(TAG,"ERROR");
return -1;
}
int ret = send(sock, buf, _size, 0);
if (ret < 0)
{
ESP_LOGE(TAG, "ERROR occurred during sneding : errmo %d", errno);
return -1;
}
return ESP_OK;
}
esp_err_t send_message_test(int buf, int _size)
{
ESP_LOGE(TAG, "Tring");
if (sock <= 0)
{
ESP_LOGI(TAG,"ERROR");
return -1;
}
int ret = send(sock, buf, _size, 0);
if (ret < 0)
{
ESP_LOGE(TAG, "ERROR occurred during sneding : errmo %d", errno);
return -1;
}
return ESP_OK;
}
对于TCP_server其实其初始化也是有固定流程,对应上面的初始化和官方的demo可以很快理清,
只看服务端就是先socket()、blind()、listen()、accept() 然后就是数据传输了
除了与官方demo一样接受上位机rev然后write之外,也单独将发送函数写出来方便ADC采样后可以上传数据,所用到的是send()函数,也即是lwip_sned(sock,const void *data,size);
buf是一个数组,将ADC采样和需要的数据通过sprintf函数合并然后放进去,当然不论什么数据类型都可以这里只是为了VOFA上位机的数据特殊格式而已;
三、ADC
ADC我用的是单次采样,连续采样我只是单独实现过感觉有点麻烦单次足够了,下面是对应代码:
#ifndef ADC_TCP_H_
#define ADC_TCP_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "soc/soc_caps.h"
#include "esp_log.h"
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
#include "WiFiTCP.h"
void ADC_START();
#endif
#include "ADC_TCP.h"
static const char *TAG = "ADC";
#define EXAMPLE_ADC1_CHAN ADC_CHANNEL_1
#define EXAMPLE_ADC_ATTEN ADC_ATTEN_DB_11
static int adc_raw = 0;
static int voltage = 0;
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle);
static void example_adc_calibration_deinit(adc_cali_handle_t handle);
void ADC_task(void)
{
//-------------ADC1 Init---------------//
adc_oneshot_unit_handle_t adc1_handle;
adc_oneshot_unit_init_cfg_t init_config1 = {
.unit_id = ADC_UNIT_1,
};
ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config1, &adc1_handle));
//-------------ADC1 Config---------------//
adc_oneshot_chan_cfg_t config = {
.bitwidth = ADC_BITWIDTH_DEFAULT,
.atten = EXAMPLE_ADC_ATTEN,
};
ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, EXAMPLE_ADC1_CHAN, &config));
adc_cali_handle_t adc1_cali_chan_handle = NULL;
bool do_calibration1_chan = example_adc_calibration_init(ADC_UNIT_1, EXAMPLE_ADC1_CHAN, EXAMPLE_ADC_ATTEN, &adc1_cali_chan_handle);
char buff[20]={0};
while (1)
{
ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, EXAMPLE_ADC1_CHAN, &adc_raw));
ESP_LOGI(TAG, "ADC%d Channel[%d] Raw Data: %d", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN, adc_raw);
if (do_calibration1_chan)
{
ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc1_cali_chan_handle, adc_raw, &voltage));
ESP_LOGI(TAG, "ADC%d Channel[%d] Cali Voltage: %d mV", ADC_UNIT_1 + 1, EXAMPLE_ADC1_CHAN, voltage);
sprintf(buff,"%d%s",voltage,"\n");
send_message(buff, strlen(buff));
ESP_LOGI(TAG,"Buff:%s",buff);
}
vTaskDelay(pdMS_TO_TICKS(1));
}
ESP_ERROR_CHECK(adc_oneshot_del_unit(adc1_handle));
if (do_calibration1_chan)
{
example_adc_calibration_deinit(adc1_cali_chan_handle);
}
}
void ADC_START()
{
ESP_LOGI(TAG, "ADC_TASK_Creating...");
xTaskCreate((void *)ADC_task, "ADC TASK", 4096, NULL, 5, NULL);
}
static bool example_adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle)
{
adc_cali_handle_t handle = NULL;
esp_err_t ret = ESP_FAIL;
bool calibrated = false;
if (!calibrated)
{
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
adc_cali_curve_fitting_config_t cali_config = {
.unit_id = unit,
.chan = channel,
.atten = atten,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
if (ret == ESP_OK)
{
calibrated = true;
}
}
*out_handle = handle;
if (ret == ESP_OK)
{
ESP_LOGI(TAG, "Calibration Success");
}
else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated)
{
ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
}
else
{
ESP_LOGE(TAG, "Invalid arg or no memory");
}
return calibrated;
}
static void example_adc_calibration_deinit(adc_cali_handle_t handle)
{
ESP_LOGI(TAG, "deregister %s calibration scheme", "Curve Fitting");
ESP_ERROR_CHECK(adc_cali_delete_scheme_curve_fitting(handle));
}
最后我们在app_main里面初始化这些任务
void app_main(void)
{
//
ESP_LOGI(TAG,"main_task begin");
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
// ESP_ERROR_CHECK(example_connect());
wifi_init_softap();
TCP_TASK();
ADC_START();
// #ifdef CONFIG_EXAMPLE_IPV4
// #endif
// #ifdef CONFIG_EXAMPLE_IPV6
// xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET6, 5, NULL);
// #endif
}
app_main后面也可以加个任务删除函数将app_main这个任务删除掉