4·ESP32-C3入门教程——从本地控制走向云端控制(TCP/IP UDP篇)

这篇具有很好参考价值的文章主要介绍了4·ESP32-C3入门教程——从本地控制走向云端控制(TCP/IP UDP篇)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        距离实现一个完整的物联网小应用只差最后一步了,今天聊聊怎么样在手机上对ESP32芯片发送指令和接收数据,并借助ESP官方的接口——rainmaker,来实现远程控制和通信。我们也借由此进入智能家居时代1.0(部分物联网概念可以看看【序】在23年谈物联网)

       

目录

level 1:通过socket广播收发实现本地控制

建立TCP SCOKET CLIENT通信

建立TCP SCOKET SEVER通信

小结

level 2:更广泛的传输--UDP通信 & 通过远程控制实现点灯

总结


        虽然在上一篇中我们已经学习到了如何让ESP32-C3通过WiFi连接互联网,以及如何通过UDP广播的方式通过手机上的esp touch为ESP32轻松配置网络(链接指路→ESP32 从scan到smart config 讲透WIFI配置)但我们仍然需要更进一步,如果把互联网比作是不同端口之间的路线的话,处理器如何判断哪些数据是需要的,哪些数据是不需要的呢?这是我们今天所想要解决的问题。

        所以这里我们就需要更进一步引入协议的概念。一般情况下基于WiFi和以太网的设备都会原生运行我们比较熟知的互联网TCP/IP协议栈,通过它,我们可以大大降低数据本身协议的适配和开发难度(but 虽然降低了,对于入门来说仍然有很多东西要学)

        TCP即传输控制协议,是一种面向连接的、可靠的、基于字节流的通信协议,分为服务器和客户端。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络
TCP通信的框图

level 1:通过socket广播收发实现本地控制

        开始广播收发之前,我们需要下载一个网络调试工具(NetAssist)的软件。可以帮助我们调节端口的位置地址以及相关的参数的,我们可以通过它来进行内容的收发。下载链接指路(链接源于网络):NetAssist网络调试助手.exe

         安装完成之后打开会看到如下的界面:

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

        

        其实乍一看SOCKET概念的时候,我还是有一点懵的,因为之前没有做过网络通信相关的实验,花了好一阵子才开始理清整体的结构和概念,上面的图一定要结合代码多看几遍(当然,我下面的注释也会按照层级来详细解释)

        首先要解释一下这个client是什么,在上面TCP通信的框图里面,我们看到了左半部分作为客户端,右半部分作为服务端;connect之后客户端可以直接提交内容和接受信息,但服务端在收发信息之前,需要bind listen accept确立状态。

        所以我们先从左半边相对简单的的客户端去分析~

建立TCP SCOKET CLIENT通信

       首先我们从整体上对整个代码的概念进行认识,因为我们esp是client模式,所以要先去看电脑端的 网络调试助手选择的ip和端口,并且填入到最上面这里#define,不然则会无法配对。关于函数部分的详细原理会在后面的小结部分梳理,先从整体上对各个功能模块认知,建立宏观的基础概念更有利于理解(个人看法)。

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

源代码如下:

#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.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 "protocol_examples_common.h"
#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sockets.h"



#define HOST_IP_ADDR    "192.168.0.133"  
#define PORT            3333

static const char *TAG = "example";
static const char *payload = "message from computer";

static void tcp_client_task(void *pvParameters)
{
    char rx_buffer[128];
    char host_ip[] = HOST_IP_ADDR;
    int addr_family = 0;
    int ip_protocol = 0;

    while (1)   //大while(1)
    {
        struct sockaddr_in dest_addr;
        dest_addr.sin_addr.s_addr = inet_addr(host_ip);
        dest_addr.sin_family = AF_INET;     //调用了另一个文件中的 #define AF_INET 2
        dest_addr.sin_port = htons(PORT);   //port 上面咱们定义过啦
        addr_family = AF_INET;              //(同上)
        ip_protocol = IPPROTO_IP;           //#define IPPROTO_IP 0;


        int sock =  socket(addr_family, SOCK_STREAM, ip_protocol);
        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);

        int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
        if (err != 0) {
            ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
            break;
        }
        ESP_LOGI(TAG, "Successfully connected");

        while (1) //大while(1)里面的while(1)
        {
            int err = send(sock, payload, strlen(payload), 0);
            if (err < 0) {
                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                break;
            }

            int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
            // Error occurred during receiving
            if (len < 0) {
                ESP_LOGE(TAG, "recv failed: errno %d", errno);
                break;
            }
            // Data received
            else {
                rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
                ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
                ESP_LOGI(TAG, "%s", rx_buffer);
            }

            vTaskDelay(2000 / portTICK_PERIOD_MS);
        }

        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }
    vTaskDelete(NULL);
}

void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    ESP_ERROR_CHECK(example_connect());

    xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 5, NULL);
}

        因为调用了其他文件夹的文件,所以需要修改一下顶层的cmakelist(注意,不是main文件夹里面的),这样可以链接到通信所需要用到的两个头文件。

cmake_minimum_required(VERSION 3.5)  
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mqtt_tcp)

建立TCP SCOKET SEVER通信

        作为SERVER端,其实代码中需要考虑的东西是更多的,不能仅仅看作只增加了bind()、listen()和accept();不过如果client部分有没看懂的部分也没关系,可能学完sever部分会有新的观念,还是老规矩,我们先从整体的角度出发:

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

源代码如下:

#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 "protocol_examples_common.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>


#define PORT                        3333
#define KEEPALIVE_IDLE              5
#define KEEPALIVE_INTERVAL          5
#define KEEPALIVE_COUNT             3

static const char *TAG = "tcp server";

void wifi_get_ip(void)
{
    tcpip_adapter_ip_info_t ipInfo;
    tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ipInfo);
    ESP_LOGI(TAG, "wifi_get_ip ip=%s", ip4addr_ntoa(&(ipInfo.ip.addr)));
}

static void do_retransmit(void *pvParameters)
{
    int sock = (int)pvParameters;
    int len;
    char rx_buffer[128];

    ESP_LOGI(TAG, "do_retransmit(%d)", sock);
    while(true)
    {
        len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        if (len < 0) {
            ESP_LOGE(TAG, "Socket(%d) Error occurred during receiving: errno %d", sock, errno);
            shutdown(sock, 0);
            close(sock);
            vTaskDelete(NULL);
        } 
        
        else if (len == 0) {
            ESP_LOGW(TAG, "Socket(%d) Connection closed", sock);
        }
        
        else {
            rx_buffer[len] = 0; // 空中止接收到任何内容都视为字符串
            ESP_LOGI(TAG, "Socket(%d) Received %d bytes: %s", sock, len, rx_buffer);

            // send() 可以返回定义长度更短的字符,所以有一点裕度更好(个人翻译的,不一定准确qwq)
            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, "Socket(%d) Error occurred during sending: errno %d", sock, errno);
                }
                to_write -= written;
            }
        }
    }
}

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;

    wifi_get_ip();
    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;
    }

    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));

    ESP_LOGI(TAG, "Socket created");

    int err = bind(listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
    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) {
        ESP_LOGI(TAG, "Socket listening");
        struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
        socklen_t addr_len = sizeof(source_addr);
        int 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
        if (source_addr.ss_family == PF_INET) {
            inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
        }
        ESP_LOGI(TAG, "Socket accepted ip address: %s | %d", addr_str, sock);

        // do_retransmit(sock);
        xTaskCreate(do_retransmit, "do_retransmit", 4096, (void*)sock, 6, NULL);
    }

CLEAN_UP:
    close(listen_sock);
    vTaskDelete(NULL);
}

void app_main(void)
{
    nvs_flash_init();
    esp_netif_init();
    esp_event_loop_create_default();
    example_connect();

    xTaskCreate(tcp_server_task, "tcp_server", 4096, (void*)AF_INET, 5, NULL);  
}

小结

整体梳理一下所用到的函数部分,其实不难看出几乎每一个步骤都是围绕着socket操作的。

socket创建

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

socket绑定

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

socket监听

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

socket连接

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

发送数据

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

接受数据

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

socket关闭

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

socket释放

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

level 2:更广泛的传输--UDP通信 & 通过远程控制实现点灯

        UDP 是 User Datagram Protocol 的简称, 中文名是用户数据报协议, 是 OSI (Open System Interconnection, 开放式系统互联)参考模型中一种无连接的传输层协议,在网络中它与 TCP 协议一样用于处理数据包,是一种无连接的协议。

        在 OSI 模型中,UDP和TCP一样,在传输层(第四层),处于 IP 协议的上一层。UDP 协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前 8 个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。UDP 有不提供数据包分组、组装和不能对数据包进行排序的缺点,也就是说,当报文发送之后,是无法得知其是否安全完整到达的。

        简单来说UDP就是没有确TCP协议。TCP每发出一个数据包都要求确认,如果有一个数据包丢失,就收不到确认,发送方就必须重发这个数据包。为了保证传输的可靠性,TCP协议在UDP基础之上建立了三次对话的确认机制,即在正式收发数据前,必须和对方建立可靠的连接。TCP数据包和UDP一样,都是由首部和数据两部分组成,唯一不同的是,TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,通常TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不必再分割。

        UDP 用来支持那些需要在计算机之间传输数据的网络应用。 包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都需要使用 UDP 协议。 UDP 协议从问世至今已经被使用了很多年, 虽然其最初的光彩已经被一些类似协议所掩盖,但是即使是在今天 UDP 仍然不失为一项非常实用和可行的网络传输层协议。

        下图是UDP的传输过程,可以看到相比于TCP协议,UDP简化了一些步骤:

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络
UDP传输过程

        这一部分原理和TCP部分大致相同,因为网上找到的例程有点小bug还没有解决,所以暂时先简单分享一下UDP方面的思路,后续理解了会更新完整代码,关键实现代码如下(led灯在另一个文件中已经定义,此处也可以删掉):

esp32 ipv6,基于ESP32的物联网开发设计,物联网,嵌入式硬件,vscode,网络协议,网络

参考代码如下:

static struct sockaddr_in dest_addr;                  //远端地址
socklen_t dest_addr_socklen = sizeof(dest_addr);
static int udp_socket = 0;                          //连接socket
TaskHandle_t xUDPRecvTask = NULL;

void udp_send_data(char* data, int len)
{

    if(udp_socket>0){

        int err = sendto(udp_socket, data, len, 0, (struct sockaddr *)&dest_addr, dest_addr_socklen);
        if (err < 0)   printf( "Error occurred during sending: errno %d", errno);
    }
}

void udp_recv_data(void *pvParameters){
    socklen_t socklen = sizeof(dest_addr);
    uint8_t rx_buffer[1024] = {0};
    printf("create udp recv\n");

    while (1)
    {
        int len = recvfrom(udp_socket, rx_buffer, sizeof(rx_buffer) - 1, 0,  (struct sockaddr *)&dest_addr, &dest_addr_socklen);
        if(len > 0){
            if(len == 2 && rx_buffer[0]=='o' && rx_buffer[1]=='n')  led_red(LED_ON);
            else if(len == 3 && rx_buffer[0]=='o' && rx_buffer[1]=='f' && rx_buffer[2]=='f')     led_red(LED_OFF);
            else
            {
                rx_buffer[len] = 0; //未尾增加"\0,确保长度"
                printf("Received %d bytes: %s.\n", len, rx_buffer);
                udp_send_data((char*)rx_buffer, len);
            }
        }
    }
}

void udp_ini_client(void *pvParameters){
    if(udp_socket>0){
        close(udp_socket);
        udp_socket=0;
    }
    udp_socket = socket(AF_INET,SOCK_DGRAM,0);
    printf("connect_socket:%d\n",udp_socket);

    if(udp_socket < 0){
       printf( "Unable to create socket: errno %d", errno);
       return;
    }

    dest_addr.sin_addr.s_addr = inet_addr("255.255.255.255"); 
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(UDP_PORT);//目标端口
    printf("Socket created, sending to 255.255.255.255:%d", UDP_PORT);

    struct sockaddr_in Loacl_addr;
    Loacl_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    Loacl_addr.sin_family = AF_INET;
    Loacl_addr.sin_port = htons(UDP_PORT); 

    uint8_t res = 0;
    res = bind(udp_socket,(struct sockaddr *)&Loacl_addr,sizeof(Loacl_addr));
    if(res != 0){
        printf("bind error\n");
    }

    if(xUDPRecvTask != NULL){
        vTaskDelete(xUDPRecvTask);
        xUDPRecvTask = NULL;
    }

    xTaskCreate(&udp_recv_data,"udp_recv_data",2048*2,NULL,10,&xUDPRecvTask);
    vTaskDelete(NULL);
}

void create_udp()
{
    xTaskCreate(&udp_ini_client, "udp_ini_client", 4096, NULL, 5, NULL);
}

总结

        对于第一次接触本地控制来说,tcp/ip真的可以算是一个难啃的骨头,会有很多新的概念,会综合很多部分的使用;最重要的是,不同例程的思路也迥然不同,官方的例程不够全面也没有注解,而第三方的例程有时候的定义和用法又需要重新理解,还有一些嵌套的思路有时候就像解一团绳结一样,如果不是对整体特别熟练,理解起来也会非常头大。

        从全局的角度一点点入手,分解、拆开之后,会清晰很多;有些小块不懂的地方其实也不用死磕,可以先记录下来,之后再一点点看,慢慢会有一些思路,很多问题在不知不觉中就明白了。

        这章主要还是聊的局域网内的本地控制,现在我们在同一个wifi下已经能通过电脑远程控制板子了,再加上之前的smart config,可以做出一些不错的尝试。下一章更近一步,通过MQTT协议和HTTP协议的学习,获得联网信息,再到接入esp rainmaker,达到远程控制。
        文章来源地址https://www.toymoban.com/news/detail-603263.html

到了这里,关于4·ESP32-C3入门教程——从本地控制走向云端控制(TCP/IP UDP篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【ESP32入门教程】ESP32开发板原理图,引脚图,引脚功能,ESP WROOM 32,介绍ESP32硬件

    记录本人学习历程,同时也分享给大家学习,仅供参考! 模组共有38个管脚,具体描述如下。 1. GPIO6至GPIO11 引脚 控制集成在模组内部的SPI Flash, 不建议用于其他功能 。 SCK/CLK GPIO6 SDO/SD0 GPIO7 SDI/SD1 GPIO8 SHD/SD2 GPIO9 SWP/SD3 GPIO10 SCS/CMD GPIO11 2.Strapping 管脚 ESP32共有5个Strapping管脚,。

    2024年02月08日
    浏览(67)
  • Arduino ESP32开发环境搭建入门教程,esp32的arduino开发环境搭建教程,arduino导入eps32开发插件

    从官网下载 Arduino IDE 软件并安装。下载链接:Software | Arduino 网盘链接:链接:https://pan.baidu.com/s/1ZuSbo1BPy8XyyXzfl4KNzg?pwd=f8yd 提取码:f8yd 1、找到Arduino IDE安装目录,打开hardware文件夹。 2、在hardware文件夹中创建一个espressif文件夹。 3、将解压出的文件夹移动到espressif文件夹中,

    2024年02月13日
    浏览(56)
  • ESP系列入门教程(一)——之用继电器简单驱动一个LED【附 ESP32 / ESP8266 通用代码】

    最近在跟着几个大佬的教学视频做项目。陆续会更新记录一些要点,便于后期总结笔记的时候进行引用。 也可以帮助有心跟着一起复刻的宝子们,更好地捋清思路。 【本系列教程 - 总目标】: 使用ESP系列板卡,通过MQTT进行数据通信,达到远程控制多个传感器的效果。 ◆配

    2024年01月19日
    浏览(50)
  • c3p0 数据池入门使用教程

    从零开始手写 mybatis (三)jdbc pool 如何从零手写实现数据库连接池 dbcp? 万字长文深入浅出数据库连接池 HikariCP/Commons DBCP/Tomcat/c3p0/druid 对比 Database Connection Pool 数据库连接池概览 c3p0 数据池入门使用教程 alibaba druid 入门介绍 数据库连接池 HikariCP 性能为什么这么快? Apache

    2024年03月15日
    浏览(58)
  • [入门教程]详细讲解STM32控制LED点阵屏(HUB75接口)

    本文适合初级入门的同学,大佬请绕道。讲解怎么用stm32驱动成品LED点阵屏显示一副七色无灰度的图片,有灰度的图片可以入门后继续研究。   使用软硬件: 软件:Keil5,程序在后面会给出 硬件:STM32F103最小系统、全彩色P4LED点阵屏(HUB75接口,16扫) 说下stm32驱动LED点阵屏用

    2024年01月19日
    浏览(92)
  • 【esp32c3配置arduino IDE教程】

    本文主线FastBond2阶段1——基于ESP32C3开发的简易IO调试设备,esp32c3环境搭建,设计目标如下 设计用户操作界面,该设备具备简单易用的操作界面,外加显示屏SSD1306和旋转编码器进行显示和控制,用户后期可进行二次开发WiFi或蓝牙连接电脑或手机监控。 多种数字和模拟信号的

    2024年02月03日
    浏览(57)
  • 006.合宙ESP32-C3+蓝牙调试器通过BLE发送接收数据教程

    在平衡小车制作过程中,需要对KP/KD/KSP/KSI等PID系数进行调试,而平衡小车无法通过USB等进行有线调试,而ESP32-C3自带蓝牙+WIFI,使用WIFI比较吃算力,故选择通过蓝牙进行调参,同时能够将Angle/Encoder/PWM等数据回传至手机端进行查看。 前期通过查找资料,发现合宙ESP32-C3自带蓝

    2024年02月03日
    浏览(57)
  • 新上架的简约版合宙ESP32C3使用arduino开发的教程

    经过两个月的缺货下架后,9块9包邮的合宙ESP32C3又重新上架了,真香。这一批都是没有带串口芯片的简约版(9块9要啥自行车)。在下架前,简约版要使用2.0.0版本的ESP32开发板库才能下载,而2.0.0版本有一些丢失arduino自带库的诡异BUG,所以一直没法用于下载。现在由于发布了2

    2024年02月05日
    浏览(92)
  • ESP32-C3 应用 篇(实例二、通过蓝牙将传感器数据发送给手机,手机端控制 SK6812 LED)

    前面文章说过,蓝牙协议博主了解不是很深入,只进行一些基础的了解,示例的测试,和初学者一样,基本上蓝牙专栏系列博文都是一步一步摸索过来的,功夫不负有心人,到目前为止,多多少少对蓝牙 GATT 有了一定的认识。 那么我们今天就要学以致用,使用 ESP32-C3 的蓝牙

    2024年02月09日
    浏览(50)
  • 物联网开发笔记(89)- 使用Micropython开发ESP32开发板之合宙ESP32 C3开发板通过串口SPI控制st7789 TFT液晶屏1.3寸

    一、目的         这一节我们学习如何使用合宙的ESP32 C3开发板控制1.3寸彩色TFT显示屏模块,分辨率240*240,SPI接口,ST7789驱动芯片。 二、环境         ESP32  C3 + Thonny + 1.3寸 st7789液晶屏模块 + 几根杜邦线 + Win10 接线方法:   三、st7789 TFT显示屏驱动 st7789py.py   四、点亮

    2024年02月11日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包