Skip to content

Commit 408303d

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

File tree

5 files changed

+439
-0
lines changed

5 files changed

+439
-0
lines changed
Lines changed: 146 additions & 0 deletions
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(BTN_BUILTIN, &handle_button_press, FALLING);
34+
attachInterrupt(BTN_BUILTIN, &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+
}
Lines changed: 140 additions & 0 deletions
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+
}
Lines changed: 32 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy