Initial Commit

This commit is contained in:
SSAnkur committed 2026-10-17 21:05:22 +06:00
commit a42a6db38b
5 files changed
+234

No files matched your search

+120
View File
@@ -0,0 +1,120 @@
/*
* BTHome v2 temperature / humidity beacon - ultra-low-power (Zephyr)
*
* Every 60 s: power up SHT41, measure T/RH, power it down, read VDDH,
* update a non-connectable BTHome v2 advertisement sent every 10 s.
* Sensor power gating, I2C pin handling, DC/DC and sleep are all
* configured in app.overlay / prj.conf.
*/
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/sys/byteorder.h>
#define MEAS_INTERVAL K_SECONDS(60)
#define ADV_INTERVAL 16000 /* 10000 ms / 0.625 ms (legacy max is 10.24 s) */
#define VDDH_DIV 5 /* SAADC input is VDDH/5 */
static const struct device *const sht = DEVICE_DT_GET(DT_NODELABEL(sht41));
static const struct adc_dt_spec vbat = ADC_DT_SPEC_GET(DT_PATH(zephyr_user));
/* BTHome v2 service data (objects in ascending ID order: 0x0C, 0x2E, 0x45) */
static uint8_t svc[] = {
0xD2, 0xFC, /* UUID 0xFCD2 */
0x40, /* BTHome v2, unencrypted, regular interval */
0x0C, 0, 0, /* voltage, 1 mV */
0x2E, 0, /* humidity, 1 % */
0x45, 0, 0, /* temperature, 0.1 degC */
};
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),
BT_DATA(BT_DATA_SVC_DATA16, svc, sizeof(svc)),
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};
static int read_sht41(void)
{
struct sensor_value t, rh;
int err = pm_device_runtime_get(sht); /* sensor power on */
if (err) {
return err;
}
err = sensor_sample_fetch(sht);
pm_device_runtime_put(sht); /* bus high-Z, then sensor power off */
if (err) {
return err;
}
sensor_channel_get(sht, SENSOR_CHAN_AMBIENT_TEMP, &t);
sensor_channel_get(sht, SENSOR_CHAN_HUMIDITY, &rh);
int32_t mc = sensor_value_to_milli(&t);
int16_t t10 = (mc + (mc < 0 ? -50 : 50)) / 100;
int32_t pct = (sensor_value_to_milli(&rh) + 500) / 1000;
sys_put_le16(t10, &svc[9]);
svc[7] = CLAMP(pct, 0, 100);
return 0;
}
static void read_battery(void)
{
static bool calibrated;
int16_t raw;
struct adc_sequence seq = {
.buffer = &raw,
.buffer_size = sizeof(raw),
.calibrate = !calibrated,
};
adc_sequence_init_dt(&vbat, &seq);
if (adc_read_dt(&vbat, &seq) == 0) {
/* Full-scale input voltage: 0.6 V ref / gain 1/2 = 1200 mV */
int32_t full_mv = adc_ref_internal(vbat.dev);
adc_gain_invert(vbat.channel_cfg.gain, &full_mv);
/*
* Scale raw straight to VDDH in one step and round to nearest:
* VDDH_mV = raw * 1200 * 5 / 2^res. Multiplying by 5 before the
* division keeps 1 mV resolution instead of 5 mV steps.
* Max 16383 * 1200 * 5 = 98 M, fits in 32 bits.
*/
uint32_t num = (uint32_t)MAX(raw, 0) * (uint32_t)full_mv * VDDH_DIV;
uint32_t mv = (num + BIT(vbat.resolution - 1)) >> vbat.resolution;
sys_put_le16(mv, &svc[4]);
calibrated = true;
}
}
static bool measure(void)
{
if (read_sht41()) {
return false; /* keep advertising last good values */
}
read_battery(); /* right after sensor load */
return true;
}
int main(void)
{
adc_channel_setup_dt(&vbat);
bt_enable(NULL);
measure(); /* advertise even if the first read fails */
bt_le_adv_start(BT_LE_ADV_PARAM(BT_LE_ADV_OPT_USE_IDENTITY, ADV_INTERVAL,
ADV_INTERVAL, NULL),
ad, ARRAY_SIZE(ad), NULL, 0);
for (;;) {
k_sleep(MEAS_INTERVAL); /* CPU in System ON idle */
if (measure()) {
bt_le_adv_update_data(ad, ARRAY_SIZE(ad), NULL, 0);
}
}
}