Initial commit
This commit is contained in:
commit
e8b9cd5ace
7 changed files with 577 additions and 0 deletions
154
src/battery-vdd.c
Normal file
154
src/battery-vdd.c
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019 Peter Bigot Consulting, LLC
|
||||
* Copyright (c) 2019-2020 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/adc.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include "battery.h"
|
||||
|
||||
LOG_MODULE_REGISTER(BATTERYVDD, CONFIG_ADC_LOG_LEVEL);
|
||||
|
||||
#define ZEPHYR_USER DT_PATH(zephyr_user)
|
||||
|
||||
/* Other boards may use dividers that only reduce battery voltage to
|
||||
* the maximum supported by the hardware (3.6 V)
|
||||
*/
|
||||
#define BATTERY_ADC_GAIN ADC_GAIN_1_6
|
||||
|
||||
struct io_channel_config {
|
||||
uint8_t channel;
|
||||
};
|
||||
|
||||
struct divider_config {
|
||||
struct io_channel_config io_channel;
|
||||
struct gpio_dt_spec power_gpios;
|
||||
/* output_ohm is used as a flag value: if it is nonzero then
|
||||
* the battery is measured through a voltage divider;
|
||||
* otherwise it is assumed to be directly connected to Vdd.
|
||||
*/
|
||||
uint32_t output_ohm;
|
||||
uint32_t full_ohm;
|
||||
};
|
||||
|
||||
static const struct divider_config dividervdd_config = {
|
||||
.io_channel = {
|
||||
DT_IO_CHANNELS_INPUT(ZEPHYR_USER),
|
||||
},
|
||||
};
|
||||
|
||||
struct divider_data {
|
||||
const struct device *adc;
|
||||
struct adc_channel_cfg adc_cfg;
|
||||
struct adc_sequence adc_seq;
|
||||
int16_t raw;
|
||||
};
|
||||
static struct divider_data dividervdd_data = {
|
||||
.adc = DEVICE_DT_GET(DT_IO_CHANNELS_CTLR(ZEPHYR_USER)),
|
||||
};
|
||||
|
||||
static int dividervdd_setup(void)
|
||||
{
|
||||
const struct divider_config *cfg = ÷rvdd_config;
|
||||
const struct io_channel_config *iocp = &cfg->io_channel;
|
||||
const struct gpio_dt_spec *gcp = &cfg->power_gpios;
|
||||
struct divider_data *ddp = ÷rvdd_data;
|
||||
struct adc_sequence *asp = &ddp->adc_seq;
|
||||
struct adc_channel_cfg *accp = &ddp->adc_cfg;
|
||||
int rc;
|
||||
|
||||
if (!device_is_ready(ddp->adc)) {
|
||||
LOG_ERR("ADC device is not ready %s", ddp->adc->name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (gcp->port) {
|
||||
if (!device_is_ready(gcp->port)) {
|
||||
LOG_ERR("%s: device not ready", gcp->port->name);
|
||||
return -ENOENT;
|
||||
}
|
||||
rc = gpio_pin_configure_dt(gcp, GPIO_OUTPUT_INACTIVE);
|
||||
if (rc != 0) {
|
||||
LOG_ERR("Failed to control feed %s.%u: %d",
|
||||
gcp->port->name, gcp->pin, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
*asp = (struct adc_sequence){
|
||||
.channels = BIT(0),
|
||||
.buffer = &ddp->raw,
|
||||
.buffer_size = sizeof(ddp->raw),
|
||||
.oversampling = 4,
|
||||
.calibrate = true,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ADC_NRFX_SAADC
|
||||
*accp = (struct adc_channel_cfg){
|
||||
.gain = BATTERY_ADC_GAIN,
|
||||
.reference = ADC_REF_INTERNAL,
|
||||
.acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40),
|
||||
};
|
||||
|
||||
accp->input_positive = SAADC_CH_PSELP_PSELP_VDD;
|
||||
|
||||
asp->resolution = 14;
|
||||
#else /* CONFIG_ADC_var */
|
||||
#error Unsupported ADC
|
||||
#endif /* CONFIG_ADC_var */
|
||||
|
||||
rc = adc_channel_setup(ddp->adc, accp);
|
||||
LOG_INF("Setup VDD AIN%u got %d", iocp->channel, rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool battery_ok;
|
||||
|
||||
int batteryvdd_setup()
|
||||
{
|
||||
int rc = dividervdd_setup();
|
||||
|
||||
battery_ok = (rc == 0);
|
||||
LOG_INF("Battery VDD setup: %d %d", rc, battery_ok);
|
||||
return rc;
|
||||
}
|
||||
|
||||
//SYS_INIT(batteryvdd_setup, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||
|
||||
int16_t batteryvdd_sample(void)
|
||||
{
|
||||
int16_t rc = -ENOENT;
|
||||
|
||||
if (battery_ok) {
|
||||
struct divider_data *ddp = ÷rvdd_data;
|
||||
//const struct divider_config *dcp = ÷rvdd_config;
|
||||
struct adc_sequence *sp = &ddp->adc_seq;
|
||||
|
||||
rc = adc_read(ddp->adc, sp);
|
||||
sp->calibrate = false;
|
||||
if (rc == 0) {
|
||||
int32_t val = ddp->raw;
|
||||
|
||||
adc_raw_to_millivolts(adc_ref_internal(ddp->adc),
|
||||
ddp->adc_cfg.gain,
|
||||
sp->resolution,
|
||||
&val);
|
||||
rc = val;
|
||||
LOG_INF("raw %u ~ %u mV", ddp->raw, val);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
56
src/battery.h
Normal file
56
src/battery.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2019 Peter Bigot Consulting, LLC
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef APPLICATION_BATTERY_H_
|
||||
#define APPLICATION_BATTERY_H_
|
||||
|
||||
/** Enable or disable measurement of the battery voltage.
|
||||
*
|
||||
* @param enable true to enable, false to disable
|
||||
*
|
||||
* @return zero on success, or a negative error code.
|
||||
*/
|
||||
int battery_measure_enable(bool enable);
|
||||
|
||||
|
||||
int batteryvdd_setup();
|
||||
|
||||
/** Measure the battery voltage.
|
||||
*
|
||||
* @return the battery voltage in millivolts, or a negative error
|
||||
* code.
|
||||
*/
|
||||
int16_t batteryvdd_sample(void);
|
||||
|
||||
/** A point in a battery discharge curve sequence.
|
||||
*
|
||||
* A discharge curve is defined as a sequence of these points, where
|
||||
* the first point has #lvl_pptt set to 10000 and the last point has
|
||||
* #lvl_pptt set to zero. Both #lvl_pptt and #lvl_mV should be
|
||||
* monotonic decreasing within the sequence.
|
||||
*/
|
||||
struct battery_level_point {
|
||||
/** Remaining life at #lvl_mV. */
|
||||
uint16_t lvl_pptt;
|
||||
|
||||
/** Battery voltage at #lvl_pptt remaining life. */
|
||||
uint16_t lvl_mV;
|
||||
};
|
||||
|
||||
/** Calculate the estimated battery level based on a measured voltage.
|
||||
*
|
||||
* @param batt_mV a measured battery voltage level.
|
||||
*
|
||||
* @param curve the discharge curve for the type of battery installed
|
||||
* on the system.
|
||||
*
|
||||
* @return the estimated remaining capacity in parts per ten
|
||||
* thousand.
|
||||
*/
|
||||
uint16_t battery_level_pptt(uint16_t batt_mV,
|
||||
const struct battery_level_point *curve);
|
||||
|
||||
#endif /* APPLICATION_BATTERY_H_ */
|
||||
229
src/main.c
Normal file
229
src/main.c
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/* main.c - Application main entry point */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2015-2016 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sys/errno.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/bluetooth/services/nus.h>
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(main);
|
||||
|
||||
#include "battery.h"
|
||||
|
||||
#define GPIO_PORT DT_NODELABEL(gpio0)
|
||||
static struct gpio_callback pir_cb_data;
|
||||
|
||||
bool btconnectable = true;
|
||||
|
||||
#define STACKSIZE 2048
|
||||
#define PRIORITY 7
|
||||
|
||||
#define SIZE_BTHOME 8
|
||||
static uint8_t mfg_bthome_data[SIZE_BTHOME] = { 0xd2, 0xfc, 0x40, 0x0c, 0x00, 0x00, 0x21, 0x00 };
|
||||
|
||||
struct bt_data ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
|
||||
BT_DATA(BT_DATA_SVC_DATA16, mfg_bthome_data, SIZE_BTHOME),
|
||||
};
|
||||
|
||||
#define BT_UUID_SMP_VAL BT_UUID_128_ENCODE(0x84aa6074, 0x528a, 0x8b86, 0xd34c, 0xb71d1ddc538d)
|
||||
|
||||
static const struct bt_data sd[] = {
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL,
|
||||
//BT_UUID_NUS_SRV_VAL,
|
||||
BT_UUID_SMP_VAL),
|
||||
};
|
||||
static void work_publish_adv_handler(struct k_work *work)
|
||||
{
|
||||
btconnectable = false;
|
||||
|
||||
bt_le_adv_stop();
|
||||
bt_le_adv_start(BT_LE_ADV_PARAM(BT_LE_ADV_OPT_USE_IDENTITY,BT_GAP_ADV_SLOW_INT_MIN, BT_GAP_ADV_SLOW_INT_MAX, NULL), ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
|
||||
}
|
||||
|
||||
K_WORK_DEFINE(work_publish_adv, work_publish_adv_handler);
|
||||
|
||||
void timer_publish_adv_handler(struct k_timer *stm)
|
||||
{
|
||||
k_work_submit(&work_publish_adv);
|
||||
}
|
||||
K_TIMER_DEFINE(timer_publish_adv, timer_publish_adv_handler, NULL);
|
||||
|
||||
|
||||
void bt_ready(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
printk("Bluetooth initialized\n");
|
||||
|
||||
/*if (IS_ENABLED(CONFIG_SETTINGS)) {
|
||||
settings_load();
|
||||
}*/
|
||||
|
||||
|
||||
// err = bt_le_adv_start(BT_LE_ADV_PARAM(BT_LE_ADV_OPT_USE_IDENTITY | BT_LE_ADV_OPT_USE_NAME, BT_GAP_ADV_SLOW_INT_MIN,BT_GAP_ADV_SLOW_INT_MAX, NULL), ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
err = bt_le_adv_start(BT_LE_ADV_PARAM(BT_LE_ADV_OPT_CONNECTABLE,BT_GAP_ADV_SLOW_INT_MIN, BT_GAP_ADV_SLOW_INT_MAX, NULL), ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
|
||||
if (err) {
|
||||
printk("Advertising failed to start (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Advertising successfully started\n");
|
||||
|
||||
k_timer_start(&timer_publish_adv, K_SECONDS(60), K_NO_WAIT);
|
||||
}
|
||||
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t err)
|
||||
{
|
||||
if (err) {
|
||||
LOG_ERR("Connection failed (err 0x%02x)", err);
|
||||
} else {
|
||||
LOG_WRN("Connected");
|
||||
k_timer_stop(&timer_publish_adv);
|
||||
}
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
LOG_WRN("Disconnected (reason 0x%02x)", reason);
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
void update_adv()
|
||||
{
|
||||
if(btconnectable == true)
|
||||
{
|
||||
bt_le_adv_update_data(ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
|
||||
} else {
|
||||
bt_le_adv_update_data(ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*void print_heap_info(void)
|
||||
{
|
||||
extern struct k_heap _system_heap;
|
||||
struct sys_memory_stats stats;
|
||||
sys_heap_runtime_stats_get(&_system_heap.heap, &stats);
|
||||
|
||||
printk("\n");
|
||||
printk("INFO: Allocated Heap = %zu\n", stats.allocated_bytes);
|
||||
printk("INFO: Free Heap = %zu\n", stats.free_bytes);
|
||||
printk("INFO: Max Allocated Heap = %zu\n", stats.max_allocated_bytes);
|
||||
printk("\n");
|
||||
|
||||
return;
|
||||
}*/
|
||||
|
||||
static void work_battery_handler(struct k_work *work)
|
||||
{
|
||||
uint16_t batt_mV = (uint16_t) batteryvdd_sample();
|
||||
LOG_INF("Battery %d mV", batt_mV);
|
||||
mfg_bthome_data[4] = (uint8_t) batt_mV;
|
||||
mfg_bthome_data[5] = (uint8_t) (batt_mV >> 8);
|
||||
update_adv();
|
||||
}
|
||||
K_WORK_DEFINE(work_battery, work_battery_handler);
|
||||
void timer_battery_handler(struct k_timer *stm)
|
||||
{
|
||||
k_work_submit(&work_battery);
|
||||
}
|
||||
K_TIMER_DEFINE(timer_battery, timer_battery_handler, NULL);
|
||||
|
||||
static void work_piroff_handler(struct k_work *work)
|
||||
{
|
||||
//const struct device *dev = DEVICE_DT_GET(GPIO_PORT);
|
||||
//uint8_t state = (uint8_t) gpio_pin_get(dev, 5);
|
||||
//LOG_INF("PIR changed to %d", state);
|
||||
LOG_INF("Reset motion");
|
||||
mfg_bthome_data[7] = 0;
|
||||
update_adv();
|
||||
}
|
||||
K_WORK_DEFINE(work_piroff, work_piroff_handler);
|
||||
|
||||
void timer_pir_handler(struct k_timer *stm)
|
||||
{
|
||||
k_work_submit(&work_piroff);
|
||||
}
|
||||
K_TIMER_DEFINE(timer_pir, timer_pir_handler, NULL);
|
||||
|
||||
static void work_piron_handler(struct k_work *work)
|
||||
{
|
||||
//const struct device *dev = DEVICE_DT_GET(GPIO_PORT);
|
||||
//uint8_t state = (uint8_t) gpio_pin_get(dev, 5);
|
||||
LOG_INF("Motion detected");
|
||||
mfg_bthome_data[7] = 1;
|
||||
update_adv();
|
||||
k_timer_stop(&timer_pir);
|
||||
k_timer_start(&timer_pir, K_SECONDS(60), K_NO_WAIT);
|
||||
}
|
||||
K_WORK_DEFINE(work_piron, work_piron_handler);
|
||||
|
||||
|
||||
void pir_changed(const struct device *dev, struct gpio_callback *cb,
|
||||
uint32_t pins)
|
||||
{
|
||||
k_work_submit(&work_piron);
|
||||
}
|
||||
|
||||
void init_gpio_pir()
|
||||
{
|
||||
const struct device *dev = DEVICE_DT_GET(GPIO_PORT);
|
||||
gpio_pin_configure(dev, 5, GPIO_INPUT);
|
||||
gpio_pin_interrupt_configure(dev, 5, GPIO_INT_EDGE_RISING);
|
||||
LOG_INF("PIR starting state %d", gpio_pin_get(dev, 5));
|
||||
gpio_init_callback(&pir_cb_data, pir_changed, BIT(5));
|
||||
gpio_add_callback(dev, &pir_cb_data);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
// k_msleep(250);
|
||||
// k_work_submit(&work_battery);
|
||||
// k_timer_start(&timer_battery, K_SECONDS(3600), K_SECONDS(3600));
|
||||
|
||||
int err = bt_enable(NULL);
|
||||
if (err) {
|
||||
LOG_ERR("Bluetooth init failed (err %d)", err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bt_ready();
|
||||
|
||||
init_gpio_pir();
|
||||
|
||||
batteryvdd_setup();
|
||||
|
||||
k_work_submit(&work_battery);
|
||||
k_timer_start(&timer_battery, K_SECONDS(3600), K_SECONDS(3600));
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue