ESP32-C3 PlatformIO 'embed_txtfiles' 修复

前言

9.9元入手了一块ESP32-C3板子, 用来取代手上引脚不足的ESP8266.
但是编译时候总是失败,从Log上看是这一步出了错误.

1
2
3
4
5
prepare_file([".pio\build\esp32c3\lite.ttf.txt.o"], ["src\lite.ttf"])
Converting .pio\build\esp32c3\lite.ttf.txt.o
'xtensa-esp32-elf-objcopy' \xb2\xbb\xca\xc7\xc4ڲ\xbf\xbb\xf2\xcdⲿ\xc3\xfc\xc1Ҳ\xb2\xbb\xcaǿ\xc9\xd4\xcb\xd0еij\xcc\xd0\xf2
\xbb\xf2\xc5\xfa\xb4\xa6\xc0\xed\xceļ\xfe\xa1\xa3
*** [.pio\build\esp32c3\lite.ttf.txt.o] Error 1

此外并没有任何提示了.

错误分析

lite.ttf是我的一个放在src目录下的文件,

为其在platformio.ini配置”board_build.embed_txtfiles = src/lite.ttf”,就可以让这个文件嵌入固件中.
从日志里看到是转化的时候出了问题, xtensa-esp32-elf-objcopy

xtensa是ESP32,ESP32-S2,ESP32-S3的处理器结构,而ESP32-C3是RISC-V的,所以这里出错应该是结构不匹配.
去ESP-IDF的文件夹搜索”elf-objcopy”后发现的确有不同很多类似关键字.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
for dir in esp32 esp32s2 esp32c3 esp32s3; do
if [ $dir = esp32 ]; then
TOOLCHAIN="xtensa-esp32-elf"
elif [ $dir = esp32s2 ]; then
TOOLCHAIN="xtensa-esp32s2-elf"
elif [ $dir = esp32c3 ]; then
TOOLCHAIN="riscv32-esp-elf"
elif [ $dir = esp32s3 ]; then
TOOLCHAIN="xtensa-esp32s3-elf"
else
echo "$dir does not exist"
fi
if [ -d "$dir" ]; then
cd $dir
git status libphy.a | grep "modified" >/dev/null 2>&1
if [ $? -eq 0 ]; then
echo $dir/libphy.a fixed
$TOOLCHAIN-objcopy --redefine-sym ets_printf=phy_printf libphy.a
fi

所以应该是PlatformIO在转化的时候调用了错误的程序导致的.

解决问题

去PlatformIO的工作目录(.platformio)下搜索xtensa-esp32-elf-objcopy

1
2
3
4
5
6
7
8
9
10
11
12
13
[
"xtensa-esp32-elf-objcopy",
"--input-target",
"binary",
"--output-target",
"elf32-xtensa-le",
"--binary-architecture",
"xtensa",
"--rename-section",
".data=.rodata.embedded",
"$SOURCE",
"$TARGET",
]

确实搜索到了多个包含这个关键字的文件_embed_files.py
(platforms\espressif32\builder\frameworks_embed_files.py)
其中platforms下有好几个文件夹

1
2
3
4
5
6
7
├── .platformio
│ └── platforms
│ └── espressif32
│ └── espressif32@src-ba2d3999402da5eaf2c9d5863ef113c7
│ └── espressif32@src-ebefb4289db63a9f0ca5ee29fa328eef
│ └── espressif8266
│ └── raspberrypi

这应该是不同项目所需的platform,凑巧这次C3项目的platform是专门下载的,所以从文件夹修改日期上来看,我这次C3使用的应该是espressif32@src-ebefb4289db63a9f0ca5ee29fa328eef
打开espressif32@src-ebefb4289db63a9f0ca5ee29fa328eef下的_embed_files.py
里面果然还在使用xtensa-esp32-elf-objcopy
按照ESP-IDF里面的描述,应该使用riscv32-esp-elf-objcopy,并且要把xtensa相关的都修改为riscv.

所以最后修改成了

1
2
3
4
5
6
7
8
9
10
11
12
13
[
"riscv32-esp-elf-objcopy",
"--input-target",
"binary",
"--output-target",
"elf32-littleriscv",
"--binary-architecture",
"riscv",
"--rename-section",
".data=.rodata.embedded",
"$SOURCE",
"$TARGET",
]

重新执行编译,这一次终于通过了.

1
2
3
4
5
6
7
8
9
10
11
12
13
Building in release mode
prepare_file([".pio\build\esp32c3\lite.ttf.txt.o"], ["src\lite.ttf"])
Converting .pio\build\esp32c3\lite.ttf.txt.o
revert_original_file([".pio\build\esp32c3\lite.ttf.txt.o"], ["src\lite.ttf"])
Linking .pio\build\esp32c3\firmware.elf
Retrieving maximum program size .pio\build\esp32c3\firmware.elf
Checking size .pio\build\esp32c3\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM: [ ] 4.7% (used 15480 bytes from 327680 bytes)
Flash: [= ] 14.1% (used 443870 bytes from 3145728 bytes)
Building .pio\build\esp32c3\firmware.bin
esptool.py v3.1
Merged 2 ELF sections

_embed_files.py

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# Copyright 2014-present PlatformIO <contact@platformio.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import shutil
from os import SEEK_CUR, SEEK_END
from os.path import basename, isfile, join

from SCons.Script import Builder

Import("env")

board = env.BoardConfig()

#
# Embedded files helpers
#


def extract_files(cppdefines, files_type):
files = []
if "build." + files_type in board:
files.extend(
[
join("$PROJECT_DIR", f)
for f in board.get("build." + files_type, "").split()
if f
]
)
else:
files_define = "COMPONENT_" + files_type.upper()
for define in cppdefines:
if files_define not in define:
continue

value = define[1]
if not isinstance(define, tuple):
print("Warning! %s macro cannot be empty!" % files_define)
return []

if not isinstance(value, str):
print(
"Warning! %s macro must contain "
"a list of files separated by ':'" % files_define
)
return []

for f in value.split(":"):
if not f:
continue
files.append(join("$PROJECT_DIR", f))

for f in files:
if not isfile(env.subst(f)):
print('Warning! Could not find file "%s"' % basename(f))

return files


def remove_config_define(cppdefines, files_type):
for define in cppdefines:
if files_type in define:
env.ProcessUnFlags("-D%s" % "=".join(str(d) for d in define))
return


def prepare_file(source, target, env):
filepath = source[0].get_abspath()
shutil.copy(filepath, filepath + ".piobkp")

with open(filepath, "rb+") as fp:
fp.seek(-1, SEEK_END)
if fp.read(1) != "\0":
fp.seek(0, SEEK_CUR)
fp.write(b"\0")


def revert_original_file(source, target, env):
filepath = source[0].get_abspath()
if isfile(filepath + ".piobkp"):
shutil.move(filepath + ".piobkp", filepath)


def embed_files(files, files_type):
for f in files:
filename = basename(f) + ".txt.o"
file_target = env.TxtToBin(join("$BUILD_DIR", filename), f)
env.Depends("$PIOMAINPROG", file_target)
if files_type == "embed_txtfiles":
env.AddPreAction(file_target, prepare_file)
env.AddPostAction(file_target, revert_original_file)
env.AppendUnique(PIOBUILDFILES=[env.File(join("$BUILD_DIR", filename))])


def transform_to_asm(target, source, env):
files = [join("$BUILD_DIR", s.name + ".S") for s in source]
return files, source


env.Append(
BUILDERS=dict(
TxtToBin=Builder(
action=env.VerboseAction(
" ".join(
[
"riscv32-esp-elf-objcopy",
"--input-target",
"binary",
"--output-target",
"elf32-littleriscv",
"--binary-architecture",
"riscv",
"--rename-section",
".data=.rodata.embedded",
"$SOURCE",
"$TARGET",
]
),
"Converting $TARGET",
),
suffix=".txt.o",
),
TxtToAsm=Builder(
action=env.VerboseAction(
" ".join(
[
join(
env.PioPlatform().get_package_dir("tool-cmake") or "",
"bin",
"cmake",
),
"-DDATA_FILE=$SOURCE",
"-DSOURCE_FILE=$TARGET",
"-DFILE_TYPE=TEXT",
"-P",
join(
env.PioPlatform().get_package_dir("framework-espidf") or "",
"tools",
"cmake",
"scripts",
"data_file_embed_asm.cmake",
),
]
),
"Generating assembly for $TARGET",
),
emitter=transform_to_asm,
single_source=True,
),
)
)


flags = env.get("CPPDEFINES")
for files_type in ("embed_txtfiles", "embed_files"):
if (
"COMPONENT_" + files_type.upper() not in env.Flatten(flags)
and "build." + files_type not in board
):
continue

files = extract_files(flags, files_type)
if "espidf" in env.subst("$PIOFRAMEWORK"):
env.Requires(join("$BUILD_DIR", "${PROGNAME}.elf"), env.TxtToAsm(files))
else:
embed_files(files, files_type)
remove_config_define(flags, files_type)

环境:

1
2
3
4
5
PLATFORM: Espressif 32 (3.3.0+sha.3b5de56) > Espressif ESP32 Dev Module
- framework-arduinoespressif32 0.0.0+sha.68daea4
- tool-esptoolpy 1.30100.210531 (3.1.0)
- toolchain-riscv-esp 1.80400.0 (8.4.0)
- toolchain-riscv32-esp 8.4.0+2021r1