250106 搭建ESP32 开发环境 ESP-IDF, VMware, VS Code, Ubuntu Server

设置简介

主机操作系统是windows 10。使用VMWare workstation虚拟机安装Ubuntu Server 20.04。VS Code安装一些插件,SSH连接到ubuntu虚拟机进行ESP32的开发。VS Code只负责编辑代码,其他操作(创建项目、配置、编译、烧录等)使用ESP-IDF的命令行命令。

参考了B站教程 https://www.bilibili.com/video/BV1eRg7exEcT/

虚拟机安装ubuntu server

开始是用VirtualBox安装ubuntu,结果遇到了网络设置的问题。网络配置好了,又遇到了ESP开发板USB连接电脑后虚拟机不能正常识别的问题。教程里用的是VMWare,本来觉得虚拟机软件应该没多大影响的,还是再试试VMWare吧,结果试了之后发现确实省了不少事……

不过最开始没用VMWare,是因为直接去VMWare官网,都不知道下载的地方在哪,得先在google搜索vmware workstation才能到下载相关的网页,但是下载之前要先注册账号。而VirtualBox进入官网就可以直接下载了。

从官网下载VMWare Workstation

看了下VMWare的历史,也是几经易手,现在是属于Broadcom了。先直接搜索“vmware workstation download”,第一个结果就是,进去之后点击"download fusion or workstation"。Fusion是Mac OS用的,Windows用workstation。
下载workstation要先注册broadcom账号,登录之后在搜索栏里搜索“workstation”就能找到下载的页面了。现在pro版本对个人也已经免费了,直接用pro版就好。

c40427d278147fc980011be21896b98f.png

2cf7d62bbc7aee50f338e1bbae85e69c.png

a5d83828411860886bc32f3ea117eceb.png

Ubuntu下载安装

安装Ubuntu server额教程。主要是注意硬盘空间分配,默认的设置不会利用所有空间,不过如果这里忘了配置后面也有补救的方法。

https://www.cnblogs.com/yeyouqing/articles/17021253.html

ubuntu.22.04-server版详细安装方法安装

搜索“ubuntu server 20.04 LTS”,进入红色框框起来的网页,下载server版本镜像。
ca89458f1485c8b3f43a75bf9839c7f1.png
虚拟机配置如下图所示。处理器数量根据主机实际处理器选择。硬盘空间,我开始选的是20G,因为想着只是用来学习ESP32的,等后面如果要着手做项目了再增加容量。后来发现就算只是用来学习20G也不够用,所以建议30G以上。但是硬盘空间不够不一定是设置里分配的空间不够,而是ubuntu server可能没有把分配的空间全部用上,详见最后一节“可能遇到的问题”。
c8c20d4af30002b03768bd19d24e6d5c.png

安装时记得勾选安装SSH。
456364f94e560b3c7c5768aa6cf298b9.png
不要安装任何更新。
0d03ded083f783b3bb0d690e96d7a1c6.png

查看ubuntu server的IP地址

参考另一篇关于配置VirtualBox中ubuntu网络的博客
241216 VirtualBox 和 ubuntu server 20.04 配置网络

在ubuntu上安装ESP-IDF

参考官方安装教程
https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/linux-macos-setup.html

将idf.py命令永久加入环境变量

虽然官方文档并不推荐这么做,因为这样之后每次启动一个console都会加载idf.py命令环境。对于正常使用linux操作系统来说应该只在需要使用idf.py的时候才载入,不过我们安装这个ubuntu server就只是为了学习esp32的开发,几乎只要打开console就会用到idf,所以加入环境变量正合适。

在home目录下使用ls -al命令可以看到一个隐藏文件.profile。使用任意编辑器修改这个文件,在最后一行加入source esp/esp-idf/export.sh。如果使按着官方文档步骤安装的IDF,那么就是这个路径esp/esp-idf/export.sh,如果安装到了别的文件夹就要改成相应的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vm@vmwareubserver2004:~$ ls -al
total 64
drwxr-xr-x 10 vm vm 4096 Dec 18 12:42 .
drwxr-xr-x 3 root root 4096 Dec 16 20:05 ..
-rw------- 1 vm vm 2857 Jan 8 17:39 .bash_history
-rw-r--r-- 1 vm vm 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 vm vm 3771 Feb 25 2020 .bashrc
drwx------ 5 vm vm 4096 Dec 16 21:04 .cache
drwxrwxr-x 3 vm vm 4096 Dec 16 21:04 .dotnet
drwxrwxr-x 4 vm vm 4096 Dec 16 20:54 esp
drwxrwxr-x 5 vm vm 4096 Dec 16 20:46 .espressif
drwxrwxr-x 3 vm vm 4096 Dec 17 09:19 .local
drwxrwxr-x 11 vm vm 4096 Jan 8 17:11 myproject
-rw-r--r-- 1 vm vm 839 Dec 17 09:22 .profile
drwx------ 2 vm vm 4096 Dec 17 09:35 .ssh
-rw-r--r-- 1 vm vm 0 Dec 16 20:10 .sudo_as_admin_successful
drwxr-x--- 5 vm vm 4096 Jan 10 14:35 .vscode-server
-rw-rw-r-- 1 vm vm 183 Jan 6 09:19 .wget-hsts
-rw------- 1 vm vm 128 Dec 18 12:42 .Xauthority

保存修改之后新打开一个console,IDF的环境就会生效了。

安装和使用VS Code相关插件

本地插件

  • Remote SSH

remote插件

这里是SSH连接到ubuntu server之后,在ubuntu上安装的VS Code插件

  • C/C++
  • ESP-IDF

ESP-IDF插件本来是需要配置ESP-IDF的安装目录的,但是好像是因为先安装了ESP-IDF,插件自动找到了安装目录,所以插件装好后就可以直接用。

设置Remote SSH插件

安装了Remote SSH插件之后左边栏会有插件图标,点击进入能看到之前连接过的机器。右上角有一个齿轮图标,点击之后可以选择config文件。
d0d5404fe2b9d0d4cc0b7d60a84cd035.png

一般是使用User\<用户名>\ssh\config这个文件。
df86e8c2f12f6303b15da912b0f3bc67.png

打开之后可以添加如下内容。Host可以填虚拟机的名字,HostName填虚拟机的IP地址,User就是ubuntu server的用户名。

1
2
3
Host ubuntu_server_2004
HostName 192.168.183.128
User vm

之后每次连接就会需要输入密码。如果想省略输入密码这一步,可以在虚拟机ubuntu里加入主机的SSH公钥。如何生成一个公钥参考https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent

简略的步骤:

  • 在命令行里输入
1
ssh-keygen -t ed25519-sk -C "your_email@example.com"
  • 在windows系统里生成的密钥默认在路径c/Users/YOU/.ssh/id_ALGORITHM
  • 打开以.pub结尾的文件,复制里面的内容
  • 粘贴到ubuntu的~/.ssh/authorized_keys文件里,保存

这样以后再用SSH连接到虚拟机Ubuntu就不用输入密码了。

使用ESP-IDF命令行和VS Code开发项目

https://docs.espressif.com/projects/esp-idf/zh_CN/v5.4/esp32/api-guides/tools/idf-py.html#selecting-idf-target

ESP32,芯片,模组,开发板

在开发项目之前,有几个概念需要搞清楚。参考https://www.bilibili.com/video/BV1tY4y1L7HV

芯片 模组 开发板

  • 芯片就是单独的芯片,不包括无线功能
  • 模组,包括芯片,会加入一些WIFI、蓝牙等无线收发器,存储器等
  • 开发板,模组装在开发板上,加了更多外设,USB、按钮,以及引出IO针脚

芯片、模组、开发板的详细参数,需要根据型号找对应的datasheet,一般很容易就能搜索到。

ESP32系列

“ESP32系列”就是一个单独系列,不包括ESP32-S2系列, ESP32-C3系列, ESP32-S3系列等。但是初学看到“ESP32系列”这个词很容易混淆,以为EPS32系列包含了所有ESP32开头的芯片

以Hello World为例,开发一个ESP32项目的简单流程

首先VS Code使用SSH连接到虚拟机Ubuntu,然后打开命令行。

创建项目

选择或者一个文件夹用来存放创建的项目,比如myproject,然后使用以下命令:
idf.py create-project helloworld

创建成功之后,在VS Code中打开文件夹,然后在弹出的窗口中选择刚创建的文件夹。VS Code就切换到helloworld项目的文件夹了。
1906a5f2210e4818e71c5088b2e795d8.png

9c6cb099f8ade5c5001324c2bcc3a8ab.png

引入IDF源码路径

编程时引用ESP-IDF源码的头文件,VS Code需要知道IDF源码在哪。设置方法:
Crtl + Shift + P,调出VS Code命令框,输入ESP-IDF: Add vscode Configuration Folder

b62a253931e5396835c92a4fe3d3c555.png

在开始编写程序之前先构建一次项目

idf.py build
这一步主要是因为有时候会出现include IDF头文件时报错,先构建一次之后就不会报错了。

Hello world代码

main/helloworld.c中加入如下代码

1
2
3
4
5
6
7
#include <stdio.h>
#include "esp_log.h"

void app_main(void)
{
ESP_LOGI("HelloWorld", "Hello, world!");
}

选择目标芯片

在VS Code中打开命令行,使用命令:
idf.py set-target esp32

如果不执行set-target这一步,构建系统默认使用esp32

配置工程

使用命令:
idf.py menuconfig

命令行里会出现:
36c92aac140d726fee65341dfaafb28c.png
可以设置的参数太多,这里举一些例子。

Flash size

进入Serial flasher config,再进入Flash size,可以设置flash大小。
11d4ff9e3c793b9acc585e8633263835.png

不知道Flash的大小?

我在这遇到了一个问题。我用的模组,从模组上的印刷文字来开,是ESP32-WROVER-IE,但是这个模组根据储存器大小细又分了好几个不同型号,而我并没有更详细的信息。于是抱着试试看的心态,启用了Detect flash size when flashing bootlodaer,发现真的可以用。

ESP PSRAM

我使用的模组还带有PSRAM,想启用PSRAM,需要以下设置:
进入Component Config->ESP PSRAM,选中
c5c4e20e2c744d30df030e3003c0c9b9.png

CPU frequency

默认的频率是160MHz,我的模组最高支持240MHz,通过以下方式修改:
进入Component config->ESP System Settings,再进入CPU frequency就可以修改频率了。
60d8b542ad397702e5eca486757c16da.png

最后记得保存。

构建工程

idf.py build

烧录工程

如果没安装驱动首先需要安装驱动。开发板USB连接到电脑上时,VMWare workstation会弹出一个对话框,让我们选择连接到主机还是虚拟机,我们当然选虚拟机。
不过如果这里选错了,之后也可以更改:
9d0fec0d2fe7306374a370852f9981de.png
连接好之后使用命令:
idf.py flash

以下是输出结果,可以看到Auto-detected Flash size: 8MB,刚才设置的自动检测flash大小生效了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
Executing action: flash
Serial port /dev/ttyUSB0
Connecting.....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting.....
Detecting chip type... ESP32
Running ninja in directory /home/vm/myproject/helloworld/build
Executing "ninja flash"...
[1/5] cd /home/vm/myproject/helloworld/build/esp-idf/esptool_py && /home/vm/.espressif/python_env/idf5.2_py...ect/helloworld/build/partition_table/partition-table.bin /home/vm/myproject/helloworld/build/helloworld.bin
helloworld.bin binary size 0x36e60 bytes. Smallest app partition is 0x100000 bytes. 0xc91a0 bytes (79%) free.
[1/1] cd /home/vm/myproject/helloworld/build/bootloader/esp-idf/esptool_py && /home/vm/.espressif/python_en...ck_sizes.py --offset 0x8000 bootloader 0x1000 /home/vm/myproject/helloworld/build/bootloader/bootloader.bin
Bootloader binary size 0x6840 bytes. 0x7c0 bytes (7%) free.
[2/3] cd /home/vm/esp/esp-idf/components/esptool_py && /usr/bin/cmake -D IDF_PATH=/home/vm/esp/esp-idf -D "...ORY=/home/vm/myproject/helloworld/build -P /home/vm/esp/esp-idf/components/esptool_py/run_serial_tool.cmake
esptool.py --chip esp32 -p /dev/ttyUSB0 -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size detect 0x1000 bootloader/bootloader.bin 0x10000 helloworld.bin 0x8000 partition_table/partition-table.bin
esptool.py v4.8.1
Serial port /dev/ttyUSB0
Connecting....
Chip is ESP32-D0WD-V3 (revision v3.0)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 10:97:bd:d4:32:b0
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 460800
Changed.
Configuring flash size...
Auto-detected Flash size: 8MB
Flash will be erased from 0x00001000 to 0x00007fff...
Flash will be erased from 0x00010000 to 0x00046fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash params set to 0x0230
Compressed 26688 bytes to 16308...
Writing at 0x00001000... (100 %)
Wrote 26688 bytes (16308 compressed) at 0x00001000 in 0.8 seconds (effective 278.4 kbit/s)...
Hash of data verified.
Compressed 224864 bytes to 111865...
Writing at 0x00041714... (100 %)
Wrote 224864 bytes (111865 compressed) at 0x00010000 in 2.6 seconds (effective 682.9 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 103...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (103 compressed) at 0x00008000 in 0.1 seconds (effective 419.6 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
Done

打开IDF监视器

idf.py monitor

输出如下。可以看到I (190) esp_psram: Found 8MB PSRAM device,检测到了8MB的PSRAM。
I (1234) HelloWorld: Hello, world!代码里的Hello World也成功打印出来了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
Executing action: monitor
Serial port /dev/ttyUSB0
Connecting....
Detecting chip type... Unsupported detection protocol, switching and trying again...
Connecting....
Detecting chip type... ESP32
Running idf_monitor in directory /home/vm/myproject/helloworld
Executing "/home/vm/.espressif/python_env/idf5.2_py3.8_env/bin/python /home/vm/esp/esp-idf/tools/idf_monitor.py -p /dev/ttyUSB0 -b 115200 --toolchain-prefix xtensa-esp32-elf- --target esp32 --revision 0 /home/vm/myproject/helloworld/build/helloworld.elf -m '/home/vm/.espressif/python_env/idf5.2_py3.8_env/bin/python' '/home/vm/esp/esp-idf/tools/idf.py'"...
--- esp-idf-monitor 1.5.0 on /dev/ttyUSB0 115200
--- Quit: Ctrl+] | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:7172
load:0x40078000,len:15540
load:0x40080400,len:4
--- 0x40080400: _init at ??:?

ho 8 tail 4 room 4
load:0x40080404,len:3904
entry 0x40080640
I (31) boot: ESP-IDF v5.2.3 2nd stage bootloader
I (31) boot: compile time Jan 10 2025 17:05:00
I (31) boot: Multicore bootloader
I (35) boot: chip revision: v3.0
I (39) boot.esp32: SPI Speed : 40MHz
I (44) boot.esp32: SPI Mode : DIO
I (48) boot.esp32: SPI Flash Size : 8MB
I (53) boot: Enabling RNG early entropy source...
I (58) boot: Partition Table:
I (62) boot: ## Label Usage Type ST Offset Length
I (69) boot: 0 nvs WiFi data 01 02 00009000 00006000
I (77) boot: 1 phy_init RF data 01 01 0000f000 00001000
I (84) boot: 2 factory factory app 00 00 00010000 00100000
I (92) boot: End of partition table
I (96) esp_image: segment 0: paddr=00010020 vaddr=3f400020 size=0d814h ( 55316) map
I (116) esp_image: segment 1: paddr=0001d83c vaddr=3ffb0000 size=022a0h ( 8864) load
I (118) esp_image: segment 2: paddr=0001fae4 vaddr=40080000 size=00534h ( 1332) load
I (124) esp_image: segment 3: paddr=00020020 vaddr=400d0020 size=17204h ( 94724) map
I (151) esp_image: segment 4: paddr=0003722c vaddr=40080534 size=0fc24h ( 64548) load
I (176) boot: Loaded app from partition at offset 0x10000
I (176) boot: Disabling RNG early entropy source...
I (187) cpu_start: Multicore app
I (188) quad_psram: This chip is ESP32-D0WD
I (190) esp_psram: Found 8MB PSRAM device
I (190) esp_psram: Speed: 40MHz
I (194) esp_psram: PSRAM initialized, cache is in low/high (2-core) mode.
W (201) esp_psram: Virtual address not enough for PSRAM, map as much as we can. 4MB is mapped
I (1116) esp_psram: SPI SRAM memory test OK
I (1124) cpu_start: Pro cpu start user code
I (1124) cpu_start: cpu freq: 240000000 Hz
I (1124) cpu_start: Application information:
I (1127) cpu_start: Project name: helloworld
I (1132) cpu_start: App version: 1
I (1137) cpu_start: Compile time: Jan 10 2025 17:04:43
I (1143) cpu_start: ELF file SHA256: 729bcc1d8...
I (1148) cpu_start: ESP-IDF: v5.2.3
I (1153) cpu_start: Min chip rev: v0.0
I (1158) cpu_start: Max chip rev: v3.99
I (1163) cpu_start: Chip rev: v3.0
I (1168) heap_init: Initializing. RAM available for dynamic allocation:
I (1175) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (1181) heap_init: At 3FFB2BE0 len 0002D420 (181 KiB): DRAM
I (1188) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (1194) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (1201) heap_init: At 40090158 len 0000FEA8 (63 KiB): IRAM
I (1207) esp_psram: Adding pool of 4096K of PSRAM memory to heap allocator
I (1216) spi_flash: detected chip: generic
I (1219) spi_flash: flash io: dio
I (1224) main_task: Started on CPU0
I (1234) esp_psram: Reserving pool of 32K of internal memory for DMA/internal allocations
I (1234) main_task: Calling app_main()
I (1234) HelloWorld: Hello, world!
I (1244) main_task: Returned from app_main()

配置过程中可能遇到的问题

Ubuntu硬盘空间不够

Ubuntu可能没有使用所有的硬盘空间
https://www.moralok.com/2023/06/24/Ubuntu-server-20-04-not-all-disk-space-was-allocated-after-installation/

烧录时提示Could not open /dev/ttyUSB*

出现这个错误的时候命令行里也有提示怎么做,使用这个命令
sudo usermod -aG dialout <usrname>,其中username需要替换成我们在ubuntu使用的用户名。