Skip to content

Commit

Permalink
[ESP32] Fix few attributes with fixed quality in DeviceInfoProvider (#…
Browse files Browse the repository at this point in the history
…32893) (#33138)

* [ESP32] Fix few attributes with fixed quality in DeviceInfoProvider

Fixed labels, supported locales, supported calendar types were being
read from the nvs(flash) and during OTA its a hassle if one wants to
upgrade these values. Added few APIs to set the data for these
attributes in ESP32DeviceInfoProvider.

* Restyled by clang-format

* Restyled by prettier-markdown

* fix the lint errors

* Add back the original Device info provider which reads from the nvs

Add StaticESP32DeviceInfoProvider along with APIs to set data

Remove changes from example and add a guide along with usage

---------

Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
shubhamdp and restyled-commits authored Apr 24, 2024
1 parent 29ed2f9 commit 5b0afc7
Show file tree
Hide file tree
Showing 7 changed files with 345 additions and 131 deletions.
1 change: 1 addition & 0 deletions docs/guides/esp32/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ example on ESP32 series of SoCs
- [Matter OTA](ota.md)
- [Generating and Using ESP Secure Cert Partition](secure_cert_partition.md)
- [BLE Settings](ble_settings.md)
- [Providers](providers.md)
10 changes: 3 additions & 7 deletions docs/guides/esp32/factory_data.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,9 @@ Following data can be added to the manufacturing partition using
- Serial Number
- Unique identifier

- Device information
- Fixed Labels
- Supported locales
- Supported calendar types
- Supported modes
- Note: As per spec at max size of label should be 64 and `\0` will be
added at the end.
- Supported modes
- Note: As per spec at max size of label should be 64 and `\0` will be
added at the end.

### Configuration Options

Expand Down
76 changes: 76 additions & 0 deletions docs/guides/esp32/providers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
## Providers Implemented for ESP32 Platform

The ESP32 platform has implemented several providers that can be used with data
stored in the factory or by setting fixed data.

Below are the providers that have been implemented:

- [Commissionable Data Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L47)
This provider reads the discriminator and setup pincode related parameters
from the factory partition.
- [Device Attestation Credentials Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L56)
This provider manages the attestation data.
- [Device Instance Info Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L86)
This provider reads basic device information from the factory partition.
- [Device Info Provider](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32DeviceInfoProvider.h#L31)
This provider provides fixed labels, supported calendar types, and supported
locales from the factory partition.
- [Supported Modes](https://github.com/project-chip/connectedhomeip/blob/master/examples/platform/esp32/mode-support/static-supported-modes-manager.h#L28)
This provider offers the supported modes for the mode-select cluster.

More information can be found in the [factory data guide](factory_data.md).

### Device Info Provider

Currently, there are two implementations for this provider:

1. [Reads data stored in the factory partition](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/ESP32FactoryDataProvider.h#L56)
_(This will be deprecated in the future)_
2. [Provides APIs to set fixed data that gets read later](https://github.com/project-chip/connectedhomeip/blob/master/src/platform/ESP32/StaticESP32DeviceInfoProvider.h)

- New products should use the `StaticESP32DeviceInfoProvider`. Utilize the
`Set...()` APIs to set the fixed data.
- Existing products using the first implementation can continue to use it if
they do not wish to change the data.
- For products using the first implementation and wanting to change the fixed
data via OTA, they should switch to the second implementation in the OTA
image and use the `Set...()` APIs to set the fixed data.

#### Example:

```cpp
#include <platform/ESP32/StaticESP32FactoryDataProvider.h>

DeviceLayer::StaticESP32DeviceInfoProvider deviceInfoProvider;

// Define array for Supported Calendar Types
using namespace chip::app::Clusters::TimeFormatLocalization::CalendarTypeEnum;
CalendarTypeEnum supportedCalendarTypes[] = {
CalendarTypeEnum::kGregorian, CalendarTypeEnum::kCoptic,
CalendarTypeEnum::kEthiopian, CalendarTypeEnum::kChinese,
};

// Define array for Supported Locales
const char* supportedLocales[] = {
"en-US",
"en-EU",
};

// Define array for Fixed labels { EndpointId, Label, Value }
struct StaticESP32DeviceInfoProvider::FixedLabelEntry fixedLabels[] = {
{ 0, "Room", "Bedroom 2" },
{ 0, "Orientation", "North" },
{ 0, "Direction", "Up" },
};

Span<CalendarTypeEnum> sSupportedCalendarTypes(supportedCalendarTypes);
Span<const char*> sSupportedLocales(supportedLocales);
Span<StaticESP32DeviceInfoProvider::FixedLabelEntry> sFixedLabels(fixedLabels);

{
deviceInfoProvider.SetSupportedLocales(sSupportedLocales);
deviceInfoProvider.SetSupportedCalendarTypes(sSupportedCalendarTypes);
deviceInfoProvider.SetFixedLabels(sFixedLabels);
DeviceLayer::SetDeviceInfoProvider(&deviceInfoProvider);
}
```
124 changes: 0 additions & 124 deletions scripts/tools/generate_esp32_chip_factory_bin.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,12 @@

import argparse
import base64
import enum
import logging
import os
import sys
from types import SimpleNamespace

import cryptography.x509
from bitarray import bitarray
from bitarray.util import ba2int
from esp_secure_cert.tlv_format import generate_partition_ds, generate_partition_no_ds, tlv_priv_key_t, tlv_priv_key_type_t

CHIP_TOPDIR = os.path.dirname(os.path.realpath(__file__))[:-len(os.path.join('scripts', 'tools'))]
Expand Down Expand Up @@ -152,84 +149,16 @@
'encoding': 'hex2bin',
'value': None,
},
# DeviceInfoProvider
'cal-types': {
'type': 'data',
'encoding': 'u32',
'value': None,
},
'locale-sz': {
'type': 'data',
'encoding': 'u32',
'value': None,
},

# Other device info provider keys are dynamically generated
# in the respective functions.
}


class CalendarTypes(enum.Enum):
Buddhist = 0
Chinese = 1
Coptic = 2
Ethiopian = 3
Gregorian = 4
Hebrew = 5
Indian = 6
Islamic = 7
Japanese = 8
Korean = 9
Persian = 10
Taiwanese = 11


# Supported Calendar types is stored as a bit array in one uint32_t.
def calendar_types_to_uint32(calendar_types):
result = bitarray(32, endian='little')
result.setall(0)
for calendar_type in calendar_types:
try:
result[CalendarTypes[calendar_type].value] = 1
except KeyError:
logging.error('Unknown calendar type: %s', calendar_type)
logging.error('Supported calendar types: %s', ', '.join(CalendarTypes.__members__))
sys.exit(1)
return ba2int(result)


def ishex(s):
try:
_ = int(s, 16)
return True
except ValueError:
return False

# get_fixed_label_dict() converts the list of strings to per endpoint dictionaries.
# example input : ['0/orientation/up', '1/orientation/down', '2/orientation/down']
# example output : {'0': [{'orientation': 'up'}], '1': [{'orientation': 'down'}], '2': [{'orientation': 'down'}]}


def get_fixed_label_dict(fixed_labels):
fl_dict = {}
for fl in fixed_labels:
_l = fl.split('/')

if len(_l) != 3:
logging.error('Invalid fixed label: %s', fl)
sys.exit(1)

if not (ishex(_l[0]) and (len(_l[1]) > 0 and len(_l[1]) < 16) and (len(_l[2]) > 0 and len(_l[2]) < 16)):
logging.error('Invalid fixed label: %s', fl)
sys.exit(1)

if _l[0] not in fl_dict.keys():
fl_dict[_l[0]] = list()

fl_dict[_l[0]].append({_l[1]: _l[2]})

return fl_dict

# get_supported_modes_dict() converts the list of strings to per endpoint dictionaries.
# example with semantic tags
# input : ['0/label1/1/"1\0x8000, 2\0x8000" 1/label2/1/"1\0x8000, 2\0x8000"']
Expand Down Expand Up @@ -373,52 +302,6 @@ def populate_factory_data(args, spake2p_params):
if args.hw_ver_str:
FACTORY_DATA['hw-ver-str']['value'] = args.hw_ver_str

if args.calendar_types:
FACTORY_DATA['cal-types']['value'] = calendar_types_to_uint32(args.calendar_types)

# Supported locale is stored as multiple entries, key format: "locale/<index>, example key: "locale/0"
if args.locales:
FACTORY_DATA['locale-sz']['value'] = len(args.locales)

for i in range(len(args.locales)):
_locale = {
'type': 'data',
'encoding': 'string',
'value': args.locales[i]
}
FACTORY_DATA.update({'locale/{:x}'.format(i): _locale})

# Each endpoint can contains the fixed lables
# - fl-sz/<index> : number of fixed labels for the endpoint
# - fl-k/<ep>/<index> : fixed label key for the endpoint and index
# - fl-v/<ep>/<index> : fixed label value for the endpoint and index
if args.fixed_labels:
dict = get_fixed_label_dict(args.fixed_labels)
for key in dict.keys():
_sz = {
'type': 'data',
'encoding': 'u32',
'value': len(dict[key])
}
FACTORY_DATA.update({'fl-sz/{:x}'.format(int(key)): _sz})

for i in range(len(dict[key])):
entry = dict[key][i]

_label_key = {
'type': 'data',
'encoding': 'string',
'value': list(entry.keys())[0]
}
_label_value = {
'type': 'data',
'encoding': 'string',
'value': list(entry.values())[0]
}

FACTORY_DATA.update({'fl-k/{:x}/{:x}'.format(int(key), i): _label_key})
FACTORY_DATA.update({'fl-v/{:x}/{:x}'.format(int(key), i): _label_value})

# SupportedModes are stored as multiple entries
# - sm-sz/<ep> : number of supported modes for the endpoint
# - sm-label/<ep>/<index> : supported modes label key for the endpoint and index
Expand Down Expand Up @@ -584,13 +467,6 @@ def any_base_int(s): return int(s, 0)
help=('128-bit unique identifier for generating rotating device identifier, '
'provide 32-byte hex string, e.g. "1234567890abcdef1234567890abcdef"'))

# These will be used by DeviceInfoProvider
parser.add_argument('--calendar-types', nargs='+',
help=('List of supported calendar types.\nSupported Calendar Types: Buddhist, Chinese, Coptic, Ethiopian, '
'Gregorian, Hebrew, Indian, Islamic, Japanese, Korean, Persian, Taiwanese'))
parser.add_argument('--locales', nargs='+', help='List of supported locales, Language Tag as defined by BCP47, eg. en-US en-GB')
parser.add_argument('--fixed-labels', nargs='+',
help='List of fixed labels, eg: "0/orientation/up" "1/orientation/down" "2/orientation/down"')
parser.add_argument('--supported-modes', type=str, nargs='+', required=False,
help='List of supported modes, eg: mode1/label1/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode" mode2/label2/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode" mode3/label3/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode"')

Expand Down
2 changes: 2 additions & 0 deletions src/platform/ESP32/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ static_library("ESP32") {
sources += [
"ESP32DeviceInfoProvider.cpp",
"ESP32DeviceInfoProvider.h",
"StaticESP32DeviceInfoProvider.cpp",
"StaticESP32DeviceInfoProvider.h",
]
}

Expand Down
122 changes: 122 additions & 0 deletions src/platform/ESP32/StaticESP32DeviceInfoProvider.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright (c) 2024 Project CHIP Authors
*
* 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.
*/
#include <lib/support/CodeUtils.h>
#include <platform/ESP32/StaticESP32DeviceInfoProvider.h>

namespace chip {
namespace DeviceLayer {

StaticESP32DeviceInfoProvider & StaticESP32DeviceInfoProvider::GetDefaultInstance(void)
{
static StaticESP32DeviceInfoProvider sInstance;
return sInstance;
}

DeviceInfoProvider::FixedLabelIterator * StaticESP32DeviceInfoProvider::IterateFixedLabel(EndpointId endpoint)
{
return chip::Platform::New<StaticFixedLabelIteratorImpl>(endpoint, mFixedLabels);
}

StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::StaticFixedLabelIteratorImpl(EndpointId endpoint,
const Span<FixedLabelEntry> & labels)
{
mEndpoint = endpoint;
mLabels = labels;
mIndex = 0;
}

size_t StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::Count()
{
size_t count = 0;
for (size_t i = 0; i < mLabels.size(); i++)
{
const FixedLabelEntry & entry = mLabels.data()[i];

if (entry.endpointId == mEndpoint)
{
count++;
}
}
return count;
}

bool StaticESP32DeviceInfoProvider::StaticFixedLabelIteratorImpl::Next(FixedLabelType & output)
{
ChipLogDetail(DeviceLayer, "Get the fixed label with index:%u at endpoint:%d", static_cast<unsigned>(mIndex), mEndpoint);

while (mIndex < mLabels.size())
{
const FixedLabelEntry & entry = mLabels.data()[mIndex++];
if (entry.endpointId == mEndpoint)
{
output.label = entry.label;
output.value = entry.value;
return true;
}
}

return false;
}

DeviceInfoProvider::SupportedLocalesIterator * StaticESP32DeviceInfoProvider::IterateSupportedLocales()
{
return chip::Platform::New<StaticSupportedLocalesIteratorImpl>(mSupportedLocales);
}

StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::StaticSupportedLocalesIteratorImpl(
const Span<CharSpan> & locales)
{
mLocales = locales;
}

size_t StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::Count()
{
return mLocales.empty() ? 0 : mLocales.size();
}

bool StaticESP32DeviceInfoProvider::StaticSupportedLocalesIteratorImpl::Next(CharSpan & output)
{
VerifyOrReturnValue(mIndex < mLocales.size(), false);
output = mLocales.data()[mIndex++];
return true;
}

DeviceInfoProvider::SupportedCalendarTypesIterator * StaticESP32DeviceInfoProvider::IterateSupportedCalendarTypes()
{
return chip::Platform::New<StaticSupportedCalendarTypesIteratorImpl>(mSupportedCalendarTypes);
}

StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::StaticSupportedCalendarTypesIteratorImpl(
const Span<CalendarType> & calendarTypes)
{
mCalendarTypes = calendarTypes;
}

size_t StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::Count()
{
return mCalendarTypes.empty() ? 0 : mCalendarTypes.size();
}

bool StaticESP32DeviceInfoProvider::StaticSupportedCalendarTypesIteratorImpl::Next(CalendarType & output)
{
VerifyOrReturnValue(mIndex < mCalendarTypes.size(), false);
output = mCalendarTypes.data()[mIndex++];
return true;
}

} // namespace DeviceLayer
} // namespace chip
Loading

0 comments on commit 5b0afc7

Please sign in to comment.