Arduino
12 maja 2026
⏱ 18 min czytania
Stacja pogodowa Arduino z OLED, WiFi i powiadomieniami na Telegram
Koszt budowy: ok. 75–90 zł | Poziom: Średniozaawansowany | Czas montażu: 3–4 godziny
Opis projektu
Stacja pogodowa to jeden z najlepszych projektów startowych – jest wystarczająco prosta, by skończyć ją w weekend, ale na tyle rozbudowana, że naprawdę uczysz się obsługi czujników, komunikacji I2C, WiFi oraz API zewnętrznych usług. Nasz projekt mierzy temperaturę, wilgotność powietrza i ciśnienie atmosferyczne, a wyniki wyświetla na wyświetlaczu OLED i wysyła co godzinę raport na Telegrama.
💡 Po co Telegram? Możesz ustawić alerty – np. gdy temperatura spada poniżej 0°C lub wilgotność przekracza 85% (ryzyko pleśni). Bot Telegram działa przez HTTPS i nie wymaga żadnych serwerów po Twojej stronie.
Lista komponentów
| Komponent | Model | Cena (przybliżona) |
| Mikrokontroler z WiFi | ESP32 DevKit v1 lub NodeMCU ESP8266 | 18–28 zł |
| Czujnik temp. + wilgotności | DHT22 (dokładniejszy niż DHT11) | 8–12 zł |
| Czujnik ciśnienia | BMP280 (I2C) | 6–10 zł |
| Wyświetlacz OLED | 0.96" SSD1306 128x64 (I2C) | 8–14 zł |
| Rezystor 10kΩ | Do linii danych DHT22 | <1 zł |
| Płytka stykowa + kable | 400-otworowa + kable DuPont | 10–15 zł |
| Zasilacz 5V micro-USB | Standard 1A+ | 10–15 zł |
Schemat połączeń
Połączenia dla ESP32 (piny mogą się różnić dla ESP8266 – sprawdź pinout swojej płytki):
DHT22:
VCC → 3.3V
GND → GND
DATA → GPIO4 (z rezystorem 10kΩ do 3.3V)
BMP280 (I2C):
VCC → 3.3V
GND → GND
SDA → GPIO21
SCL → GPIO22
OLED SSD1306 (I2C, współdzieli szyne z BMP280):
VCC → 3.3V
GND → GND
SDA → GPIO21
SCL → GPIO22
(adres domyślny: 0x3C, BMP280: 0x76)
Kompletny kod
Zainstaluj wymagane biblioteki przez Arduino Library Manager: DHT sensor library, Adafruit BMP280, Adafruit SSD1306, UniversalTelegramBot, ArduinoJson.
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <DHT.h>
#include <Adafruit_BMP280.h>
#include <Adafruit_SSD1306.h>
#include <UniversalTelegramBot.h>
// --- Konfiguracja ---
const char* SSID = "TwojeSIECI_WIFI";
const char* PASS = "TwojeHaslo";
const char* BOT_TOKEN = "TWOJ_TOKEN_TELEGRAM";
const String CHAT_ID = "TWOJE_CHAT_ID";
#define DHTPIN 4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP280 bmp;
Adafruit_SSD1306 display(128, 64, &Wire, -1);
WiFiClientSecure client;
UniversalTelegramBot bot(BOT_TOKEN, client);
unsigned long lastReport = 0;
const unsigned long REPORT_INTERVAL = 3600000UL; // 1 godzina
void setup() {
Serial.begin(115200);
dht.begin();
// OLED init
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED nie znaleziony!");
while(true);
}
display.clearDisplay();
display.setTextColor(WHITE);
// BMP280 init
if (!bmp.begin(0x76)) {
Serial.println("BMP280 nie znaleziony!");
while(true);
}
// WiFi
WiFi.begin(SSID, PASS);
display.setCursor(0, 0);
display.print("Laczenie z WiFi...");
display.display();
while (WiFi.status() != WL_CONNECTED) {
delay(500); Serial.print(".");
}
client.setInsecure(); // tylko do testów; w produkcji użyj certyfikatu
Serial.println("\nPolaczono: " + WiFi.localIP().toString());
}
void sendTelegramReport(float temp, float hum, float pres) {
String msg = "🌡 *Raport stacji pogodowej*\n";
msg += "Temperatura: *" + String(temp, 1) + " °C*\n";
msg += "Wilgotność: *" + String(hum, 1) + " %*\n";
msg += "Ciśnienie: *" + String(pres/100.0, 1) + " hPa*\n";
if (temp < 0) msg += "⚠️ _Uwaga: przymrozki!_\n";
if (hum > 85) msg += "⚠️ _Wysoka wilgotność – ryzyko pleśni_\n";
bot.sendMessage(CHAT_ID, msg, "Markdown");
}
void updateDisplay(float temp, float hum, float pres) {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
display.println("iPraktyk.pl Stacja");
display.drawLine(0, 9, 128, 9, WHITE);
display.setTextSize(2);
display.setCursor(0, 14);
display.print(temp, 1); display.println(" C");
display.setTextSize(1);
display.setCursor(0, 36);
display.print("Wilg: "); display.print(hum, 1); display.println(" %");
display.print("Cis: "); display.print(pres/100.0, 1); display.println(" hPa");
display.display();
}
void loop() {
float temp = dht.readTemperature();
float hum = dht.readHumidity();
float pres = bmp.readPressure();
if (!isnan(temp) && !isnan(hum)) {
updateDisplay(temp, hum, pres);
if (millis() - lastReport >= REPORT_INTERVAL || lastReport == 0) {
sendTelegramReport(temp, hum, pres);
lastReport = millis();
}
}
delay(5000);
}
Konfiguracja bota Telegram
1
Utwórz bota przez BotFather
Otwórz Telegram, wyszukaj @BotFather, wyślij /newbot i postępuj z instrukcjami. Otrzymasz token API – skopiuj go do zmiennej BOT_TOKEN.
2
Pobierz swoje Chat ID
Wyślij wiadomość do bota, a następnie wejdź w przeglądarce na: https://api.telegram.org/bot<TOKEN>/getUpdates. W odpowiedzi JSON znajdziesz pole chat.id.
3
Wgraj kod na ESP32
Uzupełnij dane WiFi, token i Chat ID, a następnie wgraj kod. Monitor szeregowy pokaże IP urządzenia i status połączenia.
⚠️ Bezpieczeństwo: Nigdy nie umieszczaj tokenów i haseł bezpośrednio w kodzie, jeśli udostępniasz go publicznie (np. na GitHub). Użyj pliku konfiguracyjnego lub zmiennych środowiskowych.
Arduino
8 maja 2026
⏱ 12 min czytania
Domowy alarm z czujnikiem PIR za 50 zł
Koszt: ok. 45–55 zł | Poziom: Początkujący | Czas: 1–2 godziny
Czujnik PIR (Passive Infrared) wykrywa zmiany w promieniowaniu podczerwonym – czyli ruch ciepłych obiektów, takich jak ludzie lub zwierzęta. To jeden z najtańszych i najłatwiejszych sposobów na budowę prostego systemu detekcji ruchu. Rozbudujemy go o buzzer alarmowy i opcjonalne powiadomienie przez WiFi.
Potrzebne komponenty
| Komponent | Cena |
| Arduino Uno R3 lub klon | 22–35 zł |
| Czujnik PIR HC-SR501 | 5–8 zł |
| Buzzer aktywny 5V | 2–4 zł |
| Dioda LED czerwona + rezystor 220Ω | 1 zł |
| Płytka stykowa + kable | 10–15 zł |
Kalibracja czujnika PIR
Moduł HC-SR501 ma dwa potencjometry na spodzie płytki:
- Sensitivity (Sx): Reguluje czułość (zasięg wykrywania 3–7 m). Obróć do końca zgodnie z ruchem wskazówek zegara dla maksymalnego zasięgu.
- Time delay (Tx): Czas aktywacji wyjścia po wykryciu ruchu (3 s – 5 min). Na początku ustaw na minimum.
Tryb pracy wybierasz zworką: L (Retriggering) – sygnał utrzymuje się przez czas Tx od ostatniego wykrycia ruchu (zalecany dla alarmu).
const int PIR_PIN = 7;
const int LED_PIN = 13;
const int BUZZ_PIN = 8;
const int ALARM_DUR = 3000; // czas alarmu w ms
void setup() {
pinMode(PIR_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(BUZZ_PIN, OUTPUT);
Serial.begin(9600);
// Poczekaj na kalibrację czujnika (30–60 s)
Serial.println("Kalibracja PIR...");
delay(60000);
Serial.println("Gotowy!");
}
void loop() {
if (digitalRead(PIR_PIN) == HIGH) {
Serial.println("RUCH WYKRYTY!");
digitalWrite(LED_PIN, HIGH);
// Alarm pulsujący
for (int i = 0; i < 6; i++) {
digitalWrite(BUZZ_PIN, HIGH); delay(200);
digitalWrite(BUZZ_PIN, LOW); delay(100);
}
delay(ALARM_DUR);
digitalWrite(LED_PIN, LOW);
}
delay(100);
}
🚀 Rozbudowa projektu: Dodaj moduł ESP8266 (lub zastąp Arduino Uno płytką ESP32) i wysyłaj powiadomienie push przez IFTTT lub bot Telegram gdy czujnik wykryje ruch. Połącz kilka czujników PIR na różnych pinach i zrób strefowy system alarmowy.
Arduino
2 maja 2026
⏱ 20 min czytania
Automatyczny sterownik nawadniania ogrodu z panelem webowym
Koszt: ok. 90–130 zł | Poziom: Średniozaawansowany | Czas: pół dnia
Koniec z zapominaniem o podlewaniu ogrodu. Ten projekt pozwala ustawić harmonogram nawadniania i sterować zaworami przez przeglądarkę z dowolnego urządzenia w sieci domowej. Używamy ESP32 jako serwera HTTP, przekaźnika do sterowania zaworem solenoidowym i opcjonalnie czujnika wilgotności gleby, który wyłącza podlewanie gdy gleba jest mokra.
Architektura systemu
Kluczowy fragment kodu – serwer HTTP
#include <WiFi.h>
#include <WebServer.h>
#include <RTClib.h>
WebServer server(80);
RTC_DS3231 rtc;
const int RELAY_PIN = 26;
const int SOIL_PIN = 34; // ADC
bool wateringOn = false;
int startHour = 6;
int durationMin = 20;
// HTML panelu sterowania (minified)
const char* INDEX_HTML = R"rawliteral(
<!DOCTYPE html><html lang='pl'><head>
<meta charset='UTF-8'><meta name='viewport' content='width=device-width,initial-scale=1'>
<title>Nawadnianie</title>
<style>body{font-family:sans-serif;max-width:400px;margin:2rem auto;background:#111;color:#eee;padding:1rem}
button{padding:.8rem 1.5rem;border:none;border-radius:8px;cursor:pointer;font-size:1rem;margin:.3rem}
.on{background:#22c55e;color:#000}.off{background:#ef4444;color:#fff}
.card{background:#1a2030;border:1px solid #334;border-radius:10px;padding:1rem;margin:.8rem 0}
input[type=number]{background:#222;color:#fff;border:1px solid #444;border-radius:6px;padding:.4rem;width:60px}</style>
</head><body>
<h2>🌱 Sterownik nawadniania</h2>
<div class='card' id='status'>Ładowanie...</div>
<div class='card'>
<p>Godzina startu: <input type='number' id='sh' min='0' max='23' value='6'> : 00</p>
<p>Czas trwania: <input type='number' id='dur' min='1' max='120' value='20'> minut</p>
<button class='on' onclick='setSchedule()'>Ustaw harmonogram</button>
</div>
<div class='card'>
<button class='on' onclick='ctrl(1)'>💧 Włącz teraz</button>
<button class='off' onclick='ctrl(0)'>⏹ Wyłącz</button>
</div>
<script>
function ctrl(s){fetch('/control?state='+s).then(r=>r.text()).then(t=>document.getElementById('status').innerText=t)}
function setSchedule(){fetch('/schedule?hour='+document.getElementById('sh').value+'&dur='+document.getElementById('dur').value)}
setInterval(()=>fetch('/status').then(r=>r.json()).then(d=>{
document.getElementById('status').innerHTML=
'💧 Zawór: '+(d.valve?'<b style=color:#22c55e>OTWARTY</b>':'ZAMKNIĘTY')+
'<br>🌡 Wilgotność gleby: '+d.soil+'%'+
'<br>⏰ Czas: '+d.time
}),3000);
</script></body></html>)rawliteral";
String getSoilPercent() {
int raw = analogRead(SOIL_PIN);
int pct = map(raw, 4095, 1500, 0, 100);
return String(constrain(pct, 0, 100));
}
void handleRoot() { server.send(200, "text/html", INDEX_HTML); }
void handleControl() {
bool s = server.arg("state") == "1";
digitalWrite(RELAY_PIN, s ? LOW : HIGH);
wateringOn = s;
server.send(200, "text/plain", s ? "Nawadnianie włączone" : "Nawadnianie wyłączone");
}
void handleStatus() {
DateTime now = rtc.now();
char timebuf[9]; sprintf(timebuf, "%02d:%02d:%02d", now.hour(), now.minute(), now.second());
String json = "{\"valve\":" + String(wateringOn ? "true" : "false") +
",\"soil\":" + getSoilPercent() +
",\"time\":\"" + timebuf + "\"}";
server.send(200, "application/json", json);
}
void setup() {
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, HIGH); // przekaźnik NC – wysoki = otwarty obwód
rtc.begin();
WiFi.begin("TwojeSSID", "TwojeHaslo");
while (WiFi.status() != WL_CONNECTED) delay(500);
Serial.println("IP: " + WiFi.localIP().toString());
server.on("/", handleRoot);
server.on("/control", handleControl);
server.on("/status", handleStatus);
server.begin();
}
void loop() {
server.handleClient();
// Sprawdź harmonogram
DateTime now = rtc.now();
if (now.hour() == startHour && now.minute() == 0 && now.second() < 10) {
int soil = analogRead(SOIL_PIN);
if (soil > 2500) { // gleba sucha
digitalWrite(RELAY_PIN, LOW);
wateringOn = true;
delay((long)durationMin * 60000L);
digitalWrite(RELAY_PIN, HIGH);
wateringOn = false;
}
}
}
Narzędzia
25 kwietnia 2026
⏱ 10 min czytania
Arduino w VS Code – porzuć stare IDE raz na zawsze
Oficjalne Arduino IDE 2.x jest znacznie lepsze niż jego poprzednik, ale VS Code z Arduino CLI oferuje to, czego żadne dedykowane IDE nie da: pełne podpowiedzi IntelliSense, świetne zarządzanie plikami projektu, integrację z Git, terminal wbudowany i możliwość pracy z dowolnym zestawem narzędzi. Jeśli programujesz już w VS Code w innych językach – zostań tam na zawsze.
Instalacja krok po kroku
1
Zainstaluj Arduino CLI
Pobierz plik binarny ze strony arduino.github.io/arduino-cli lub przez menedżer pakietów:
# Linux / macOS
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
# Windows (PowerShell)
winget install Arduino.ArduinoCLI
2
Zainstaluj rozszerzenie Arduino w VS Code
W VS Code otwórz Extensions (Ctrl+Shift+X) i wyszukaj Arduino od Microsoft. Po zainstalowaniu wejdź w ustawienia rozszerzenia i ustaw ścieżkę do arduino-cli.
3
Skonfiguruj profil tablicy
# Dodaj rdzeń dla Arduino AVR (Uno, Nano, Mega)
arduino-cli core install arduino:avr
# Dla ESP8266
arduino-cli config add board_manager.urls \
https://arduino.esp8266.com/stable/package_esp8266com_index.json
arduino-cli core install esp8266:esp8266
# Dla ESP32
arduino-cli config add board_manager.urls \
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json
arduino-cli core install esp32:esp32
4
Skompiluj i wgraj przez CLI
# Kompilacja (fqbn = fully qualified board name)
arduino-cli compile --fqbn arduino:avr:uno MojProjekt/
# Wykryj port COM/ttyUSB
arduino-cli board list
# Wgraj firmware
arduino-cli upload -p /dev/ttyUSB0 --fqbn arduino:avr:uno MojProjekt/
# Monitor szeregowy
arduino-cli monitor -p /dev/ttyUSB0 --config baudrate=9600
💡 Tip pro: Dodaj plik .clangd do projektu z flagami kompilatora AVR, a IntelliSense przestanie pokazywać fałszywe błędy dla typów specyficznych dla Arduino (byte, String, Serial).