fix: CG-22 document OpenThread version requirements and add RCP version check
All checks were successful
ci/woodpecker/push/build Pipeline was successful

- Add THREAD_RCP_MIN_VERSION constants to thread_br.h (1.3.0)
- Implement rcp_version_check() function to validate RCP firmware version
- Return ESP_ERR_NOT_SUPPORTED if RCP version is incompatible
- Log version comparison and error details at initialization
- Add error_log entry for version mismatch for user visibility
- Document version requirements in CLAUDE.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
ClearGrow Agent
2025-12-11 06:26:23 -07:00
parent 7ca6fff539
commit af019bcc22
3 changed files with 113 additions and 0 deletions

View File

@@ -62,6 +62,20 @@ idf.py size-components # Size analysis
| `network_api/` | REST API, MQTT |
| `ota_manager/` | Firmware updates |
## Thread/OpenThread Version Requirements
| Component | Version | Notes |
|-----------|---------|-------|
| ESP-IDF | >=5.2.0 | Required for Thread 1.3 support |
| OpenThread | Thread 1.3 | Bundled with ESP-IDF component |
| RCP Firmware | >=1.3.0 | nRF52840 probe firmware |
The controller verifies RCP firmware version at initialization. Incompatible versions will prevent Thread network startup with `ESP_ERR_NOT_SUPPORTED`.
Version constants defined in `components/thread_manager/include/thread_br.h`:
- `THREAD_RCP_MIN_VERSION`: Minimum version string ("1.3.0")
- `THREAD_RCP_MIN_VERSION_MAJOR/MINOR/PATCH`: Numeric version components
## Hardware
- **Display**: 800x480 RGB LCD, 16bpp

View File

@@ -33,6 +33,32 @@ extern "C" {
#define THREAD_PARTITION_MAX_HEAL_ATTEMPTS 3
#define THREAD_PARTITION_CHECK_INTERVAL_MS 30000 /**< 30 seconds */
/**
* @defgroup thread_version Thread/OpenThread Version Requirements
*
* ESP-IDF 5.2+ uses OpenThread from the ESP-IDF component registry.
* The Thread stack implements Thread 1.3.0 specification.
*
* RCP firmware (nRF52840) must be compatible with the host OpenThread version.
* Version mismatch can cause communication failures or undefined behavior.
*
* @{
*/
/** Minimum required RCP firmware version (Thread 1.3 compatible) */
#define THREAD_RCP_MIN_VERSION "1.3.0"
/** Major version number for compatibility check */
#define THREAD_RCP_MIN_VERSION_MAJOR 1
/** Minor version number for compatibility check */
#define THREAD_RCP_MIN_VERSION_MINOR 3
/** Patch version number for compatibility check */
#define THREAD_RCP_MIN_VERSION_PATCH 0
/** @} */
/**
* @brief Network partition status
*/

View File

@@ -70,6 +70,7 @@ static esp_err_t dataset_restore_from_nvs(void);
static esp_err_t dataset_generate_unique(void);
static void pairing_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
static void pairing_callback(code_pairing_result_t result, const uint8_t *eui64, void *user_data);
static bool rcp_version_check(const char *version_str);
typedef struct {
int64_t now_ms;
uint8_t *unreachable_count;
@@ -166,9 +167,24 @@ esp_err_t thread_br_init(void)
if (version_ret == ESP_OK && version_len > 0) {
version_str[version_len] = '\0';
ESP_LOGI(TAG, "OpenThread RCP version: %s", version_str);
ESP_LOGI(TAG, "Minimum required version: %s", THREAD_RCP_MIN_VERSION);
/* Check version compatibility */
if (!rcp_version_check(version_str)) {
ESP_LOGE(TAG, "RCP firmware version %s is older than minimum required %s",
version_str, THREAD_RCP_MIN_VERSION);
ESP_LOGE(TAG, "Update probe firmware to Thread 1.3 compatible version");
error_log_add(ERROR_CAT_THREAD, ESP_ERR_NOT_SUPPORTED,
"RCP version incompatible",
"Update probe firmware");
/* Return error - incompatible RCP prevents safe operation */
return ESP_ERR_NOT_SUPPORTED;
}
ESP_LOGI(TAG, "RCP version compatibility check: PASSED");
} else {
ESP_LOGW(TAG, "Could not query OpenThread RCP version: %s",
esp_err_to_name(version_ret));
ESP_LOGW(TAG, "Version compatibility cannot be verified - proceeding with caution");
}
/* Register event handlers for pairing requests from UI */
@@ -850,6 +866,63 @@ esp_err_t thread_br_register_partition_callback(thread_partition_cb_t cb)
return ESP_OK;
}
/**
* @brief Check if RCP version meets minimum requirements
*
* Parses version string and compares against THREAD_RCP_MIN_VERSION.
* Accepts version strings in format "X.Y.Z" or "OPENTHREAD/X.Y.Z..."
*
* @param version_str RCP version string from SPINEL_PROP_NCP_VERSION
* @return true if version is compatible, false otherwise
*/
static bool rcp_version_check(const char *version_str)
{
if (!version_str || version_str[0] == '\0') {
return false;
}
int major = 0, minor = 0, patch = 0;
const char *ver_start = version_str;
/* Skip "OPENTHREAD/" prefix if present */
const char *ot_prefix = strstr(version_str, "OPENTHREAD/");
if (ot_prefix) {
ver_start = ot_prefix + 11; /* strlen("OPENTHREAD/") */
}
/* Also handle "OpenThread/" prefix (case variant) */
const char *ot_prefix_lc = strstr(version_str, "OpenThread/");
if (ot_prefix_lc && (!ot_prefix || ot_prefix_lc < ot_prefix)) {
ver_start = ot_prefix_lc + 11;
}
/* Parse version numbers */
int parsed = sscanf(ver_start, "%d.%d.%d", &major, &minor, &patch);
if (parsed < 2) {
ESP_LOGW(TAG, "Could not parse RCP version: %s", version_str);
return false;
}
/* Compare versions */
if (major > THREAD_RCP_MIN_VERSION_MAJOR) {
return true;
}
if (major < THREAD_RCP_MIN_VERSION_MAJOR) {
return false;
}
/* major == min_major */
if (minor > THREAD_RCP_MIN_VERSION_MINOR) {
return true;
}
if (minor < THREAD_RCP_MIN_VERSION_MINOR) {
return false;
}
/* major == min_major && minor == min_minor */
return patch >= THREAD_RCP_MIN_VERSION_PATCH;
}
static void rcp_health_timer_callback(void *arg)
{
if (!TAKE_MUTEX_NOWAIT()) {