another update add alarm config

This commit is contained in:
PoliEcho 2025-09-28 13:48:35 +02:00
parent 4e6396459a
commit ecfb7ef1bc
10 changed files with 407 additions and 27 deletions

View File

@ -40,6 +40,7 @@ add_executable(smart_alarm smart_alarm.cpp
sound.cpp
ui.cpp
fonts/Font5x7FixedMono.c
alarm.cpp
)
pico_set_program_name(smart_alarm "smart_alarm")

125
alarm.cpp Normal file
View File

@ -0,0 +1,125 @@
#include <array>
#include <cstdint>
#include <cstdlib>
#include <vector>
#include "alarm.h"
alarm::alarm(uint8_t hours, uint8_t minutes, const std::array<bool, 7> &days_enabled, bool every_other_week, bool even_week){
this->minutes_store = minutes;
// Initialize scheduled field
this->scheduled = 0;
// Set bits 0-6 for days of the week
for (int i = 0; i < 7; i++) {
if (days_enabled[i]) {
this->scheduled |= (1 << i);
}
}
// Set bit 7 for every other week functionality
if (every_other_week) {
this->scheduled |= (1 << 7);
}
// Initialize hours field
this->hours_and_stuff_store = 0;
// Set bit 0 for even/odd week (only relevant if every_other_week is true)
if (every_other_week && even_week) {
this->hours_and_stuff_store |= (1 << 0);
}
// Set bit 1 as disabled flag
this->hours_and_stuff_store &= ~(1 << 1);
// Bit 2 is reserved (left as 0)
// Set bits 3-7 with actual hours value (shift left by 3)
this->hours_and_stuff_store |= (hours & 0x1F) << 3; // 0x1F masks to 5 bits (0-31)
};
uint8_t alarm::minutes() const {
return minutes_store;
}
uint8_t alarm::hours() const {
return (hours_and_stuff_store >> 3) & 0x1F; // Extract bits 3-7 and mask to 5 bits
}
std::array<bool, 7> alarm::days_enabled() const {
std::array<bool, 7> days;
for (int i = 0; i < 7; i++) {
days[i] = (scheduled & (1 << i)) != 0;
}
return days;
}
bool alarm::every_other_week() const {
return (scheduled & (1 << 7)) != 0;
}
bool alarm::even_week() const {
return (hours_and_stuff_store & (1 << 0)) != 0;
}
bool alarm::enabled() const {
return (hours_and_stuff_store & (1 << 1)) != 0;
}
// Enable/disable methods
void alarm::enable() {
hours_and_stuff_store |= (1 << 1); // Set bit 1
}
void alarm::disable() {
hours_and_stuff_store &= ~(1 << 1); // Clear bit 1
}
void alarm::set_state(bool enabled) {
if (enabled) {
enable();
} else {
disable();
}
}
void alarm::set_minutes(uint8_t minutes) {
this->minutes_store = minutes;
}
void alarm::set_hours(uint8_t hours) {
this->hours_and_stuff_store &= ~(0x1F << 3); // Clear bits 3-7
this->hours_and_stuff_store |= (hours & 0x1F) << 3; // Set new hours
}
void alarm::set_days_enabled(const std::array<bool, 7>& days_enabled) {
this->scheduled &= (1 << 7); // Keep only bit 7 (every_other_week)
// Set new day bits
for (int i = 0; i < 7; i++) {
if (days_enabled[i]) {
this->scheduled |= (1 << i);
}
}
}
void alarm::set_every_other_week(bool every_other_week) {
if (every_other_week) {
this->scheduled |= (1 << 7); // Set bit 7
} else {
this->scheduled &= ~(1 << 7); // Clear bit 7
}
}
void alarm::set_even_week(bool even_week) {
if (even_week) {
this->hours_and_stuff_store |= (1 << 0); // Set bit 0
} else {
this->hours_and_stuff_store &= ~(1 << 0); // Clear bit 0
}
}
std::vector<alarm> alarms;

36
alarm.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef SMART_ALARM_PERSISTANCE_H
#define SMART_ALARM_PERSISTANCE_H
#include <array>
#include <cstdint>
#include <vector>
struct alarm
{
private:
uint8_t scheduled; // BITs used to mark days 1 ring on that day 0 dosn't ring on that day last bit marks if it should rind only every other week, if all BITs are 0 it will ring only once
uint8_t hours_and_stuff_store; // if every other week is selected it will check first bit of this if it is 1 it will ring on even weeks and if 0 it will ring on odd week next 1 is enabled flags, next BIT is reserved rest is hours
uint8_t minutes_store;
public:
alarm(uint8_t hours, uint8_t minutes, const std::array<bool, 7> &days_enabled, bool every_other_week, bool even_week);
[[nodiscard]] uint8_t minutes() const;
[[nodiscard]] uint8_t hours() const;
[[nodiscard]] std::array<bool, 7> days_enabled() const;
[[nodiscard]] bool every_other_week() const;
[[nodiscard]] bool even_week() const;
[[nodiscard]] bool enabled() const;
// Enable/disable methods
void enable();
void disable();
void set_state(bool enabled);
void set_minutes(uint8_t minutes);
void set_hours(uint8_t hours);
void set_days_enabled(const std::array<bool, 7>& days_enabled);
void set_every_other_week(bool every_other_week);
void set_even_week(bool even_week);
};
extern std::vector<alarm> alarms;
#endif //SMART_ALARM_PERSISTANCE_H

View File

@ -10,11 +10,17 @@
#include "pico/time.h"
#include "spi.h"
#include <cmath>
#include <cstring>
#include <stdio.h>
#include "Font5x7FixedMono.h"
#include "alarm.h"
#include "control.h"
#include "macros.h"
#include "multicore_events.h"
#include "hardware/rtc.h"
#include "pico/multicore.h"
int32_t t_fine;
uint16_t dig_T1;
@ -74,18 +80,25 @@ gc9a01_GC9A01_obj_t create_lcd() {
gc9a01_GC9A01_obj_t display;
void draw_ui_circle(uint16_t color)
{
for (int i = 720; i > 0; i--) {
int x = 120 + (int)(119.0 * sin((180.0 + i / 2.0) * M_TWOPI / 360.0));
int y = 120 + (int)(119.0 * cos((180.0 + i / 2.0) * M_TWOPI / 360.0));
void draw_circle(int16_t x_center, int16_t y_center, int16_t r,
uint16_t color) {
for (uint16_t i = 720; i > 0; i--) {
uint8_t x = x_center +
static_cast<int16_t>(static_cast<float>(r) *
sin((180.0 + i / 2.0) * M_TWOPI / 360.0));
uint8_t y = y_center +
static_cast<int16_t>(static_cast<float>(r) *
cos((180.0 + i / 2.0) * M_TWOPI / 360.0));
gc9a01_draw_pixel(&display, x, y, color);
}
}
void draw_ui_circle(uint16_t color) { draw_circle(120, 120, 119, color); }
std::array<uint16_t,2> get_ui_circle_vertical_pos(uint16_t x, uint16_t center_x, uint16_t center_y, uint16_t radius)
{
std::array<uint16_t, 2> get_ui_circle_vertical_pos(uint16_t x,
uint16_t center_x,
uint16_t center_y,
uint16_t radius) {
// Ctalculate horizontal distance from point to center
uint16_t dx = x - center_x;
uint16_t dx_squared = dx * dx;
@ -93,7 +106,7 @@ std::array<uint16_t,2> get_ui_circle_vertical_pos(uint16_t x, uint16_t center_x,
// Check if point's x-coordinate is within circle bounds
if (dx_squared >= radius_squared) {
return {0,0}; // Outside circle - no vertical space
return {0, 0}; // Outside circle - no vertical space
}
// Calculate vertical distance from center to circle edge at this x
@ -103,14 +116,12 @@ std::array<uint16_t,2> get_ui_circle_vertical_pos(uint16_t x, uint16_t center_x,
uint16_t y_top = center_y - (uint16_t)vertical_offset;
uint16_t y_bottom = center_y + (uint16_t)vertical_offset;
std::array<uint16_t,2> positions ={y_top,y_bottom};
std::array<uint16_t, 2> positions = {y_top, y_bottom};
return positions;
}
void clear_display() {
gc9a01_fill(&display, BLACK);
}
void clear_display() { gc9a01_fill(&display, BLACK); }
void display_start() {
char buf[10];
@ -121,31 +132,158 @@ void display_start() {
sleep_ms(100);
clear_display();
draw_ui_circle(MAGENTA);
}
void print_time(const bool force_refresh) {
datetime_t t;
if (rtc_get_datetime(&t)) {
const GFXfont* font = &VGA1_16x32;
const GFXfont *font = &VGA1_16x32;
char buf[11];
sprintf(buf,"%02d:%02d:%02d", t.hour, t.min, t.sec);
gc9a01_text(&display,font,buf,120-((16*8)/2),120-16,WHITE, BLACK);
sprintf(buf, "%02d:%02d:%02d", t.hour, t.min, t.sec);
gc9a01_text(&display, font, buf, 120 - ((16 * 8) / 2), 120 - 16, WHITE,
BLACK);
static uint8_t last_day = UINT8_MAX;
if (last_day != t.day || force_refresh) {
font = &Font5x7FixedMono;
sprintf(buf,"%02d.%02d.%04d",t.day,t.month,t.year);
gc9a01_text_gfx_buffered(&display,font,buf,120-((6*10)/2),((120-16)+32)+7,WHITE,BLACK);
sprintf(buf, "%02d.%02d.%04d", t.day, t.month, t.year);
gc9a01_text_gfx_buffered(&display, font, buf, 120 - ((6 * 10) / 2),
((120 - 16) + 32) + 7, WHITE, BLACK);
last_day = t.day;
}
} else {
const char* rtc_fail= "NO SYNC!";
const GFXfont* font = &Font5x7FixedMono;
gc9a01_text_gfx_buffered(&display,font,rtc_fail,120-((5*8)/2),120-((7*1)/2),RED,BLACK);
const char *rtc_fail = "NO SYNC!";
const GFXfont *font = &Font5x7FixedMono;
gc9a01_text_gfx_buffered(&display, font, rtc_fail, 120 - ((5 * 8) / 2),
120 - ((7 * 1) / 2), RED, BLACK);
}
}
void print_alarm(alarm *alarm, selected_t selected, uint8_t x, u_int8_t y) {
bool setting_sub = false;
uint8_t selected_item = UINT8_MAX;
do
{
uint8_t item_index = 0;
gc9a01_rect(&display, x, y, ALARM_BOX_WIDTH, ALARM_BOX_HEIGHT, selected);
char buf[3];
sprintf(buf, "%02d", alarm->hours());
if (selected_item == item_index) {
gc9a01_fill_rect(&display,((x + ALARM_BOX_WIDTH / 2) - ((strlen(buf) * 5) / 2)) - 2,y + 3,(5*2)+1,7,BLACK);
}
gc9a01_text_gfx_buffered(&display, &Font5x7FixedMono, buf,((x + ALARM_BOX_WIDTH / 2) - ((strlen(buf) * 5) / 2)) - 2, y + 10, selected_item == item_index ? setting_sub ? SETTING : SELECTED : WHITE,BLACK);
item_index++;
sprintf(buf, ":");
gc9a01_text_gfx_buffered(&display, &Font5x7FixedMono, buf,(((x + ALARM_BOX_WIDTH / 2) - ((strlen(buf) * 5) / 2)) - 2)+(5*2)-(5/2), y + 10, WHITE,BLACK);
sprintf(buf, "%02d", alarm->minutes());
if (selected_item == item_index) {
gc9a01_fill_rect(&display,(((x + ALARM_BOX_WIDTH / 2) - ((strlen(buf) * 5) / 2)) - 2)+5*3,y + 3,(5*2)+1,7,BLACK);
}
gc9a01_text_gfx_buffered(&display, &Font5x7FixedMono, buf,(((x + ALARM_BOX_WIDTH / 2) - ((strlen(buf) * 5) / 2)) - 2)+5*3, y + 10, selected_item == item_index ? setting_sub ? SETTING : SELECTED : WHITE,BLACK);
item_index++;
gc9a01_fill_rect(&display, x+ALARM_BOX_WIDTH-7,
y+2,5,7,alarm->enabled() ? GREEN : RED);
if (selected_item == item_index) {
gc9a01_rect(&display, x+ALARM_BOX_WIDTH-7,
y+2,5,7,alarm->enabled() ? RED : GREEN);
}
item_index++;
u_int8_t x_text_abrivs = x + (5 / 2);
{
std::array<bool, 7> days_enabled = alarm->days_enabled();
for (uint8_t i = 0; i < days_enabled.size(); i++) {
gc9a01_fill_rect(&display, x_text_abrivs,
(y + ALARM_BOX_HEIGHT) - 10,5,7,days_enabled[i] ? GREEN : RED);
if (selected_item == item_index) {
gc9a01_rect(&display, x_text_abrivs,
(y + ALARM_BOX_HEIGHT) - 10,5,7,days_enabled[i] ? RED : GREEN);
}
item_index++;
x_text_abrivs += 5 + 5 / 2;
}
}
if (alarm->every_other_week()) {
char c = '=';
if (selected_item == item_index) {
gc9a01_fill_rect(&display, x_text_abrivs,(y + ALARM_BOX_HEIGHT) - 7, 5, 1, BLACK);
}
gc9a01_text_gfx_buffered(&display, &Font5x7FixedMono,&c,x_text_abrivs,(y + ALARM_BOX_HEIGHT) - 3,selected_item == item_index ? SELECTED : MAGENTA,BLACK);
item_index++;
x_text_abrivs += 5 + 5 / 2;
c = alarm->even_week() ? '2' : '1';
if (selected_item == item_index && c == '1') {
gc9a01_fill_rect(&display, x_text_abrivs+4,(y + ALARM_BOX_HEIGHT) - 9,1,2,BLACK);
}
gc9a01_text_gfx_buffered(&display, &Font5x7FixedMono,&c,x_text_abrivs,(y + ALARM_BOX_HEIGHT) - 3,selected_item == item_index ? SELECTED : alarm->even_week() ? MAGENTA : CYAN,BLACK);
item_index++;
} else {
constexpr char c = '-';
if (selected_item == item_index) {
gc9a01_fill_rect(&display, x_text_abrivs,(y + ALARM_BOX_HEIGHT) - 8, 5, 3, BLACK);
}
gc9a01_text_gfx_buffered(&display, &Font5x7FixedMono,&c,x_text_abrivs,(y + ALARM_BOX_HEIGHT) - 3,selected_item == item_index ? SELECTED : CYAN,BLACK);
item_index++;
}
if (selected == selected_t::SETTING) {
switch (static_cast<multicore_event_t>(multicore_fifo_pop_blocking())) {
case PRIMARY_BUTTON_PRESSED:
if (setting_sub) {
setting_sub = false;
break;
}
switch (selected_item) {
case 0: // hours
case 1: // minutes
setting_sub = true;
break;
case 2: // enable/disable
alarm->set_state(!alarm->enabled());
break;
case 10: // every other week
alarm->set_every_other_week(!alarm->every_other_week());
break;
case 11: // even week
alarm->set_even_week(!alarm->even_week());
break;
default: // days of the week
std::array<bool, 7> days_enabled = alarm->days_enabled();
days_enabled[selected_item-3] = !days_enabled[selected_item-3];
alarm->set_days_enabled(days_enabled);
}
break;
case SECONDARY_BUTTON_PRESSED:
goto exit_print_alarm;
case KNOB_CHANGE: {
if (!setting_sub)
{selected_item = std::min(static_cast<uint8_t>(static_cast<float>(get_knob_percentage())/100.0f*static_cast<float>(item_index)),static_cast<uint8_t>(item_index-1));} else {
switch (selected_item) {
case 0:
alarm->set_hours(std::min(static_cast<uint8_t>(static_cast<float>(get_knob_percentage())/100.0f*static_cast<float>(24)),static_cast<uint8_t>(23)));
break;
case 1:
alarm->set_minutes(std::min(static_cast<uint8_t>(static_cast<float>(get_knob_percentage())/100.0f*static_cast<float>(60)),static_cast<uint8_t>(59)));
break;
default:
printf("ERROR: invalid option selected!!!\n");
}}
}
break;
}
}
} while (selected == selected_t::SETTING);
exit_print_alarm:
return; // fix waring about C23 extension
}
void print_add_button(uint8_t center_x, uint8_t center_y, uint8_t w, uint16_t color) {
draw_circle(center_x, center_y, w/2, color);
const uint8_t plus_length = w - 7;
const uint8_t half_length = plus_length / 2;
gc9a01_hline(&display, center_x - half_length, center_y, plus_length, color);
gc9a01_vline(&display, center_x, center_y - half_length, plus_length, color);
}

View File

@ -3,6 +3,7 @@
#include <array>
#include <cstdint>
#include "alarm.h"
#include "gc9a01.h"
void display_start();
void print_time(bool force_refresh = false);
@ -12,6 +13,16 @@ std::array<uint16_t,2> get_ui_circle_vertical_pos(uint16_t x, uint16_t center_x
extern gc9a01_GC9A01_obj_t display;
enum selected_t {
NOT_SELECTED = WHITE,
SELECTED = GREEN,
SETTING = RED
};
void print_alarm(alarm* alarm, selected_t selected, uint8_t x,u_int8_t y);
void print_add_button(uint8_t center_x, uint8_t center_y,uint8_t w, uint16_t color);
#define MAX_X 240
#define MAX_Y 240
#define ALARM_BOX_WIDTH 70
#define ALARM_BOX_HEIGHT 30
#endif //SMART_ALARM_DISPLAY_H

1
fonts/SOURCE Normal file
View File

@ -0,0 +1 @@
https://github.com/robjen/GFX_fonts

4
gen_sound.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
xxd -i alarm_sound.wav > alarm_sound.h
sed -i 's/unsigned/inline constexpr unsigned/g' alarm_sound.h
sed -i '1i\#pragma once\' alarm_sound.h

2
pins.h
View File

@ -9,3 +9,5 @@
#define BUTTON_SECONDARY_PW 7
#define BUTTON_SECONDARY_IN 6
#define LED_PIN 5

View File

@ -117,6 +117,8 @@ int main() {
setup_pwm_audio();
timezone_index = 353; // todo load from flash
gpio_init(LED_PIN);
gpio_set_dir(LED_PIN, GPIO_OUT);
// sign of life
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, true);

62
ui.cpp
View File

@ -171,7 +171,67 @@ void time_page() {
}
}
void alarm_set_page() { draw_ui_circle(BLUE); }
static uint16_t get_alarm_x(uint8_t index) {
return (index % 2 == 0) ? 40 : (MAX_X - 40 - ALARM_BOX_WIDTH);
}
static uint8_t get_alarm_y(uint8_t index) {
return 40 + (index / 2) * (ALARM_BOX_HEIGHT + 5);
}
[[noreturn]] void alarm_set_page() {
draw_ui_circle(BLUE);
full_redraw:
multicore_fifo_drain();
uint8_t y_pos = 40;
uint8_t selected_index = static_cast<uint8_t>(static_cast<float>(get_knob_percentage())/100.0f*static_cast<float>(alarms.size()));
for (uint8_t i = 0; i < alarms.size(); i++) {
print_alarm(&alarms[i], i == selected_index ? SELECTED : NOT_SELECTED, !(i % 2) ? 40 : MAX_X-40-ALARM_BOX_WIDTH,y_pos);
if (i % 2) {
y_pos += ALARM_BOX_HEIGHT+5;
}
}
print_add_button(MAX_X/2,MAX_Y-14,20,selected_index == alarms.size() ? SELECTED : NOT_SELECTED);
while (true) {
switch (static_cast<multicore_event_t>(multicore_fifo_pop_blocking())) {
case PRIMARY_BUTTON_PRESSED:
if (selected_index == alarms.size()) {
alarms.emplace_back(0, 0, std::array<bool, 7>{false, false, false, false, false, false, false}, false, false);
goto full_redraw;
} else {
secondary_button_override = true;
print_alarm(&alarms[selected_index], SETTING,get_alarm_x(selected_index),get_alarm_y(selected_index));
secondary_button_override = false;
goto full_redraw;
}
break;
case SECONDARY_BUTTON_PRESSED:
break;
case KNOB_CHANGE: {
uint8_t new_index = static_cast<uint8_t>(static_cast<float>(get_knob_percentage())/100.0f*static_cast<float>(alarms.size()));
if (new_index != selected_index) {
if (selected_index < alarms.size()) {
print_alarm(&alarms[selected_index], NOT_SELECTED,get_alarm_x(selected_index),get_alarm_y(selected_index));
} else {
print_add_button(MAX_X/2,MAX_Y-14,20,NOT_SELECTED);
}
if (new_index >= alarms.size()) {
print_add_button(MAX_X/2,MAX_Y-14,20,SELECTED);
} else {
print_alarm(&alarms[new_index], SELECTED,get_alarm_x(new_index),get_alarm_y(new_index));
}
selected_index = new_index;
}
break;
}
}
multicore_fifo_drain(); // prevent event stucking
}
}
std::array<void (*)(), 3> page_functions = {config_page, time_page,
alarm_set_page};