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

WIP: Add LED Alerts #16

Open
wants to merge 4 commits 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
3 changes: 2 additions & 1 deletion app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

project(app LANGUAGES C VERSION 0.1.0)
project(app LANGUAGES C VERSION 0.2.1)

configure_file(app_version.h.in ${CMAKE_BINARY_DIR}/app/include/app_version.h)
target_include_directories(app PRIVATE ${CMAKE_BINARY_DIR}/app/include src)
Expand All @@ -22,6 +22,7 @@ set(PYRRHA_SOURCES
src/bluetooth.c
src/storage.c
src/timestamp.c
src/alerts.c
)
target_include_directories(app PRIVATE include src)
target_sources(app PRIVATE ${PYRRHA_SOURCES})
26 changes: 26 additions & 0 deletions app/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ module = PYRRHA
module-str = PYRRHA
source "subsys/logging/Kconfig.template.log_config"

module = ALERTS
module-str = ALERTS
source "subsys/logging/Kconfig.template.log_config"

menu "pyrrha threads"
menu "stacks"
config PYRRHA_COLLECTION_STACKSIZE
Expand Down Expand Up @@ -56,6 +60,28 @@ config PYRRHA_FCB_MAGIC
Magic 32-bit word for to identify valid settings area
endmenu

menu "alerts"

config CO_ALERT_THRESHOLD_PPM
int "Threshold in ppm for co alerts"
range 1 1000
default 420
help
Threshold in PPM for CO level when an alert is generated.
420ppm is the AEGL2 10 minute limit.
config NO2_ALERT_THRESHOLD_PPM
int "Threshold in ppm for no2 alerts"
range 1 100
default 8
help
Threshold in PPM for NO2 level when an alert is generated.
20ppm is the AEGL2 10 minute limit.
config TEMPERATURE_ALERT_THRESHOLD_CELCIUS
int "Threshold in celcius for temperature alerts"
range 60 120
default 80
endmenu

config PYRRHA_SAMPLE_PERIOD
int "Period (in seconds) of sensor data collection"
range 1 300
Expand Down
27 changes: 27 additions & 0 deletions app/boards/nrf52840dk_nrf52840.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@
};
};

&pwm0 {
status = "okay";
ch0-pin = <13>;
ch0-inverted;
ch1-pin = <14>;
ch1-inverted;
ch2-pin = <15>;
ch2-inverted;
};

/ {
gas_sensor: mics4514 {
label = "MICS4514";
Expand All @@ -35,4 +45,21 @@
rload-red-ohms = <47000>;
rload-ox-ohms = <22000>;
};
pwmleds {
compatible = "pwm-leds";
pwm_led0: pwm_led_0 {
pwms = <&pwm0 13>;
};
pwm_led1: pwm_led_1 {
pwms = <&pwm0 14>;
};
pwm_led2: pwm_led_2 {
pwms = <&pwm0 15>;
};
};
aliases {
red-pwm-led = &pwm_led0;
green-pwm-led = &pwm_led1;
blue-pwm-led = &pwm_led2;
};
};
1 change: 1 addition & 0 deletions app/configs/debug.conf
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ CONFIG_UART_CONSOLE=y
CONFIG_LOG=y
CONFIG_LOG_MODE_IMMEDIATE=y
CONFIG_PYRRHA_LOG_LEVEL_DBG=y
CONFIG_ALERTS_LOG_LEVEL_DBG=y
CONFIG_SENSOR_LOG_LEVEL_DBG=y
CONFIG_I2C_LOG_LEVEL_DBG=y

Expand Down
48 changes: 48 additions & 0 deletions app/include/alerts.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* @file alerts.h
* @author Brian Bradley (brian.bradley.p@gmail.com)
* @date 2022-01-26
*
* @copyright Copyright (C) 2022 LION
*
* SPDX-License-Identifier: Apache-2.0
*
*/


#ifndef __PYRRHA_BLUETOOTH_H
#define __PYRRHA_BLUETOOTH_H

#include <collector.h>

/**
* @brief Check for and generate any alerts necessary based on
* sensor data
*
* @param data : sensor data used to match alert conditions
* @retval 0 on success
* @retval -ENOLCK if alert mutex could not be locked within 250ms
* @retval -errno otherwise
*/
int generate_alerts(struct pyrrha_data * data);

/**
* @brief Override the current alert condition.
* This will block local alert generation until a release is issued.
*
* @param color : hex representation of led color used in alarm
* @retval 0 on success
* @retval -ENOLCK if alert mutex could not be locked within 250ms
*/
int override_alert(uint32_t color);

/**
* @brief Release the current override for the LED. For instance,
* if the host device disconnects and can no longer control the alarm
*
* @retval 0 on success
* @retval -ENOLCK if alert mutex could not be locked within 250ms
*/
int release_alert_override();

#endif
1 change: 1 addition & 0 deletions app/prj.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ CONFIG_JSON_LIBRARY=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_FCB=y
CONFIG_PWM=y
172 changes: 172 additions & 0 deletions app/src/alerts.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/**
* @file alerts.c
* @author Brian Bradley (brian.bradley.p@gmail.com)
* @brief
* @date 2022-01-26
*
* @copyright Copyright (C) 2022 LION
*
* SPDX-License-Identifier: Apache-2.0
*
*/

#include <alerts.h>
#include <logging/log.h>
#include <zephyr.h>
#include <device.h>
#include <drivers/pwm.h>

LOG_MODULE_REGISTER(alerts, CONFIG_PYRRHA_LOG_LEVEL);

#define OVER_TEMPERATURE(temp) (temp > CONFIG_TEMPERATURE_ALERT_THRESHOLD_CELCIUS)
#define OVER_CO(co) (co > CONFIG_CO_ALERT_THRESHOLD_PPM)
#define OVER_NO2(no2) (no2 > CONFIG_NO2_ALERT_THRESHOLD_PPM)

/*
* 50hz is flicker fusion threshold. Modulated light will be perceived
* as steady by our eyes when blinking rate is at least 50.
*/
#define PERIOD_USEC (USEC_PER_SEC / 50U)

#define RED_LED_NODE DT_ALIAS(red_pwm_led)
#define GREEN_LED_NODE DT_ALIAS(green_pwm_led)
#define BLUE_LED_NODE DT_ALIAS(blue_pwm_led)

#define COLOR_TO_PULSE(color, period) (color == UINT8_MAX ? (period) : (color * (period) >> 8))

/**
* @brief union which allows easy set/conversion to hex or rgb directly
*
*/
typedef union Color{
uint32_t hex;
struct{
uint8_t red;
uint8_t green;
uint8_t blue;
};
}color_t;

/**
* @brief Configuration table for pwm led
*
*/
struct pwm_cfg{
const struct device * pwm;
const uint8_t pin;
const uint8_t flags;
const uint32_t period;
};

/**
* @brief Container for all pwm leds associated with the rgb led
*
*/
struct rgb_led_device{
const struct pwm_cfg red;
const struct pwm_cfg green;
const struct pwm_cfg blue;
};

K_MUTEX_DEFINE(mtx_alert);
static color_t g_alert_override = {0};

const struct rgb_led_device g_led = {
.red = {
.pwm = DEVICE_DT_GET(DT_PWMS_CTLR(RED_LED_NODE)),
.pin = DT_PWMS_CHANNEL(RED_LED_NODE),
.flags = DT_PWMS_FLAGS(RED_LED_NODE),
.period = PERIOD_USEC
},
.green = {
.pwm = DEVICE_DT_GET(DT_PWMS_CTLR(GREEN_LED_NODE)),
.pin = DT_PWMS_CHANNEL(GREEN_LED_NODE),
.flags = DT_PWMS_FLAGS(GREEN_LED_NODE),
.period = PERIOD_USEC
},
.blue = {
.pwm = DEVICE_DT_GET(DT_PWMS_CTLR(BLUE_LED_NODE)),
.pin = DT_PWMS_CHANNEL(BLUE_LED_NODE),
.flags = DT_PWMS_FLAGS(BLUE_LED_NODE),
.period = PERIOD_USEC
},
};

/**
* @brief Set pwm output directly to the coresponding 8-bit color value
*
* @param cfg : configuration table for pwm device
* @param color : 8-bit color value for the specified pwm
* @retval 0 on success
* @retval -ENODEV if no configuration table
* @retval -EFAULT if pwm set error
*/
static int set_pwm(const struct pwm_cfg * cfg, uint8_t color)
{
if (cfg == NULL){
return -ENODEV;
}
const struct device *dev = cfg->pwm;
uint32_t pulse = COLOR_TO_PULSE(color, cfg->period);
/* Verify pwm_pin_set_usec() */
if (pwm_pin_set_usec(dev, cfg->pin, cfg->period, pulse, cfg->flags)) {
LOG_ERR("Fail to set the period and pulse width");
return -EFAULT;
}
return 0;
}

/**
* @brief Set the alert led to a specific color
*
* @param color : rgb representation of desired color
* @retval 0 on success
* @retval -errno otherwise
*/
static int set_alert_led(color_t color){
set_pwm(&g_led.red, color.red);
set_pwm(&g_led.green, color.green);
set_pwm(&g_led.blue, color.blue);
return 0;
}

/**
* @brief generate an alert from sensor data exceeding set thresholds
*/
static void threshold_alert(void){
const color_t alert_color = {
.red = 0xff
};
set_alert_led(alert_color);
}

int override_alert(uint32_t color){
if (k_mutex_lock(&mtx_alert, K_MSEC(250)) != 0){
return -ENOLCK;
}
g_alert_override.hex = color;
set_alert_led(g_alert_override);
k_mutex_unlock(&mtx_alert);
return 0;
}

int release_alert_override(){
return override_alert(0);
}

int generate_alerts(struct pyrrha_data * data){
if (k_mutex_lock(&mtx_alert, K_MSEC(250)) != 0){
return -ENOLCK;
}
/* Only generate an alert when there is no override condition */
if (g_alert_override.hex == 0){
if (OVER_TEMPERATURE(data->rht.temperature.val1) ||
OVER_CO(data->gas.co.val1) ||
OVER_NO2(data->gas.no2.val1))
{
threshold_alert();
}
}
k_mutex_unlock(&mtx_alert);
return 0;
}
6 changes: 6 additions & 0 deletions app/src/collector.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <collector.h>
#include <messenger.h>
#include <timestamp.h>
#include <alerts.h>
LOG_MODULE_REGISTER(collector, CONFIG_PYRRHA_LOG_LEVEL);

void data_collection_process(void){
Expand All @@ -25,7 +26,12 @@ void data_collection_process(void){
data.err |= (capture_rht_data(&data.rht) != 0 ? ERR_RHT_SENSOR : 0);
data.timestamp = get_timestamp();
capture_rht_data(&data.rht);

/* Queue data for storage and / or notification to a host */
queue_sensor_data(&data);
/* Process any alerts based on current sample data */
generate_alerts(&data);

k_sleep(K_SECONDS(CONFIG_PYRRHA_SAMPLE_PERIOD));
}
}
Expand Down