Skip to content

Commit faf3005

Browse files
committed
change(arduino): Rework arduino app
1 parent 8916539 commit faf3005

File tree

12 files changed

+119
-65
lines changed

12 files changed

+119
-65
lines changed

examples/arduino/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ adapt the build folder appropriately when run from a different location.
1818
On success, this will create a `build` directory under the `hello_world`
1919
example.
2020

21+
The Arduino service requires only the build directory to work properly.
22+
The app path is not required but can be used to derive the build directory. If not specified, it will be set to the current working directory.
23+
24+
The build directory is the directory that contains the binary and configuration files.
25+
It can be specified as an absolute path or a relative path to the app path.
26+
If nothing is specified, it will look for the `build` directory in the app path. If it still doesn't find it, it will assume the build directory is the app path.
27+
2128
### Run the tests
2229
2330
```shell
@@ -33,3 +40,9 @@ $ pytest examples/arduino -k test_hello_arduino
3340
3441
This will parse the `build` directory created earlier, flash the chip and
3542
expect the `Hello Arduino!` text to be printed.
43+
44+
You can run the tests specifiying the build directory used to build the example:
45+
46+
```shell
47+
$ pytest --build-dir build examples/arduino -k test_hello_arduino
48+
```
Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import json
2+
import logging
23
import os
3-
from typing import ClassVar
44

55
from pytest_embedded.app import App
66

@@ -13,60 +13,74 @@ class ArduinoApp(App):
1313
sketch (str): Sketch name.
1414
fqbn (str): Fully Qualified Board Name.
1515
target (str) : ESPxx chip.
16-
flash_files (List[Tuple[int, str, str]]): List of (offset, file path, encrypted) of files need to be flashed in.
16+
flash_settings (dict[str, str]): Flash settings for the target.
17+
binary_file (str): Merged binary file path.
18+
elf_file (str): ELF file path.
1719
"""
1820

19-
#: dict of flash settings
20-
flash_settings: ClassVar[dict[str, dict[str, str]]] = {
21-
'esp32': {'flash-mode': 'dio', 'flash-size': 'detect', 'flash-freq': '80m'},
22-
'esp32c2': {'flash-mode': 'dio', 'flash-size': 'detect', 'flash-freq': '60m'},
23-
'esp32c3': {'flash-mode': 'dio', 'flash-size': 'detect', 'flash-freq': '80m'},
24-
'esp32c5': {'flash-mode': 'dio', 'flash-size': 'detect', 'flash-freq': '80m'},
25-
'esp32c6': {'flash-mode': 'dio', 'flash-size': 'detect', 'flash-freq': '80m'},
26-
'esp32c61': {'flash-mode': 'dio', 'flash-size': 'detect', 'flash-freq': '80m'},
27-
'esp32h2': {'flash-mode': 'dio', 'flash-size': 'detect', 'flash-freq': '48m'},
28-
'esp32p4': {'flash-mode': 'dio', 'flash-size': 'detect', 'flash-freq': '80m'},
29-
'esp32s2': {'flash-mode': 'dio', 'flash-size': 'detect', 'flash-freq': '80m'},
30-
'esp32s3': {'flash-mode': 'dio', 'flash-size': 'detect', 'flash-freq': '80m'},
31-
}
32-
33-
#: dict of binaries' offset.
34-
binary_offsets: ClassVar[dict[str, list[int]]] = {
35-
'esp32': [0x1000, 0x8000, 0x10000],
36-
'esp32c2': [0x0, 0x8000, 0x10000],
37-
'esp32c3': [0x0, 0x8000, 0x10000],
38-
'esp32c5': [0x2000, 0x8000, 0x10000],
39-
'esp32c6': [0x0, 0x8000, 0x10000],
40-
'esp32c61': [0x0, 0x8000, 0x10000],
41-
'esp32h2': [0x0, 0x8000, 0x10000],
42-
'esp32p4': [0x2000, 0x8000, 0x10000],
43-
'esp32s2': [0x1000, 0x8000, 0x10000],
44-
'esp32s3': [0x0, 0x8000, 0x10000],
45-
}
46-
4721
def __init__(
4822
self,
4923
**kwargs,
5024
):
5125
super().__init__(**kwargs)
5226

53-
self.sketch = os.path.basename(self.app_path)
27+
# If no valid binary path is found, assume the build directory is the app path
28+
if not self.binary_path and self.app_path:
29+
self.binary_path = self.app_path
30+
31+
# Extract sketch name from binary files in the build directory
32+
self.sketch = self._get_sketch_name(self.binary_path)
5433
self.fqbn = self._get_fqbn(self.binary_path)
5534
self.target = self.fqbn.split(':')[2]
56-
self.flash_files = self._get_bin_files(self.binary_path, self.sketch, self.target)
35+
self.flash_settings = self._get_flash_settings()
36+
self.binary_file = os.path.realpath(os.path.join(self.binary_path, self.sketch + '.ino.merged.bin'))
5737
self.elf_file = os.path.realpath(os.path.join(self.binary_path, self.sketch + '.ino.elf'))
5838

59-
def _get_fqbn(self, build_path) -> str:
39+
logging.debug(f'Sketch name: {self.sketch}')
40+
logging.debug(f'FQBN: {self.fqbn}')
41+
logging.debug(f'Target: {self.target}')
42+
logging.debug(f'Flash settings: {self.flash_settings}')
43+
logging.debug(f'Binary file: {self.binary_file}')
44+
logging.debug(f'ELF file: {self.elf_file}')
45+
46+
def _get_sketch_name(self, build_path: str) -> str:
47+
"""Extract sketch name from binary files in the build directory."""
48+
if not build_path or not os.path.isdir(build_path):
49+
logging.warning('No build path found. Using default sketch name "sketch".')
50+
return 'sketch'
51+
52+
# Look for .ino.bin or .ino.merged.bin files
53+
for filename in os.listdir(build_path):
54+
if filename.endswith('.ino.bin') or filename.endswith('.ino.merged.bin'):
55+
# Extract sketch name (everything before .ino.bin or .ino.merged.bin)
56+
if filename.endswith('.ino.merged.bin'):
57+
return filename[: -len('.ino.merged.bin')]
58+
else:
59+
return filename[: -len('.ino.bin')]
60+
61+
# If no .ino.bin or .ino.merged.bin files found, raise an error
62+
raise ValueError(f'No .ino.bin or .ino.merged.bin file found in {build_path}')
63+
64+
def _get_fqbn(self, build_path: str) -> str:
65+
"""Get FQBN from build.options.json file."""
6066
options_file = os.path.realpath(os.path.join(build_path, 'build.options.json'))
6167
with open(options_file) as f:
6268
options = json.load(f)
6369
fqbn = options['fqbn']
6470
return fqbn
6571

66-
def _get_bin_files(self, build_path, sketch, target) -> list[tuple[int, str, bool]]:
67-
bootloader = os.path.realpath(os.path.join(build_path, sketch + '.ino.bootloader.bin'))
68-
partitions = os.path.realpath(os.path.join(build_path, sketch + '.ino.partitions.bin'))
69-
app = os.path.realpath(os.path.join(build_path, sketch + '.ino.bin'))
70-
files = [bootloader, partitions, app]
71-
offsets = self.binary_offsets[target]
72-
return [(offsets[i], files[i], False) for i in range(3)]
72+
def _get_flash_settings(self) -> dict[str, str]:
73+
"""Get flash settings from flash_args file."""
74+
flash_args_file = os.path.realpath(os.path.join(self.binary_path, 'flash_args'))
75+
with open(flash_args_file) as f:
76+
flash_args = f.readline().split(' ')
77+
78+
flash_settings = {}
79+
for i, arg in enumerate(flash_args):
80+
if arg.startswith('--'):
81+
flash_settings[arg[2:].strip()] = flash_args[i + 1].strip()
82+
83+
if flash_settings == {}:
84+
raise ValueError(f'Flash settings not found in {flash_args_file}')
85+
86+
return flash_settings

pytest-embedded-arduino/pytest_embedded_arduino/serial.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,9 @@ def flash(self) -> None:
3939
"""
4040
Flash the binary files to the board.
4141
"""
42-
flash_files = []
43-
for offset, path, encrypted in self.app.flash_files:
44-
if encrypted:
45-
continue
46-
flash_files.extend((str(offset), path))
4742

4843
flash_settings = []
49-
for k, v in self.app.flash_settings[self.app.target].items():
44+
for k, v in self.app.flash_settings.items():
5045
flash_settings.append(f'--{k}')
5146
flash_settings.append(v)
5247

@@ -55,7 +50,14 @@ def flash(self) -> None:
5550

5651
try:
5752
esptool.main(
58-
['--chip', self.app.target, 'write-flash', *flash_files, *flash_settings],
53+
[
54+
'--chip',
55+
self.app.target,
56+
'write-flash',
57+
'0x0', # Merged binary is flashed at offset 0
58+
self.app.binary_file,
59+
*flash_settings,
60+
],
5961
esp=self.esp,
6062
)
6163
except Exception:

pytest-embedded-arduino/tests/test_arduino.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,38 @@
22

33

44
def test_arduino_serial_flash(testdir):
5-
testdir.makepyfile("""
5+
bin_path = os.path.join(
6+
testdir.tmpdir,
7+
'hello_world_arduino',
8+
'build',
9+
'hello_world_arduino.ino.merged.bin'
10+
)
11+
12+
testdir.makepyfile(f"""
613
import pexpect
714
import pytest
815
916
def test_arduino_app(app, dut):
10-
assert len(app.flash_files) == 3
17+
expected_bin = '{bin_path}'
18+
assert app.binary_file == expected_bin
1119
assert app.target == 'esp32'
12-
assert app.fqbn == 'espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app'
20+
expected_fqbn = (
21+
"espressif:esp32:esp32:"
22+
"UploadSpeed=921600,"
23+
"CPUFreq=240,"
24+
"FlashFreq=80,"
25+
"FlashMode=qio,"
26+
"FlashSize=4M,"
27+
"PartitionScheme=huge_app,"
28+
"DebugLevel=none,"
29+
"PSRAM=enabled,"
30+
"LoopCore=1,"
31+
"EventsCore=1,"
32+
"EraseFlash=none,"
33+
"JTAGAdapter=default,"
34+
"ZigbeeMode=default"
35+
)
36+
assert app.fqbn == expected_fqbn
1337
dut.expect('Hello Arduino!')
1438
with pytest.raises(pexpect.TIMEOUT):
1539
dut.expect('foo bar not found', timeout=1)
@@ -19,10 +43,8 @@ def test_arduino_app(app, dut):
1943
'-s',
2044
'--embedded-services',
2145
'arduino,esp',
22-
'--app-path',
23-
os.path.join(testdir.tmpdir, 'hello_world_arduino'),
2446
'--build-dir',
25-
'build',
47+
os.path.join(testdir.tmpdir, 'hello_world_arduino', 'build'),
2648
)
2749

2850
result.assert_outcomes(passed=1)

pytest-embedded-wokwi/tests/test_wokwi.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def test_pexpect_by_wokwi(dut):
1818
dut.expect('Hello world!')
1919
dut.expect('Restarting')
2020
with pytest.raises(pexpect.TIMEOUT):
21-
dut.expect('foo bar not found', timeout=1)
21+
dut.expect('Hello world! or Restarting not found', timeout=1)
2222
""")
2323

2424
result = testdir.runpytest(
@@ -40,15 +40,15 @@ def test_pexpect_by_wokwi_esp32_arduino(testdir):
4040
def test_pexpect_by_wokwi(dut):
4141
dut.expect('Hello Arduino!')
4242
with pytest.raises(pexpect.TIMEOUT):
43-
dut.expect('foo bar not found', timeout=1)
43+
dut.expect('Hello Arduino! not found', timeout=1)
4444
""")
4545

4646
result = testdir.runpytest(
4747
'-s',
4848
'--embedded-services',
4949
'arduino,wokwi',
50-
'--app-path',
51-
os.path.join(testdir.tmpdir, 'hello_world_arduino'),
50+
'--build-dir',
51+
os.path.join(testdir.tmpdir, 'hello_world_arduino', 'build'),
5252
'--wokwi-diagram',
5353
os.path.join(testdir.tmpdir, 'hello_world_arduino/esp32.diagram.json'),
5454
)
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
{
22
"additionalFiles": "",
3-
"builtInLibrariesFolders": "",
4-
"builtInToolsFolders": "/Applications/Arduino.app/Contents/Java/tools-builder",
3+
"builtInLibrariesFolders": "/Users/lucassvaz/Library/Arduino15/libraries",
54
"compiler.optimization_flags": "-Os",
6-
"customBuildProperties": "",
7-
"fqbn": "espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app",
8-
"hardwareFolders": "/Users/prochy/Documents/Arduino/hardware",
9-
"otherLibrariesFolders": "/Users/prochy/Documents/Arduino/libraries",
10-
"runtime.ide.version": "10810",
11-
"sketchLocation": "/Users/prochy/Documents/Arduino/hardware/espressif/esp32/tests/hello_world/hello_world.ino"
5+
"customBuildProperties": "build.warn_data_percentage=75",
6+
"fqbn": "espressif:esp32:esp32:UploadSpeed=921600,CPUFreq=240,FlashFreq=80,FlashMode=qio,FlashSize=4M,PartitionScheme=huge_app,DebugLevel=none,PSRAM=enabled,LoopCore=1,EventsCore=1,EraseFlash=none,JTAGAdapter=default,ZigbeeMode=default",
7+
"hardwareFolders": "/Users/lucassvaz/Library/Arduino15/packages,/Users/lucassvaz/Espressif/Arduino/hardware",
8+
"otherLibrariesFolders": "/Users/lucassvaz/Espressif/Arduino/libraries",
9+
"sketchLocation": "/Users/lucassvaz/Espressif/Arduino/sketches/hello_world_arduino"
1210
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
--flash-mode dio --flash-freq 80m --flash-size 4MB
2+
0x1000 hello_world_arduino.ino.bootloader.bin
3+
0x8000 hello_world_arduino.ino.partitions.bin
4+
0xe000 boot_app0.bin
5+
0x10000 hello_world_arduino.ino.bin
Binary file not shown.
Binary file not shown.
-444 KB
Binary file not shown.

0 commit comments

Comments
 (0)