Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libraries/Matter: Add MatterPowerSource.h and battery reporting example #51

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// SPDX-License-Identifier: MIT

#include <Wire.h>
#include <SparkFun_MAX1704x_Fuel_Gauge_Arduino_Library.h>
#include <Matter.h>
#include <MatterSwitch.h>
#include <MatterPowerSource.h>

SFE_MAX1704X lipo(MAX1704X_MAX17048);
uint32_t battery_gauge_timeout = 900000; // 15min
void read_battery();

MatterSwitch matter_switch;
MatterPowerSource matter_power_source;
volatile bool button_pressed = false;
void handle_button_press();
void handle_button_release();

void setup() {
Serial.begin(115200);
Serial.println("Matter Switch with Battery");
Wire.begin();
Matter.begin();
matter_switch.begin();
matter_power_source.begin();

// Set up the onboard button
#ifndef BTN_BUILTIN
#define BTN_BUILTIN PA0
#endif
pinMode(BTN_BUILTIN, INPUT_PULLUP);

attachInterrupt(BTN_BUILTIN, &handle_button_press, FALLING);
attachInterrupt(BTN_BUILTIN, &handle_button_release, RISING);

if (!Matter.isDeviceCommissioned()) {
Serial.println("Matter device is not commissioned");
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
Serial.printf("Manual pairing code: %s\n", Matter.getManualPairingCode().c_str());
Serial.printf("QR code URL: %s\n", Matter.getOnboardingQRCodeUrl().c_str());
}
while (!Matter.isDeviceCommissioned()) {
delay(200);
}
Serial.println("Matter device is commissioned");

if (!Matter.isDeviceThreadConnected()) {
Serial.println("Waiting for Thread network connection...");
}
while (!Matter.isDeviceThreadConnected()) {
delay(200);
}
Serial.println("Connected to Thread network");

if (!matter_switch.is_online()) {
Serial.println("Waiting for Matter switch device discovery...");
}
while (!matter_switch.is_online()) {
delay(200);
}
if (!matter_power_source.is_online()) {
Serial.println("Waiting for Matter power source device discovery...");
}
while (!matter_power_source.is_online()) {
delay(200);
}
Serial.println("Matter device is now online");

if (!lipo.begin()) {
Serial.println("No MAX17048 LiPo fuel gauge detected");
} else {
lipo.quickStart();
lipo.setThreshold(20);
read_battery();
}
}

void loop() {
static bool button_pressed_last = false;
static bool switch_state_last = false;
static uint32_t battery_gauge_last = 0;

// If the physical button state changes - update the switch's state
if (button_pressed != button_pressed_last) {
button_pressed_last = button_pressed;
matter_switch.set_state(button_pressed);
}

// Get the current state of the Matter switch
bool switch_state_current = matter_switch.get_state();

// If the current state is 'pressed' and the previous was 'not pressed' - switch pressed
if (switch_state_current && !switch_state_last) {
switch_state_last = switch_state_current;
Serial.println("Button pressed");
read_battery();
}

// If the current state is 'not pressed' and the previous was 'pressed' - switch released
if (!switch_state_current && switch_state_last) {
switch_state_last = switch_state_current;
Serial.println("Button released");
}

uint32_t time = millis();
if (time > battery_gauge_last + battery_gauge_timeout) {
battery_gauge_last = time;
read_battery();
}
}

void read_battery() {
double voltage = lipo.getVoltage();
double soc = lipo.getSOC();
bool alert = lipo.getAlert();

Serial.printf("Battery Voltage: %4.2f V\n", voltage);
Serial.printf("Battery Percent: %4.0f %%\n", soc);
matter_power_source.set_bat_percent_remaining(soc);

if (alert) {
Serial.println("Battery Alert!");
}
}

void handle_button_press() {
static uint32_t button_press_last = 0;
uint32_t time = millis();
if (time < button_press_last + 200) {
return;
}

button_press_last = time;
button_pressed = true;
}

void handle_button_release() {
static uint32_t button_press_last = 0;
uint32_t time = millis();
if (time < button_press_last + 200) {
return;
}

button_press_last = time;
button_pressed = false;
}
140 changes: 140 additions & 0 deletions libraries/Matter/src/MatterPowerSource.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: MIT

#include "MatterPowerSource.h"

using namespace ::chip;
using namespace ::chip::Platform;
using namespace ::chip::Credentials;
using namespace ::chip::app::Clusters;

const EmberAfDeviceType gPowerSourceDeviceTypes[] = { { DEVICE_TYPE_POWER_SOURCE, DEVICE_VERSION_DEFAULT } };

// Power source cluster attributes
DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(powerSourceAttrs)
DECLARE_DYNAMIC_ATTRIBUTE(PowerSource::Attributes::BatPercentRemaining::Id, INT8U, 1, 0), /* BatPercentRemaining */
DECLARE_DYNAMIC_ATTRIBUTE(PowerSource::Attributes::FeatureMap::Id, BITMAP32, 4, 0), /* FeatureMap */
DECLARE_DYNAMIC_ATTRIBUTE(PowerSource::Attributes::ClusterRevision::Id, INT16U, 2, 0), /* ClusterRevision */
DECLARE_DYNAMIC_ATTRIBUTE_LIST_END();

// Power source cluster list
DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(powerSourceEndpointClusters)
DECLARE_DYNAMIC_CLUSTER(PowerSource::Id, powerSourceAttrs, nullptr, nullptr),
DECLARE_DYNAMIC_CLUSTER(Descriptor::Id, descriptorAttrs, nullptr, nullptr),
DECLARE_DYNAMIC_CLUSTER(BridgedDeviceBasicInformation::Id, bridgedDeviceBasicAttrs, nullptr, nullptr)
DECLARE_DYNAMIC_CLUSTER_LIST_END;

/***************************************************************************//**
* Constructor for MatterPowerSource
******************************************************************************/
MatterPowerSource::MatterPowerSource() :
power_source_device(nullptr),
device_endpoint(nullptr),
endpoint_dataversion_storage(nullptr),
initialized(false)
{
;
}

/***************************************************************************//**
* Destructor for MatterPowerSource
******************************************************************************/
MatterPowerSource::~MatterPowerSource()
{
this->end();
}

/***************************************************************************//**
* Initializes the MatterPowerSource instance
*
* @return true if the initialization succeeded, false otherwise
******************************************************************************/
bool MatterPowerSource::begin()
{
if (this->initialized) {
return false;
}

// Create new device
DevicePowerSource* power_source = new (std::nothrow)DevicePowerSource("Power Source", 0);
if (power_source == nullptr) {
return false;
}
power_source->SetReachable(true);
power_source->SetProductName("Power Source");

// Set the device instance pointer in the base class
this->base_matter_device = power_source;

// Create new endpoint
EmberAfEndpointType* new_endpoint = (EmberAfEndpointType*)malloc(sizeof(EmberAfEndpointType));
if (new_endpoint == nullptr) {
delete(power_source);
return false;
}
new_endpoint->cluster = powerSourceEndpointClusters;
new_endpoint->clusterCount = ArraySize(powerSourceEndpointClusters);
new_endpoint->endpointSize = 0;

// Create data version storage for the endpoint
size_t dataversion_size = ArraySize(powerSourceEndpointClusters) * sizeof(DataVersion);
DataVersion* new_power_source_data_version = (DataVersion*)malloc(dataversion_size);
if (new_power_source_data_version == nullptr) {
delete(power_source);
free(new_endpoint);
return false;
}

// Add new endpoint
int result = AddDeviceEndpoint(power_source,
new_endpoint,
Span<const EmberAfDeviceType>(gPowerSourceDeviceTypes),
Span<DataVersion>(new_power_source_data_version, dataversion_size), 1);
if (result < 0) {
delete(power_source);
free(new_endpoint);
free(new_power_source_data_version);
return false;
}

this->power_source_device = power_source;
this->device_endpoint = new_endpoint;
this->endpoint_dataversion_storage = new_power_source_data_version;
this->initialized = true;
return true;
}

/***************************************************************************//**
* Deinitializes the MatterPowerSource instance
******************************************************************************/
void MatterPowerSource::end()
{
if (!this->initialized) {
return;
}
(void)RemoveDeviceEndpoint(this->power_source_device);
free(this->device_endpoint);
free(this->endpoint_dataversion_storage);
delete(this->power_source_device);
this->initialized = false;
}

void MatterPowerSource::set_bat_percent_remaining_raw(uint8_t value)
{
if (!this->initialized) {
return;
}
PlatformMgr().LockChipStack();
this->power_source_device->SetBatPercentRemaining(value);
PlatformMgr().UnlockChipStack();
}

void MatterPowerSource::set_bat_percent_remaining(double percent)
{
uint8_t out_value = static_cast<uint8_t>(percent * 2.0l);
this->set_bat_percent_remaining_raw(out_value);
}

uint8_t MatterPowerSource::get_bat_percent_remaining()
{
return this->power_source_device->GetBatPercentRemaining();
}
32 changes: 32 additions & 0 deletions libraries/Matter/src/MatterPowerSource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: MIT

#ifndef MATTER_POWER_SOURCE_H
#define MATTER_POWER_SOURCE_H

#include "Matter.h"
#include "devices/DevicePowerSource.h"
#include <platform/CHIPDeviceLayer.h>
#include <app-common/zap-generated/attributes/Accessors.h>

using namespace chip;
using namespace ::chip::DeviceLayer;

class MatterPowerSource : public ArduinoMatterAppliance {
public:
MatterPowerSource();
~MatterPowerSource();
bool begin();
void end();
void set_bat_percent_remaining_raw(uint8_t value);
void set_bat_percent_remaining(double percent);
uint8_t get_bat_percent_remaining();
void operator=(uint8_t value);

private:
DevicePowerSource* power_source_device;
EmberAfEndpointType* device_endpoint;
DataVersion* endpoint_dataversion_storage;
bool initialized;
};

#endif // MATTER_POWER_SOURCE_H
Loading