examples: create example for both Python and CLI parttool interfaces

This commit is contained in:
Renz Christian Bagaporo
2019-05-27 11:09:35 +08:00
parent 1de627e68a
commit 5d41322412
4 changed files with 141 additions and 163 deletions

View File

@@ -4,10 +4,11 @@ This example demonstrates common operations the partitions tool [parttool.py](..
- reading, writing and erasing partitions,
- retrieving info on a certain partition,
- dumping the entire partition table, and
- generating a blank partition file.
- dumping the entire partition table
Users taking a look at this example should focus on the contents of the python script [parttool_example.py](parttool_example.py). The script contains programmatic invocations of [parttool.py](../../../components/partition_table/parttool.py) in Python for the operations mentioned above; and can serve as a guide for users wanting to do the same in their applications.
Users taking a look at this example should focus on the contents of the Python script [parttool_example.py](parttool_example.py) or shell script [parttool_example.sh](parttool_example.sh). The scripts contain
programmatic invocation of the tool's functions via the Python API and command-line interface, respectively. Note
that on Windows, the shell script example requires a POSIX-compatible environment via MSYS2/Git Bash/WSL etc.
The example performs the operations mentioned above in a straightforward manner: it performs writes to partitions and then verifies correct content
by reading it back. For partitions, contents are compared to the originally written file. For the partition table, contents are verified against the partition table CSV
@@ -17,50 +18,54 @@ file. An erased partition's contents is compared to a generated blank file.
### Build and Flash
Before running the example script [parttool_example.py](parttool_example.py), it is necessary to build and flash the firmware using the usual means:
Before running either of the example scripts, it is necessary to build and flash the firmware using the usual means:
Make:
```bash
# If using Make
make build flash
```
# If using CMake
CMake:
```bash
idf.py build flash
```
### Running [parttool_example.py](parttool_example.py)
The example can be executed by running the script [parttool_example.py](parttool_example.py). Either run it directly using
```bash
./parttool_example.py
```
or run it using
The example can be executed by running the script [parttool_example.py](parttool_example.py) or [parttool_example.sh](parttool_example.sh).
Python script:
```bash
python parttool_example.py
```
Shell script:
```
./parttool_example.sh
```
The script searches for valid target devices connected to the host and performs the operations on the first one it finds. To perform the operations on a specific device, specify the port it is attached to during script invocation:
Python script:
```bash
# The target device is attached to /dev/ttyUSB2, for example
python parttool_example.py --port /dev/ttyUSB2
```
Shell script:
```
./parttool_example.sh /dev/ttyUSB2
```
## Example output
Running the script produces the following output:
```
Checking if device app binary matches built binary
Checking if device partition table matches partition table csv
Retrieving data partition offset and size
Found data partition at offset 0x110000 with size 0x10000
Writing to data partition
Reading data partition
Erasing data partition
Generating blank data partition file
Reading data partition
Partition tool operations performed successfully!

View File

@@ -18,19 +18,12 @@
# limitations under the License.
import os
import sys
import subprocess
import argparse
IDF_PATH = os.path.expandvars("$IDF_PATH")
PARTTOOL_PY = os.path.join(IDF_PATH, "components", "partition_table", "parttool.py")
PARTITION_TABLE_OFFSET = 0x8000
INVOKE_ARGS = [sys.executable, PARTTOOL_PY, "-q", "--partition-table-offset", str(PARTITION_TABLE_OFFSET)]
PARTITION_TABLE_DIR = os.path.join("components", "partition_table", "")
def sized_file_compare(file1, file2):
def assert_file_same(file1, file2, err):
with open(file1, "rb") as f1:
with open(file2, "rb") as f2:
f1 = f1.read()
@@ -41,121 +34,17 @@ def sized_file_compare(file1, file2):
else:
f1 = f1[:len(f2)]
return f1 == f2
def check(condition, message):
if not condition:
print("Error: " + message)
sys.exit(1)
def write_data_partition(size):
print("Writing to data partition")
with open("write.bin", "wb") as f:
# Create a file to write to the data partition with randomly generated content
f.write(os.urandom(int(size, 16)))
# Invokes the command
#
# parttool.py --partition-table-offset 0x8000 -q --partition-name storage write_partition --input write.bin
#
# to write the contents of a file to a partition in the device.
invoke_args = INVOKE_ARGS + ["--partition-name", "storage", "write_partition", "--input", f.name]
subprocess.check_call(invoke_args)
return f.name
def read_data_partition():
print("Reading data partition")
# Invokes the command
#
# parttool.py --partition-table-offset 0x8000 -q --partition-name storage read_partition --output read.bin
#
# to read the contents of a partition in the device, which is then written to a file.
f = "read.bin"
invoke_args = INVOKE_ARGS + ["--partition-name", "storage", "read_partition", "--output", f]
subprocess.check_call(invoke_args)
return f
def get_data_partition_info():
print("Retrieving data partition offset and size")
# Invokes the command
#
# parttool.py --partition-table-offset 0x8000 -q --partition-name storage get_partition_info --info offset size
#
# to get the offset and size of a partition named 'storage'.
invoke_args = INVOKE_ARGS + ["--partition-name", "storage", "get_partition_info", "--info", "offset", "size"]
(offset, size) = subprocess.check_output(invoke_args).strip().split(b" ")
return (offset, size)
def check_app(args):
print("Checking if device app binary matches built binary")
# Invokes the command
#
# parttool.py --partition-table-offset 0x8000 --partition-type app --partition-subtype factory read_partition --output app.bin"
#
# to read the app binary and write it to a file. The read app binary is compared to the built binary in the build folder.
invoke_args = INVOKE_ARGS + ["--partition-type", "app", "--partition-subtype", "factory", "read_partition", "--output", "app.bin"]
subprocess.check_call(invoke_args)
app_same = sized_file_compare("app.bin", args.binary)
check(app_same, "Device app binary does not match built binary")
def check_partition_table():
sys.path.append(os.path.join(IDF_PATH, "components", "partition_table"))
import gen_esp32part as gen
print("Checking if device partition table matches partition table csv")
# Invokes the command
#
# parttool.py --partition-table-offset 0x8000 get_partition_info --table table.bin
#
# to read the device partition table and write it to a file. The read partition table is compared to
# the partition table csv.
invoke_args = INVOKE_ARGS + ["get_partition_info", "--table", "table.bin"]
subprocess.check_call(invoke_args)
with open("table.bin", "rb") as read:
partition_table_csv = os.path.join(IDF_PATH, "examples", "storage", "parttool", "partitions_example.csv")
with open(partition_table_csv, "r") as csv:
read = gen.PartitionTable.from_binary(read.read())
csv = gen.PartitionTable.from_csv(csv.read())
check(read == csv, "Device partition table does not match csv partition table")
def erase_data_partition():
print("Erasing data partition")
# Invokes the command
#
# parttool.py --partition-table-offset 0x8000 --partition-name storage erase_partition
#
# to erase the 'storage' partition.
invoke_args = INVOKE_ARGS + ["--partition-name", "storage", "erase_partition"]
subprocess.check_call(invoke_args)
def generate_blank_data_file():
print("Generating blank data partition file")
# Invokes the command
#
# parttool.py --partition-table-offset 0x8000 --partition-name storage generate_blank_partition_file --output blank.bin
#
# to generate a blank partition file and write it to a file. The blank partition file has the same size as the
# 'storage' partition.
f = "blank.bin"
invoke_args = INVOKE_ARGS + ["--partition-name", "storage", "generate_blank_partition_file", "--output", f]
subprocess.check_call(invoke_args)
return f
if not f1 == f2:
raise Exception(err)
def main():
global INVOKE_ARGS
COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components"))
PARTTOOL_DIR = os.path.join(COMPONENTS_PATH, "partition_table")
sys.path.append(PARTTOOL_DIR)
from parttool import PartitionName, PartitionType, ParttoolTarget
from gen_empty_partition import generate_blanked_file
parser = argparse.ArgumentParser("ESP-IDF Partitions Tool Example")
@@ -164,43 +53,53 @@ def main():
args = parser.parse_args()
if args.port:
INVOKE_ARGS += ["--port", args.port]
target = ParttoolTarget(args.port)
# Before proceeding, do checks to verify whether the app and partition table in the device matches the built binary and
# the generated partition table during build
check_app(args)
check_partition_table()
# Read app partition and save the contents to a file. The app partition is identified
# using type-subtype combination
print("Checking if device app binary matches built binary")
factory = PartitionType("app", "factory")
target.read_partition(factory, "app.bin")
assert_file_same(args.binary, "app.bin", "Device app binary does not match built binary")
# Get the offset and size of the data partition
(offset, size) = get_data_partition_info()
# Retrieve info on data storage partition, this time identifying it by name.
storage = PartitionName("storage")
storage_info = target.get_partition_info(storage)
print("Found data partition at offset 0x{:x} with size 0x{:x}".format(storage_info.offset, storage_info.size))
print("Found data partition at offset %s with size %s" % (offset, size))
# Create a file whose contents will be written to the storage partition
with open("write.bin", "wb") as f:
# Create a file to write to the data partition with randomly generated content
f.write(os.urandom(storage_info.size))
# Write a generated file of random bytes to the found data partition
written = write_data_partition(size)
# Write the contents of the created file to storage partition
print("Writing to data partition")
target.write_partition(storage, "write.bin")
# Read back the contents of the data partition
read = read_data_partition()
# Read back the contents of the storage partition
print("Reading data partition")
target.read_partition(storage, "read.bin")
# Compare the written and read back data
data_same = sized_file_compare(read, written)
check(data_same, "Read contents of the data partition does not match written data")
assert_file_same("write.bin", "read.bin", "Read contents of storage partition does not match source file contents")
# Erase the data partition
erase_data_partition()
# Erase contents of the storage partition
print("Erasing data partition")
target.erase_partition(storage)
# Read back the erase data partition, which should be all 0xFF's after erasure
read = read_data_partition()
# Read back the erased data partition
print("Reading data partition")
target.read_partition(storage, "read.bin")
# Generate blank partition file (all 0xFF's)
blank = generate_blank_data_file()
# Generate a file of all 0xFF
generate_blanked_file(storage_info.size, "blank.bin")
# Verify that the partition has been erased by comparing the contents to the generated blank file
data_same = sized_file_compare(read, blank)
check(data_same, "Erased data partition contents does not match blank partition file")
assert_file_same("blank.bin", "read.bin", "Contents of storage partition not fully erased")
# Example end and cleanup
print("\nPartition tool operations performed successfully!")
clean_files = ["app.bin", "read.bin", "blank.bin", "write.bin"]
for clean_file in clean_files:
os.unlink(clean_file)
if __name__ == '__main__':

View File

@@ -0,0 +1,73 @@
#!/bin/bash
#
# Demonstrates command-line interface of Partition Tool, parttool.py
#
#
# $1 - serial port where target device to operate on is connnected to, by default the first found valid serial port
# $2 - path to this example's built binary file (parttool.bin), by default $PWD/build/parttool.bin
PORT=$1
PARTTOOL_PY="python $IDF_PATH/components/partition_table/parttool.py -q"
if [[ "$PORT" != "" ]]; then
PARTTOOL_PY="$PARTTOOL_PY --port $PORT"
fi
GEN_EMPTY_PARTITION_PY="python $IDF_PATH/components/partition_table/gen_empty_partition.py"
BINARY=$2
if [[ "$BINARY" == "" ]]; then
BINARY=build/parttool.bin
fi
function assert_file_same()
{
sz_a=$(stat -c %s $1)
sz_b=$(stat -c %s $2)
sz=$((sz_a < sz_b ? sz_a : sz_b))
res=$(cmp -s -n $sz $1 $2) ||
(echo "!!!!!!!!!!!!!!!!!!!"
echo "FAILURE: $3"
echo "!!!!!!!!!!!!!!!!!!!")
}
# Read app partition and save the contents to a file. The app partition is identified
# using type-subtype combination
echo "Checking if device app binary matches built binary"
$PARTTOOL_PY read_partition --partition-type=app --partition-subtype=factory --output=app.bin
assert_file_same app.bin $BINARY "Device app binary does not match built binary"
# Retrieve info on data storage partition, this time identifying it by name.
offset=$($PARTTOOL_PY get_partition_info --partition-name=storage --info offset)
size=$($PARTTOOL_PY get_partition_info --partition-name=storage --info size)
echo "Found data partition at offset $offset with size $size"
# Create a file whose contents will be written to the storage partition
head -c $(($size)) /dev/urandom > write.bin
# Write the contents of the created file to storage partition
echo "Writing to data partition"
$PARTTOOL_PY write_partition --partition-name=storage --input write.bin
# Read back the contents of the storage partition
echo "Reading data partition"
$PARTTOOL_PY read_partition --partition-name=storage --output read.bin
assert_file_same write.bin read.bin "Read contents of storage partition does not match source file contents"
# Erase contents of the storage partition
echo "Erasing data partition"
$PARTTOOL_PY erase_partition --partition-name=storage
# Read back the erased data partition
echo "Reading data partition"
$PARTTOOL_PY read_partition --partition-name=storage --output read.bin
# Generate a file of all 0xFF
$GEN_EMPTY_PARTITION_PY $(($size)) blank.bin
assert_file_same read.bin blank.bin "Contents of storage partition not fully erased"
# Example end and cleanup
printf "\nPartition tool operations performed successfully\n"
rm -rf app.bin read.bin blank.bin write.bin

View File

@@ -22,6 +22,7 @@ examples/build_system/cmake/idf_as_lib/build.sh
examples/build_system/cmake/idf_as_lib/run-esp32.sh
examples/build_system/cmake/idf_as_lib/run.sh
examples/storage/parttool/parttool_example.py
examples/storage/parttool/parttool_example.sh
examples/system/ota/otatool/otatool_example.py
tools/check_kconfigs.py
tools/check_python_dependencies.py