|

Zbuduj własną kostkę do zliczania czasu pracy

Zbuduj własną kostkę do zliczania czasu pracy

W tym artykule pokażę Ci krok po kroku, jak zbudować prostą, ale bardzo praktyczną kostkę do zliczania czasu pracy. Urządzenie rozpoznaje swoje położenie za pomocą czujnika MPU6050, dzięki czemu wystarczy obrócić kostkę na odpowiednią stronę, aby rozpocząć lub zatrzymać licznik. Aktualny czas pracy wyświetlany jest na małym ekranie OLED, a po upływie określonego czasu urządzenie przypomna sygnałem dźwiękowym, że warto zrobić sobie krótką przerwę.

Projekt powstał z myślą o prostym monitorowaniu czasu spędzonego przy biurku, komputerze lub konkretnym zadaniu. Nie trzeba klikać żadnych przycisków ani uruchamiać aplikacji. Wystarczy fizycznie obrócić kostkę. Jedna pozycja oznacza pracę, druga przerwę. Dzięki temu całość jest bardzo intuicyjna i trochę przypomina elektroniczną wersję kostki produktywności.

Do budowy wykorzystamy mikrokontroler ESP32, wyświetlacz OLED 128×64 px, czujnik ruchu MPU6050 oraz aktywny buzzer. Całość zamkniemy w prostej obudowie drukowanej 3D, tak aby urządzenie wyglądało estetycznie i mogło normalnie stać na biurku.

Lista potrzebnych elementów

Do wykonania tej wersji projektu wykorzystaliśmy następujące elementy:

ElementOpis
ESP32-C3 Super MiniGłówna płytka sterująca projektem. Odpowiada za odczyt danych z czujnika MPU6050, sterowanie wyświetlaczem OLED oraz całą logikę działania kostki
Wyświetlacz OLED 0,96″ 128x64pxNiewielki ekran do wyświetlania aktualnego trybu pracy Praca/Przerwa, zliczonego czasu oraz paska postępu
Akcelerometr i żyroskop MPU6050Czujnik ruchu akcelerometr + żyroskop, który pozwala wykrywać orientację kostki i na tej podstawie przełączać tryby
Buzzer aktywny sterowany stanem niskimElement odpowiedzialny za sygnały dźwiękowe informujące o zmianie trybu, resecie oraz przypomnieniu o przerwie
Uniwersalna płytka PCB 5x7cm dwustronnaUmożliwia trwałe zamontowanie i połączenie wszystkich elementów w jednej konstrukcji bez użycia płytki stykowej
Obudowa druk 3DObudowa w formie kostki, w której montowane są wszystkie elementy, nadająca projektowi estetyczny wygląd i funkcjonalność
Śruby M2 4mm do montażu wyświetlacza i obudowy Elementy montażowe służące do stabilnego przykręcenia wyświetlacza do panelu przedniego. W projekcie wykorzystujemy 6 sztuki.
Zestaw przewodów silikonowych AWG28Elastyczne przewody do wykonywania połączeń na płytce oraz między modułami. Ułatwiają montaż i poprawiają estetykę projektu. W projekcie wykorzystujemy 1 zestaw (oczywiście niecały 😎).
Przewód typu Kynar 0,24 mm 8 kolorów, rolkaMniejszej średnicy przewody do połączeń na płytce oraz między modułami.

Do tego projektu przygotowaliśmy gotowy zestaw do zakupu, dlatego nie trzeba kompletować elementów osobno. Wszystkie części potrzebne do zbudowania dokładnie takiego samego zestawu jak nasz znajdują się w jednym komplecie. Wyjątkiem są przewody połączeniowe, ich nie ma w zestawie.

Schemat połączeń

Poniżej przedstawiono sposób podłączenia poszczególnych komponentów. Należy zachować ostrożność i zgodność ze schematem, prawidłowe połączenia są kluczowe dla działania układu.

WYŚWIETLACZ OLED → ESP32-C3

Pin wyświetlaczaPin ESP32-C3Uwagi
VCC3,3 VZasilanie wyświetlacza
GNDGNDMasa
SDAGPIO 8Linia danych I2C
SCKGPIO 9Linia zegarowa I2C

MPU6050 → ESP32-C3

Pin MPU6050Pin ESP32-C3Uwagi
VCC3,3 VZasilanie czujnika
GNDGNDMasa
SDAGPIO 8Wspólna linia danych I2C
SCLGPIO 9Wspólna linia zegarowa I2C

BUZZER → ESP32-C3

Pin buzzeraPin ESP32-C3Uwagi
VCC3,3 VZasilanie modułu buzzera
GNDGNDMasa
I/OGPIO 10Sterowanie buzzerem aktywny stan niski

Montaż urządzenia

Po przygotowaniu wszystkich elementów oraz zapoznaniu się ze schematem połączeń możemy przejść do etapu montażu. W tym kroku zaczniemy składać cały układ w jedną całość, tak aby finalnie zmieścił się w przygotowanej obudowie. Warto robić to spokojnie i dokładnie, ponieważ od tego etapu zależy estetyka oraz niezawodność działania urządzenia.

Na tym etapie przechodzimy do przygotowania płytki prototypowej, na której zamontujemy wszystkie elementy. Standardowa płytka, której używamy, ma rozmiar 5×7 cm, natomiast nasza obudowa została zaprojektowana pod wymiar 5×5 cm, dlatego konieczne będzie jej przycięcie.

Płytkę najprościej przyciąć za pomocą niewielkiej piłki. Warto robić to powoli, żeby nie uszkodzić ścieżek oraz pól lutowniczych. Najlepiej zaznaczyć sobie wcześniej linię cięcia, tak aby zachować równy kształt i estetykę.

Po przycięciu dobrze jest lekko wyrównać krawędzie, na przykład papierem ściernym, żeby płytka bez problemu weszła do obudowy i nie haczyła o jej ścianki.

Dzięki temu uzyskamy kompaktową bazę pod cały układ, idealnie dopasowaną do naszej kostki.

zaczynamy od rozmieszczenia wszystkich elementów na płytce prototypowej. Warto ułożyć je dokładnie tak, jak na zdjęciu poglądowym, zwracając szczególną uwagę na pozycję płytki ESP32. Kluczowe jest to, aby złącze USB-C znalazło się w odpowiednim miejscu, tak żeby po zamknięciu obudowy było łatwo dostępne z zewnątrz. Dobrze jest na tym etapie nic jeszcze nie lutować, tylko „na sucho” sprawdzić, czy wszystko pasuje i czy obudowa zamyka się bez problemu.

Gdy mamy już poprawnie rozmieszczone wszystkie elementy, możemy przejść do wykonywania połączeń. Należy je wykonać dokładnie zgodnie ze schematem przedstawionym w tabelkach powyżej, ponieważ kod został przygotowany pod konkretne przypisanie pinów. Wszelkie zmiany w połączeniach mogą spowodować, że układ nie będzie działał poprawnie. Warto więc lutować wszystko starannie i na bieżąco kontrolować poprawność połączeń.

Dla ułatwienia wykonywania połączeń obok zamieszczamy schemat wybranej przez nas płytki

Gdy wszystkie elementy są już poprawnie polutowane i upewniliśmy się, że układ działa prawidłowo, możemy przejść do finalnego montażu w obudowie. Na początku przykręcamy wyświetlacz OLED do przedniej części obudowy, wykorzystując do tego 4 śrubki, dzięki czemu będzie stabilnie osadzony i dobrze widoczny z zewnątrz.

Następnie umieszczamy całą elektronikę wewnątrz obudowy, zwracając uwagę, aby przewody nie były naprężone i nic nie przeszkadzało w jej zamknięciu.

Na koniec zdejmujemy naklejkę z buzzera żeby nie tłumił dźwięku, zamykamy obudowę i skręcamy ją pozostałymi 2 śrubkami, uzyskując gotową, zwartą konstrukcję naszej kostki.

Przygotowanie środowiska

Kolejnym krokiem jest przygotowanie środowiska oraz wgranie programu do naszej kostki. Na początek pobieramy gotowy kod projektu, który odpowiada za obsługę wyświetlacza, czujnika MPU6050 oraz całej logiki działania urządzenia.

Następnie musimy doinstalować wymagane biblioteki. W tym projekcie korzystamy z dwóch bibliotek: Adafruit_SSD1306 do obsługi wyświetlacza OLED oraz Adafruit_MPU6050 do komunikacji z czujnikiem ruchu. Instalację wykonujemy w Arduino IDE wchodząc w zakładkę „Menedżer bibliotek”, wyszukując odpowiednie nazwy i instalując je jednym kliknięciem. Warto również upewnić się, że zainstalowana zostanie biblioteka zależna Adafruit GFX, która jest wymagana do poprawnego działania wyświetlacza.

Kolejnym krokiem jest dodanie obsługi płytek ESP32 do Arduino IDE. W tym celu wchodzimy w ustawienia programu, a następnie w pole „Dodatkowe adresy URL do menedżera płytek” dodajemy adres:
https://espressif.github.io/arduino-esp32/package_esp32_index.json
Po zapisaniu zmian przechodzimy do „Menedżera płytek”, wyszukujemy „ESP32” i instalujemy pakiet od Espressif Systems.

Po zakończeniu instalacji wybieramy odpowiednią płytkę, w naszym przypadku ESP32C3 Dev Module, ustawiamy właściwy port COM i możemy przejść do wgrania programu na urządzenie.

Jak działa kod programu?

Kod został podzielony na kilka prostych części, dzięki czemu łatwiej zrozumieć działanie całego projektu. Program obsługuje wyświetlacz OLED, czujnik położenia MPU6050, buzzer oraz licznik czasu pracy. Całość działa automatycznie, bez żadnych przycisków, wystarczy obrócić kostkę w odpowiednią stronę.

Na początku programu dołączamy potrzebne biblioteki. Biblioteka Wire.h odpowiada za komunikację I2C, Adafruit_MPU6050.h obsługuje czujnik ruchu, a Adafruit_SSD1306.h oraz Adafruit_GFX.h służą do sterowania wyświetlaczem OLED.

#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

W kolejnej części definiujemy rozmiar wyświetlacza oraz tworzymy obiekty odpowiedzialne za ekran i czujnik MPU6050.

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_MPU6050 mpu;

Następnie ustawiamy piny, do których podłączone są poszczególne elementy. W naszym przypadku magistrala I2C działa na pinach GPIO 8 oraz GPIO 9, a buzzer został podłączony do GPIO 10.

// PINY
static const int I2C_SDA = 8;
static const int I2C_SCL = 9;
static const int BUZZER_PIN = 10;

Ważnym fragmentem kodu są ustawienia odpowiedzialne za wykrywanie położenia kostki. Program sprawdza wartość przyspieszenia w osi Z i na tej podstawie rozpoznaje, czy kostka znajduje się w pozycji pracy, czy przerwy.

Wartość ORIENTATION_THRESHOLD określa próg wykrywania położenia. Dzięki temu kostka nie reaguje na drobne przechylenia. Z kolei STABLE_TIME_MS oznacza, że nowe położenie musi być stabilne przez 500 ms, zanim zostanie zaakceptowane.

static const float ORIENTATION_THRESHOLD = 7.0f;
static const unsigned long STABLE_TIME_MS = 500;

Warto też zwrócić uwagę na sposób zarządzania trybem pracy urządzenia. W kodzie wykorzystana została prosta maszyna stanów oparta o enum, dzięki czemu program jest czytelny i łatwy do rozbudowy. Zamiast używać np. wartości typu true/false, mamy jasno zdefiniowane stany: STATE_STOP przerwa oraz STATE_WORK praca. Dzięki temu od razu widać, w jakim trybie aktualnie znajduje się urządzenie.

Aktualny stan przechowywany jest w zmiennej currentState, a dodatkowo mamy też lastConfirmedState, która pozwala upewnić się, że zmiana położenia była rzeczywiście stabilna, zanim przełączymy tryb.

Dzięki takiemu podejściu łatwo kontrolować logikę programu, na przykład wiemy dokładnie, kiedy rozpocząć zliczanie czasu, kiedy je zatrzymać oraz kiedy zaktualizować ekran czy uruchomić buzzer. W przyszłości bez problemu można też dodać kolejne stany, np. tryb pauzy czy tryb ustawień.

enum WorkState {
  STATE_STOP = 0,
  STATE_WORK = 1
};

WorkState currentState = STATE_STOP;
WorkState lastConfirmedState = STATE_STOP;

Kolejny fragment odpowiada za buzzer. Ponieważ w projekcie używamy buzzera aktywnego stanem niskim, włączenie dźwięku odbywa się przez ustawienie pinu na LOW, a wyłączenie przez HIGH.

void buzzerOn() {
  digitalWrite(BUZZER_PIN, LOW);
}

void buzzerOff() {
  digitalWrite(BUZZER_PIN, HIGH);
}

Najważniejszą funkcją odpowiedzialną za zliczanie czasu jest getDisplayedTotalMs(). Jeżeli kostka znajduje się w trybie pracy, funkcja dodaje do zapamiętanego czasu aktualnie trwającą sesję. Jeżeli kostka jest w trybie przerwy, pokazuje tylko wcześniej naliczony czas.

unsigned long getDisplayedTotalMs() {
  if (currentState == STATE_WORK) {
    return accumulatedWorkMs + (millis() - sessionStartMs);
  }
  return accumulatedWorkMs;
}

Za wygląd ekranu odpowiada funkcja drawScreen(). To ona czyści ekran, wypisuje aktualny tryb, pokazuje czas oraz rysuje pasek postępu na dole wyświetlacza.

void drawScreen() {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);

  display.setTextSize(2);
  display.setCursor(12, 0);

  if (currentState == STATE_WORK) {
    display.println("Praca");
  } else {
    display.println("Przerwa");
  }

  display.setTextSize(1);
  display.setCursor(0, 24);
  display.println("Dzisiaj:");

  display.setTextSize(2);
  display.setCursor(0, 36);
  display.println(formatTime(getDisplayedTotalMs()));

  drawProgressBlocks();

  display.display();
}

Najważniejsza logika przełączania trybu znajduje się w funkcji handleOrientation(). Program sprawdza wartość osi Z z czujnika MPU6050. Jeżeli wartość jest dodatnia i przekracza ustalony próg, urządzenie przechodzi w tryb przerwy. Jeżeli wartość jest ujemna, przechodzi w tryb pracy.

Dzięki temu cała obsługa urządzenia sprowadza się do obrócenia kostki. Nie musimy używać żadnych przycisków ani dodatkowego menu.

if (az > ORIENTATION_THRESHOLD) {
  detectedOrientation = 1;     // STOP
} else if (az < -ORIENTATION_THRESHOLD) {
  detectedOrientation = -1;    // PRACA
}

W kodzie znajduje się również funkcja resetowania czasu przez potrząśnięcie. Program wykrywa nagłą zmianę przyspieszenia i jeżeli takich ruchów pojawi się kilka w krótkim czasie, licznik zostaje wyzerowany. Jeżeli różnica względem standardowego przyspieszenia ziemskiego jest odpowiednio duża, program uznaje to za potrząśnięcie.

float magnitude = sqrt(ax * ax + ay * ay + az * az);
float delta = fabs(magnitude - 9.81f);

Na końcu warto wspomnieć o funkcji loop(). To właśnie tutaj program cyklicznie odczytuje dane z MPU6050, sprawdza położenie kostki, obsługuje reset, przypomnienie o przerwie oraz odświeża ekran.

Dzięki takiemu podziałowi kod jest czytelny i łatwo można go później modyfikować. Możemy na przykład zmienić czas jednego bloczka na pasku postępu, próg wykrywania położenia, czas przypomnienia o przerwie albo sposób działania buzzera.

void loop() {
  unsigned long now = millis();

  if (now - lastSensorReadMs >= SENSOR_INTERVAL_MS) {
    lastSensorReadMs = now;

    sensors_event_t a, g, temp;
    mpu.getEvent(&a, &g, &temp);

    handleOrientation(a.acceleration.z);
    handleShake(a.acceleration.x, a.acceleration.y, a.acceleration.z);
    handleBreakAlert();
  }

  if (now - lastDisplayRefreshMs >= DISPLAY_REFRESH_MS) {
    lastDisplayRefreshMs = now;
    drawScreen();
  }
}

Możliwości rozbudowy projektu

Ten projekt to tak naprawdę dopiero początek i spokojnie możesz go dalej rozwijać pod swoje potrzeby. Najprostsza modyfikacja to zmiana czasu przypomnienia o przerwie albo długości jednego bloku na pasku postępu. Wystarczy zmienić kilka wartości w kodzie i już masz dopasowane urządzenie pod swój styl pracy.

Jeżeli chcesz pójść krok dalej, możesz dodać np. zapis czasu do pamięci, żeby nie zerował się po odłączeniu zasilania, komunikację Bluetooth lub WiFi i wysyłanie statystyk na telefon albo komputer. Ciekawą opcją jest też dodanie przycisku lub enkodera do zmiany trybów bez obracania kostki, albo wykorzystanie większego wyświetlacza z dodatkowymi informacjami.

Możesz też pobawić się samą obudową, zmienić jej kształt, dodać podświetlenie LED albo zrobić wersję bardziej „biurkową” zamiast kostki. Tutaj ogranicza Cię praktycznie tylko wyobraźnia.

Efekt końcowy

Po złożeniu wszystkich elementów, wgraniu kodu i zamknięciu obudowy nasza kostka do zliczania czasu jest gotowa do pracy. Całość prezentuje się kompaktowo, a dzięki wyświetlaczowi OLED od razu widzimy aktualny tryb oraz naliczony czas.

Podsumowanie

W ten sposób udało nam się zbudować prosty, ale praktyczny licznik czasu pracy oparty na ESP32-C3, wyświetlaczu OLED i czujniku MPU6050. Projekt nie wymaga żadnych przycisków, ponieważ cała obsługa odbywa się przez obracanie kostki. To fajny przykład wykorzystania czujnika ruchu w praktycznym urządzeniu, które może realnie przydać się na biurku podczas pracy, nauki albo realizowania własnych projektów elektronicznych.


Prezentowaną w artykule elektronikę znajdziesz oczywiście w naszym sklepie 👉 sklep.msalamon.pl 👈Zapraszamy również na nasze social media, gdzie na bieżąco informujemy o nowych produktach oraz o najciekawszych promocjach 😎👇


Podobne wpisy

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *