esp-matter LoadProhibited error when updating attribute
Posted: Tue Aug 01, 2023 5:59 pm
Trying to implement an AC controller with an ESP32 and IR transmitter. Everything seems to be working fine until adding matter (which is crucial at this point), when I control the status of the AC which should send the IR command, the system crashes with LoadProhibited error.
I've narrowed the problem to be when trying to send the IR command. But can't wrap my head around where or what might be causing the issue.
A brief explanation:
There's an `IRTransceiver` class that handles the rmt driver initialisation and interfaces with it in order to send the IR command. This is passed to an `AirConditioner` object that is intended to hold the state of the AC unit as selected on the controller APP. At the moment it is only a placeholder as `state` is not being updated anywhere in the code, and it always sends the same state value. `State` is not being modified anywhere in the code. Just sent whenever the `power` property is changed.
With this in mind, this seems to point to the root of the problem. When initialised, `state` is this as shown on log with `ESP_LOG` calls:
However, when accessing it in the `sendState` method, `state` has somehow changed:
I figured a workaround would be to use a local variable, while finding out why of a method and not accessing the value of `state` but the code crashes anyway. So, something is "moving" the memory locations or something like this. But I don't know what and how can I find it out.
Matter requires for a series of callbacks to be implemented and the `AirConditioner` object is passed as a reference as priv_data so it can be recalled afterwards on the callback:
This might be the start of the issue but I believe from examples I've seen this is what should be done and don't know how to check if this is the root cause or not.
Then `sendState()`simply passes the `state` property to the `IRTransceiver` so it can be sent through the IR transmitter.
then both `sendIRCommand`and `sendDefaultState` work similarly:
But both crash when called from the esp-matter callback.
However, when using it from a debug main directly without involving callbacks, they work as expected, the command is sent and the system doesn't crash which point to proper implementation on `IRTransceiver` and `AirConditioner`
Any ideas?
I've narrowed the problem to be when trying to send the IR command. But can't wrap my head around where or what might be causing the issue.
A brief explanation:
There's an `IRTransceiver` class that handles the rmt driver initialisation and interfaces with it in order to send the IR command. This is passed to an `AirConditioner` object that is intended to hold the state of the AC unit as selected on the controller APP. At the moment it is only a placeholder as `state` is not being updated anywhere in the code, and it always sends the same state value. `State` is not being modified anywhere in the code. Just sent whenever the `power` property is changed.
With this in mind, this seems to point to the root of the problem. When initialised, `state` is this as shown on log with `ESP_LOG` calls:
Code: Select all
I (934) AirConditioner: State is init here, contents:
I (944) AirConditioner: ==========================
I (944) AirConditioner: ==========================
I (954) AirConditioner: ==========================
I (954) AirConditioner: ========= STATE ==========
I (964) AirConditioner: 131
I (964) AirConditioner: 6
I (974) AirConditioner: 4
I (974) AirConditioner: 114
I (974) AirConditioner: 0
I (984) AirConditioner: 0
I (984) AirConditioner: 148
I (984) AirConditioner: 56
I (994) AirConditioner: 0
I (994) AirConditioner: 0
I (994) AirConditioner: 0
I (1004) AirConditioner: 128
I (1004) AirConditioner: 25
I (1004) AirConditioner: 67
I (1014) AirConditioner: 0
I (1014) AirConditioner: 1
I (1014) AirConditioner: 0
I (1024) AirConditioner: 0
I (1024) AirConditioner: 0
I (1024) AirConditioner: 0
I (1034) AirConditioner: 1
I (1034) AirConditioner: ======= END STATE ========
I (1044) AirConditioner: ==========================
I (1044) AirConditioner: ==========================
I (1054) AirConditioner: ==========================
Code: Select all
I (13244) AirConditioner: SetPower called
I (13244) AirConditioner: Setting new power value
I (13254) AirConditioner: Sending new state...
I (13254) AirConditioner: passing state for sending.
I (13264) AirConditioner: ==========================
I (13264) AirConditioner: ==========================
I (13274) AirConditioner: ==========================
I (13274) AirConditioner: ========= STATE ==========
I (13274) AirConditioner: 0
I (13294) AirConditioner: 0
I (13294) AirConditioner: 0
I (13294) AirConditioner: 0
I (13304) AirConditioner: 0
I (13304) AirConditioner: 0
I (13304) AirConditioner: 0
I (13304) AirConditioner: 0
I (13314) AirConditioner: 0
I (13314) AirConditioner: 60
I (13314) AirConditioner: 55
I (13314) AirConditioner: 64
I (13324) AirConditioner: 63
I (13334) AirConditioner: 48
I (13334) AirConditioner: 188
I (13334) AirConditioner: 251
I (13344) AirConditioner: 63
I (13344) AirConditioner: 16
I (13344) AirConditioner: 188
I (13354) AirConditioner: 251
I (13354) AirConditioner: 63
I (13354) AirConditioner: ======= END STATE ========
I (13374) AirConditioner: ==========================
I (13374) AirConditioner: ==========================
I (13374) AirConditioner: ==========================
Matter requires for a series of callbacks to be implemented and the `AirConditioner` object is passed as a reference as priv_data so it can be recalled afterwards on the callback:
Code: Select all
esp_err_t matterDriverInit(AirConditioner ac) {
node::config_t nodeConfig;
node_t *node = node::create(&nodeConfig, handleAttributeUpdate, handleIdentification);
on_off_plugin_unit::config_t acConfig;
acConfig.on_off.on_off = false;
endpoint_t *endpoint = on_off_plugin_unit::create(node, &acConfig, ENDPOINT_FLAG_NONE, &ac);
//other stuff
}
[code]
then on the callback used for attribute update handling it is casted back to `AirConditioner`:
[code]
esp_err_t handleAttributeUpdate(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(endpoint_id == acEndpointId) {
AirConditioner* ac = (AirConditioner*)priv_data;
if (cluster_id == OnOff::Id) {
if(attribute_id == OnOff::Attributes::OnOff::Id) {
ac->setPower(val->val.b); // this calls for sendstate afterwards.
}
}
}
}
//else for error if needed...
return err;
Then `sendState()`simply passes the `state` property to the `IRTransceiver` so it can be sent through the IR transmitter.
Code: Select all
void AirConditioner::sendState() {
#ifdef DEBUG
ESP_LOGI(TAG_AC, "passing state for sending.");
ESP_LOGI(TAG_AC, "==========================");
ESP_LOGI(TAG_AC, "==========================");
ESP_LOGI(TAG_AC, "==========================");
ESP_LOGI(TAG_AC, "========= STATE ==========");
for (int i=0; i<21; i++) {
ESP_LOGI(TAG_AC, "%d ", this->state[i] );
}
ESP_LOGI(TAG_AC, "======= END STATE ========");
ESP_LOGI(TAG_AC, "==========================");
ESP_LOGI(TAG_AC, "==========================");
ESP_LOGI(TAG_AC, "==========================");
#endif
//this->transceiver.sendIRCommand(this->state);
this->transceiver.sendDefaultState();//workaround
}
Code: Select all
size_t IRTransceiver::sendIRCommand(uint8_t state[21]){
#ifdef DEBUG
ESP_LOGI(TAG, "State received:");
ESP_LOGI(TAG, "==========================");
ESP_LOGI(TAG, "==========================");
ESP_LOGI(TAG, "==========================");
ESP_LOGI(TAG, "========= STATE ==========");
for (int i=0; i<21; i++) {
ESP_LOGI(TAG, "%d ", state[i] );
}
ESP_LOGI(TAG, "======= END STATE ========");
ESP_LOGI(TAG, "==========================");
ESP_LOGI(TAG, "==========================");
ESP_LOGI(TAG, "==========================");
#endif
ESP_LOGI(TAG, "Sending IR Command");
return rmt_transmit(this->txChannel, this->encoder, &state, sizeof(&state), &this->txConfig);
}
size_t IRTransceiver::sendDefaultState(){
ESP_LOGI(TAG, "Sending IR Command for default state");
uint8_t defaultState[21] = {0x83, 0x06, 0x04, 0x72, 0x00, 0x00, 0x94, 0x38, 0x00, 0x00, 0x00, 0x80, 0x19, 0x43, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01};
return rmt_transmit(this->txChannel, this->encoder, &defaultState, sizeof(&defaultState), &this->txConfig);
ESP_LOGI(TAG, "Sent IR Command");
}
However, when using it from a debug main directly without involving callbacks, they work as expected, the command is sent and the system doesn't crash which point to proper implementation on `IRTransceiver` and `AirConditioner`
Code: Select all
extern "C" void app_main() {
std::string protocol = "KELON168";
//Initialize IR TX
IRTransceiver transceiver(IR_TX_PIN,protocol);
esp_err_t res = transceiver.enableTx();
if (res == ESP_OK) {
ESP_LOGI("DEBUG_IR", "sucessfully enabled IR transmission");
}
else {
ESP_LOGI("DEBUG_IR", "error enabling IR transmission");
}
AirConditioner ac(25,1,1,false,transceiver);
if (res == ESP_OK) {
ESP_LOGI("DEBUG_IR", "sucessfully enabled IR transmission");
}
else {
ESP_LOGI("DEBUG_IR", "error enabling IR transmission");
}
ESP_LOGI("DEBUG_IR", "Updating power to true");
ac.setPower(true);
ESP_LOGI("DEBUG_IR", "Updating power to false");
ac.setPower(false);
ESP_LOGI("DEBUG_IR", "did not crash :o");
}
Any ideas?