Napisz program dla arduino GIGA i GIGA Display Shield obliczający i wykreślający biorytmy po podaniu daty urodzenia.
Do wykorzystania:
#include "Arduino_H7_Video.h"
#include "lvgl.h"
#include "Arduino_GigaDisplayTouch.h"
Arduino_GigaDisplayTouch TouchDetector;
#include "Arduino_GigaDisplay_GFX.h"
Skopiuj do nowego szkicu i skompiluj dla „Arduino GIGA R1 WiFi”.
#include "Arduino_H7_Video.h"
#include "lvgl.h"
#include "Arduino_GigaDisplayTouch.h"
#include "Arduino_GigaDisplay_GFX.h"
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
// Upewnij się, że w lv_conf.h jest: #define LV_COLOR_DEPTH 16
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
// Sprzęt
Arduino_H7_Video Display(800, 480, GigaDisplayShield);
Arduino_GigaDisplayTouch TouchDetector;
// Parametry ekranu
static const uint16_t SCREEN_W = 800;
static const uint16_t SCREEN_H = 480;
// Buffery LVGL (ok. 60 linii)
static lv_color_t buf1[SCREEN_W * 60];
static lv_color_t buf2[SCREEN_W * 60];
static lv_disp_draw_buf_t draw_buf;
// Obiekty LVGL
static lv_obj_t *ta_day, *ta_month, *ta_year;
static lv_obj_t *btn_calc, *lbl_status, *lbl_today, *lbl_vals;
static lv_obj_t *chart;
static lv_chart_series_t *ser_phys, *ser_emot, *ser_intl;
// --- Funkcje pomocnicze daty i czasu ----------------------------------------
static long days_from_civil(int y, unsigned m, unsigned d) {
// Howard Hinnant’s algorithm: dni od 1970-01-01 (może być ujemne)
y -= (m <= 2);
const int era = (y >= 0 ? y : y - 399) / 400;
const unsigned yoe = (unsigned)(y - era * 400); // [0, 399]
const unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; // [0, 365]
const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
return era * 146097 + (int)doe - 719468;
}
static bool is_leap(int y) {
return ( (y % 4 == 0) && (y % 100 != 0) ) || (y % 400 == 0);
}
static int mdays(int y, int m) {
static const int md[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
if(m == 2) return md[1] + (is_leap(y) ? 1 : 0);
return md[m-1];
}
static bool valid_date(int y, int m, int d) {
if(y < 1900 || y > 2099) return false;
if(m < 1 || m > 12) return false;
if(d < 1 || d > mdays(y,m)) return false;
return true;
}
static void get_today(int &y, int &m, int &d) {
// 1) RTC/OS (jeśli dostępny)
time_t now = time(nullptr);
if(now > 0) {
struct tm *lt = localtime(&now);
if(lt) {
y = lt->tm_year + 1900;
m = lt->tm_mon + 1;
d = lt->tm_mday;
return;
}
}
// 2) Fallback: data kompilacji (__DATE__)
// Format: "Mmm dd yyyy"
char mon_str[4] = {0};
int dd = 1, yyyy = 2026;
sscanf(__DATE__, "%3s %d %d", mon_str, &dd, &yyyy);
const char *months = "JanFebMarAprMayJunJulAugSepOctNovDec";
const char *p = strstr(months, mon_str);
int mm = p ? ((int)(p - months) / 3 + 1) : 1;
y = yyyy; m = mm; d = dd;
}
// --- Biorytmy ---------------------------------------------------------------
static inline float biorhythm(long days, int period) {
// Zwraca zakres [-1..1]
return sinf(2.0f * (float)M_PI * (float)days / (float)period);
}
// --- Sterowniki LVGL: flush i dotyk ----------------------------------------
static void my_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
uint32_t w = (area->x2 - area->x1 + 1);
uint32_t h = (area->y2 - area->y1 + 1);
Display.beginDraw();
// Biblioteka używa RGB565 — lv_color_t przy LV_COLOR_DEPTH 16 także.
Display.drawBlock(area->x1, area->y1, w, h, (uint16_t *)color_p);
Display.endDraw();
lv_disp_flush_ready(disp);
}
static void my_touch_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data) {
// Minimalistyczna obsługa 1 punktu dotyku
uint8_t cnt = TouchDetector.read(); // liczba punktów
if (cnt > 0) {
// W niektórych wersjach: GDTpoint_t p = TouchDetector.getPoint(0);
// Jeśli biblioteka udostępnia inny interfejs, dopasuj poniższą linię.
GDTpoint_t p = TouchDetector.getPoint(0);
data->state = LV_INDEV_STATE_PR;
data->point.x = p.x;
data->point.y = p.y;
} else {
data->state = LV_INDEV_STATE_REL;
}
}
// --- UI: klawiatura ekranowa dla pól liczbowych -----------------------------
static void kb_event_cb(lv_event_t *e) {
lv_event_code_t code = lv_event_get_code(e);
lv_obj_t *kb = lv_event_get_target(e);
if(code == LV_EVENT_READY || code == LV_EVENT_CANCEL) {
lv_obj_del(kb);
}
}
static void ta_focus_cb(lv_event_t *e) {
if(lv_event_get_code(e) == LV_EVENT_FOCUSED) {
lv_obj_t *ta = lv_event_get_target(e);
lv_obj_t *kb = lv_keyboard_create(lv_scr_act());
lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_NUMBER);
lv_keyboard_set_textarea(kb, ta);
lv_obj_add_event_cb(kb, kb_event_cb, LV_EVENT_ALL, NULL);
}
}
// --- Rysowanie wykresu ------------------------------------------------------
static void plot_biorhythms(int yb, int mb, int db) {
int yt, mt, dt;
get_today(yt, mt, dt);
long days_birth = days_from_civil(yb, mb, db);
long days_today = days_from_civil(yt, mt, dt);
long alive = days_today - days_birth;
if(alive < 0) {
lv_label_set_text(lbl_status, "Błąd: data urodzenia z przyszłości.");
return;
}
// Wyzeruj i ustaw liczbę punktów (31)
lv_chart_set_point_count(chart, 31);
lv_chart_set_all_value(chart, ser_phys, LV_CHART_POINT_NONE);
lv_chart_set_all_value(chart, ser_emot, LV_CHART_POINT_NONE);
lv_chart_set_all_value(chart, ser_intl, LV_CHART_POINT_NONE);
for(int i = -15; i <= 15; ++i) {
long t = alive + i;
lv_chart_set_next_value(chart, ser_phys, (lv_coord_t)(biorhythm(t, 23) * 100.0f));
lv_chart_set_next_value(chart, ser_emot, (lv_coord_t)(biorhythm(t, 28) * 100.0f));
lv_chart_set_next_value(chart, ser_intl, (lv_coord_t)(biorhythm(t, 33) * 100.0f));
}
lv_chart_refresh(chart);
// Aktualne wartości (dzień 0)
float p = biorhythm(alive, 23) * 100.0f;
float e = biorhythm(alive, 28) * 100.0f;
float i = biorhythm(alive, 33) * 100.0f;
char buf[160];
snprintf(buf, sizeof(buf), "Fiz: %.1f%% Emo: %.1f%% Intel: %.1f%%", p, e, i);
lv_label_set_text(lbl_vals, buf);
char tb[64], bufs[96];
snprintf(tb, sizeof(tb), "Dziś: %04d-%02d-%02d", yt, mt, dt);
lv_label_set_text(lbl_today, tb);
snprintf(bufs, sizeof(bufs), "Dni życia: %ld (okno: -15..+15)", alive);
lv_label_set_text(lbl_status, bufs);
}
// --- Obsługa przycisku „Oblicz” --------------------------------------------
static void btn_calc_cb(lv_event_t *e) {
if(lv_event_get_code(e) != LV_EVENT_CLICKED) return;
int d = atoi(lv_textarea_get_text(ta_day));
int m = atoi(lv_textarea_get_text(ta_month));
int y = atoi(lv_textarea_get_text(ta_year));
if(!valid_date(y,m,d)) {
lv_label_set_text(lbl_status, "Błędna data! Użyj RRRR 1900..2099, poprawny dzień/miesiąc.");
return;
}
plot_biorhythms(y, m, d);
}
// --- Budowa GUI -------------------------------------------------------------
static void build_ui() {
lv_obj_t *scr = lv_obj_create(NULL);
lv_scr_load(scr);
lv_obj_set_style_bg_color(scr, lv_color_hex(0x0B2A3A), 0);
// Tytuł
lv_obj_t *lbl_title = lv_label_create(scr);
lv_label_set_text(lbl_title, "Kalkulator Biorytmów (Arduino GIGA + GIGA Display Shield)");
lv_obj_set_style_text_color(lbl_title, lv_color_hex(0xFFFFFF), 0);
lv_obj_set_style_text_font(lbl_title, &lv_font_montserrat_20, 0);
lv_obj_align(lbl_title, LV_ALIGN_TOP_MID, 0, 10);
// Pola daty
lv_obj_t *lbl = lv_label_create(scr);
lv_label_set_text(lbl, "Data urodzenia (DD / MM / RRRR):");
lv_obj_set_style_text_color(lbl, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(lbl, LV_ALIGN_TOP_LEFT, 16, 50);
ta_day = lv_textarea_create(scr);
lv_textarea_set_max_length(ta_day, 2);
lv_textarea_set_one_line(ta_day, true);
lv_textarea_set_accepted_chars(ta_day, "0123456789");
lv_textarea_set_placeholder_text(ta_day, "DD");
lv_obj_set_width(ta_day, 60);
lv_obj_align(ta_day, LV_ALIGN_TOP_LEFT, 16, 80);
lv_obj_add_event_cb(ta_day, ta_focus_cb, LV_EVENT_FOCUSED, NULL);
ta_month = lv_textarea_create(scr);
lv_textarea_set_max_length(ta_month, 2);
lv_textarea_set_one_line(ta_month, true);
lv_textarea_set_accepted_chars(ta_month, "0123456789");
lv_textarea_set_placeholder_text(ta_month, "MM");
lv_obj_set_width(ta_month, 60);
lv_obj_align_to(ta_month, ta_day, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
lv_obj_add_event_cb(ta_month, ta_focus_cb, LV_EVENT_FOCUSED, NULL);
ta_year = lv_textarea_create(scr);
lv_textarea_set_max_length(ta_year, 4);
lv_textarea_set_one_line(ta_year, true);
lv_textarea_set_accepted_chars(ta_year, "0123456789");
lv_textarea_set_placeholder_text(ta_year, "RRRR");
lv_obj_set_width(ta_year, 90);
lv_obj_align_to(ta_year, ta_month, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
lv_obj_add_event_cb(ta_year, ta_focus_cb, LV_EVENT_FOCUSED, NULL);
// Ustaw przykładową datę
lv_textarea_set_text(ta_day, "01");
lv_textarea_set_text(ta_month, "01");
lv_textarea_set_text(ta_year, "2000");
// Przycisk „Oblicz”
btn_calc = lv_btn_create(scr);
lv_obj_set_size(btn_calc, 160, 48);
lv_obj_align(btn_calc, LV_ALIGN_TOP_LEFT, 16, 130);
lv_obj_add_event_cb(btn_calc, btn_calc_cb, LV_EVENT_CLICKED, NULL);
lv_obj_t *lbl_btn = lv_label_create(btn_calc);
lv_label_set_text(lbl_btn, "Oblicz");
lv_obj_center(lbl_btn);
// Etykiety statusu
lbl_today = lv_label_create(scr);
lv_obj_set_style_text_color(lbl_today, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(lbl_today, LV_ALIGN_TOP_LEFT, 16, 185);
lbl_status = lv_label_create(scr);
lv_obj_set_style_text_color(lbl_status, lv_color_hex(0x87CEEB), 0);
lv_obj_align(lbl_status, LV_ALIGN_TOP_LEFT, 16, 205);
lbl_vals = lv_label_create(scr);
lv_obj_set_style_text_color(lbl_vals, lv_color_hex(0xFFFFFF), 0);
lv_obj_align(lbl_vals, LV_ALIGN_TOP_LEFT, 16, 225);
// Wykres
chart = lv_chart_create(scr);
lv_obj_set_size(chart, 480, 300);
lv_obj_align(chart, LV_ALIGN_RIGHT_MID, -16, 10);
lv_chart_set_type(chart, LV_CHART_TYPE_LINE);
lv_chart_set_point_count(chart, 31);
lv_chart_set_range(chart, LV_CHART_AXIS_PRIMARY_Y, -100, 100);
lv_chart_set_div_line_count(chart, 5, 8); // siatka
lv_obj_set_style_bg_color(chart, lv_color_hex(0x001820), 0);
ser_phys = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_RED), LV_CHART_AXIS_PRIMARY_Y);
ser_emot = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_BLUE), LV_CHART_AXIS_PRIMARY_Y);
ser_intl = lv_chart_add_series(chart, lv_palette_main(LV_PALETTE_GREEN), LV_CHART_AXIS_PRIMARY_Y);
// Linia „0”
lv_obj_set_style_line_color(chart, lv_color_hex(0x204050), LV_PART_MAIN);
// Początkowa informacja
int yt, mt, dt; get_today(yt, mt, dt);
char tb[64];
snprintf(tb, sizeof(tb), "Dziś: %04d-%02d-%02d", yt, mt, dt);
lv_label_set_text(lbl_today, tb);
lv_label_set_text(lbl_status, "Wprowadź datę i naciśnij „Oblicz”.");
}
// --- setup() / loop() -------------------------------------------------------
void setup() {
Serial.begin(115200);
// Ekran + dotyk
Display.begin();
TouchDetector.begin();
// W razie potrzeby: TouchDetector.setRotation(1);
// LVGL
lv_init();
lv_disp_draw_buf_init(&draw_buf, buf1, buf2, SCREEN_W * 60);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = SCREEN_W;
disp_drv.ver_res = SCREEN_H;
disp_drv.flush_cb = my_disp_flush;
disp_drv.draw_buf = &draw_buf;
lv_disp_drv_register(&disp_drv);
static lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_touch_read;
lv_indev_drv_register(&indev_drv);
build_ui();
}
void loop() {
static uint32_t last = millis();
uint32_t now = millis();
lv_tick_inc(now - last);
last = now;
lv_timer_handler();
delay(5);
}