walker2048

  • 2025-09-02
  • 回复了主题帖: 【米尔RK3506入门级国产开发板】开箱及上电

    大佬看看同时跑Linux和RTT是怎么搞,能不能不要搞这么大的sdk

  • 2025-08-29
  • 回复了主题帖: 「拼多多4寸720x720分辨率掌机改造系列」3、添加USB hub,修改掌机USB电路

    吾妻思萌 发表于 2025-8-29 11:39 贴主 ,你这个还需要考虑下功率和散热 戴载东西多了容易功率跟不上。 主要是接键盘和U盘,鼠标这些,所以功耗并不大。

  • 2025-08-21
  • 回复了主题帖: 长达70多个小时的短路测试,自恢复保险丝烧掉

    牛逼,是一直通着电过流么?

  • 2025-08-19
  • 回复了主题帖: 【迈来芯MLX90642测评】第1辑:开箱及简介

    这个分辨率实际拍摄效果怎样?

  • 2025-08-13
  • 回复了主题帖: 「拼多多4寸720x720分辨率掌机改造系列」3、添加USB hub,修改掌机USB电路

    wangerxian 发表于 2025-8-13 10:06 等于需要修改驱动呗,感觉可以玩出花来~ 是的,需要自己注意驱动的问题

  • 2025-08-12
  • 回复了主题帖: 「拼多多4寸720x720分辨率掌机改造系列」3、添加USB hub,修改掌机USB电路

    wangerxian 发表于 2025-8-12 17:42 在掌机上加一个USB Hub?掌机还能作为USB主机接其他什么设备呀? U盘,键盘,wifi,摄像头,开发板能接啥,就能接啥,内核自己编译

  • 2025-08-11
  • 回复了主题帖: 「拼多多4寸720x720分辨率掌机改造系列」3、添加USB hub,修改掌机USB电路

    秦天qintian0303 发表于 2025-8-11 11:49 你这准备都用那种免驱的USB模块?     可以自己编译内核放进去啊。星闪也是要自己编译驱动的,这pdd的掌机可以当开发板玩,除了没有io扩展以外。

  • 2025-08-10
  • 发表了主题帖: 「米尔基于瑞芯微RK3562开发板」7、使用开发板的蓝牙控制球形机器人的方向和速度

    本帖最后由 walker2048 于 2025-8-11 11:10 编辑 去年在淘宝上买了一个sphero的sprk+球形机器人,买回来也是简单玩了一下,大部分时候是女儿拿去逗猫了。正版的如下图,我买的可能是盗版,没有logo,可以用官方的app操作和控制。   现在就拿出来试试,在开发板上连接和控制这个球。 首先需要使用蓝牙工具先扫描到这个设备的地址。这里我们使用bluetoothctl这个工具,在命令行里运行这个工具后,再执行对应的指令 power on/off这个指令用来开启/关闭蓝牙适配器(基础操作,操作设备前必选),一般建议先运行一下,万一设备没有开启,后续的指令没用。 scan on/off接下来使用这个指令开始/停止扫描周边设备(实时显示MAC地址和名称),我们先使用scan on开启扫描,扫描到设备后,执行scan off关闭扫描(否则会一直输出扫描到的蓝牙设备信息) 由于这个设备之前已经匹配过了,可以直接在devices指令里查看。 devices列出所有已扫描或已配对的设备(含MAC地址和名称)      接下来尝试配对和连接设备。常用的指令如下,例如这里我们可以在首次连接时,使用pair 68:86:E7:05:93:64来配对设备,如果已经配对过了,可以使用trust 68:86:E7:05:93:64设置为信任设备。再次连接时,可以使用connect 68:86:E7:05:93:64来连接蓝牙设备,正常情况下,连接上设备后,可以看到这个设备的具体属性和服务内容。     ​​指令​​ ​​功能​​ ​​使用场景​​ pair <MAC地址> 发起配对请求(部分设备需输入PIN码) 首次连接设备时使用 trust <MAC地址> 设为信任设备(自动接受后续连接) 避免重复确认连接 connect <MAC地址> 连接已配对设备 日常重连耳机/音箱 disconnect <MAC地址> 断开当前连接(不解除配对) 临时断开外设 remove <MAC地址> 移除设备(解除配对关系) 清理无用设备   结果在连接机器人球体时,提示如下:     似乎是连接不上sphero球体设备,在搜索引擎上搜索后,没找到什么有效的可执行方案。 然后我执行了一下info 68:86:E7:05:93:64,想查看一下对应的设备信息,信息如下。   从输出可以看到,这个设备未连接上,同时可以发现,该设备带一个蓝牙串口功能,那我们似乎可以使用绑定蓝牙串口的方式去连接。 接下来,我们可以使用以下命令查看蓝牙设备上的服务通道。 sdptool records 68:86:E7:05:93:64   然后使用命令绑定该服务通道。   绑定失败,我们再打开蓝牙服务监听工具,btmon,看看具体的事务流程。   重新运行一下rfcomm指令 rfcomm connect /dev/rfcomm0 68:86:E7:05:93:64 1                 可以看到btmon的输出如上,应该是内核模块没有使能rfcomm支持导致的。重新编译内核后,运行相关指令可以连上。 设备连上后,我们就可以使用串口发送指令,命令小球按照对应指令执行了,代码如下 import serial import time SPHERO_PORT = "/dev/rfcomm0" BAUD_RATE = 115200 def calc_checksum(data_bytes): # 计算Sphero协议的CHK return (~sum(data_bytes) & 0xFF) def send_command(did, cid, seq, data): # 发送命令到 Sphero dlen = len(data) + 1 # 数据长度 + CHK字节 body = [did, cid, seq, dlen] + data chk = calc_checksum(body) packet = bytes([0xFF, 0xFF] + body + [chk]) ser.write(packet) ser.flush() time.sleep(0.05) resp = ser.read_all() if resp: print("收到响应:", resp) def send_drive(speed, heading, state=1, seq=0x00): heading_bytes = heading.to_bytes(2, 'big') data = [speed, heading_bytes[0], heading_bytes[1], state] send_command(0x02, 0x30, seq, data) ser = serial.Serial(SPHERO_PORT, BAUD_RATE, timeout=1) try: # 设置LED为红色 send_command(0x02, 0x20, 0x01, [255, 0, 0]) time.sleep(1) send_command(0x02, 0x20, 0x01, [0, 255, 0]) time.sleep(1) send_command(0x02, 0x20, 0x01, [0, 0, 255]) time.sleep(1) send_command(0x02, 0x20, 0x01, [0, 0, 0]) # 让球以固定速度旋转 seq = 0 for h in range(0, 360, 10): send_drive(50, h, 1, seq) seq = (seq + 1) & 0xFF time.sleep(0.05) # 停止 send_drive(0, 0, 0, seq) finally: ser.close() 实际上官方并没有公开串口部分的控制指令和代码,这些是我在chatGPT上找到的,经测试可以正常使用,控制演示视频如下。 [localvideo]25bbca52e54e773f5689275480009eb3[/localvideo]  

  • 回复了主题帖: 「拼多多4寸720x720分辨率掌机改造系列」3、添加USB hub,修改掌机USB电路

    Jacktang 发表于 2025-8-10 09:47 QFN封装的不好焊接啊 还行,加热台热一热就好,锡网也不用打,电烙铁过一遍当锡膏

  • 2025-08-09
  • 发表了主题帖: 「米尔基于瑞芯微RK3562开发板」6、移植星闪模组ws73驱动并测试

    为了方便以后测试星闪sle的功能,购买了星闪模组,安信可的星闪模组有两种,一种是SDIO接口,一种是USB接口,两种模组使用的芯片是不一样的。   这里我使用的是USB接口的模组,人比较懒,就先直接飞线焊接测试了,焊接好的模组图片如下图   由于原厂的SDK适配的是5.10版本的内核,我先在全志的开发板上编译和测试了,模组和驱动可以正常工作和联网。 然后瑞芯微的SDK基本上是使用了6.10版本的内核,这就有点麻烦了。 首先解压缩WS73的SDK,然后修改6.10/build/config/ws73_usb_light_v2.config这个文件,这里需要注意的是 1、配置交叉编译工具链 2、设置内核目录 3、添加编译参数,去掉一些代码里的警告信息,否则会报错。 4、设置目标为arm64 5、接口设置成USB接口   由于6.10版本的API和文件有调整,需要对WS73的SDK驱动文件进行一些修改。 例如: 1、头文件引用调整      有多个文件,可以直接用目录搜索和全部替换方式解决。 2、api差异消除 例如内存访问机制差异,这个也不是很清楚,看了其他大佬的解决方案,也是直接注释掉对应语句和数据结构的定义即可。AI给的解释如下     这个内存访问差异在不少文件里都有,需要一个个搜索出来注释掉,否则会影响编译,无法通过。   还有下面这个是api接口函数的差异,有几个,需要全局搜索替换掉。    80211驱动层的参数结构体和初始化差异也需要调整。     各种内容调整完毕后,就可以编译和测试了。在SDK目录的根目录,依次执行以下命令,编译平台驱动,wifi模块,ble和sle模块。顺利编译出来的模块信息如下图。 make platform make wifi make ble make sle     将对应模块上传到开发板上,就可以进行测试了。接好硬件,加载驱动模块(先加载plat_soc, 然后加载wifi_soc)。可以看到wifi模块顺利识别到了。   然后使用ip addr指令,也可以看到多出了两个无线节点wlan1和wlan2,分别是sta模式和ap模式节点。用wpa_supplicant和配置文件设置好自己家的wifi连接配置后,就可以愉快的连上网了。 如下图所示,就能ping通外围,并且使用wifi模组了。     ble和sle的功能,我对这两个协议也不是很熟悉,手头上也没有合适的星闪sle设备进行调试,就等以后有时间再测试吧。

  • 发表了主题帖: 「拼多多4寸720x720分辨率掌机改造系列」3、添加USB hub,修改掌机USB电路

    由于机子上只有一个USB接口,如果想接多个USB设备的话,需要接一个USB集线器,平时都不知道集线器丢哪里,用起来也麻烦得很。然后我这边有时候也需要接wifi模块使用,TypeC,转接头,USB口3道环节这么一过,接触不良也是有可能的。所以把USB hub内置到游戏机里,其中一个下级接到wifi模组,然后充电口和原来的USB口就可以多接两个usb设备了。   然后这里可以选的usb hub芯片方案也不少,例如以前买的SL2.1A或者其他的芯片,只是现在要放到这么小的设备了,还是考虑一下更小型的贴片型号,例如ch334p。 CH334P​​以​​工业级可靠性、高集成度和灵活配置​​成为USB2.0扩展的理想选择,尤其适合空间受限且需多设备并发的场景(如工控主板、嵌入式系统)。其​​MTT模式​​显著提升吞吐量,而​​QFN封装​​兼顾成本与体积,是设计高效外设方案的优质基础芯片。   这是官方文档里,关于ch334的介绍。可以看出,这个芯片的性能还是非常不错的。     同时,由于内置了LDO,USB的上拉和下拉电阻,以及高精度时钟,外围可以做到非常精简的程度。     简化的方案图如下图所示,当然我这边是没有两个供电这么复杂,由于想要使用开关或者gpio来控制USB hub和USB外设的供电,这样电池供电的时候,如果不需要使用wifi或者USB设备,就可以更好的节省电量,需要修改一下设计图纸。     最终原理图如下: 其实X1外部晶振是可以省略的,直接把1号引脚,也就是XO引脚接地就可以了。然后使用了一个MT9700作为电子开关,这样就能通过外部的滑动开关,或者GPIO来控制USB电源输出。         电路图也是非常简单的,大容量的电解电容,也是为了应对wifi模组的峰值电流,问了AI去匹配的,焊接测试后,wifi模组可以正常工作。 大概是这样的一个板子,安装在机器内部,就不考虑座子什么的了,直接飞线焊接吧。     实物焊接测试后,确实可以正常工作,接到上位机,或者游戏机里,USB wifi模组可以正常驱动和联网。

  • 2025-08-04
  • 回复了主题帖: 群友说他有一台类似的老古董笔记本,现在值不值五毛钱?

    你要是让你照片里的朋友当场在电脑上签个名,一边签一边录视频,然后你蹦了他,估计能拍卖个几百万吧。

  • 回复了主题帖: 有CP2102N-QFN24的原理图吗

    中心焊盘地为啥不接?

  • 发表了主题帖: 「米尔基于瑞芯微RK3562开发板」5、在开发板上运行小智AI智能聊天机器人

    py-xiaozhi 是一个使用 Python 实现的小智语音客户端,旨在通过代码学习和在没有硬件条件下体验 AI 小智的语音功能。   我也没有部署过这个,这次就试一试。首先需要使用Ubuntu系统,之前刷的tf卡系统已经不知道是哪张卡了,干脆直接刷一个最新版本到EMMC里吧。刷系统到EMMC里,需要先将板卡的J16接口,接上双头USBA接口,另一头接到电脑上,然后按说明书里烧录注意事项操作一下。进入烧录模式后,就可以直接用镜像烧录到板子的EMMC里了。   烧录完毕后,简单调整一下国内源(不知道是网络问题还是什么原配置的阿里源连不上,先注释掉),设置下root账户密码,就可以用ssh远程登录到设备上了。   登录好以后,我们先安装一些需要用到的依赖包。其他的依赖系统已经自带了,就不用折腾了。 apt-get install python3-pyaudio portaudio19-dev libopus0 libopus-dev build-essential python3-venv 然后需要安装miniconda,安装这个是已经习惯了,尤其是Python环境的,如果不小心安装错了python的软件包或者把环境整坏了,直接删掉重新建一个就行,非常方便。 wget -O Miniconda3-latest-Linux-aarch64.sh https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh chmod +x Miniconda3-latest-*.sh ./Miniconda3-latest-*.sh miniconda的安装过程这里就不写了,按默认的做就行了。安装完之后,需要重新加载.bashrc配置文件(source ~/.bashrc),然后修改一下国内源,在~/.condarc文件添加以下内容 channels: - defaults show_channel_urls: true default_channels: - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r 然后创建一个conda环境。并且激活这个环境 conda create -n xiaozhi python=3.9 -y conda activate xiaozhi 接着下载py-xiaozhi的项目源码。 git clone --depth=1 https://github.com/huangjunsen0406/py-xiaozhi.git cd py-xiaozhi/ 然后设置pip的国内源 pip config set global.index-url https://mirrors.aliyun.com/pypi/simple pip config set install.trusted-host mirrors.aliyun.com 然后安装相关的依赖 pip install -r requirements.txt 安装依赖的时候,需要注意一下,第一个依赖comtypes是windows环境下需要用的,我们可以直接删掉。   实话实说,我也不知道这个包是干嘛的,问了下GPT,说这是windows才需要的东西。   其他的就看自己连什么源最快了。安装其他软件依赖包,在最后需要等很长时间构建,一边等一边刷番剧就好了。     等了很久,由于板子上没有足够的资源(主要是内存耗尽了),失败了。   根据GPT给的提示,我们可以从国内源,或者conda安装已经编译好的pyQT5包,我选择conda吧,运气比较好,可以直接安装,大概1分钟左右,就安装好了 conda install -c conda-forge pyqt=5.15.11   其他的包,尽量自己找一个国内源,网络好的来装上 pip install -r requirements.txt -i https://mirrors.cloud.tencent.com/pypi/simple/   这里我用的腾讯源,顺利安装上了,很多包都有编译好的包可以直接安装。接下来,我们检查一下关键依赖是否正常安装。 # 检查关键依赖 python -c "import sounddevice; print('SoundDevice OK')" python -c "import opuslib; print('Opus OK')" python -c "import PyQt5.QtCore; print('PyQt5 OK')" 显示以下内容就应该是安装好了。   然后在命令行里输入命令开启小智AI,然后在小智官网注册一下,激活设备就可以了 python main.py         由于这个开发板似乎并不支持HDMI音频输出,我这边也没有音箱可以连,(似乎音频输出也有点问题)。就不好测试语音相关内容了,只是手动输入一些聊天内容,然后联网有一些响应。 [localvideo]0d5260cfa0a38078a81ee1fbec7a000e[/localvideo]    

  • 2025-08-03
  • 发表了主题帖: 「米尔基于瑞芯微RK3562开发板」3、通过开发板的USB口,实现espnow远程控制其他设备

    ESP-NOW 是乐鑫定义的一种无线通信协议,能够在无路由器的情况下直接、快速、低功耗地控制智能设备。它能够与 Wi-Fi 和 Bluetooth LE 共存,支持乐鑫 ESP8266、ESP32、ESP32-S 和 ESP32-C 等多系列 SoC。ESP-NOW 广泛应用于智能家电、远程控制和传感器等领域。   我用无线通信的时间并不是很长,wifi,蓝牙ble,星闪sle都玩过一些,也不是很深入。espnow是目前我最喜欢的一个无线通信协议,响应快,设备数量少就不需要组网,用起来虽然麻烦一些。       架构介绍 首先开发板这边是不支持espnow协议的,所以必须通过一个esp32模块作为中转站,向其他的esp32模块发送espnow信息。 大致确定下来后,就可以确认一下esp32和上位机如何交互了,首先优先考虑串口通信,USB通信或者SPI,SDIO通信就不考虑了。时间很短,还是直接用串口最简单。 espnow这边也不考虑什么组网管理,设备管理,或者消息转发那些,直接广播就好。       espnow设备功能设计 espnow设备这边就不用考虑什么很复杂的东西了,功能非常简单,首先是驱动彩色的2812LED灯。这个直接用官方example的代码就可以了。 /* * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ #include "driver/rmt_tx.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "led_strip_encoder.h" #include <string.h> #define RMT_LED_STRIP_RESOLUTION_HZ \ 10000000 // 10MHz resolution, 1 tick = 0.1us (led strip needs a high // resolution) #define RMT_LED_STRIP_GPIO_NUM 8 #define EXAMPLE_LED_NUMBERS 1 #define EXAMPLE_CHASE_SPEED_MS 5 static const char *TAG = "example"; static uint8_t led_strip_pixels[EXAMPLE_LED_NUMBERS * 3]; /** * [url=home.php?mod=space&uid=159083]@brief[/url] Simple helper function, converting HSV color space to RGB color space * * Wiki: https://en.wikipedia.org/wiki/HSL_and_HSV * */ void led_strip_hsv2rgb(uint32_t h, uint32_t s, uint32_t v, uint32_t *r, uint32_t *g, uint32_t *b) { h %= 360; // h -> [0,360] uint32_t rgb_max = v * 2.55f; uint32_t rgb_min = rgb_max * (100 - s) / 100.0f; uint32_t i = h / 60; uint32_t diff = h % 60; // RGB adjustment amount by hue uint32_t rgb_adj = (rgb_max - rgb_min) * diff / 60; switch (i) { case 0: *r = rgb_max; *g = rgb_min + rgb_adj; *b = rgb_min; break; case 1: *r = rgb_max - rgb_adj; *g = rgb_max; *b = rgb_min; break; case 2: *r = rgb_min; *g = rgb_max; *b = rgb_min + rgb_adj; break; case 3: *r = rgb_min; *g = rgb_max - rgb_adj; *b = rgb_max; break; case 4: *r = rgb_min + rgb_adj; *g = rgb_min; *b = rgb_max; break; default: *r = rgb_max; *g = rgb_min; *b = rgb_max - rgb_adj; break; } } uint16_t g_led_hue = 120; void led_main(void) { uint32_t red = 0; uint32_t green = 0; uint32_t blue = 0; ESP_LOGI(TAG, "Create RMT TX channel"); rmt_channel_handle_t led_chan = NULL; rmt_tx_channel_config_t tx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT, // select source clock .gpio_num = RMT_LED_STRIP_GPIO_NUM, .mem_block_symbols = 64, // increase the block size can make the LED less flickering .resolution_hz = RMT_LED_STRIP_RESOLUTION_HZ, .trans_queue_depth = 4, // set the number of transactions that can be // pending in the background }; ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &led_chan)); ESP_LOGI(TAG, "Install led strip encoder"); rmt_encoder_handle_t led_encoder = NULL; led_strip_encoder_config_t encoder_config = { .resolution = RMT_LED_STRIP_RESOLUTION_HZ, }; ESP_ERROR_CHECK(rmt_new_led_strip_encoder(&encoder_config, &led_encoder)); ESP_LOGI(TAG, "Enable RMT TX channel"); ESP_ERROR_CHECK(rmt_enable(led_chan)); ESP_LOGI(TAG, "Start LED rainbow chase"); rmt_transmit_config_t tx_config = { .loop_count = 0, // no transfer loop }; while (1) { led_strip_hsv2rgb(g_led_hue, 100, 10, &red, &green, &blue); led_strip_pixels[0] = green; led_strip_pixels[1] = blue; led_strip_pixels[2] = red; // Flush RGB values to LEDs ESP_ERROR_CHECK(rmt_transmit(led_chan, led_encoder, led_strip_pixels, sizeof(led_strip_pixels), &tx_config)); ESP_ERROR_CHECK(rmt_tx_wait_all_done(led_chan, portMAX_DELAY)); vTaskDelay(pdMS_TO_TICKS(EXAMPLE_CHASE_SPEED_MS)); } }   这个代码其实也没啥好说的,纯粹就是把原来彩色循环的代码,改成使用g_led_hue全局变量,定期设置led的颜色,很简单。 然后照抄一下espnow示范代码,修改一下,变成下面的内容。 /* ESPNOW Example This example code is in the Public Domain (or CC0 licensed, at your option.) Unless required by applicable law or agreed to in writing, this software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ /* This example shows how to use ESPNOW. Prepare two device, one for sending ESPNOW data and another for receiving ESPNOW data. */ #include "esp_crc.h" #include "esp_event.h" #include "esp_log.h" #include "esp_mac.h" #include "esp_netif.h" #include "esp_now.h" #include "esp_random.h" #include "esp_wifi.h" #include "espnow_example.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/timers.h" #include "nvs_flash.h" #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define ESPNOW_MAXDELAY 512 #define CLIENT_ID 101 extern uint16_t g_led_hue; static const char *TAG = "espnow_example"; static QueueHandle_t s_example_espnow_queue; static uint8_t s_example_broadcast_mac[ESP_NOW_ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; /* WiFi should start before using ESPNOW */ static void example_wifi_init(void) { ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); ESP_ERROR_CHECK(esp_wifi_set_mode(ESPNOW_WIFI_MODE)); ESP_ERROR_CHECK(esp_wifi_start()); ESP_ERROR_CHECK( esp_wifi_set_channel(CONFIG_ESPNOW_CHANNEL, WIFI_SECOND_CHAN_NONE)); ESP_ERROR_CHECK(esp_wifi_set_protocol( ESPNOW_WIFI_IF, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR)); } /* ESPNOW sending or receiving callback function is called in WiFi task. * Users should not do lengthy operations from this task. Instead, post * necessary data to a queue and handle it from a lower priority task. */ static void example_espnow_send_cb(const uint8_t *mac_addr, esp_now_send_status_t status) { example_espnow_event_t evt; example_espnow_event_send_cb_t *send_cb = &evt.info.send_cb; if (mac_addr == NULL) { ESP_LOGE(TAG, "Send cb arg error"); return; } evt.id = EXAMPLE_ESPNOW_SEND_CB; memcpy(send_cb->mac_addr, mac_addr, ESP_NOW_ETH_ALEN); send_cb->status = status; if (xQueueSend(s_example_espnow_queue, &evt, ESPNOW_MAXDELAY) != pdTRUE) { ESP_LOGW(TAG, "Send send queue fail"); } } static void example_espnow_recv_cb(const esp_now_recv_info_t *recv_info, const uint8_t *data, int len) { example_espnow_event_t evt; example_espnow_event_recv_cb_t *recv_cb = &evt.info.recv_cb; uint8_t *mac_addr = recv_info->src_addr; uint8_t *des_addr = recv_info->des_addr; if (mac_addr == NULL || data == NULL || len <= 0) { ESP_LOGE(TAG, "Receive cb arg error"); return; } // ESP_LOG_BUFFER_HEX(TAG, data, len); if (IS_BROADCAST_ADDR(des_addr)) { /* If added a peer with encryption before, the receive packets may be * encrypted as peer-to-peer message or unencrypted over the broadcast * channel. Users can check the destination address to distinguish it. */ ESP_LOGD(TAG, "Receive broadcast ESPNOW data"); } else { ESP_LOGD(TAG, "Receive unicast ESPNOW data"); } evt.id = EXAMPLE_ESPNOW_RECV_CB; memcpy(recv_cb->mac_addr, mac_addr, ESP_NOW_ETH_ALEN); recv_cb->data = malloc(len); if (recv_cb->data == NULL) { ESP_LOGE(TAG, "Malloc receive data fail"); return; } memcpy(recv_cb->data, data, len); recv_cb->data_len = len; if (xQueueSend(s_example_espnow_queue, &evt, ESPNOW_MAXDELAY) != pdTRUE) { ESP_LOGW(TAG, "Send receive queue fail"); free(recv_cb->data); } } /* Prepare ESPNOW data to be sent. */ void example_espnow_data_prepare(example_espnow_send_param_t *send_param) { example_espnow_data_t *buf = (example_espnow_data_t *)send_param->buffer; buf->type = IS_BROADCAST_ADDR(send_param->dest_mac) ? EXAMPLE_ESPNOW_DATA_BROADCAST : EXAMPLE_ESPNOW_DATA_UNICAST; buf->state = send_param->state; buf->crc = 0; buf->magic = send_param->magic; buf->frame_type = FRAME_GET_LED_STATUS_RESPONE; led_date_t data = {.client_id = CLIENT_ID, .hue_value = g_led_hue}; memcpy(buf->payload, &data, sizeof(data)); buf->crc = esp_crc16_le(UINT16_MAX, (uint8_t const *)buf, send_param->len); } static esp_err_t espnow_send_led_status() { example_espnow_send_param_t *send_param; /* Initialize sending parameters. */ send_param = malloc(sizeof(example_espnow_send_param_t)); if (send_param == NULL) { ESP_LOGE(TAG, "Malloc send parameter fail"); vSemaphoreDelete(s_example_espnow_queue); esp_now_deinit(); return ESP_FAIL; } memset(send_param, 0, sizeof(example_espnow_send_param_t)); send_param->unicast = false; send_param->broadcast = true; send_param->state = 0; send_param->magic = esp_random(); send_param->count = CONFIG_ESPNOW_SEND_COUNT; send_param->delay = CONFIG_ESPNOW_SEND_DELAY; send_param->len = sizeof(led_date_t) + sizeof(example_espnow_data_t); send_param->buffer = malloc(sizeof(led_date_t) + sizeof(example_espnow_data_t)); if (send_param->buffer == NULL) { ESP_LOGE(TAG, "Malloc send buffer fail"); free(send_param); vSemaphoreDelete(s_example_espnow_queue); esp_now_deinit(); return ESP_FAIL; } memcpy(send_param->dest_mac, s_example_broadcast_mac, ESP_NOW_ETH_ALEN); example_espnow_data_prepare(send_param); if (esp_now_send(send_param->dest_mac, send_param->buffer, send_param->len) != ESP_OK) { ESP_LOGE(TAG, "Send error"); } return ESP_OK; } /* Parse received ESPNOW data. */ int example_espnow_data_parse(uint8_t *data, uint16_t data_len, uint8_t *state, uint8_t *frame_type, int *magic) { example_espnow_data_t *buf = (example_espnow_data_t *)data; *state = buf->state; *frame_type = buf->frame_type; // ESP_LOGI(TAG, "frame type %d", buf->frame_type); *magic = buf->magic; if (buf->frame_type == FRAME_SET_LED_STATUS) { led_date_t *data = (led_date_t *)buf->payload; if (data->client_id == CLIENT_ID) { g_led_hue = data->hue_value; ESP_LOGI(TAG, "Set led hue value: %d", g_led_hue); } } else if (buf->frame_type == FRAME_GET_LED_STATUS_REQUEST) { espnow_send_led_status(); } return buf->type; } static void example_espnow_task(void *pvParameter) { example_espnow_event_t evt; uint8_t recv_state = 0; uint8_t recv_frame_type = 0; int recv_magic = 0; int ret; while (xQueueReceive(s_example_espnow_queue, &evt, portMAX_DELAY) == pdTRUE) { switch (evt.id) { case EXAMPLE_ESPNOW_SEND_CB: { // example_espnow_event_send_cb_t *send_cb = &evt.info.send_cb; // ESP_LOGD(TAG, "Send data to " MACSTR ", status1: %d", // MAC2STR(send_cb->mac_addr), send_cb->status); // ESP_LOGI(TAG, "send data to " MACSTR "", MAC2STR(send_cb->mac_addr)); break; } case EXAMPLE_ESPNOW_RECV_CB: { example_espnow_event_recv_cb_t *recv_cb = &evt.info.recv_cb; ret = example_espnow_data_parse(recv_cb->data, recv_cb->data_len, &recv_state, &recv_frame_type, &recv_magic); free(recv_cb->data); if (ret == EXAMPLE_ESPNOW_DATA_BROADCAST) { // ESP_LOGI( // TAG, // "Receive frame type %d broadcast data from: " MACSTR ", len: %d", // recv_frame_type, MAC2STR(recv_cb->mac_addr), recv_cb->data_len); /* If MAC address does not exist in peer list, add it to peer list. */ if (esp_now_is_peer_exist(recv_cb->mac_addr) == false) { esp_now_peer_info_t *peer = malloc(sizeof(esp_now_peer_info_t)); if (peer == NULL) { ESP_LOGE(TAG, "Malloc peer information fail"); vTaskDelete(NULL); } memset(peer, 0, sizeof(esp_now_peer_info_t)); peer->channel = CONFIG_ESPNOW_CHANNEL; peer->ifidx = ESPNOW_WIFI_IF; peer->encrypt = true; memcpy(peer->lmk, CONFIG_ESPNOW_LMK, ESP_NOW_KEY_LEN); memcpy(peer->peer_addr, recv_cb->mac_addr, ESP_NOW_ETH_ALEN); ESP_ERROR_CHECK(esp_now_add_peer(peer)); free(peer); } } else if (ret == EXAMPLE_ESPNOW_DATA_UNICAST) { ESP_LOGI(TAG, "Receive %dth unicast data from: " MACSTR ", len: %d", recv_frame_type, MAC2STR(recv_cb->mac_addr), recv_cb->data_len); } else { ESP_LOGI(TAG, "Receive error data from: " MACSTR "", MAC2STR(recv_cb->mac_addr)); } break; } default: ESP_LOGE(TAG, "Callback type error: %d", evt.id); break; } } } static esp_err_t example_espnow_init(void) { s_example_espnow_queue = xQueueCreate(ESPNOW_QUEUE_SIZE, sizeof(example_espnow_event_t)); if (s_example_espnow_queue == NULL) { ESP_LOGE(TAG, "Create mutex fail"); return ESP_FAIL; } /* Initialize ESPNOW and register sending and receiving callback function. */ ESP_ERROR_CHECK(esp_now_init()); ESP_ERROR_CHECK(esp_now_register_send_cb(example_espnow_send_cb)); ESP_ERROR_CHECK(esp_now_register_recv_cb(example_espnow_recv_cb)); /* Set primary master key. */ ESP_ERROR_CHECK(esp_now_set_pmk((uint8_t *)CONFIG_ESPNOW_PMK)); /* Add broadcast peer information to peer list. */ esp_now_peer_info_t *peer = malloc(sizeof(esp_now_peer_info_t)); if (peer == NULL) { ESP_LOGE(TAG, "Malloc peer information fail"); vSemaphoreDelete(s_example_espnow_queue); esp_now_deinit(); return ESP_FAIL; } memset(peer, 0, sizeof(esp_now_peer_info_t)); peer->channel = CONFIG_ESPNOW_CHANNEL; peer->ifidx = ESPNOW_WIFI_IF; peer->encrypt = false; memcpy(peer->peer_addr, s_example_broadcast_mac, ESP_NOW_ETH_ALEN); ESP_ERROR_CHECK(esp_now_add_peer(peer)); free(peer); xTaskCreate(example_espnow_task, "example_espnow_task", 2048, NULL, 4, NULL); return ESP_OK; } extern void led_main(void); void app_main(void) { // Initialize NVS 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); example_wifi_init(); example_espnow_init(); led_main(); } 看起来代码很多,实际上没啥难度,就是初始化espnow,编写espnow的接收和发送回调函数,接收到和本机设置的client_id设置完全一样的id,就根据帧协议修改全局g_led_hue变量。 写好设备端代码后,就需要写控制端的代码了,其实也没多复杂,也是复制espnow的案例,改成广播,只是多增加一个按钮测试发送的功能。这里代码就不放了,后面直接丢压缩包一起。   接下来,就写串口通信部分,这部分直接抄原本的串口echo代码,改改就行。 /* Configure parameters of an UART driver, * communication pins and install the driver */ uart_config_t uart_config = { .baud_rate = 115200, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .source_clk = UART_SCLK_DEFAULT, }; int intr_alloc_flags = 0; #if CONFIG_UART_ISR_IN_IRAM intr_alloc_flags = ESP_INTR_FLAG_IRAM; #endif ESP_ERROR_CHECK( uart_driver_install(1, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags)); ESP_ERROR_CHECK(uart_param_config(1, &uart_config)); ESP_ERROR_CHECK(uart_set_pin(1, 4, 5, -1, -1)); // Configure a temporary buffer for the incoming data uint8_t *data = (uint8_t *)malloc(BUF_SIZE); uint8_t uart_buff[UART_BUFF_LEN] = {0}; uart_buff[0] = FRAME_HEADER_START; uart_buff[UART_BUFF_LEN - 1] = '\n'; uint16_t new_hue = 0; while (true) { // Read data from the UART int len = uart_read_bytes(1, data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS); if (len >= 5) { if (data[0] == FRAME_HEADER_START && data[len - 1] == '\n') { switch (data[1]) { case FRAME_SET_LED_STATUS: new_hue = data[2] << 8 | data[3]; espnow_set_led_status(new_hue); case FRAME_GET_LED_STATUS_REQUEST: default: vTaskDelay(pdMS_TO_TICKS(15)); espnow_get_led_status(); vTaskDelay(pdMS_TO_TICKS(30)); uart_buff[2] = g_led_hue >> 8; uart_buff[3] = g_led_hue & 0xFF; uart_buff[4] = 0; uart_buff[5] = CLIENT_ID; uart_write_bytes(1, uart_buff, UART_BUFF_LEN); break; } } } } 代码也是非常简单,就是初始化串口1,然后定期读取串口1的数据,假如符合设定的数据帧,就发起espnow通信,例如读取设备的led灯状态,或者设置设备的led状态。 测试好之后,就可以开始写上位机的代码了。   上位机的软件,一开始就打算好了,直接用golang来实现,这样我可以方便的在X86电脑上测试,并且交叉编译到arm64设备上使用。 这里的程序也是非常简单的,读取命令行参数,根据命令行参数读取串口(默认设置的ttyACM0),并解析对应的数据。然后向串口写入数据,设置led灯颜色。 package main import ( "flag" "github.com/gookit/color" "github.com/jacobsa/go-serial/serial" ) var cliDev = flag.String("dev", "ttyACM0", "Serial device name") var cliHue = flag.Int("hue", 0, "Hue value") func main() { flag.Parse() color.Greenp("Use " + *cliDev + " as serial device\n") options := serial.OpenOptions{ PortName: "/dev/" + *cliDev, BaudRate: 115200, DataBits: 8, StopBits: 1, MinimumReadSize: 4, } // Open the port. port, err := serial.Open(options) if err != nil { color.Redln("serial.Open: %v", err) } // Make sure to close it later. defer port.Close() // 读取颜色 b := []byte{0x55, 0x01, 0x00, 0x00, 0x00, 0x00, 0x0A} _, err = port.Write(b) if err != nil { color.Redln("port.Write: %v", err) } buf := make([]byte, 128) _, err = port.Read(buf) if err != nil { color.Redln("read err:", err) } c_hue := int(buf[2])*256 + int(buf[3]) color.Greenp("Get client color ", c_hue, "\n") c_hue = *cliHue // 设置颜色 b[1] = 0 b[2] = byte(c_hue >> 8) b[3] = byte(c_hue & 0xFF) _, err = port.Write(b) if err != nil { color.Redln("port.Write: %v\n", err) } color.Cyanln("Set color ", c_hue) _, err = port.Read(buf) if err != nil { color.Redln("read err:", err) } c_hue = int(buf[2])*256 + int(buf[3]) // color.Green.Printf("%x\n", buf) color.Greenp("Get client color ", c_hue, "\n") } 代码写好以后,可以直接使用以前写好的脚本编译好,然后上传到米尔开发板上。ssh我已经用公钥登陆配置好了,所以直接执行就行。 #/bin/bash # 设置主机IP host_ip="192.168.50.85" # 设置路径 host_path="~/" # go 编译配置 build_config="GOARCH=arm64 CGO_ENABLED=0 GOOS=linux" if [ -z "$1" ]; then echo "error: no target define." exit 1 fi file=$1.go app=$1 if [ -f "$1.go" ]; then # 交叉编译 env ${build_config} go build -ldflags "-s -w" -o ${app} ${file} && \ # 压缩 upx -3 ${app} && \ # 上传到板子上 scp ${app} root@${host_ip}:${host_path} && \ rm ${app} else echo "error: ${file} file no found." fi   执行这个脚本,会交叉编译golang程序,然后使用upx压缩程序,再上传到米尔开发板上。 最后,就是直接在米尔开发板上运行程序了。先检查一下串口是哪个,不出意外,确实是ttyACM0   那就不用管tty设备名称了,默认就是这个了。只需要指定hue颜色就行,毕竟是偷懒写的程序,每次修改颜色都得执行一次。 同时esp32的中转     所有的源码都放在附件里了,有兴趣的自己下载看吧

  • 2025-08-02
  • 回复了主题帖: ESP32 Arduino Flash 空间分区可以都给APP吗

    esp32的分区看用途来分,如果你不需要使用文件系统存东西,就不需要那个SPIFFS分区,另一个MODEL分区我不知道是啥。你可以问问AI怎样调整,我不用arduino不知道arduino必须用哪些分区。

  • 2025-07-31
  • 回复了主题帖: 「米尔基于瑞芯微RK3562开发板」4、使用Coze制作一个模仿原神瑶瑶角色的聊天机器人

    lugl4313820 发表于 2025-7-31 16:55 楼主好谦虚呀!智能体,现在是不是非常流行? 反正在视频网站上各种吹AI,我也不懂这些

  • 回复了主题帖: 「米尔基于瑞芯微RK3562开发板」4、使用Coze制作一个模仿原神瑶瑶角色的聊天机器人

    tobot 发表于 2025-7-31 16:32 扣子? 是的,就是扣子

  • 回复了主题帖: 「米尔基于瑞芯微RK3562开发板」4、使用Coze制作一个模仿原神瑶瑶角色的聊天机器人

    wangerxian 发表于 2025-7-31 16:02 coze是哪个大厂的?感觉最近好多平台都在推。 字节跳动呀

  • 回复了主题帖: 「拼多多4寸720x720分辨率掌机改造系列」1、开箱和拆解,离谱的EMMC速率

    stonism 发表于 2025-7-31 17:21 arkos和ee比较有多少提升? 没啥提升吧,也不关注这个。

最近访客

< 1/6 >

统计信息

已有481人来访过

  • 芯积分:944
  • 好友:5
  • 主题:89
  • 回复:297

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言