Skip to content

Commit 3dbc4b0

Browse files
committed
Added cmake script for generating partitions at build time
1 parent b364ee6 commit 3dbc4b0

File tree

5 files changed

+209
-8
lines changed

5 files changed

+209
-8
lines changed

config/esp32/components/chip/CMakeLists.txt

+6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ if(NOT CHIP_ROOT)
3030
endif()
3131

3232
include(${CMAKE_CURRENT_LIST_DIR}/ota-image.cmake)
33+
include(${CMAKE_CURRENT_LIST_DIR}/factory.cmake)
3334

3435
set(CHIP_REQUIRE_COMPONENTS esp_eth freertos lwip bt mbedtls fatfs app_update console openthread nvs_flash spi_flash)
3536

@@ -520,6 +521,11 @@ if(CONFIG_ENABLE_PW_RPC)
520521
endforeach()
521522
endif()
522523

524+
if(CONFIG_ENABLE_BUILD_TIME_PARTITION_SCRIPT)
525+
generate_build_time_partition(fctry esp_secure_cert ${CHIP_ROOT} FLASH_IN_PROJECT)
526+
add_dependencies(build_time_partition app)
527+
endif()
528+
523529
# Build Matter OTA image
524530
if (CONFIG_CHIP_OTA_IMAGE_BUILD)
525531
chip_ota_image(chip-ota-image

config/esp32/components/chip/Kconfig

+11-2
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ menu "CHIP Device Layer"
883883
NVS namespace. If this option is enabled, the application can use an API to set a CD,
884884
the configured CD will be used for subsequent CD reads.
885885

886-
config ENABLE_ESP_INSIGHTS_TRACE
886+
config ENABLE_ESP_INSIGHTS_TRACE
887887
bool "Enable Matter ESP Insights"
888888
depends on ESP_INSIGHTS_ENABLED
889889
default y
@@ -892,12 +892,21 @@ menu "CHIP Device Layer"
892892
Enabling the above option will enable the esp32 specific tracing functionality and report the
893893
diagnostic information to the insights cloud.
894894

895-
config ENABLE_ESP_INSIGHTS_SYSTEM_STATS
895+
config ENABLE_ESP_INSIGHTS_SYSTEM_STATS
896896
bool "Enable System Stats for insights"
897897
depends on ESP_INSIGHTS_ENABLED
898898
default n
899899
help
900900
This option enables the system statistics to be sent to the insights cloud.
901+
902+
config ENABLE_BUILD_TIME_PARTITION_SCRIPT
903+
bool "Enable script to build fctry and esp_secure_cert partition in during build time."
904+
default n
905+
help
906+
Enables the cmake script to generate and map the factory parition bin and esp-secure-cert
907+
partition bin to their corresponding addresses specified in partitions.csv and flash the partitons
908+
in a single idf.py flash command.
909+
901910
endmenu
902911

903912

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
#
2+
# Copyright (c) 2023 Project CHIP Authors
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
17+
function(generate_build_time_partition fctry_partition esp_secure_cert_partition chip_root)
18+
set(options FLASH_IN_PROJECT)
19+
set(multi DEPENDS)
20+
cmake_parse_arguments(arg "${options}" "" "${multi}" "${ARGN}")
21+
get_filename_component(chip_root_abs_path ${chip_root} ABSOLUTE)
22+
23+
set(generate_esp32_chip_factory_bin.py ${PYTHON} ${chip_root}/scripts/tools/generate_esp32_chip_factory_bin.py)
24+
25+
partition_table_get_partition_info(fctry_partition_size "--partition-name ${fctry_partition}" "size")
26+
partition_table_get_partition_info(fctry_partition_offset "--partition-name ${fctry_partition}" "offset")
27+
28+
partition_table_get_partition_info(secure_cert_partition_size "--partition-name ${esp_secure_cert_partition}" "size")
29+
partition_table_get_partition_info(secure_cert_partition_offset "--partition-name ${esp_secure_cert_partition}" "offset")
30+
31+
message(STATUS "fctry_partition_size : ${fctry_partition_size}")
32+
message(STATUS "fctry_partition_offset : ${fctry_partition_offset}")
33+
message(STATUS "secure_cert_partition_size : ${secure_cert_partition_size}")
34+
message(STATUS "secure_cert_partition_offset : ${secure_cert_partition_offset}")
35+
36+
if("${fctry_partition_size}" AND "${fctry_partition_offset}")
37+
set(DEFAULT_DEVICE_NAME "My bulb")
38+
set(DEFAULT_VENDOR_NAME "Test-vendor")
39+
set(DEFAULT_HARDWARE_VERSION 1)
40+
set(DEFAULT_HARDWARE_VERSION_STR "Devkit")
41+
set(DEFAULT_VENDOR_ID 0xFFF2)
42+
set(DEFAULT_PRODUCT_ID 0x8001)
43+
set(DEFAULT_DAC_CERT "${chip_root_abs_path}/credentials/test/attestation/Chip-Test-DAC-FFF2-8001-0008-Cert.der")
44+
set(DEFAULT_DAC_KEY "${chip_root_abs_path}/credentials/test/attestation/Chip-Test-DAC-FFF2-8001-0008-Key.der")
45+
set(DEFAULT_PAI_CERT "${chip_root_abs_path}/credentials/test/attestation/Chip-Test-PAI-FFF2-8001-Cert.der")
46+
set(DEFAULT_CERT_DCLRN "${chip_root_abs_path}/credentials/test/certification-declaration/Chip-Test-CD-FFF2-8001.der")
47+
set(DEFAULT_PASSCODE 20202020)
48+
set(DEFAULT_DISCRIMINATOR 3841)
49+
50+
set(DEVICE_NAME ${DEFAULT_DEVICE_NAME} CACHE STRING "My bulb")
51+
set(VENDOR_NAME ${DEFAULT_VENDOR_NAME} CACHE STRING "Test-vendor")
52+
set(HARDWARE_VERSION ${DEFAULT_HARDWARE_VERSION} CACHE STRING 1)
53+
set(HARDWARE_VERSION_STR ${DEFAULT_HARDWARE_VERSION_STR} CACHE STRING "Devkit")
54+
set(VENDOR_ID ${DEFAULT_VENDOR_ID} CACHE STRING 0xFFF2)
55+
set(PRODUCT_ID ${DEFAULT_PRODUCT_ID} CACHE STRING 0x8001)
56+
set(DAC_CERT ${DEFAULT_DAC_CERT} CACHE STRING "${chip_root_abs_path}/credentials/test/attestation/Chip-Test-DAC-FFF2-8001-0008-Cert.der")
57+
set(DAC_KEY ${DEFAULT_DAC_KEY} CACHE STRING "${chip_root_abs_path}/credentials/test/attestation/Chip-Test-DAC-FFF2-8001-0008-Key.der")
58+
set(PAI_CERT ${DEFAULT_PAI_CERT} CACHE STRING "${chip_root_abs_path}/credentials/test/attestation/Chip-Test-PAI-FFF2-8001-Cert.der")
59+
set(CERT_DCLRN ${DEFAULT_CERT_DCLRN} CACHE STRING "${chip_root_abs_path}/credentials/test/certification-declaration/Chip-Test-CD-FFF2-8001.der")
60+
set(PASSCODE ${DEFAULT_PASSCODE} CACHE STRING 20202020)
61+
set(DISCRIMINATOR ${DEFAULT_DISCRIMINATOR} CACHE STRING 3841)
62+
63+
message(STATUS "Bulb Name: ${DEVICE_NAME}")
64+
message(STATUS "Vendor Name: ${VENDOR_NAME}")
65+
message(STATUS "Hardware Version: ${HARDWARE_VERSION}")
66+
message(STATUS "Hardware Version String: ${HARDWARE_VERSION_STR}")
67+
message(STATUS "Vendor ID: ${VENDOR_ID}")
68+
message(STATUS "Product ID: ${PRODUCT_ID}")
69+
message(STATUS "DAC Cert: ${DAC_CERT}")
70+
message(STATUS "DAC Key: ${DAC_KEY}")
71+
message(STATUS "PAI Cert: ${PAI_CERT}")
72+
message(STATUS "Certification Declaration: ${CERT_DCLRN}")
73+
message(STATUS "Passcode: ${PASSCODE}")
74+
message(STATUS "Discriminator: ${DISCRIMINATOR}")
75+
76+
77+
# Execute Factory partition image generation; this always executes as there is no way to specify for CMake to watch for
78+
# contents of the base dir changing.
79+
add_custom_target(build_time_partition ALL
80+
COMMAND ${generate_esp32_chip_factory_bin.py} -d ${DISCRIMINATOR}
81+
-p ${PASSCODE}
82+
--product-name "${DEVICE_NAME}"
83+
--vendor-name "${VENDOR_NAME}"
84+
--vendor-id ${VENDOR_ID}
85+
--product-id ${PRODUCT_ID}
86+
--hw-ver ${HARDWARE_VERSION}
87+
--hw-ver-str "${HARDWARE_VERSION_STR}"
88+
--dac-cert ${DAC_CERT}
89+
--dac-key ${DAC_KEY}
90+
--pai-cert ${PAI_CERT}
91+
--cd ${CERT_DCLRN}
92+
--dac-in-secure-cert
93+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
94+
)
95+
96+
97+
set(factory_partition_bin ${CMAKE_BINARY_DIR}/bin/factory_partition.bin)
98+
set(esp_secure_cert_partition_bin ${CMAKE_BINARY_DIR}/bin/esp_secure_cert_partititon.bin)
99+
idf_component_get_property(main_args esptool_py FLASH_ARGS)
100+
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
101+
102+
esptool_py_flash_target(${fctry_partition}-flash "${main_args}" "${sub_args}" ALWAYS_PLAINTEXT)
103+
esptool_py_flash_to_partition(${fctry_partition}-flash "${fctry_partition}" "${factory_partition_bin}")
104+
105+
esptool_py_flash_target(${esp_secure_cert_partition}-flash "${main_args}" "${sub_args}" ALWAYS_PLAINTEXT)
106+
esptool_py_flash_to_partition(${esp_secure_cert_partition}-flash "${esp_secure_cert_partition}" "${esp_secure_cert_partition_bin}")
107+
108+
add_dependencies(${fctry_partition}-flash build_time_partition)
109+
add_dependencies(${esp_secure_cert_partition}-flash build_time_partition)
110+
111+
if(arg_FLASH_IN_PROJECT)
112+
esptool_py_flash_to_partition(flash "${fctry_partition}" "${factory_partition_bin}")
113+
esptool_py_flash_to_partition(flash "${esp_secure_cert_partition}" "${esp_secure_cert_partition_bin}")
114+
add_dependencies(flash build_time_partition)
115+
endif()
116+
else()
117+
set(message "Failed to create Factory partition image for partition '${partition}'. "
118+
"Check project configuration if using the correct partition table file.")
119+
fail_at_build_time(factory_${partition}_bin "${message}")
120+
121+
endif()
122+
endfunction()

scripts/setup/requirements.esp32.txt

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ reedsolo>=1.5.3,<=1.5.4
99
bitstring>=3.1.6,<4
1010
ecdsa>=0.16.0
1111
construct==2.10.54
12+
pypng==0.0.21
13+
PyQRCode==1.2.1
1214
python-socketio<5
1315
itsdangerous<2.1 ; python_version < "3.11"
1416
esp_idf_monitor==1.1.1

scripts/tools/generate_esp32_chip_factory_bin.py

+68-6
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
1818

1919
import argparse
2020
import base64
21+
import csv
2122
import enum
23+
import hashlib
24+
import json
2225
import logging
2326
import os
27+
import pyqrcode
2428
import sys
25-
from types import SimpleNamespace
2629

30+
from types import SimpleNamespace
2731
import cryptography.x509
2832
from bitarray import bitarray
2933
from bitarray.util import ba2int
@@ -32,6 +36,8 @@
3236
CHIP_TOPDIR = os.path.dirname(os.path.realpath(__file__))[:-len(os.path.join('scripts', 'tools'))]
3337
sys.path.insert(0, os.path.join(CHIP_TOPDIR, 'scripts', 'tools', 'spake2p'))
3438
from spake2p import generate_verifier # noqa: E402 isort:skip
39+
sys.path.insert(0,os.path.join(CHIP_TOPDIR, 'src', 'setup_payload','python'))
40+
from generate_setup_payload import SetupPayload, CommissioningFlow
3541

3642
if os.getenv('IDF_PATH'):
3743
sys.path.insert(0, os.path.join(os.getenv('IDF_PATH'),
@@ -48,11 +54,13 @@
4854

4955
TOOLS = {}
5056

51-
5257
FACTORY_PARTITION_CSV = 'nvs_partition.csv'
5358
FACTORY_PARTITION_BIN = 'factory_partition.bin'
5459
NVS_KEY_PARTITION_BIN = 'nvs_key_partition.bin'
5560
ESP_SECURE_CERT_PARTITION_BIN = 'esp_secure_cert_partititon.bin'
61+
CONFIG_FILE = 'config.json'
62+
ONBOARDING_DATA_FILE = 'onboarding_codes.csv'
63+
QROCDE_FILE = 'qrcode.png'
5664

5765
FACTORY_DATA = {
5866
# CommissionableDataProvider
@@ -167,6 +175,25 @@
167175
}
168176

169177

178+
def save_config(args):
179+
with open(CONFIG_FILE, 'w') as config_file:
180+
json.dump(vars(args), config_file)
181+
182+
def load_config():
183+
try:
184+
with open(CONFIG_FILE, 'r') as config_file:
185+
return json.load(config_file)
186+
except FileNotFoundError:
187+
return None
188+
189+
def calculate_hash(data):
190+
return hashlib.sha256(data.encode('utf-8')).hexdigest()
191+
192+
193+
def args_changed(current_args, saved_args):
194+
return calculate_hash(json.dumps(vars(current_args))) != calculate_hash(json.dumps(saved_args))
195+
196+
170197
class CalendarTypes(enum.Enum):
171198
Buddhist = 0
172199
Chinese = 1
@@ -602,6 +629,12 @@ def any_base_int(s): return int(s, 0)
602629
help='Do not generate the factory partition binary')
603630
parser.add_argument('--output_dir', type=str, default='bin', help='Created image output file path')
604631

632+
parser.add_argument('-cf', '--commissioning-flow', type=any_base_int, default=0,
633+
help='Device commissioning flow, 0:Standard, 1:User-Intent, 2:Custom. \
634+
Default is 0.', choices=[0, 1, 2])
635+
parser.add_argument('-dm', '--discovery-mode', type=any_base_int, default=1,
636+
help='Commissionable device discovery networking technology. \
637+
0:WiFi-SoftAP, 1:BLE, 2:On-network. Default is BLE.', choices=[0, 1, 2])
605638
parser.set_defaults(generate_bin=True)
606639

607640
return parser.parse_args()
@@ -632,13 +665,42 @@ def generate_factory_partiton_binary(args):
632665
def set_up_out_dirs(args):
633666
os.makedirs(args.output_dir, exist_ok=True)
634667

668+
def generate_onboarding_data(args):
669+
payloads = SetupPayload(args.discriminator, args.passcode, args.discovery_mode, CommissioningFlow(args.commissioning_flow),
670+
args.vendor_id, args.product_id)
671+
logging.info('Discovery mode' + str(args.discovery_mode))
672+
chip_qrcode = payloads.generate_qrcode()
673+
chip_manualcode = payloads.generate_manualcode()
674+
# ToDo: remove this if qrcode tool can handle the standard manual code format
675+
if args.commissioning_flow == CommissioningFlow.Standard:
676+
chip_manualcode = chip_manualcode[:4] + '-' + chip_manualcode[4:7] + '-' + chip_manualcode[7:]
677+
else:
678+
chip_manualcode = '"' + chip_manualcode[:4] + '-' + chip_manualcode[4:7] + '-' + chip_manualcode[7:11] + '\n' + chip_manualcode[11:15] + '-' + chip_manualcode[15:18] + '-' + chip_manualcode[18:20] + '-' + chip_manualcode[20:21] + '"'
679+
680+
logging.info('Generated QR code: ' + chip_qrcode)
681+
logging.info('Generated manual code: ' + chip_manualcode)
682+
683+
csv_data = 'qrcode,manualcode,discriminator,passcode\n'
684+
csv_data += chip_qrcode + ',' + chip_manualcode + ',' + str(args.discriminator) + ',' + str(args.passcode) + '\n'
685+
686+
with open(os.path.join(args.output_dir,ONBOARDING_DATA_FILE), 'w') as f:
687+
f.write(csv_data)
688+
689+
chip_qr = pyqrcode.create(chip_qrcode, version=2, error='M')
690+
chip_qr.png(os.path.join(args.output_dir,QROCDE_FILE), scale=6)
691+
635692

636693
def main():
694+
saved_args = load_config()
637695
args = get_args()
638-
set_up_out_dirs(args)
639-
set_up_factory_data(args)
640-
generate_factory_partiton_binary(args)
641-
696+
if saved_args is None or args_changed(args, saved_args):
697+
set_up_out_dirs(args)
698+
set_up_factory_data(args)
699+
generate_factory_partiton_binary(args)
700+
generate_onboarding_data(args)
701+
save_config(args)
702+
else:
703+
logging.info("No changes in arguments. Skipping partition generation.")
642704

643705
if __name__ == "__main__":
644706
logging.basicConfig(format='[%(asctime)s] [%(levelname)7s] - %(message)s', level=logging.INFO)

0 commit comments

Comments
 (0)