YOGYUI

[PROJ] Matter 밝기 측정 클러스터 개발 예제 (ESP32) 본문

PROJECT

[PROJ] Matter 밝기 측정 클러스터 개발 예제 (ESP32)

요겨 2024. 3. 2. 18:22
반응형

Matter - Illuminance Measuremet Cluster Developing Example using ESP32 SoC

지난 글에서 밝기 측정 (Illuminance Measurement) 관련 클러스터의 Matter 스펙을 알아봤다

Matter Specification - Illuminance Measurement Cluster

 

Matter Specification - Illuminance Measurement Cluster

Matter :: Illuminance Measurement Cluster The Illuminance Measurement cluster provides an interface to illuminance measurement functionality, including configuration and provision of notifications of illuminance measurements. 조도 센서 디바이스를

yogyui.tistory.com

디지털 조도 센서 모듈을 사용해 간단하게 Matter 밝기 측정 센서 디바이스를 만들어보자

1. Hardware

1.1. Main Processor

  • ESP32-WROOM-32E-N4

이번 글에서도 역시 EspressIf사의 ESP32 모듈을 사용

1.2. Sensor Module

 

조도 센서 모듈은 DFRobot 사에서 만든 Digital Ambient Light Sensor(V1.0)을 사용했다

이 모듈에는 Vishay사의 VEML7700을 장착되어 있는데, 별도로 Analog to Digital Converter(ADC)없이 I2C 인터페이스로 조도값을 디지털 통신으로 받아볼 수 있어 단순한 광다이오드(Photodiode)보다 사용하기 편하다 (동작전압도 3.3V 근방이라 ESP32같은 MCU와 함께 회로를 구성하기에도 용이하다)

https://www.vishay.com/docs/84286/veml7700.pdf

2. Software

2.1. Software Development Kit (SDK)

2.2. 소스코드 커밋

https://github.com/YOGYUI/matter-esp32-veml7700

 

GitHub - YOGYUI/matter-esp32-veml7700: Matter illumination measurement sensor (ESP32 + VEML7700) example

Matter illumination measurement sensor (ESP32 + VEML7700) example - YOGYUI/matter-esp32-veml7700

github.com

2.3. I2C 통신 클래스 구현

VEML7700과의 I2C 통신을 위한 클래스는 앞서 포스팅한 온습도센서 Si7021(링크)에서 구현했던 CI2CMaster 를 그대로 사용했다 (한번 잘 만들어놓으면 사골을 쪽쪽 빨아먹을 수 있다)

2.4. 조도 센서 (VEML7700) 클래스 구현

#include "veml7700.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include <inttypes.h>

#define VEML7700_I2CADDR_DEFAULT    0x10    /**< I2C address */

#define VEML7700_ALS_CONFIG         0x00    /**< Light configuration register */
#define VEML7700_ALS_THREHOLD_HIGH  0x01    /**< Light high threshold for irq */
#define VEML7700_ALS_THREHOLD_LOW   0x02    /**< Light low threshold for irq */
#define VEML7700_ALS_POWER_SAVE     0x03    /**< Power save regiester */
#define VEML7700_ALS_DATA           0x04    /**< The light data output */
#define VEML7700_WHITE_DATA         0x05    /**< The white light data output */
#define VEML7700_INTERRUPTSTATUS    0x06    /**< What IRQ (if any) */
#define VEML7700_DEVICE_ID          0x07    /**< Device ID */

#define VEML7700_INTERRUPT_HIGH     0x4000  /**< Interrupt status for high threshold */
#define VEML7700_INTERRUPT_LOW      0x8000  /**< Interrupt status for low threshold */

#define VEML7700_GAIN_1             0x00    /**< ALS gain 1x */
#define VEML7700_GAIN_2             0x01    /**< ALS gain 2x */
#define VEML7700_GAIN_1_8           0x02    /**< ALS gain 1/8x */
#define VEML7700_GAIN_1_4           0x03    /**< ALS gain 1/4x */

#define VEML7700_IT_100MS           0x00    /**< ALS intetgration time 100ms */
#define VEML7700_IT_200MS           0x01    /**< ALS intetgration time 200ms */
#define VEML7700_IT_400MS           0x02    /**< ALS intetgration time 400ms */
#define VEML7700_IT_800MS           0x03    /**< ALS intetgration time 800ms */
#define VEML7700_IT_50MS            0x08    /**< ALS intetgration time 50ms */
#define VEML7700_IT_25MS            0x0C    /**< ALS intetgration time 25ms */

#define VEML7700_PERS_1             0x00    /**< ALS irq persistence 1 sample */
#define VEML7700_PERS_2             0x01    /**< ALS irq persistence 2 samples */
#define VEML7700_PERS_4             0x02    /**< ALS irq persistence 4 samples */
#define VEML7700_PERS_8             0x03    /**< ALS irq persistence 8 samples */

#define VEML7700_POWERSAVE_MODE1    0x00    /**< Power saving mode 1 */
#define VEML7700_POWERSAVE_MODE2    0x01    /**< Power saving mode 2 */
#define VEML7700_POWERSAVE_MODE3    0x02    /**< Power saving mode 3 */
#define VEML7700_POWERSAVE_MODE4    0x03    /**< Power saving mode 4 */

CVeml7700Ctrl* CVeml7700Ctrl::_instance = nullptr;

CVeml7700Ctrl::CVeml7700Ctrl()
{
    m_i2c_master = nullptr;
    m_config_reg_val = 0;
    m_pwr_save_reg_val = 0;
}

CVeml7700Ctrl::~CVeml7700Ctrl()
{
}

CVeml7700Ctrl* CVeml7700Ctrl::Instance()
{
    if (!_instance) {
        _instance = new CVeml7700Ctrl();
    }

    return _instance;
}

bool CVeml7700Ctrl::initialize(CI2CMaster *i2c_master)
{
    m_i2c_master = i2c_master;

    uint16_t dev_id_value = 0;
    if (read_device_id(&dev_id_value)) {
        GetLogger(eLogType::Info)->Log("Slave address option code: 0x%02X, Device ID Code: 0x%02X", (uint8_t)(dev_id_value >> 8), (uint8_t)(dev_id_value & 0xFF));
    } else {
        return false;
    }

    // initialize register value to member variables
    read_configure_register(&m_config_reg_val);
    read_power_saving_register(&m_pwr_save_reg_val);

    // initialize IC
    shutdown();
    set_enable_interrupt(false);
    set_als_persistence(VEML7700_PERS_1);
    set_gain(VEML7700_GAIN_1_8);
    set_als_integration_time(VEML7700_IT_100MS);
    set_power_saving_mode(false);
    power_on();

    GetLogger(eLogType::Info)->Log("Initialized");
    return true;
}

bool CVeml7700Ctrl::release()
{
    shutdown();
    return true;
}

bool CVeml7700Ctrl::read_measurement(float *result)
{
    /* Automatically adjust gain and integration time to obtain goot result */
    const uint8_t gain_word_array[] = {VEML7700_GAIN_1_8, VEML7700_GAIN_1_4, VEML7700_GAIN_1, VEML7700_GAIN_2};
    const uint8_t integ_time_word_array[] = {VEML7700_IT_25MS, VEML7700_IT_50MS, VEML7700_IT_100MS, VEML7700_IT_200MS, VEML7700_IT_400MS, VEML7700_IT_800MS};

    int gain_idx = 0;
    int integ_idx = 2;
    uint16_t als_value = 0;
    bool correction = false;

    set_gain(gain_word_array[gain_idx]);
    set_als_integration_time(integ_time_word_array[integ_idx]);
    read_als_high_resolution_output_data(&als_value);

    if (als_value <= 100) {
        while ((als_value <= 100) && !((gain_idx == 3) && (integ_idx == 5))) {
            if (gain_idx < 3) {
                set_gain(gain_word_array[++gain_idx]);
            } else if (integ_idx < 5) {
                set_als_integration_time(integ_time_word_array[++integ_idx]);
            }
            read_als_high_resolution_output_data(&als_value);
        }
    } else {
        correction = true;
        while ((als_value > 10000) && (integ_idx > 0)) {
            set_als_integration_time(integ_time_word_array[--integ_idx]);
            read_als_high_resolution_output_data(&als_value);
        }
    }

    // calculation 
    if (result)
        *result = convert_raw_to_lux(als_value, correction);

    return true;
}

static float real_integration_time(uint8_t val)
{
    float real_value;

    switch(val) {
    case VEML7700_IT_25MS:
        real_value = 25.f;
        break;
    case VEML7700_IT_50MS:
        real_value = 50.f;
        break;
    case VEML7700_IT_100MS:
        real_value = 100.f;
        break;
    case VEML7700_IT_200MS:
        real_value = 200.f;
        break;
    case VEML7700_IT_400MS:
        real_value = 400.f;
        break;
    case VEML7700_IT_800MS:
        real_value = 800.f;
        break;
    default:
        real_value = -1.f;
        break;
    }

    return real_value;
}

static float real_gain(uint8_t val)
{
    float real_value;

    switch (val) {
    case VEML7700_GAIN_1_8:
        real_value = 0.125f;
        break;
    case VEML7700_GAIN_1_4:
        real_value = 0.25f;
        break;
    case VEML7700_GAIN_1:
        real_value = 1.f;
        break;
    case VEML7700_GAIN_2:
        real_value = 2.f;
        break;
    default:
        real_value = -1.f;
        break;
    }

    return real_value;
}

float CVeml7700Ctrl::convert_raw_to_lux(uint16_t raw, bool correction)
{
    uint8_t integ_time_raw, gain_raw;
    
    get_als_integration_time(&integ_time_raw);
    get_gain(&gain_raw);
    
    float integ_time_val = real_integration_time(integ_time_raw);
    float gain_val = real_gain(gain_raw);

    float resolution = 0.0036f * (800.f / integ_time_val) * (2.f / gain_val);
    float calc = (float)raw * resolution;
    if (correction) {
        calc = (((6.0135e-13 * calc - 9.3924e-9) * calc + 8.1488e-5) * calc + 1.0023) * calc;
    }

    return calc;
}

bool CVeml7700Ctrl::power_on()
{
    m_config_reg_val &= 0xFFFE;
    return write_configure_register(m_config_reg_val);
}

bool CVeml7700Ctrl::shutdown()
{
    m_config_reg_val |= 0x0001;
    return write_configure_register(m_config_reg_val);
}

bool CVeml7700Ctrl::is_running(bool *running, bool read_register/*=false*/)
{
    if (read_register) {
        if (!read_configure_register(&m_config_reg_val))
            return false;
    }

    if (running) {
        *running = !(bool)(m_config_reg_val & 0x0001);
    }

    return true;
}

bool CVeml7700Ctrl::set_enable_interrupt(bool enable)
{
    if (enable)
        m_config_reg_val |= 0x0002;
    else
        m_config_reg_val &= 0xFFFD;
    return write_configure_register(m_config_reg_val);
}

bool CVeml7700Ctrl::is_interrupt_enabled(bool *enabled, bool read_register/*=false*/)
{
    if (read_register) {
        if (!read_configure_register(&m_config_reg_val))
            return false;
    }

    if (enabled) {
        *enabled = !(bool)(m_config_reg_val & 0x0002);
    }

    return true;
}

bool CVeml7700Ctrl::set_als_persistence(uint8_t value)
{
    m_config_reg_val &= 0xFFCF;
    if (value >= 4) {
        GetLogger(eLogType::Info)->Log("Exceeded value range");
        return false;
    }
    m_config_reg_val |= (uint16_t)value << 4;
    return write_configure_register(m_config_reg_val);
}

bool CVeml7700Ctrl::get_als_persistence(uint8_t *value, bool read_register/*=false*/)
{
    if (read_register) {
        if (!read_configure_register(&m_config_reg_val))
            return false;
    }

    if (value) {
        *value = (uint8_t)((m_config_reg_val & 0x0030) >> 4);
    }

    return true;
}

bool CVeml7700Ctrl::set_als_integration_time(uint8_t value)
{
    m_config_reg_val &= 0xFC3F;
    m_config_reg_val |= (uint16_t)value << 6;
    return write_configure_register(m_config_reg_val);
}

bool CVeml7700Ctrl::get_als_integration_time(uint8_t *value, bool read_register/*=false*/)
{
    if (read_register) {
        if (!read_configure_register(&m_config_reg_val))
            return false;
    }

    if (value) {
        *value = (uint8_t)((m_config_reg_val & 0x03C0) >> 6);
    }

    return true;
}

bool CVeml7700Ctrl::set_gain(uint8_t value)
{
    m_config_reg_val &= 0xE7FF;
    if (value >= 4) {
        GetLogger(eLogType::Info)->Log("Exceeded value range");
        return false;
    }
    m_config_reg_val |= (uint16_t)value << 11;
    return write_configure_register(m_config_reg_val);
}

bool CVeml7700Ctrl::get_gain(uint8_t *value, bool read_register/*=false*/)
{
    if (read_register) {
        if (!read_configure_register(&m_config_reg_val))
            return false;
    }

    if (value) {
        *value = (uint8_t)((m_config_reg_val & 0x1800) >> 11);
    }

    return true;
}

bool CVeml7700Ctrl::set_enable_power_saving(bool enable)
{
    if (enable)
        m_pwr_save_reg_val |= 0x0001;
    else
        m_pwr_save_reg_val &= 0xFFFE;
    return write_power_saving_register(m_pwr_save_reg_val);
}

bool CVeml7700Ctrl::is_power_saving_enabled(bool *enable, bool read_register/*=false*/)
{
    if (read_register) {
        if (!read_power_saving_register(&m_pwr_save_reg_val))
            return false;
    }

    if (enable) {
        *enable = (bool)(m_pwr_save_reg_val & 0x0001);
    }

    return true;
}

bool CVeml7700Ctrl::set_power_saving_mode(uint8_t value)
{
    m_pwr_save_reg_val &= 0xFFF9;
    m_pwr_save_reg_val |= (uint16_t)value << 1;
    return write_power_saving_register(m_pwr_save_reg_val);
}

bool CVeml7700Ctrl::get_power_saving_mode(uint8_t *value, bool read_register/*=false*/)
{
    if (read_register) {
        if (!read_power_saving_register(&m_pwr_save_reg_val))
            return false;
    }

    if (value) {
        *value = (uint8_t)((m_pwr_save_reg_val & 0x0006) >> 1);
    }

    return true;
}

bool CVeml7700Ctrl::read_register_common(uint8_t code, uint16_t *value)
{
    if (!m_i2c_master) {
        GetLogger(eLogType::Error)->Log("I2C Controller is null");
        return false;
    }

    uint8_t data_write[1] = {code};
    uint8_t data_read[2] = {0, };
    if (!m_i2c_master->write_and_read_bytes(VEML7700_I2CADDR_DEFAULT, data_write, sizeof(data_write), data_read, sizeof(data_read)))
        return false;
    
    if (value) {
        *value = ((uint16_t)data_read[1] << 8) | (uint16_t)data_read[0];
    }

    return true;
}

bool CVeml7700Ctrl::write_register_common(uint8_t code, uint16_t value)
{
    if (!m_i2c_master) {
        GetLogger(eLogType::Error)->Log("I2C Controller is null");
        return false;
    }

    uint8_t data_write[3] = {
        code,
        (uint8_t)(value & 0xFF),
        (uint8_t)(value >> 8)
    };
    if (!m_i2c_master->write_bytes(VEML7700_I2CADDR_DEFAULT, data_write, sizeof(data_write)))
        return false;
    
    return true;
}

bool CVeml7700Ctrl::read_configure_register(uint16_t *value)
{
    return read_register_common(VEML7700_ALS_CONFIG, value);
}

bool CVeml7700Ctrl::write_configure_register(uint16_t value)
{
    return write_register_common(VEML7700_ALS_CONFIG, value);
}

bool CVeml7700Ctrl::read_power_saving_register(uint16_t *value)
{
    return read_register_common(VEML7700_ALS_POWER_SAVE, value);
}

bool CVeml7700Ctrl::write_power_saving_register(uint16_t value)
{
    return write_register_common(VEML7700_ALS_POWER_SAVE, value);
}

bool CVeml7700Ctrl::read_low_threshold_window_setting(uint16_t *value)
{
    return read_register_common(VEML7700_ALS_THREHOLD_LOW, value);
}

bool CVeml7700Ctrl::write_low_threshold_window_setting(uint16_t value)
{
    return write_register_common(VEML7700_ALS_THREHOLD_LOW, value);
}

bool CVeml7700Ctrl::read_high_threshold_window_setting(uint16_t *value)
{
    return read_register_common(VEML7700_ALS_THREHOLD_HIGH, value);
}

bool CVeml7700Ctrl::write_high_threshold_window_setting(uint16_t value)
{
    return write_register_common(VEML7700_ALS_THREHOLD_HIGH, value);
}

void CVeml7700Ctrl::wait_for_read_measurement()
{
    uint8_t integ_time_raw = 0;
    get_als_integration_time(&integ_time_raw);
    float integ_time_val = real_integration_time(integ_time_raw);
    if (integ_time_val > 0)
        vTaskDelay((uint32_t)(integ_time_val * 2) / portTICK_PERIOD_MS);
}

bool CVeml7700Ctrl::read_als_high_resolution_output_data(uint16_t *value, bool wait/*=true*/)
{
    if (wait)
        wait_for_read_measurement();
    return !read_register_common(VEML7700_ALS_DATA, value);
}

bool CVeml7700Ctrl::read_white_channel_output_data(uint16_t *value, bool wait/*=true*/)
{
    if (wait)
        wait_for_read_measurement();
    return read_register_common(VEML7700_WHITE_DATA, value);
}

bool CVeml7700Ctrl::read_device_id(uint16_t *value)
{
    return read_register_common(VEML7700_DEVICE_ID, value);
}

 

Raw ALS 값으로부터 Lux 단위의 밝기값 계산 과정을 제외하면 나머지 기능들은 단순히 I2C 레지스터 값을 읽고 쓰는게 전부라 VEML7700 데이터시트를 참고해서 쉽게 구현할 수 있다

※ I2C 레지스터 관련 스펙 및 실제 구현 시 참고해야 할 사항들은 다음 두 문서를 참고

2.5. 밝기 측정 센서 엔드포인트 생성

밝기 측정 엔드포인트는 esp-matter sdk의 endpoint::light_sensor::create 함수로 손쉽게 추가할 수 있다

- illumication measurement 클러스터가 디폴트로 내장된 엔드포인트가 추가된다

#include "lightsensor.h"
#include "system.h"
#include "logger.h"
#include <math.h>

CLightSensor::CLightSensor()
{
    m_matter_update_by_client_clus_illummeas_attr_measureval = false;
}


bool CLightSensor::matter_init_endpoint()
{
    esp_matter::node_t *root = GetSystem()->get_root_node();
    esp_matter::endpoint::light_sensor::config_t config_endpoint;
    uint8_t flags = esp_matter::ENDPOINT_FLAG_DESTROYABLE;
    m_endpoint = esp_matter::endpoint::light_sensor::create(root, &config_endpoint, flags, nullptr);
    if (!m_endpoint) {
        GetLogger(eLogType::Error)->Log("Failed to create endpoint");
        return false;
    }
    return CDevice::matter_init_endpoint();
}

bool CLightSensor::matter_config_attributes()
{
    return true;
}

bool CLightSensor::set_min_measured_value(uint16_t value)
{
    esp_matter::cluster_t *cluster = esp_matter::cluster::get(m_endpoint, chip::app::Clusters::IlluminanceMeasurement::Id);
    if (!cluster) {
        GetLogger(eLogType::Error)->Log("Failed to get IlluminanceMeasurement cluster");
        return false;
    }
    esp_matter::attribute_t *attribute = esp_matter::attribute::get(cluster, chip::app::Clusters::IlluminanceMeasurement::Attributes::MinMeasuredValue::Id);
    if (!attribute) {
        GetLogger(eLogType::Error)->Log("Failed to get MinMeasuredValue attribute");
        return false;
    }
    esp_matter_attr_val_t val = esp_matter_nullable_uint16(value);
    esp_err_t ret = esp_matter::attribute::set_val(attribute, &val);
    if (ret != ESP_OK) {
        GetLogger(eLogType::Error)->Log("Failed to set MinMeasuredValue attribute value (ret: %d)", ret);
        return false;
    }

    return true;
}

bool CLightSensor::set_max_measured_value(uint16_t value)
{
    esp_matter::cluster_t *cluster = esp_matter::cluster::get(m_endpoint, chip::app::Clusters::IlluminanceMeasurement::Id);
    if (!cluster) {
        GetLogger(eLogType::Error)->Log("Failed to get IlluminanceMeasurement cluster");
        return false;
    }
    esp_matter::attribute_t *attribute = esp_matter::attribute::get(cluster, chip::app::Clusters::IlluminanceMeasurement::Attributes::MaxMeasuredValue::Id);
    if (!attribute) {
        GetLogger(eLogType::Error)->Log("Failed to get MaxMeasuredValue attribute");
        return false;
    }
    esp_matter_attr_val_t val = esp_matter_nullable_uint16(value);
    esp_err_t ret = esp_matter::attribute::set_val(attribute, &val);
    if (ret != ESP_OK) {
        GetLogger(eLogType::Error)->Log("Failed to set MaxMeasuredValue attribute value (ret: %d)", ret);
        return false;
    }

    return true;
}

void CLightSensor::matter_on_change_attribute_value(esp_matter::attribute::callback_type_t type, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *value)
{
    if (cluster_id == chip::app::Clusters::IlluminanceMeasurement::Id) {
        if (attribute_id == chip::app::Clusters::IlluminanceMeasurement::Attributes::MeasuredValue::Id) {
            if (m_matter_update_by_client_clus_illummeas_attr_measureval) {
                m_matter_update_by_client_clus_illummeas_attr_measureval = false;
            }
        }
    }
}

void CLightSensor::matter_update_all_attribute_values()
{
    matter_update_clus_illummeas_attr_measureval();
}

void CLightSensor::update_measured_value_illuminance(uint16_t value)
{
    m_measured_value_illuminance = (uint16_t)(10000. * log10((double)value) + 1.);
    if (m_measured_value_illuminance != m_measured_value_illuminance_prev) {
        GetLogger(eLogType::Info)->Log("Update measured illuminance value as %u", m_measured_value_illuminance);
        matter_update_clus_illummeas_attr_measureval();
    }
    m_measured_value_illuminance_prev = m_measured_value_illuminance;
}

void CLightSensor::matter_update_clus_illummeas_attr_measureval(bool force_update/*=false*/)
{
    esp_matter_attr_val_t target_value = esp_matter_nullable_uint16(m_measured_value_illuminance);
    matter_update_cluster_attribute_common(
        m_endpoint_id,
        chip::app::Clusters::IlluminanceMeasurement::Id,
        chip::app::Clusters::IlluminanceMeasurement::Attributes::MeasuredValue::Id,
        target_value,
        &m_matter_update_by_client_clus_illummeas_attr_measureval,
        force_update
    );
}

 

한가지 중요한 점은, lux 단위의 밝기 값을 MeasureValue 어트리뷰트에 값을 쓸 때는 아래 Matter 스펙과 같이 값을 계산해서 대입해야 한다는 점이다

 

Log10 사용을 위해 <math.h> 헤더파일만 포함해주면 되므로 크게 어려운 작업은 아니다

2.6. Measurement Task 생성

void CSystem::task_timer_function(void *param)
{
    CSystem *obj = static_cast<CSystem *>(param);
    int64_t current_tick_us;
    int64_t last_tick_us = 0;
    CDevice * dev;
    float illum_lux = 0.f;

    GetLogger(eLogType::Info)->Log("Realtime task (timer) started");
    while (obj->m_keepalive) {
        if (obj->m_initialized) {
            current_tick_us = esp_timer_get_time();
            if (current_tick_us - last_tick_us >= MEASURE_PERIOD_US) {
                if (GetVeml7700Ctrl()->read_measurement(&illum_lux)) {
                    dev = obj->find_device_by_endpoint_id(1);
                    if (dev) {
                        dev->update_measured_value_illuminance((uint16_t)illum_lux);
                    }
                    GetLogger(eLogType::Info)->Log("Measured illumination from sensor: %g lux", illum_lux);
                }
                last_tick_us = current_tick_us;
            }
        }

        vTaskDelay(pdMS_TO_TICKS(50));
    }
    GetLogger(eLogType::Info)->Log("Realtime task (timer) terminated");
    vTaskDelete(nullptr);
}

3. DEMO

3.1. Google Home 액세서리 추가

Matter 지원 기기 선택 - QR Code 촬영
Matter device commissioning (BLE-WiFi)
디바이스 설정

 

Matter 디바이스를 추가하면 아래와 같이 조도가 기재되는 액세서리가 대시보드에 추가된다

액세서리 세부사항을 팝업하면 그냥 현재 조도만 덩그러니 디스플레이된다 ㅋㅋ

 

 

- 정밀한 척(?) 소수점 4자리까지 표기된다

- 2.5. 에서 언급했듯이 lux 단위의 밝기값을 환산해서 어트리뷰트 값에 대입하는데, Matter fabric의 어플리케이션 단에서 이를 다시 역환산해서 디스플레이하는 것 같다

3.2. 밝기 변화 감지 예시

몇가지 상황에서의 밝기값 변화 반영 여부를 사진으로 찍어봤다

손가락으로 완전히 가리면 0Lux가 된다

 

이정도 수준이면 충분히 홈IoT 자동화에 사용할 수 있을 것 같다

※ 센서의 측정주기를 1초 정도로 잡아 밝기 변화에 빠르게 대응하도록 만드는게 중요할듯... 근데 이러면 센서 모듈의 전력 소모량이 증가하기 때문에 VEML7700의 Threshold와 인터럽트 기능을 적극 활용해 저전력 구동이 가능하도록 코드를 고도화할 필요가 있다 (센서 IoT 모듈의 핵심 요구사항은 수mW 수준의 저전력소모랄까)

3.3. ESP32 로그


I (66088) logger: [CSystem::task_timer_function] Measured illumination from sensor: 16.9344 lux [system.cpp:434]
I (67088) logger: [CLightSensor::update_measured_value_illuminance] Update measured illuminance value as 12305 [lightsensor.cpp:94]
I (67088) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 12305 **********
I (67098) logger: [CSystem::task_timer_function] Measured illumination from sensor: 17.6832 lux [system.cpp:434]
I (67108) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 12305 **********
I (67128) chip[EM]: <<< [E:29881i S:64557 M:265281875] (S) Msg TX to 1:00000000CB36C304 [C4AA] [UDP:[FD31:761D:8AAD:751D:47A8:EBE3:1E4D:2F3D%st1]:5540] --- Type 0001:05 (IM:ReportData)
I (67158) chip[EM]: >>> [E:29881i S:64557 M:115678578 (Ack:265281875)] (S) Msg RX from 1:00000000CB36C304 [C4AA] --- Type 0001:01 (IM:StatusResponse)
I (67168) chip[IM]: Received status response, status is 0x00
I (67168) chip[EM]: <<< [E:29881i S:64557 M:265281876 (Ack:115678578)] (S) Msg TX to 1:00000000CB36C304 [C4AA] [UDP:[FD31:761D:8AAD:751D:47A8:EBE3:1E4D:2F3D%st1]:5540] --- Type 0000:10 (SecureChannel:StandaloneAck)
I (68108) logger: [CLightSensor::update_measured_value_illuminance] Update measured illuminance value as 11761 [lightsensor.cpp:94]
I (68108) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 11761 **********
I (68118) logger: [CSystem::task_timer_function] Measured illumination from sensor: 15.4368 lux [system.cpp:434]
I (68128) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 11761 **********
I (68148) chip[EM]: <<< [E:29882i S:64557 M:265281877] (S) Msg TX to 1:00000000CB36C304 [C4AA] [UDP:[FD31:761D:8AAD:751D:47A8:EBE3:1E4D:2F3D%st1]:5540] --- Type 0001:05 (IM:ReportData)
I (68178) chip[EM]: >>> [E:29882i S:64557 M:115678579 (Ack:265281877)] (S) Msg RX from 1:00000000CB36C304 [C4AA] --- Type 0001:01 (IM:StatusResponse)
I (68178) chip[IM]: Received status response, status is 0x00
I (68188) chip[EM]: <<< [E:29882i S:64557 M:265281878 (Ack:115678579)] (S) Msg TX to 1:00000000CB36C304 [C4AA] [UDP:[FD31:761D:8AAD:751D:47A8:EBE3:1E4D:2F3D%st1]:5540] --- Type 0000:10 (SecureChannel:StandaloneAck)
I (69128) logger: [CLightSensor::update_measured_value_illuminance] Update measured illuminance value as 12305 [lightsensor.cpp:94]
I (69128) esp_matter_attribute: ********** W : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 12305 **********
I (69138) logger: [CSystem::task_timer_function] Measured illumination from sensor: 17.568 lux [system.cpp:434]
I (69148) esp_matter_attribute: ********** R : Endpoint 0x0001's Cluster 0x00000400's Attribute 0x00000000 is 12305 **********
I (69168) chip[EM]: <<< [E:29883i S:64557 M:265281879] (S) Msg TX to 1:00000000CB36C304 [C4AA] [UDP:[FD31:761D:8AAD:751D:47A8:EBE3:1E4D:2F3D%st1]:5540] --- Type 0001:05 (IM:ReportData)
I (69198) chip[EM]: >>> [E:29883i S:64557 M:115678580 (Ack:265281879)] (S) Msg RX from 1:00000000CB36C304 [C4AA] --- Type 0001:01 (IM:StatusResponse)
I (69198) chip[IM]: Received status response, status is 0x00
I (69208) chip[EM]: <<< [E:29883i S:64557 M:265281880 (Ack:115678580)] (S) Msg TX to 1:00000000CB36C304 [C4AA] [UDP:[FD31:761D:8AAD:751D:47A8:EBE3:1E4D:2F3D%st1]:5540] --- Type 0000:10 (SecureChannel:StandaloneAck)

 



흠... 다음엔 또 뭘 만들어볼까나...

이것저것 센서 디바이스를 만들어보고는 있는데, 워낙 Matter 스펙 자체가 단순해 구현하기도 쉬워 약간 허전한 느낌?

Thermostat이나 한번 만들어볼까...

얼마전에 라돈 측정 센서를 사긴 했는데, 실컷 구현해봤자 검증할 테스터기가 없어 차일피일 미루는중 -_-

반응형
Comments