espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.69k stars 7.29k forks source link

ESP32C6 Variables Set by HP Core are 0 in LP Core (IDFGH-13049) #13995

Closed patrickslarson8 closed 4 months ago

patrickslarson8 commented 4 months ago

Answers checklist.

IDF version.

5.2.1

Espressif SoC revision.

ESP32C6

Operating System used.

Linux

How did you build your project?

VS Code IDE

If you are using Windows, please specify command line type.

None

Development Kit.

ESP32-C6-WROOM-1

Power Supply used.

USB

What is the expected behavior?

Variables declared in LP Core and linked into HP core are then set by HP core and are available by LP core.

What is the actual behavior?

Variables when read by LP core are all 0.

Steps to reproduce.

Steps

  1. Build and flash
  2. Monitor serial consoles. A FTDI is attached to the LP core to enable UART output for monitoring.

Overview

The variables are declared and linked following the instructions here: https://docs.espressif.com/projects/esp-idf/en/stable/esp32c6/api-reference/system/ulp-lp-core.html#accessing-the-ulp-lp-core-program-variables The HP sets the values at the variable addresses that are defined by some defines in a header file. After these are all set, the LP core is started and the HP core does not access these variables again (to prevent race conditions).

Code snippet in LP core:

Variables declared

volatile uint32_t shared_deadman_threshold;

bmi_160_register struct for reference

typedef struct {
    uint8_t reg;
    uint8_t value;
} bmi_160_register;

Variable usage in LP core

    lp_core_printf("dogvalue:%d, dogaddress:%d\n\r",
       (int) shared_deadman_threshold,
       (int) &shared_deadman_threshold);

Code snippet in HP core

    volatile uint32_t *shared_watchdog_threshold = &ulp_shared_deadman_threshold;
    *shared_watchdog_threshold = LP_WATCHDOG_THRESH;
    printf("dogaddress:%d, shareddogvalue:%d\n\r",
       (int) &ulp_shared_deadman_threshold,
       (int) ulp_shared_deadman_threshold);

Debug Logs.

Output

HP Core

dogaddress:1342183744, shareddogvalue:5128

LP Core

dogvalue:0, dogaddress:1342183744

The address translates to 0x50001940, which is in the LP SRAM according to the technical manual (to be expected).

More Information.

I have other variables and they are all reported as 0. The variable chosen to demonstrate is specifically a uint32_t to rule out any casting behavior.

Full Code

app_main.cpp

/*
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <esp_err.h>
#include <esp_log.h>
#include <nvs_flash.h>

#include <esp_matter.h>
#include <esp_matter_console.h>
#include <esp_matter_ota.h>

#include <common_macros.h>
#include <app_priv.h>
#include <app_reset.h>
#include <bmi160_defs.h>

#include <stdio.h>
#include "lp_core_main.h"
#include "ulp_lp_core.h"
#include "lp_core_i2c.h"
#include "lp_core_uart.h"

#include <platform/ESP32/OpenthreadLauncher.h>

#include <app/server/CommissioningWindowManager.h>

#include <app/server/Server.h>

extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_main_bin_start");
extern const uint8_t lp_core_main_bin_end[]   asm("_binary_lp_core_main_bin_end");

static const char *TAG = "app_main";
uint16_t light_endpoint_id = 0;
uint16_t door_endpoint_id = 1;
uint16_t window_endpoint_id = 2;

using namespace esp_matter;
using namespace esp_matter::attribute;
using namespace esp_matter::endpoint;
using namespace chip::app::Clusters;

constexpr auto k_timeout_seconds = 300;

uint32_t lp_status;
uint32_t lp_address_given;

#if CONFIG_ENABLE_ENCRYPTED_OTA
extern const char decryption_key_start[] asm("_binary_esp_image_encryption_key_pem_start");
extern const char decryption_key_end[] asm("_binary_esp_image_encryption_key_pem_end");

static const char *s_decryption_key = decryption_key_start;
static const uint16_t s_decryption_key_len = decryption_key_end - decryption_key_start;
#endif // CONFIG_ENABLE_ENCRYPTED_OTA

static void lp_uart_init(void)
{
    lp_core_uart_cfg_t cfg;
    cfg.uart_pin_cfg.tx_io_num = GPIO_NUM_5; 
    cfg.uart_pin_cfg.rx_io_num = GPIO_NUM_4;
    cfg.uart_pin_cfg.rts_io_num = GPIO_NUM_2;
    cfg.uart_pin_cfg.cts_io_num = GPIO_NUM_3;
    cfg.uart_proto_cfg.baud_rate = 115200;
    cfg.uart_proto_cfg.data_bits = UART_DATA_8_BITS;
    cfg.uart_proto_cfg.parity = UART_PARITY_DISABLE;
    cfg.uart_proto_cfg.stop_bits = UART_STOP_BITS_1;
    cfg.uart_proto_cfg.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
    cfg.uart_proto_cfg.rx_flow_ctrl_thresh = 0;
    cfg.lp_uart_source_clk = LP_UART_SCLK_DEFAULT;

    ESP_ERROR_CHECK(lp_core_uart_init(&cfg));

    printf("LP UART initialized successfully\n");
}

static void lp_i2c_init(void)
{
    esp_err_t ret = ESP_OK;

    /* Initialize LP I2C with default configuration */
    lp_core_i2c_cfg_t i2c_cfg;
    i2c_cfg.i2c_pin_cfg.sda_io_num = GPIO_NUM_6;
    i2c_cfg.i2c_pin_cfg.scl_io_num = GPIO_NUM_7;
    i2c_cfg.i2c_pin_cfg.sda_pullup_en = true;
    i2c_cfg.i2c_pin_cfg.scl_pullup_en = true;
    i2c_cfg.i2c_timing_cfg.clk_speed_hz = 400000;
    i2c_cfg.i2c_src_clk = LP_I2C_SCLK_LP_FAST;

    ret = lp_core_i2c_master_init(LP_I2C_NUM_0, (const lp_core_i2c_cfg_t*)&i2c_cfg);
    if (ret != ESP_OK) {
        ESP_LOGI(TAG,"LP I2C init failed\n");
        abort();
    }
    ESP_LOGI(TAG, "LP I2C initialized successfully\n");
}

static void lp_core_init(void)
{
    /* Set LP core wakeup source as the HP CPU */
    ulp_lp_core_cfg_t cfg = {
        .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_LP_TIMER,
        .lp_timer_sleep_duration_us = 1000000,
    };

    /* Load LP core firmware */
    ESP_ERROR_CHECK(ulp_lp_core_load_binary(lp_core_main_bin_start, (lp_core_main_bin_end - lp_core_main_bin_start)));

    /* Run LP core */
    ESP_ERROR_CHECK(ulp_lp_core_run(&cfg));

    printf("LP core loaded with firmware and running successfully\n");
}

static void lp_msg_callback(void *arg, void *data){
    ESP_LOGI(TAG, "Message from LP");
    //drive pin high
    gpio_set_level(MP_GPIO_DRIVE, 1);
    // copy memory
    lp_status = ulp_shared_out_status;
    lp_address_given = ulp_shared_out_address;
    // drive pin low
    gpio_set_level(MP_GPIO_DRIVE, 0);
    // perform actions
    ESP_LOGI(TAG, "LP core says %d at %d\n", (int)lp_status, (int)lp_address_given);
}

void load_bmi_defaults(){
    bmi_160_register *shared_bmi_config = (bmi_160_register *)&ulp_shared_bmi_config;
    for (int i = 0; i < BMI_CONFIG_SIZE; i++){
        shared_bmi_config[i] = default_bmi_config[i];
    }
    uint8_t *shared_bmi_addresses = (uint8_t *)&ulp_shared_bmi_addresses;
        for (int i = 0; i < LP_NUM_ADDRESSES; i++){
        shared_bmi_addresses[i] = lp_addresses[i];
    }
    uint8_t *shared_num_bmi = (uint8_t *)&ulp_shared_num_bmi;
    *shared_num_bmi = LP_NUM_ADDRESSES;
    bmi_160_register *shared_register_masks = (bmi_160_register*)&ulp_shared_register_masks;
    shared_register_masks[0] = read_anymotion_tripped_mask;
    shared_register_masks[1] = read_low_g_tripped_mask;
    uint8_t *shared_num_masks = (uint8_t *)&ulp_shared_num_masks;
    *shared_num_masks = 2;

    uint8_t *shared_timers = (uint8_t*)&ulp_shared_timer_addresses;
    uint8_t addr_0 = TIMER_ADDRESS_0;
    uint8_t addr_1 = TIMER_ADDRESS_1;
    uint8_t addr_2 = TIMER_ADDRESS_2;
    shared_timers[0] = addr_0;
    shared_timers[1] = addr_1;
    shared_timers[2] = addr_2;

    uint8_t *shared_num_timers = (uint8_t *)&ulp_shared_num_timers;
    *shared_num_timers = 3;

    ulp_shared_deadman_threshold = LP_WATCHDOG_THRESH;
    // volatile uint32_t *shared_watchdog_threshold = &ulp_shared_deadman_threshold;
    // *shared_watchdog_threshold = LP_WATCHDOG_THRESH;
    printf("dogaddress:%d, shareddogvalue:%d\n\r",
     (int) &ulp_shared_deadman_threshold,
     (int) ulp_shared_deadman_threshold);
    // printf("address0:%d, num:%d, mask0:%d, num:%d, timer0:%d, num:%d, dog:%d\n\r",
    //     (int)shared_bmi_addresses[0],
    //     (int)*shared_num_bmi,
    //     (int)shared_register_masks[0].reg,
    //     (int)*shared_num_masks,
    //     (int)shared_timers[0],
    //     (int)*shared_num_timers,
    //     (int)*shared_watchdog_threshold);

}

static void app_event_cb(const ChipDeviceEvent *event, intptr_t arg)
{
    switch (event->Type) {
    case chip::DeviceLayer::DeviceEventType::kInterfaceIpAddressChanged:
        ESP_LOGI(TAG, "Interface IP Address changed");
        break;

    case chip::DeviceLayer::DeviceEventType::kCommissioningComplete:
        ESP_LOGI(TAG, "Commissioning complete");
        break;

    case chip::DeviceLayer::DeviceEventType::kFailSafeTimerExpired:
        ESP_LOGI(TAG, "Commissioning failed, fail safe timer expired");
        break;

    case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStarted:
        ESP_LOGI(TAG, "Commissioning session started");
        break;

    case chip::DeviceLayer::DeviceEventType::kCommissioningSessionStopped:
        ESP_LOGI(TAG, "Commissioning session stopped");
        break;

    case chip::DeviceLayer::DeviceEventType::kCommissioningWindowOpened:
        ESP_LOGI(TAG, "Commissioning window opened");
        break;

    case chip::DeviceLayer::DeviceEventType::kCommissioningWindowClosed:
        ESP_LOGI(TAG, "Commissioning window closed");
        break;

    case chip::DeviceLayer::DeviceEventType::kFabricRemoved:
        {
            ESP_LOGI(TAG, "Fabric removed successfully");
            if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0)
            {
                chip::CommissioningWindowManager & commissionMgr = chip::Server::GetInstance().GetCommissioningWindowManager();
                constexpr auto kTimeoutSeconds = chip::System::Clock::Seconds16(k_timeout_seconds);
                if (!commissionMgr.IsCommissioningWindowOpen())
                {
                    /* After removing last fabric, this example does not remove the Wi-Fi credentials
                     * and still has IP connectivity so, only advertising on DNS-SD.
                     */
                    CHIP_ERROR err = commissionMgr.OpenBasicCommissioningWindow(kTimeoutSeconds,
                                                    chip::CommissioningWindowAdvertisement::kDnssdOnly);
                    if (err != CHIP_NO_ERROR)
                    {
                        ESP_LOGE(TAG, "Failed to open commissioning window, err:%" CHIP_ERROR_FORMAT, err.Format());
                    }
                }
            }
        break;
        }

    case chip::DeviceLayer::DeviceEventType::kFabricWillBeRemoved:
        ESP_LOGI(TAG, "Fabric will be removed");
        break;

    case chip::DeviceLayer::DeviceEventType::kFabricUpdated:
        ESP_LOGI(TAG, "Fabric is updated");
        break;

    case chip::DeviceLayer::DeviceEventType::kFabricCommitted:
        ESP_LOGI(TAG, "Fabric is committed");
        break;

    case chip::DeviceLayer::DeviceEventType::kBLEDeinitialized:
        ESP_LOGI(TAG, "BLE deinitialized and memory reclaimed");
        break;

    default:
        break;
    }
}

// This callback is invoked when clients interact with the Identify Cluster.
// In the callback implementation, an endpoint can identify itself. (e.g., by flashing an LED or light).
static esp_err_t app_identification_cb(identification::callback_type_t type, uint16_t endpoint_id, uint8_t effect_id,
                                       uint8_t effect_variant, void *priv_data)
{
    ESP_LOGI(TAG, "Identification callback: type: %u, effect: %u, variant: %u", type, effect_id, effect_variant);
    return ESP_OK;
}

// This callback is called for every attribute update. The callback implementation shall
// handle the desired attributes and return an appropriate error code. If the attribute
// is not of your interest, please do not return an error code and strictly return ESP_OK.
static esp_err_t app_attribute_update_cb(attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id,
                                         uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data)
{
    esp_err_t err = ESP_OK;

    if (type == PRE_UPDATE) {
        /* Driver update */
        app_driver_handle_t driver_handle = (app_driver_handle_t)priv_data;
        err = app_driver_attribute_update(driver_handle, endpoint_id, cluster_id, attribute_id, val);
    }

    return err;
}

extern "C" void app_main()
{
    esp_err_t err = ESP_OK;

    /* Initialize the ESP NVS layer */
    nvs_flash_init();

    /* Initialize LP_I2C from the main processor */
    lp_i2c_init();
    lp_uart_init();

    // gpio network
    app_driver_mp_gpio_init(lp_msg_callback);

    /* Load LP Core binary and start the coprocessor */
    load_bmi_defaults();
    lp_core_init();

    /* Initialize driver */
    app_driver_handle_t light_handle = app_driver_light_init();
    app_driver_handle_t button_handle = app_driver_button_init();
    app_reset_button_register(button_handle);
    app_driver_handle_t door_button_handle = app_driver_door_init();

    /* Create a Matter node and add the mandatory Root Node device type on endpoint 0 */
    node::config_t node_config;
    node_t *node = node::create(&node_config, app_attribute_update_cb, app_identification_cb);
    ABORT_APP_ON_FAILURE(node != nullptr, ESP_LOGE(TAG, "Failed to create Matter node"));

    extended_color_light::config_t light_config;
    light_config.on_off.on_off = DEFAULT_POWER;
    light_config.on_off.lighting.start_up_on_off = nullptr;
    light_config.level_control.current_level = DEFAULT_BRIGHTNESS;
    light_config.level_control.lighting.start_up_current_level = DEFAULT_BRIGHTNESS;
    light_config.color_control.color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
    light_config.color_control.enhanced_color_mode = (uint8_t)ColorControl::ColorMode::kColorTemperature;
    light_config.color_control.color_temperature.startup_color_temperature_mireds = nullptr;
    endpoint_t *endpoint = extended_color_light::create(node, &light_config, ENDPOINT_FLAG_NONE, light_handle);
    ABORT_APP_ON_FAILURE(endpoint != nullptr, ESP_LOGE(TAG, "Failed to create extended color light endpoint"));
    light_endpoint_id = endpoint::get_id(endpoint);
    ESP_LOGI(TAG, "Light created with endpoint_id %d", light_endpoint_id);
    cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id);
    attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id);
    attribute::set_deferred_persistence(current_level_attribute);
    cluster_t *color_control_cluster = cluster::get(endpoint, ColorControl::Id);
    attribute_t *current_x_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::CurrentX::Id);
    attribute::set_deferred_persistence(current_x_attribute);
    attribute_t *current_y_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::CurrentY::Id);
    attribute::set_deferred_persistence(current_y_attribute);
    attribute_t *color_temp_attribute = attribute::get(color_control_cluster, ColorControl::Attributes::ColorTemperatureMireds::Id);
    attribute::set_deferred_persistence(color_temp_attribute);

    // Start my clusters
    contact_sensor::config_t door_config;
    endpoint_t *door_endpoint = contact_sensor::create(node, &door_config, ENDPOINT_FLAG_NONE, NULL);
    ABORT_APP_ON_FAILURE(endpoint != nullptr, ESP_LOGE(TAG, "Failed to create door sensor endpoint"));
    door_endpoint_id = endpoint::get_id(door_endpoint);
    ESP_LOGI(TAG, "Door sensor created with endpoint_id %d", door_endpoint_id);

    contact_sensor::config_t window_config;
    endpoint_t *window_endpoint = contact_sensor::create(node, &window_config, ENDPOINT_FLAG_NONE, NULL);
    ABORT_APP_ON_FAILURE(endpoint != nullptr, ESP_LOGE(TAG, "Failed to create window sensor endpoint"));
    window_endpoint_id = endpoint::get_id(window_endpoint);
    ESP_LOGI(TAG, "Window sensor created with endpoint_id %d", window_endpoint_id);

    /* Set OpenThread platform config */
    esp_openthread_platform_config_t config = {
        .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG(),
        .host_config = ESP_OPENTHREAD_DEFAULT_HOST_CONFIG(),
        .port_config = ESP_OPENTHREAD_DEFAULT_PORT_CONFIG(),
    };
    set_openthread_platform_config(&config);

    /* Matter start */
    err = esp_matter::start(app_event_cb);
    ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to start Matter, err:%d", err));

    /* Starting driver with default values */
    app_driver_light_set_defaults(light_endpoint_id);
    if (app_driver_get_button_state(door_button_handle)){
        app_driver_set_door_closed();
        ESP_LOGI(TAG, "Set door initially closed");
    } else {
        app_driver_set_door_opened();
        ESP_LOGI(TAG, "Set door initially open");
    }

#if CONFIG_ENABLE_ENCRYPTED_OTA
    err = esp_matter_ota_requestor_encrypted_init(s_decryption_key, s_decryption_key_len);
    ABORT_APP_ON_FAILURE(err == ESP_OK, ESP_LOGE(TAG, "Failed to initialized the encrypted OTA, err: %d", err));
#endif // CONFIG_ENABLE_ENCRYPTED_OTA

#if CONFIG_ENABLE_CHIP_SHELL
    esp_matter::console::diagnostics_register_commands();
    esp_matter::console::wifi_register_commands();
#if CONFIG_OPENTHREAD_CLI
    esp_matter::console::otcli_register_commands();
#endif
    esp_matter::console::init();
#endif

}

app_priv.h

/*
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#pragma once

#include <esp_err.h>
#include <esp_matter.h>
#include <hal/gpio_types.h>
#include <iot_button.h>
#include "driver/gpio.h"
#include "lp_core_main.h"

#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
#include "esp_openthread_types.h"
#endif

/** Standard max values (used for remapping attributes) */
#define STANDARD_BRIGHTNESS 255
#define STANDARD_HUE 360
#define STANDARD_SATURATION 255
#define STANDARD_TEMPERATURE_FACTOR 1000000

/** Matter max values (used for remapping attributes) */
#define MATTER_BRIGHTNESS 254
#define MATTER_HUE 254
#define MATTER_SATURATION 254
#define MATTER_TEMPERATURE_FACTOR 1000000

/** Default attribute values used during initialization */
#define DEFAULT_POWER true
#define DEFAULT_BRIGHTNESS 64
#define DEFAULT_HUE 128
#define DEFAULT_SATURATION 254

/** GPIO Pin Assignments for sensors **/
#define DOOR_GPIO_PIN 2
#define WINDOW_GPIO_PIN 3

/** **/
#define MP_GPIO_DRIVEN GPIO_NUM_10
#define MP_GPIO_DRIVE GPIO_NUM_11
#define MP_GPIO_DRIVEN_MASK (1ULL<<MP_GPIO_DRIVEN)
#define MP_GPIO_DRIVE_MASK (1ULL<<MP_GPIO_DRIVE)
#define LP_GPIO_DRIVEN 2
#define LP_GPIO_DRIVE 3

const button_config_t door_btn_cfg = {
    .type = BUTTON_TYPE_GPIO,
    .long_press_time = 1000,
    .short_press_time = 100,
    .gpio_button_config = {
        .gpio_num = DOOR_GPIO_PIN,
        .active_level = 0,
    },
};

const button_config_t lp_interface_btn_cfg = {
    .type = BUTTON_TYPE_GPIO,
    .long_press_time = 1000,
    .short_press_time = 50,
    .gpio_button_config = {
        .gpio_num = MP_GPIO_DRIVEN,
        .active_level = 1,
    },
};

typedef void *app_driver_handle_t;

/** Initialize the light driver
 *
 * This initializes the light driver associated with the selected board.
 *
 * @return Handle on success.
 * @return NULL in case of failure.
 */
app_driver_handle_t app_driver_light_init();

/** Initialize the button driver
 *
 * This initializes the button driver associated with the selected board.
 *
 * @return Handle on success.
 * @return NULL in case of failure.
 */
app_driver_handle_t app_driver_button_init();

app_driver_handle_t app_driver_door_init();
bool app_driver_get_button_state(app_driver_handle_t handle);
void app_driver_set_door_opened();
void app_driver_set_door_closed();
app_driver_handle_t app_driver_mp_gpio_init(button_cb_t handler);

/** Driver Update
 *
 * This API should be called to update the driver for the attribute being updated.
 * This is usually called from the common `app_attribute_update_cb()`.
 *
 * @param[in] endpoint_id Endpoint ID of the attribute.
 * @param[in] cluster_id Cluster ID of the attribute.
 * @param[in] attribute_id Attribute ID of the attribute.
 * @param[in] val Pointer to `esp_matter_attr_val_t`. Use appropriate elements as per the value type.
 *
 * @return ESP_OK on success.
 * @return error in case of failure.
 */
esp_err_t app_driver_attribute_update(app_driver_handle_t driver_handle, uint16_t endpoint_id, uint32_t cluster_id,
                                      uint32_t attribute_id, esp_matter_attr_val_t *val);

/** Set defaults for light driver
 *
 * Set the attribute drivers to their default values from the created data model.
 *
 * @param[in] endpoint_id Endpoint ID of the driver.
 *
 * @return ESP_OK on success.
 * @return error in case of failure.
 */
esp_err_t app_driver_light_set_defaults(uint16_t endpoint_id);

#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG()                                           \
    {                                                                                   \
        .radio_mode = RADIO_MODE_NATIVE,                                                \
    }

#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG()                                            \
    {                                                                                   \
        .host_connection_mode = HOST_CONNECTION_MODE_NONE,                              \
    }

#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG()                                            \
    {                                                                                   \
        .storage_partition_name = "nvs", .netif_queue_size = 10, .task_queue_size = 10, \
    }
#endif

bmi160_defs.h

/*
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#pragma once

#include <esp_err.h>
#include <esp_matter.h>
#include <hal/gpio_types.h>
#include <iot_button.h>
#include "driver/gpio.h"
#include "lp_core_main.h"

#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
#include "esp_openthread_types.h"
#endif

/** Standard max values (used for remapping attributes) */
#define STANDARD_BRIGHTNESS 255
#define STANDARD_HUE 360
#define STANDARD_SATURATION 255
#define STANDARD_TEMPERATURE_FACTOR 1000000

/** Matter max values (used for remapping attributes) */
#define MATTER_BRIGHTNESS 254
#define MATTER_HUE 254
#define MATTER_SATURATION 254
#define MATTER_TEMPERATURE_FACTOR 1000000

/** Default attribute values used during initialization */
#define DEFAULT_POWER true
#define DEFAULT_BRIGHTNESS 64
#define DEFAULT_HUE 128
#define DEFAULT_SATURATION 254

/** GPIO Pin Assignments for sensors **/
#define DOOR_GPIO_PIN 2
#define WINDOW_GPIO_PIN 3

/** **/
#define MP_GPIO_DRIVEN GPIO_NUM_10
#define MP_GPIO_DRIVE GPIO_NUM_11
#define MP_GPIO_DRIVEN_MASK (1ULL<<MP_GPIO_DRIVEN)
#define MP_GPIO_DRIVE_MASK (1ULL<<MP_GPIO_DRIVE)
#define LP_GPIO_DRIVEN 2
#define LP_GPIO_DRIVE 3

const button_config_t door_btn_cfg = {
    .type = BUTTON_TYPE_GPIO,
    .long_press_time = 1000,
    .short_press_time = 100,
    .gpio_button_config = {
        .gpio_num = DOOR_GPIO_PIN,
        .active_level = 0,
    },
};

const button_config_t lp_interface_btn_cfg = {
    .type = BUTTON_TYPE_GPIO,
    .long_press_time = 1000,
    .short_press_time = 50,
    .gpio_button_config = {
        .gpio_num = MP_GPIO_DRIVEN,
        .active_level = 1,
    },
};

typedef void *app_driver_handle_t;

/** Initialize the light driver
 *
 * This initializes the light driver associated with the selected board.
 *
 * @return Handle on success.
 * @return NULL in case of failure.
 */
app_driver_handle_t app_driver_light_init();

/** Initialize the button driver
 *
 * This initializes the button driver associated with the selected board.
 *
 * @return Handle on success.
 * @return NULL in case of failure.
 */
app_driver_handle_t app_driver_button_init();

app_driver_handle_t app_driver_door_init();
bool app_driver_get_button_state(app_driver_handle_t handle);
void app_driver_set_door_opened();
void app_driver_set_door_closed();
app_driver_handle_t app_driver_mp_gpio_init(button_cb_t handler);

/** Driver Update
 *
 * This API should be called to update the driver for the attribute being updated.
 * This is usually called from the common `app_attribute_update_cb()`.
 *
 * @param[in] endpoint_id Endpoint ID of the attribute.
 * @param[in] cluster_id Cluster ID of the attribute.
 * @param[in] attribute_id Attribute ID of the attribute.
 * @param[in] val Pointer to `esp_matter_attr_val_t`. Use appropriate elements as per the value type.
 *
 * @return ESP_OK on success.
 * @return error in case of failure.
 */
esp_err_t app_driver_attribute_update(app_driver_handle_t driver_handle, uint16_t endpoint_id, uint32_t cluster_id,
                                      uint32_t attribute_id, esp_matter_attr_val_t *val);

/** Set defaults for light driver
 *
 * Set the attribute drivers to their default values from the created data model.
 *
 * @param[in] endpoint_id Endpoint ID of the driver.
 *
 * @return ESP_OK on success.
 * @return error in case of failure.
 */
esp_err_t app_driver_light_set_defaults(uint16_t endpoint_id);

#if CHIP_DEVICE_CONFIG_ENABLE_THREAD
#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG()                                           \
    {                                                                                   \
        .radio_mode = RADIO_MODE_NATIVE,                                                \
    }

#define ESP_OPENTHREAD_DEFAULT_HOST_CONFIG()                                            \
    {                                                                                   \
        .host_connection_mode = HOST_CONNECTION_MODE_NONE,                              \
    }

#define ESP_OPENTHREAD_DEFAULT_PORT_CONFIG()                                            \
    {                                                                                   \
        .storage_partition_name = "nvs", .netif_queue_size = 10, .task_queue_size = 10, \
    }
#endif

main.c (low power core)

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "ulp_lp_core_gpio.h"
#include "ulp_lp_core_i2c.h"
#include "ulp_lp_core_uart.h"
#include "ulp_lp_core_utils.h"
#include "../bmi160_defs.h"

#define LP_UART_PORT_NUM    LP_UART_NUM_0
// #define LP_I2C_NUM_0 0

#define LP_I2C_TRANS_TIMEOUT_CYCLES 5000
#define LP_I2C_TRANS_WAIT_FOREVER   -1
#define REGISTER_WRITE_SIZE 2
#define REGISTER_READ_SIZE 1
#define INTERRUPT_CHECK_PERIOD_uS 100000

#define GPIO_DRIVE LP_IO_NUM_4
#define GPIO_DRIVEN LP_IO_NUM_5

/* These will all be set by the MP before the lp core starts */
volatile bmi_160_register shared_bmi_config[20];     // config will be loaded into all bmi on startup
volatile uint8_t shared_bmi_addresses[2];
volatile uint8_t shared_num_bmi;
volatile bmi_160_register shared_register_masks[20]; // will be checked and discrepancies reported
volatile uint8_t shared_num_masks;
volatile uint8_t shared_timer_addresses[3];
volatile uint8_t shared_num_timers;
volatile uint32_t shared_deadman_threshold;

// set by LP core to send messages back
volatile int shared_out_status = -2;                     // -1 for normal, -2 for watchdog checkin, pos num for register that was tripped
volatile uint32_t shared_out_address = 0;

esp_err_t write_register(const uint16_t address, const bmi_160_register *reg){
    uint8_t buffer[2];
    buffer[0] = reg->reg;
    buffer[1] = reg->value;
    return lp_core_i2c_master_write_to_device(LP_I2C_NUM_0, address, &buffer, REGISTER_WRITE_SIZE, LP_I2C_TRANS_TIMEOUT_CYCLES);
}

esp_err_t read_register(const uint16_t address, uint8_t reg, uint8_t *buffer){
    esp_err_t err;
    err = lp_core_i2c_master_write_to_device(LP_I2C_NUM_0, address, &reg, REGISTER_READ_SIZE, LP_I2C_TRANS_TIMEOUT_CYCLES);
    if (err != ESP_OK) { return err; }
    err = lp_core_i2c_master_read_from_device(LP_I2C_NUM_0, address, &buffer, REGISTER_READ_SIZE, LP_I2C_TRANS_TIMEOUT_CYCLES);
    return err;
}

esp_err_t compare_register(const uint16_t address, const bmi_160_register *reg){
    uint8_t buffer;
    esp_err_t err;
    uint8_t address_reg = reg->reg;
    uint8_t value = reg->value;
    err = lp_core_i2c_master_write_to_device(LP_I2C_NUM_0, address, &address_reg, REGISTER_READ_SIZE, LP_I2C_TRANS_TIMEOUT_CYCLES);
    if (err != ESP_OK) { return err; }
    err = lp_core_i2c_master_read_from_device(LP_I2C_NUM_0, address, &buffer, REGISTER_READ_SIZE, LP_I2C_TRANS_TIMEOUT_CYCLES);
    if (buffer != reg->value) { return ESP_ERR_INVALID_RESPONSE;}
    return err;
}

esp_err_t check_register_mask(const uint16_t address, const bmi_160_register *reg, bool *return_value){
    uint8_t buffer;
    esp_err_t err;
    uint8_t reg_num = reg->reg;
    uint8_t mask = reg->value;
    err = lp_core_i2c_master_write_to_device(LP_I2C_NUM_0, address, &reg_num, REGISTER_READ_SIZE, LP_I2C_TRANS_TIMEOUT_CYCLES);
    err = lp_core_i2c_master_read_from_device(LP_I2C_NUM_0, address, &buffer, REGISTER_READ_SIZE, LP_I2C_TRANS_TIMEOUT_CYCLES);
    uint8_t compared = buffer & mask;
    *return_value = (compared != 0);
    return err;
}

esp_err_t configure_bmi(const uint16_t address){
    esp_err_t error;
    for (int i = 0; i < BMI_CONFIG_SIZE; i++){
        error = write_register(address, &shared_bmi_config[i]);
        if (error != ESP_OK) return error;
    }
    return error;
}

esp_err_t validate_bmi_configuration(const uint16_t address){
    esp_err_t error = ESP_OK;
    for (int i = 0; i < BMI_CONFIG_SIZE; i++){
        error = compare_register(address, &shared_bmi_config[i]);
        if (error != ESP_OK) return error;
    }
    return error;
}

esp_err_t time_for_checkin(uint32_t* time, uint32_t watchdog_threshold, bool* result){
    esp_err_t err;
    uint32_t previous_time = *time;
    *time = 0;
    uint8_t buffer;
    uint8_t bitshift = 0;
    for (int i = 0; i < shared_num_timers; i++){
        uint8_t reg = shared_timer_addresses[i];
        err = read_register(shared_bmi_addresses[0], reg, &buffer);
        if (err != ESP_OK) return err;
        *time = *time | (uint32_t)buffer << (8*i); // combines the three 8 bit registers into a single 32 bit
    }
    if (*time < previous_time) *result = true; // timer wrapped around, so checkin
    if ((*time - previous_time) > watchdog_threshold) *result = true;
    return err;
}

esp_err_t init_bmi(){
    esp_err_t err = ESP_ERR_NOT_FINISHED;
    lp_core_printf("sharednumbmi:%d\r\n", (int)shared_num_bmi);
    for (int i = 0; i < shared_num_bmi; i++){
        uint16_t address = shared_bmi_addresses[i];
        lp_core_printf("address:%d\r\n", (int)address);
        err = configure_bmi(address);
        if (err != ESP_OK){return err;}
        err = validate_bmi_configuration(address);
        if (err != ESP_OK){return err;}
        err = write_register(address, &write_cmd_start_accel);
        if (err != ESP_OK){return err;}
    }
    return err;
}

void init_gpio(){
    ulp_lp_core_gpio_init(GPIO_DRIVE);
    ulp_lp_core_gpio_output_enable(GPIO_DRIVE);
    ulp_lp_core_gpio_set_output_mode(GPIO_DRIVE, RTCIO_LL_OUTPUT_NORMAL);
    ulp_lp_core_gpio_set_level(GPIO_DRIVE, 0);

    ulp_lp_core_gpio_init(GPIO_DRIVEN);
    ulp_lp_core_gpio_pulldown_enable(GPIO_DRIVEN);
    ulp_lp_core_gpio_input_enable(GPIO_DRIVEN);
}

int main (void)
{
    lp_core_printf("dogvalue:%d, dogaddress:%d\n\r",
     (int) shared_deadman_threshold,
     (int) &shared_deadman_threshold);
    lp_core_printf("address0:%d, num:%d, mask0:%d, num:%d, timer0:%d, num:%d, dog:%d\n\r",
        (int)shared_bmi_addresses[0],
        (int)shared_num_bmi,
        (int)shared_register_masks[0].reg,
        (int)shared_num_masks,
        (int)shared_timer_addresses[0],
        (int)shared_num_timers,
        (int)shared_deadman_threshold);
    esp_err_t err = ESP_OK;
    bool mask_tripped = false;
    bool checkin_due = false;
    int msg_out = -1;
    uint8_t trouble_address = 0;
    uint32_t time_ticks = 0;
    // init_gpio();
    err = init_bmi();

    while(1){
        if (err != ESP_OK) break; // quit and let MP restart LP core
        if (msg_out != -1){
            lp_core_printf("msg:%d\n\r", (int)msg_out);
            shared_out_status = msg_out;
            shared_out_address = trouble_address;
            ulp_lp_core_gpio_set_level(GPIO_DRIVE, 1);
            while (!ulp_lp_core_gpio_get_level(GPIO_DRIVEN)); // wait for main processor to acknowledge
            ulp_lp_core_gpio_set_level(GPIO_DRIVE, 0);
            while (ulp_lp_core_gpio_get_level(GPIO_DRIVEN));  // wait for main processor to read
            msg_out = -1;
        }
        for (int i = 0; i < shared_num_bmi; i++){
            for (int j = 0; j < shared_num_masks; j++){
                // If a mask register is tripped, report it back to the main processor to handle
                err = check_register_mask(shared_bmi_addresses[i], &shared_register_masks[j], &mask_tripped);
                if (err != ESP_OK) break;
                if (mask_tripped){
                    trouble_address = i;
                    msg_out = j;
                    lp_core_printf("mask tripped on %d num %d\n\r", (int)trouble_address, (int)msg_out);
                    continue;
                }
            }
            if (err != ESP_OK) break;
        }
        if (err != ESP_OK) break;
        err = time_for_checkin(&time_ticks, shared_deadman_threshold, &checkin_due);
        if (checkin_due){
            lp_core_printf("watchdog\n\r");
            msg_out = -2;
        } 
    };

    return 0;
}
SoucheSouche commented 4 months ago

Hello @patrickslarson8, thanks for reporting this issue. We will take a look a tit a come back to you as soon as possible.

sudeep-mohanty commented 4 months ago

Hello @patrickslarson8, Thanks for approaching us with this query. I see that the variable ulp_shared_deadman_threshold is initialized by the HP core in the function load_bmi_defaults() before the LP core is initialized. Once the LP core starts, it re-initializes the same variable shared_deadman_threshold to 0 since it is a volatile variable declared in global scope. Hence, you see this variable reported as 0 once the LP core starts to print. You should be able to see the value being set by the HP core if you set it after the LP core is up and running.

patrickslarson8 commented 4 months ago

I see, so it is expected behavior then. Thanks for the help. I am closing the issue.