// ==================== MODUL 4 - SENZORJI VITRINE Z DS18B20 ====================
#include <esp_now.h>
#include <esp_wifi.h>
#include <WiFi.h>
#include <DHT.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// ==================== DEFINICIJE PINOV ====================
#define DHTPIN 14
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
#define LDR_PIN 4
#define SOIL_MOISTURE_PIN 5
// DS18B20 senzor temperature zemlje
#define ONE_WIRE_BUS 10
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature soilTempSensor(&oneWire);
#define MODULE_ID 4
// MAC naslov glavnega sistema (master ESP32)
// PREVERITE TA NASLOV - MORA BITI PRAVILEN!
uint8_t masterMAC[] = {0xB4, 0x3A, 0x45, 0xF3, 0xEB, 0xF0};
const unsigned long SEND_INTERVAL = 10000; // 10 sekund
// ==================== GLOBALNE SPREMENLJIVKE ====================
float airTemperature = 0;
float airHumidity = 0;
int lightPercent = 0;
float soilTemperature = 0;
int soilMoisturePercent = 0;
bool dhtInitialized = false;
bool ds18b20Initialized = false;
// Kalibracijske vrednosti
int ldrMinADC = 500;
int ldrMaxADC = 3000;
int SOIL_DRY_VALUE = 4095;
int SOIL_WET_VALUE = 1500;
unsigned long lastSendTime = 0;
unsigned long lastSensorRead = 0;
#define SENSOR_READ_INTERVAL 2000
// ==================== FUNKCIJE ZA BRANJE SENZORJEV ====================
void readDHT22() {
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(t) || isnan(h)) {
delay(100);
h = dht.readHumidity();
t = dht.readTemperature();
}
if (!isnan(t) && !isnan(h)) {
airTemperature = t;
airHumidity = h;
Serial.printf(" DHT22: T=%.1f°C, H=%.1f%%\n", airTemperature, airHumidity);
dhtInitialized = true;
} else {
Serial.println(" DHT22: Napaka pri branju!");
}
}
void readLDR() {
int rawValue = analogRead(LDR_PIN);
rawValue = constrain(rawValue, 0, 4095);
if (ldrMaxADC > ldrMinADC) {
lightPercent = map(rawValue, ldrMinADC, ldrMaxADC, 100, 0);
} else {
lightPercent = map(rawValue, 500, 3000, 100, 0);
}
lightPercent = constrain(lightPercent, 0, 100);
Serial.printf(" LDR: ADC=%d, Light=%d%%\n", rawValue, lightPercent);
}
void readSoilMoisture() {
int rawValue = analogRead(SOIL_MOISTURE_PIN);
rawValue = constrain(rawValue, 0, 4095);
if (SOIL_DRY_VALUE > SOIL_WET_VALUE) {
soilMoisturePercent = map(rawValue, SOIL_DRY_VALUE, SOIL_WET_VALUE, 0, 100);
} else {
soilMoisturePercent = map(rawValue, 4095, 1500, 0, 100);
}
soilMoisturePercent = constrain(soilMoisturePercent, 0, 100);
Serial.printf(" Soil Moisture: ADC=%d, Percent=%d%%\n", rawValue, soilMoisturePercent);
}
void readSoilTemperature() {
// Zahtevaj temperaturo od DS18B20
soilTempSensor.requestTemperatures();
// Majhna pavza za konverzijo (DS18B20 potrebuje do 750ms)
delay(100);
// Preberi temperaturo v stopinjah Celzija
float temp = soilTempSensor.getTempCByIndex(0);
// Preveri, ali je odčitek veljaven (DS18B20 vrne -127 če ni senzorja)
if (temp != DEVICE_DISCONNECTED_C && temp > -50 && temp < 150) {
soilTemperature = temp;
ds18b20Initialized = true;
Serial.printf(" DS18B20 (zemlja): %.1f°C\n", soilTemperature);
} else {
soilTemperature = 0;
Serial.println(" DS18B20: Napaka pri branju ali senzor ni priklopljen!");
}
}
void readAllSensors() {
Serial.println("\n📊 Branje senzorjev:");
readDHT22();
readLDR();
readSoilMoisture();
readSoilTemperature(); // NOVO: branje temperature zemlje
}
// ==================== POŠILJANJE PODATKOV ====================
void sendDataToMaster() {
if (!dhtInitialized && airTemperature == 0 && airHumidity == 0) {
Serial.println("⚠ DHT22 ni inicializiran, preskakujem pošiljanje");
return;
}
// Format: M4:temp_zraka,vlaga_zraka,tlak_zraka,svetloba,temp_zemlje,vlaga_tal,TVOC,CO2,CH2O
// TVOC, CO2 in CH2O so 0 (nimamo senzorjev za te parametre)
char buffer[256];
snprintf(buffer, sizeof(buffer),
"M4:%.1f,%.1f,1013.2,%d,%.1f,%d,0,0,0",
airTemperature, airHumidity, lightPercent,
soilTemperature, soilMoisturePercent);
Serial.printf("\n📡 Pošiljam podatke: %s\n", buffer);
Serial.printf(" → Temp zraka: %.1f°C\n", airTemperature);
Serial.printf(" → Vlaga zraka: %.1f%%\n", airHumidity);
Serial.printf(" → Svetloba: %d%%\n", lightPercent);
Serial.printf(" → Temp zemlje: %.1f°C\n", soilTemperature);
Serial.printf(" → Vlaga tal: %d%%\n", soilMoisturePercent);
esp_err_t result = esp_now_send(masterMAC, (uint8_t*)buffer, strlen(buffer));
if (result == ESP_OK) {
Serial.println(" ✅ Podatki poslani!");
} else {
Serial.printf(" ❌ Napaka pri pošiljanju: %d\n", result);
if (result == ESP_ERR_ESPNOW_NOT_FOUND) {
Serial.println(" → Peer ne obstaja, poskusim ponovno dodati...");
esp_now_peer_info_t peerInfo;
memset(&peerInfo, 0, sizeof(peerInfo));
memcpy(peerInfo.peer_addr, masterMAC, 6);
peerInfo.channel = 1;
peerInfo.encrypt = false;
peerInfo.ifidx = WIFI_IF_STA;
if (esp_now_add_peer(&peerInfo) == ESP_OK) {
Serial.println(" ✅ Peer ponovno dodan!");
// Ponovni poskus pošiljanja
result = esp_now_send(masterMAC, (uint8_t*)buffer, strlen(buffer));
if (result == ESP_OK) {
Serial.println(" ✅ Podatki poslani (po ponovnem dodajanju)!");
}
}
}
}
}
// ==================== INICIALIZACIJA ====================
void initSensors() {
Serial.println("\n🔧 INICIALIZACIJA SENZORJEV");
Serial.print(" DHT22... ");
dht.begin();
delay(1000);
float testH = dht.readHumidity();
float testT = dht.readTemperature();
if (!isnan(testT) && !isnan(testH)) {
dhtInitialized = true;
airTemperature = testT;
airHumidity = testH;
Serial.printf("OK (T=%.1f°C, H=%.1f%%)\n", testT, testH);
} else {
Serial.println("NAPAKA!");
dhtInitialized = false;
}
Serial.print(" DS18B20 (temperatura zemlje)... ");
soilTempSensor.begin();
// Preveri, ali je DS18B20 priklopljen
int deviceCount = soilTempSensor.getDeviceCount();
if (deviceCount > 0) {
Serial.printf("OK (%d naprav najdenih)\n", deviceCount);
// Počakaj, da se DS18B20 stabilizira PRED prvo meritvijo
Serial.print(" Počakam na stabilizacijo DS18B20... ");
delay(750); // Počakaj da se senzor stabilizira
// Zahtevaj temperaturo
soilTempSensor.requestTemperatures();
delay(100); // Počakaj na konverzijo
// Preberi prvo temperaturo za test
float testSoilTemp = soilTempSensor.getTempCByIndex(0);
if (testSoilTemp != DEVICE_DISCONNECTED_C && testSoilTemp > -50 && testSoilTemp < 150) {
ds18b20Initialized = true;
soilTemperature = testSoilTemp;
Serial.printf("OK (%.1f°C)\n", testSoilTemp);
} else {
// Poskusi še enkrat z malo daljšim čakanjem
Serial.print("Prvi poskus neuspešen, poskusim znova... ");
delay(500);
soilTempSensor.requestTemperatures();
delay(100);
testSoilTemp = soilTempSensor.getTempCByIndex(0);
if (testSoilTemp != DEVICE_DISCONNECTED_C && testSoilTemp > -50 && testSoilTemp < 150) {
ds18b20Initialized = true;
soilTemperature = testSoilTemp;
Serial.printf("OK (%.1f°C)\n", testSoilTemp);
} else {
Serial.println("NAPAKA - DS18B20 ne odgovarja!");
Serial.println(" Preverite: pull-up upor, povezave in napajanje");
ds18b20Initialized = false;
}
}
} else {
Serial.println("NAPAKA - DS18B20 ni najden!");
Serial.println(" Preverite: pull-up upor, povezave in napajanje");
ds18b20Initialized = false;
}
Serial.print(" LDR... ");
pinMode(LDR_PIN, INPUT);
Serial.println("OK");
Serial.print(" Soil Moisture... ");
pinMode(SOIL_MOISTURE_PIN, INPUT);
Serial.println("OK");
}
void initESPNow() {
Serial.println("\n🔧 INICIALIZACIJA ESP-NOW");
WiFi.mode(WIFI_STA);
delay(100);
// ========== POMEMBNO: KANAL 1 (ISTI KOT GLAVNI SISTEM IN OSTALI MODULI) ==========
esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
esp_wifi_set_promiscuous(false);
Serial.println(" 📡 Kanal nastavljen na 1");
if (esp_now_init() != ESP_OK) {
Serial.println(" ❌ Napaka pri inicializaciji ESP-NOW!");
return;
}
Serial.println(" ✅ ESP-NOW inicializiran!");
// Dodaj master kot peerja - KANAL 1
esp_now_peer_info_t peerInfo;
memset(&peerInfo, 0, sizeof(peerInfo));
memcpy(peerInfo.peer_addr, masterMAC, 6);
peerInfo.channel = 1;
peerInfo.encrypt = false;
peerInfo.ifidx = WIFI_IF_STA;
if (esp_now_add_peer(&peerInfo) == ESP_OK) {
Serial.println(" ✅ Peer (glavni sistem) dodan!");
Serial.print(" MAC naslov glavnega sistema: ");
for(int i = 0; i < 6; i++) {
Serial.printf("%02X", masterMAC[i]);
if(i < 5) Serial.print(":");
}
Serial.println();
} else {
Serial.println(" ❌ Napaka pri dodajanju peer-ja!");
// Poskusi z drugačno konfiguracijo
memset(&peerInfo, 0, sizeof(peerInfo));
memcpy(peerInfo.peer_addr, masterMAC, 6);
peerInfo.channel = 0; // kanal 0 pomeni "trenutni kanal"
peerInfo.encrypt = false;
peerInfo.ifidx = WIFI_IF_STA;
if (esp_now_add_peer(&peerInfo) == ESP_OK) {
Serial.println(" ✅ Peer (glavni sistem) dodan z kanalom 0!");
}
}
}
// ==================== DIAGNOSTIČNE FUNKCIJE ====================
void printDiagnostics() {
Serial.println("\n╔════════════════════════════════════════════════════╗");
Serial.println("║ DIAGNOSTIKA MODULA 4 ║");
Serial.println("╚════════════════════════════════════════════════════╝");
Serial.printf("\n📡 WiFi status: %d\n", WiFi.status());
Serial.printf("📡 MAC naslov: %s\n", WiFi.macAddress().c_str());
Serial.printf("📡 WiFi kanal: %d\n", WiFi.channel());
Serial.println("\n🔌 Senzorji:");
Serial.printf(" DHT22: %s\n", dhtInitialized ? "✓ OK" : "✗ NAPAKA");
Serial.printf(" DS18B20: %s\n", ds18b20Initialized ? "✓ OK" : "✗ NAPAKA");
Serial.printf(" LDR: ✓ OK\n");
Serial.printf(" Soil Moisture: ✓ OK\n");
Serial.printf("\n📊 Zadnje meritve:\n");
Serial.printf(" Temp zraka: %.1f°C\n", airTemperature);
Serial.printf(" Vlaga zraka: %.1f%%\n", airHumidity);
Serial.printf(" Svetloba: %d%%\n", lightPercent);
Serial.printf(" Temp zemlje: %.1f°C\n", soilTemperature);
Serial.printf(" Vlaga tal: %d%%\n", soilMoisturePercent);
Serial.print("\n🎯 MAC naslov glavnega sistema: ");
for(int i = 0; i < 6; i++) {
Serial.printf("%02X", masterMAC[i]);
if(i < 5) Serial.print(":");
}
Serial.println();
bool peerExists = esp_now_is_peer_exist(masterMAC);
Serial.printf("Peer obstaja: %s\n", peerExists ? "DA" : "NE");
Serial.println("\n╔════════════════════════════════════════════════════╗");
Serial.println("║ KONEC DIAGNOSTIKE ║");
Serial.println("╚════════════════════════════════════════════════════╝\n");
}
// ==================== SETUP ====================
void setup() {
Serial.begin(115200);
// POMEMBNO: Počakaj, da se Serial monitor priklopi
// To prepreči napačne znake na začetku
delay(2000); // Počakaj 2 sekund
Serial.println("\n\n╔════════════════════════════════════════════════════╗");
Serial.println("║ MODUL 4 - SENZORJI VITRINE Z DS18B20 ║");
Serial.println("║ (KANAL 1 - ESP-NOW) ║");
Serial.println("╚════════════════════════════════════════════════════╝\n");
// Inicializacija WiFi
WiFi.mode(WIFI_STA);
delay(100);
Serial.print("📡 MAC naslov Modula 4: ");
Serial.println(WiFi.macAddress());
// Inicializacija senzorjev
initSensors();
// Inicializacija ESP-NOW
initESPNow();
// Počakaj, da se DS18B20 stabilizira (dodatno varnost)
delay(500);
// Začetne meritve
Serial.println("\n📊 Začetne meritve:");
readAllSensors();
// Diagnostika
printDiagnostics();
Serial.println("\n✅ Modul 4 pripravljen!");
Serial.println("📡 Kanal: 1 (isti kot glavni sistem)");
Serial.println("📡 Pošiljanje podatkov vsakih 10 sekund...");
Serial.println("🌡️ DS18B20 meri temperaturo zemlje na pinu 10\n");
lastSendTime = millis();
lastSensorRead = millis();
}
// ==================== LOOP ====================
void loop() {
unsigned long now = millis();
// ========== PREVERI IN POPRAVI KANAL NA 1 ==========
static unsigned long lastChannelCheck = 0;
if (now - lastChannelCheck > 10000) {
int currentChannel = WiFi.channel();
if (currentChannel != 1) {
Serial.printf("⚠ Kanal je %d, nastavljam na 1...\n", currentChannel);
WiFi.setChannel(1);
delay(50);
esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);
esp_wifi_set_promiscuous(false);
if (WiFi.channel() == 1) {
Serial.println("✓ Kanal uspešno nastavljen na 1");
}
}
lastChannelCheck = now;
}
// ========== PREVERI ALI PEER OBSTAJA ==========
static unsigned long lastPeerCheck = 0;
if (now - lastPeerCheck > 30000) {
if (!esp_now_is_peer_exist(masterMAC)) {
Serial.println("⚠ Peer ne obstaja - ponovno dodajanje...");
esp_now_peer_info_t peerInfo;
memset(&peerInfo, 0, sizeof(peerInfo));
memcpy(peerInfo.peer_addr, masterMAC, 6);
peerInfo.channel = 1;
peerInfo.encrypt = false;
peerInfo.ifidx = WIFI_IF_STA;
if (esp_now_add_peer(&peerInfo) == ESP_OK) {
Serial.println("✓ Peer ponovno dodan");
} else {
Serial.println("✗ Napaka pri ponovnem dodajanju peer-ja!");
}
}
lastPeerCheck = now;
}
// ========== BRANJE SENZORJEV ==========
if (now - lastSensorRead > SENSOR_READ_INTERVAL) {
readAllSensors();
lastSensorRead = now;
}
// ========== POŠILJANJE PODATKOV ==========
if (now - lastSendTime > SEND_INTERVAL) {
sendDataToMaster();
lastSendTime = now;
}
delay(50);
}GRETJE
HLAJENJE