Skip to content

Commit 3889013

Browse files
committed
libraries/Matter: Add MatterPowerSource.h and a battery percent reporting example
1 parent 65a739a commit 3889013

File tree

5 files changed

+439
-0
lines changed

5 files changed

+439
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
#include <Wire.h>
4+
#include <SparkFun_MAX1704x_Fuel_Gauge_Arduino_Library.h>
5+
#include <Matter.h>
6+
#include <MatterSwitch.h>
7+
#include <MatterPowerSource.h>
8+
9+
SFE_MAX1704X lipo(MAX1704X_MAX17048);
10+
uint32_t battery_gauge_timeout = 900000; // 15min
11+
void read_battery();
12+
13+
MatterSwitch matter_switch;
14+
MatterPowerSource matter_power_source;
15+
volatile bool button_pressed = false;
16+
void handle_button_press();
17+
void handle_button_release();
18+
19+
void setup() {
20+
Serial.begin(115200);
21+
Serial.println("Matter Switch with Battery");
22+
Wire.begin();
23+
Matter.begin();
24+
matter_switch.begin();
25+
matter_power_source.begin();
26+
27+
// Set up the onboard button
28+
#ifndef BTN_BUILTIN
29+
#define BTN_BUILTIN PA0
30+
#endif
31+
pinMode(BTN_BUILTIN, INPUT_PULLUP);
32+
33+
attachInterrupt(button_pin, &handle_button_press, FALLING);
34+
attachInterrupt(button_pin, &handle_button_release, RISING);
35+
36+
if (!Matter.isDeviceCommissioned()) {
37+
Serial.println("Matter device is not commissioned");
38+
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
39+
Serial.printf("Manual pairing code: %s\n", Matter.getManualPairingCode().c_str());
40+
Serial.printf("QR code URL: %s\n", Matter.getOnboardingQRCodeUrl().c_str());
41+
}
42+
while (!Matter.isDeviceCommissioned()) {
43+
delay(200);
44+
}
45+
Serial.println("Matter device is commissioned");
46+
47+
if (!Matter.isDeviceThreadConnected()) {
48+
Serial.println("Waiting for Thread network connection...");
49+
}
50+
while (!Matter.isDeviceThreadConnected()) {
51+
delay(200);
52+
}
53+
Serial.println("Connected to Thread network");
54+
55+
if (!matter_switch.is_online()) {
56+
Serial.println("Waiting for Matter switch device discovery...");
57+
}
58+
while (!matter_switch.is_online()) {
59+
delay(200);
60+
}
61+
if (!matter_power_source.is_online()) {
62+
Serial.println("Waiting for Matter power source device discovery...");
63+
}
64+
while (!matter_power_source.is_online()) {
65+
delay(200);
66+
}
67+
Serial.println("Matter device is now online");
68+
69+
if (!lipo.begin()) {
70+
Serial.println("No MAX17048 LiPo fuel gauge detected");
71+
} else {
72+
lipo.quickStart();
73+
lipo.setThreshold(20);
74+
read_battery();
75+
}
76+
}
77+
78+
void loop() {
79+
static bool button_pressed_last = false;
80+
static bool switch_state_last = false;
81+
static uint32_t battery_gauge_last = 0;
82+
83+
// If the physical button state changes - update the switch's state
84+
if (button_pressed != button_pressed_last) {
85+
button_pressed_last = button_pressed;
86+
matter_switch.set_state(button_pressed);
87+
}
88+
89+
// Get the current state of the Matter switch
90+
bool switch_state_current = matter_switch.get_state();
91+
92+
// If the current state is 'pressed' and the previous was 'not pressed' - switch pressed
93+
if (switch_state_current && !switch_state_last) {
94+
switch_state_last = switch_state_current;
95+
Serial.println("Button pressed");
96+
read_battery();
97+
}
98+
99+
// If the current state is 'not pressed' and the previous was 'pressed' - switch released
100+
if (!switch_state_current && switch_state_last) {
101+
switch_state_last = switch_state_current;
102+
Serial.println("Button released");
103+
}
104+
105+
uint32_t time = millis();
106+
if (time > battery_gauge_last + battery_gauge_timeout) {
107+
battery_gauge_last = time;
108+
read_battery();
109+
}
110+
}
111+
112+
void read_battery() {
113+
double voltage = lipo.getVoltage();
114+
double soc = lipo.getSOC();
115+
bool alert = lipo.getAlert();
116+
117+
Serial.printf("Battery Voltage: %4.2f V\n", voltage);
118+
Serial.printf("Battery Percent: %4.0f %%\n", soc);
119+
matter_power_source.set_bat_percent_remaining(soc);
120+
121+
if (alert) {
122+
Serial.println("Battery Alert!");
123+
}
124+
}
125+
126+
void handle_button_press() {
127+
static uint32_t button_press_last = 0;
128+
uint32_t time = millis();
129+
if (time < button_press_last + 200) {
130+
return;
131+
}
132+
133+
button_press_last = time;
134+
button_pressed = true;
135+
}
136+
137+
void handle_button_release() {
138+
static uint32_t button_press_last = 0;
139+
uint32_t time = millis();
140+
if (time < button_press_last + 200) {
141+
return;
142+
}
143+
144+
button_press_last = time;
145+
button_pressed = false;
146+
}
+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
#include "MatterPowerSource.h"
4+
5+
using namespace ::chip;
6+
using namespace ::chip::Platform;
7+
using namespace ::chip::Credentials;
8+
using namespace ::chip::app::Clusters;
9+
10+
const EmberAfDeviceType gPowerSourceDeviceTypes[] = { { DEVICE_TYPE_POWER_SOURCE, DEVICE_VERSION_DEFAULT } };
11+
12+
// Power source cluster attributes
13+
DECLARE_DYNAMIC_ATTRIBUTE_LIST_BEGIN(powerSourceAttrs)
14+
DECLARE_DYNAMIC_ATTRIBUTE(PowerSource::Attributes::BatPercentRemaining::Id, INT8U, 1, 0), /* BatPercentRemaining */
15+
DECLARE_DYNAMIC_ATTRIBUTE(PowerSource::Attributes::FeatureMap::Id, BITMAP32, 4, 0), /* FeatureMap */
16+
DECLARE_DYNAMIC_ATTRIBUTE(PowerSource::Attributes::ClusterRevision::Id, INT16U, 2, 0), /* ClusterRevision */
17+
DECLARE_DYNAMIC_ATTRIBUTE_LIST_END();
18+
19+
// Power source cluster list
20+
DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(powerSourceEndpointClusters)
21+
DECLARE_DYNAMIC_CLUSTER(PowerSource::Id, powerSourceAttrs, nullptr, nullptr),
22+
DECLARE_DYNAMIC_CLUSTER(Descriptor::Id, descriptorAttrs, nullptr, nullptr),
23+
DECLARE_DYNAMIC_CLUSTER(BridgedDeviceBasicInformation::Id, bridgedDeviceBasicAttrs, nullptr, nullptr)
24+
DECLARE_DYNAMIC_CLUSTER_LIST_END;
25+
26+
/***************************************************************************//**
27+
* Constructor for MatterPowerSource
28+
******************************************************************************/
29+
MatterPowerSource::MatterPowerSource() :
30+
power_source_device(nullptr),
31+
device_endpoint(nullptr),
32+
endpoint_dataversion_storage(nullptr),
33+
initialized(false)
34+
{
35+
;
36+
}
37+
38+
/***************************************************************************//**
39+
* Destructor for MatterPowerSource
40+
******************************************************************************/
41+
MatterPowerSource::~MatterPowerSource()
42+
{
43+
this->end();
44+
}
45+
46+
/***************************************************************************//**
47+
* Initializes the MatterPowerSource instance
48+
*
49+
* @return true if the initialization succeeded, false otherwise
50+
******************************************************************************/
51+
bool MatterPowerSource::begin()
52+
{
53+
if (this->initialized) {
54+
return false;
55+
}
56+
57+
// Create new device
58+
DevicePowerSource* power_source = new (std::nothrow)DevicePowerSource("Power Source", 0);
59+
if (power_source == nullptr) {
60+
return false;
61+
}
62+
power_source->SetReachable(true);
63+
power_source->SetProductName("Power Source");
64+
65+
// Set the device instance pointer in the base class
66+
this->base_matter_device = power_source;
67+
68+
// Create new endpoint
69+
EmberAfEndpointType* new_endpoint = (EmberAfEndpointType*)malloc(sizeof(EmberAfEndpointType));
70+
if (new_endpoint == nullptr) {
71+
delete(power_source);
72+
return false;
73+
}
74+
new_endpoint->cluster = powerSourceEndpointClusters;
75+
new_endpoint->clusterCount = ArraySize(powerSourceEndpointClusters);
76+
new_endpoint->endpointSize = 0;
77+
78+
// Create data version storage for the endpoint
79+
size_t dataversion_size = ArraySize(powerSourceEndpointClusters) * sizeof(DataVersion);
80+
DataVersion* new_power_source_data_version = (DataVersion*)malloc(dataversion_size);
81+
if (new_power_source_data_version == nullptr) {
82+
delete(power_source);
83+
free(new_endpoint);
84+
return false;
85+
}
86+
87+
// Add new endpoint
88+
int result = AddDeviceEndpoint(power_source,
89+
new_endpoint,
90+
Span<const EmberAfDeviceType>(gPowerSourceDeviceTypes),
91+
Span<DataVersion>(new_power_source_data_version, dataversion_size), 1);
92+
if (result < 0) {
93+
delete(power_source);
94+
free(new_endpoint);
95+
free(new_power_source_data_version);
96+
return false;
97+
}
98+
99+
this->power_source_device = power_source;
100+
this->device_endpoint = new_endpoint;
101+
this->endpoint_dataversion_storage = new_power_source_data_version;
102+
this->initialized = true;
103+
return true;
104+
}
105+
106+
/***************************************************************************//**
107+
* Deinitializes the MatterPowerSource instance
108+
******************************************************************************/
109+
void MatterPowerSource::end()
110+
{
111+
if (!this->initialized) {
112+
return;
113+
}
114+
(void)RemoveDeviceEndpoint(this->power_source_device);
115+
free(this->device_endpoint);
116+
free(this->endpoint_dataversion_storage);
117+
delete(this->power_source_device);
118+
this->initialized = false;
119+
}
120+
121+
void MatterPowerSource::set_bat_percent_remaining_raw(uint8_t value)
122+
{
123+
if (!this->initialized) {
124+
return;
125+
}
126+
PlatformMgr().LockChipStack();
127+
this->power_source_device->SetBatPercentRemaining(value);
128+
PlatformMgr().UnlockChipStack();
129+
}
130+
131+
void MatterPowerSource::set_bat_percent_remaining(double percent)
132+
{
133+
uint8_t out_value = static_cast<uint8_t>(percent * 2.0l);
134+
this->set_bat_percent_remaining_raw(out_value);
135+
}
136+
137+
uint8_t MatterPowerSource::get_bat_percent_remaining()
138+
{
139+
return this->power_source_device->GetBatPercentRemaining();
140+
}
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
#ifndef MATTER_POWER_SOURCE_H
4+
#define MATTER_POWER_SOURCE_H
5+
6+
#include "Matter.h"
7+
#include "devices/DevicePowerSource.h"
8+
#include <platform/CHIPDeviceLayer.h>
9+
#include <app-common/zap-generated/attributes/Accessors.h>
10+
11+
using namespace chip;
12+
using namespace ::chip::DeviceLayer;
13+
14+
class MatterPowerSource : public ArduinoMatterAppliance {
15+
public:
16+
MatterPowerSource();
17+
~MatterPowerSource();
18+
bool begin();
19+
void end();
20+
void set_bat_percent_remaining_raw(uint8_t value);
21+
void set_bat_percent_remaining(double percent);
22+
uint8_t get_bat_percent_remaining();
23+
void operator=(uint8_t value);
24+
25+
private:
26+
DevicePowerSource* power_source_device;
27+
EmberAfEndpointType* device_endpoint;
28+
DataVersion* endpoint_dataversion_storage;
29+
bool initialized;
30+
};
31+
32+
#endif // MATTER_POWER_SOURCE_H

0 commit comments

Comments
 (0)