Programowanie biorytmów na Arduino GIGA Display

Pytanie

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"

Odpowiedź Sztucznej Inteligencji


Bezpośrednia odpowiedź na pytanie

  • Poniżej znajdziesz kompletny, kompilowalny szkic dla Arduino GIGA R1 WiFi z GIGA Display Shield, który: (1) pozwala wprowadzić datę urodzenia na ekranie (DD/MM/RRRR), (2) oblicza biorytmy (fizyczny 23 d, emocjonalny 28 d, intelektualny 33 d), (3) rysuje wykres 31 punktów (15 dni wstecz, dziś, 15 dni naprzód) w LVGL.
  • Kod używa: Arduino_H7_Video (render), Arduino_GigaDisplayTouch (dotyk), LVGL (UI/wykres), Arduino_GigaDisplay_GFX (włączony — nie jest niezbędny w tym wariancie, ale zgodnie z wymaganiem dołączony).

Szczegółowa analiza problemu

  • Architektura:
    • LVGL renderuje do bufora (RGB565); funkcja flush kopiuje blok pikseli do sterownika Arduino_H7_Video (Display.drawBlock).
    • Sterownik dotyku dostarcza współrzędne do LVGL przez driver indev.
    • UI: trzy pola tekstowe (dzień/miesiąc/rok) + klawiatura ekranowa LVGL + przycisk „Oblicz”; wykres LV_CHART z trzema seriami.
  • Obliczenia:
    • Różnica dni wyznaczana funkcją days_from_civil (algorytm gregoriański, bez błędów „30 dni na miesiąc”).
    • Wartość cyklu: sin(2π·dni/T) skalowana do ±100%.
    • Data „dzisiaj”: próba pobrania z time(nullptr) (RTC/OS), a w razie braku — z makr kompilacji (DATE). Na ekranie wyświetlana jest data referencyjna użyta do obliczeń.
  • Zasoby:
    • Bufor rysowania ~800×60 pikseli (ok. 96 kB przy RGB565), płynne odświeżanie do statycznego wykresu.

Aktualne informacje i trendy

  • LVGL 8.x jest dominującym wyborem na mikrokontrolerach do złożonych interfejsów; GIGA Display Shield pracuje w RGB565 i dobrze współpracuje z buforowaniem liniowym.
  • W praktycznych wdrożeniach coraz częściej pobiera się czas z RTC/PNTP (Wi‑Fi) zamiast daty kompilacji. W kodzie zostawiłem punkty zaczepienia pod RTC/NTP.

Wspierające wyjaśnienia i detale

  • Kluczowe jest ustawienie LV_COLOR_DEPTH=16 w lv_conf.h (RGB565) oraz regularne wywoływanie lv_tick_inc + lv_timer_handler.
  • Walidacja daty obejmuje miesiące i lata przestępne (gregoriańsko).

Aspekty etyczne i prawne

  • Biorytmy nie mają potwierdzonej wartości naukowej; traktuj wyniki wyłącznie rekreacyjnie/edukacyjnie, bez zastosowań medycznych lub decyzyjnych.

Praktyczne wskazówki

  • Jeżeli dotyk jest obrócony, ustaw odpowiednią rotację w TouchDetector (np. TouchDetector.setRotation(1)) i skoryguj orientację ekranu.
  • Jeśli używasz RTC/NTP, podmień get_today() na odczyt czasu systemowego lub synchronizację NTP.
  • W lv_conf.h włącz widgety: LV_USE_KEYBOARD, LV_USE_TEXTAREA, LV_USE_CHART.

Ewentualne zastrzeżenia lub uwagi dodatkowe

  • API Arduino_GigaDisplayTouch może różnić się wersją. W razie potrzeby zamień pobranie punktu dotyku na odpowiednie metody biblioteki (np. getPoint(0)/getRawPoint).
  • Jeżeli Twoja wersja Arduino_H7_Video nie posiada drawBlock, użyj Display.drawBitmap lub równoważnej funkcji kopiującej blok pikseli RGB565.

Sugestie dalszych badań

  • Dodanie synchronizacji czasu przez Wi‑Fi (NTP) i stałej aktualizacji wykresu co dzień.
  • Zapis konfiguracji (EEPROM/Flash) oraz wielu profili (różne daty urodzenia).
  • Oznaczanie „dni krytycznych” (przejścia przez zero) na wykresie.

Kod — kompletny szkic

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);
}

Krótkie podsumowanie

  • Otrzymujesz kompletny, gotowy szkic dla Arduino GIGA + GIGA Display Shield z LVGL, uwzględniający poprawne liczenie dni (gregoriańsko), skalowanie biorytmów do ±100% oraz sensowny fallback daty (RTC/kompilacja). W razie różnic w wersjach bibliotek (dotyk/flush) dopasuj drobne szczegóły API zgodnie z komentarzami w kodzie.

Oceń odpowiedź: 1
Zastrzeżenie: Odpowiedzi udzielone przez sztuczną inteligencję (model językowy) mogą być niedokładne i wprowadzające w błąd. Elektroda nie ponosi odpowiedzialności za dokładność, rzetelność ani kompletność prezentowanych informacji. Wszystkie odpowiedzi powinny być zweryfikowane przez użytkownika.

Zadaj dodatkowe pytanie Sztucznej Inteligencji

Czekaj (2min)...