Skocz do zawartości

Pomocna odpowiedź

Napisano (edytowany)

Wstęp

Jako, że ostatnio dość często rozliczam się korzystając z arkuszy kalkulacyjnych (i to dość dokładnie) postanowiłem spróbować zaprojektować urządzenie, które pozwoli w szybki sposób wpisać datę i godzinę do odpowiednich pól.

IMG_1376.thumb.jpg.8ae2d4277172ce96a285053220339186.jpg
Wygląd urządzenia

Artykuł powstał we współpracy z firmą Botland.

Założenia i próba z UIFlow

Urządzenie miało być akceptowalnie małe rozmiarowo i działać jako klawiatura USB. W celu minimalizacji czasu montażu postanowiłem użyć systemu M5Stack. Pierwszy wybór padł na Atom Echo oraz moduł przycisku. Rozpocząłem projekt aplikacji w UI Flow 2 i okazało się, że moduł Echo nie wspiera USB Device (nie można go użyć jako klawiatury). Trochę mnie to zdenerwowało, ale od czego jest partner, który dostarczył moduł Echo S3R, który taką funkcję już (teoretycznie) posiada.

obraz.thumb.png.e7855809e8d6d9607246cac40f7f8d47.png
Program UIFlow2

Teoretycznie, bo gdy skończyłem projekt (i w przysłowiowym międzyczasie wydrukowałem obudowę) wyskoczyła kolejna niespodzianka: UIFlow2 nie wspiera klawiatury dla modułów Echo (mimo, że pojawia się ona na liście bloczków). MicroPython wyrzuca brak modułu USB. Teoretycznie można bawić się w kopilowanie własnego zestawu ustawień, ale to droga dla zapaleńców, a ja wolę prostotę.

Padło więc na zmianę środowiska na stare zaufane PlatformIO z frameworkiem Arduino.

Wersja, która nareszcie działa

Po szybkim kodowaniu z pomocą Google Gemini (Claude akurat miał przerwę w działaniu) stworzyłem w pełni funkcjonalną aplikację.

#ifndef CONFIG_H
#define CONFIG_H

// Ustawienia WiFi
#define WIFI_SSID     "SSID"
#define WIFI_PASSWORD "PASSWD"

// Ustawienia NTP
#define NTP_SERVER    "pool.ntp.org"
#define GMT_OFFSET_SEC 0 // Obsolete     
#define DAYLIGHT_OFFSET_SEC 0 // Obsolete
#define TZ_INFO "CET-1CEST,M3.5.0,M10.5.0/3" // Strefa czasowa

// Format daty i czasu (zgodny ze strftime)
// Użyj tych definicji, aby dostosować wysyłany tekst klawiatury
#define DATE_FORMAT "%d/%m/%Y"   // DD/MM/YYYY
#define TIME_FORMAT "%H:%M"      // HH:MM
#define FULL_FORMAT "%Y-%m-%d %H:%M:%S" // Opcjonalny alternatywny format

// Przypisanie pinów dla zewnętrznego modułu przycisku (Port A)
// W Atom Echo S3R Port A: G1 (SDA) oraz G2 (SCL)
// Zazwyczaj moduły przycisków od M5 używają żółtego przewodu (G1)
#define EXTERNAL_BUTTON_PIN 1 

#endif

Powyżej plik konfiguracyjny 😉 

#include <M5Unified.h>
#include <WiFi.h>
#include "config.h"
#include "time.h"
#include "USB.h"
#include "USBHIDKeyboard.h"

USBHIDKeyboard Keyboard;

// Stałe czasowe
constexpr unsigned long DEBOUNCE_MS = 250;
unsigned long lastPressTime = 0;

// Funkcja zwracająca sformatowany ciąg czasu
String getFormattedTime(const char* format) {
    tm timeInfo;
    if (!getLocalTime(&timeInfo)) {
        return "NTP_ERR";
    }
    char buffer[32];
    strftime(buffer, sizeof(buffer), format, &timeInfo);
    return String(buffer);
}

void setup() {
    const auto cfg = M5.config();
    M5.begin(cfg);

    // Połączenie z WiFi
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    while (WiFi.status() != WL_CONNECTED) {
        // To opóźnienie pozwala poczekać aż ESP znajdzie sieć WiFi (czasem to chwilę zajmuje)
        delay(500);
    }

    // Inicjalizacja NTP
    configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER);
    setenv("TZ", TZ_INFO, 1);
    tzset();

    // Inicjalizacja pinu zewnętrznego przycisku
    pinMode(EXTERNAL_BUTTON_PIN, INPUT_PULLUP);

    // Inicjalizacja klawiatury USB HID
    USB.begin();
    Keyboard.begin();
}

void loop() {
    M5.update();
    const unsigned long currentMillis = millis();

    // 1. Wbudowany przycisk (Data)
    // M5.BtnA.wasPressed() obsługuje już wewnętrzne usuwanie drgań styków
    if (M5.BtnA.wasPressed()) {
        if (currentMillis - lastPressTime >= DEBOUNCE_MS) {
            const String dateStr = getFormattedTime(DATE_FORMAT);
            Keyboard.print(dateStr);
            lastPressTime = currentMillis;
        }
    }

    // 2. Zewnętrzny moduł przycisku (Czas)
    static bool lastExtState = HIGH;
    const bool currentExtState = digitalRead(EXTERNAL_BUTTON_PIN);

    // Wykrycie zbocza opadającego (stan aktywny niski)
    if (currentExtState == LOW && lastExtState == HIGH) {
        if (currentMillis - lastPressTime >= DEBOUNCE_MS) {
            const String timeStr = getFormattedTime(TIME_FORMAT);
            Keyboard.print(timeStr);
            lastPressTime = currentMillis;
        }
    }
    lastExtState = currentExtState;
}

Oraz sam kod programu, który inicjuje peryferia i WiFi, konfiguruje NTP (serwer do pobierania czasu), a w pętli oczekuje na wciśnięcie przycisku i tworzy "debouncing", który w rzeczywistości pozwala na łatwą reakcję na kliknięcie. Teoretycznie mogłem użyć wbudowanego systemu do obsługi kliknięc, ale zdarzało mu się podwójnie wykryć niektóre kliknięcia.

Urządzenie dzięki wbudowanemu głośnikowi można również rozbudować o sygnał dźwiękowy odtwarzany po naciśnięciu dowolnego z przycisków.

Dla ułatwienia również konfiguracja PlatformIO:

[env:m5stack-atoms3r]
platform = espressif32@6.7.0
board = esp32-s3-devkitc-1
framework = arduino
board_build.arduino.memory_type = qio_opi
build_flags =
    -DESP32S3
    -DBOARD_HAS_PSRAM
    -mfix-esp32-psram-cache-issue
    -DCORE_DEBUG_LEVEL=5
    -DARDUINO_USB_CDC_ON_BOOT=1
    -DARDUINO_USB_MODE=1
lib_deps =
    m5stack/M5Unified @ ^0.1.12

Obudowa

Obudowa została wykonana w pełni na drukarce 3D. Przyciski zostały rozdzielone na dwie części składane za pomocą imadła / prasy (taka technika pozwala na uniknięcie drukowania podpór). Dodatkowo za pomocą pracy, w odpowiednich wycięciach, są mocowane symbole D oraz H wydrukowane w innym kolorze.

Użyty materiał to Fiberlogy Easy PETG (black oraz blue).

Wszystkie elementy (włącznie z przewodem USB A do USB-C) zostały zamocowane wewnątrz obudowy. Moduł przycisku został przymocowany śrubami M4x12, a moduł Echo jest dociskany przez spodnią klapkę. Jedyna wada tej konstrukcji to możliwość rozłączenia przewodu USB (krótkotrwała) ze względu na dość słabą jakość gniazd USB-C w modułach Atom. 

Obudowa została zamknięta klapką mocowaną na 4 śruby M3x12.

Pliki 3D - DateTime Keyboard.zip

IMG_1377.thumb.jpg.b2313d43df3fa08300b6d7f6fea1ef25.jpg
Prawidłowo złożony przycisk

IMG_1373.thumb.jpg.8e9fff86fb7d36f3aed0b38cad8d9ab2.jpg
Wnętrze obudowy

Filmik z działania

 

 

Edytowano przez H1M4W4R1
  • Lubię! 2
(edytowany)

To dziwne, bo według mojej wiedzy ten wspaniały przyrządzik, sterowany przedstawionym programem, powinien pokazywać obecnie czas letni.

Polecenie configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER); zawsze sumuje czasy przedstawione jako parametry i nie ma żadnej wiedzy o konieczności przestawiania czasu w marcu i październiku.

Aby zmiana czasu odbywała się automatycznie to najlepiej zastosować inną funkcję:

const char* TZ_INFO = "CET-1CEST,M3.5.0,M10.5.0/3";   // określamy kiedy następuje zmiana czasu - CET-1 (czas standardowy +1h), CEST (czas letni)
//...
configTzTime(TZ_INFO, NTP_SERVER);

A jeśli jednak ktoś jest emocjonalnie związany z configTime(), to powinno to tak wyglądać:

  const char* TZ_INFO = "CET-1CEST,M3.5.0,M10.5.0/3";
  //...
  configTime(0, 0, NTP_SERVER);
  setenv("TZ",TZ_INFO, 1);
  tzset();

 

Edytowano przez jand
  • Lubię! 1
8 godzin temu, jand napisał:

Polecenie configTime(GMT_OFFSET_SEC, DAYLIGHT_OFFSET_SEC, NTP_SERVER); zawsze sumuje czasy przedstawione jako parametry i nie ma żadnej wiedzy o konieczności przestawiania czasu w marcu i październiku.

Akurat tu byś się pomylił 😉 Tylko w sumie masz też sporo racji, bo nie mam pewności czy na 100% czas będzie przestawiony o właściwej porze... No i na dodatek Twoja wersja jest "trochę" czytelniejsza. Wrzuciłem poprawkę póki jeszcze mam możliwość edycji.

Ale wracając do tematu... ustawienie configTime z parametrami 3600, 3600, NTP daje strefę czasową (zmienną środowiskową TZ) UTC-1DST, która powinna pokrywać się godzinami ze strefą CET/CEST. Dlatego działa (na ten moment) poprawnie.

No rzeczywiście, się pomyliłem - oba czasy (GMT_OFFSET_SEC oraz DAYLIGHT_OFFSET_SEC, NTP_SERVER) będą sumowane nie od razu, ale dopiero, gdy poinformujemy system, że obecnie jest czas letni. Ponieważ takiej informacji nie ma, więc na razie jest wyświetlany prawidłowo czas zimowy. Ale faktem jest, że w pierwotnej wersji programu pod koniec marca czas by się sam nie przestawił.

  • Lubię! 1

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...