Skip to main content

12 Channel Datalogger

Code tham khảo

// Enable debug console
// Set CORE_DEBUG_LEVEL = 3 first
#define ERA_DEBUG


/* Select ERa host location (VN: Viet Nam, SG: Singapore) */
#define ERA_LOCATION_VN


// You should get Auth Token in the ERa App or ERa Dashboard
// and not share this token with anyone.
#define ERA_AUTH_TOKEN "your_token"

#include <Arduino.h>
#include <ERa.hpp>
#include <Automation/ERaSmart.hpp>
#include <Time/ERaEspTime.hpp>
#include "MAX6675.h"
#include <Wire.h>
#include "PCF8574.h"

PCF8574 PCF(0x27);

#define LED_PIN  2

#define RELAY_1  27
#define RELAY_2  26
#define RELAY_3  25

#define RELAY_9  13

#define RELAY_10  19
#define RELAY_11  18
#define RELAY_12  17
#define RELAY_13  16

#define RELAY_14  4

// biến lưu trạng thái relay
int Relay1 = 0, Relay2 = 0, Relay3 = 0, Relay4 = 0, Relay5 = 0, Relay6 = 0, Relay7 = 0, Relay8 = 0, Relay9 = 0, Relay10 = 0, Relay11 = 0, Relay12 = 0, Relay13 = 0;
int* relayStates[] = {
    &Relay1, &Relay2, &Relay3,
    &Relay4, &Relay5, &Relay6, &Relay7, &Relay8,
    &Relay9, &Relay10, &Relay11, &Relay12, &Relay13
};
// biến lưu giá trị nhiệt độ từng kênh
float tempRelay1 = 0, tempRelay2 = 0, tempRelay3 = 0, tempRelay4 = 0, tempRelay5 = 0, tempRelay6 = 0, tempRelay7 = 0, tempRelay8 = 0, tempRelay9 = 0, tempRelay10 = 0, tempRelay11 = 0, tempRelay12 = 0, tempRelay13 = 0;
// biến lưu nhiệt độ cao/thấp nhất
float maxTemp = 0, minTemp = 0;
// biến để chọn chế đô đọc
int readAllEnabled = 0;
int readEnabled = 0;

// Mảng chứa danh sách relay
static int relaySteps[] = {
    RELAY_1, RELAY_2, RELAY_3, // GPIO
    100, 101, 102, 103, 104,   // PCF8574: 100 + index
    RELAY_9, RELAY_10, RELAY_11, RELAY_12, RELAY_13
};
const int relayNumSteps = 13;
// Biến trạng thái cho quy trình non-blocking đọc toàn bộ
int relayStepAll = 0;
bool readAllRelayFlag = false; // Đang bật relay hay đang đọc nhiệt độ

// Biến trạng thái cho quy trình non-blocking đọc chọn lọc
int relayStep = 0;
bool relayReadFlag  = false; // Biến trạng thái cho quy trình non-blocking đọc 
unsigned long lastMillisRelay = 0;

unsigned long previousMillis_V20 = 0;
const long interval_V20 = 4000;

//max6675
const int dataPin   = 12;
const int clockPin  = 14;
const int selectPin = 15;

MAX6675 thermoCouple(selectPin, dataPin, clockPin);

const char ssid[] = "Your_SSID";
const char pass[] = "Your_PASSWORD";
  
ERaEspTime syncTime;
ERaSmart smart(ERa, syncTime);
// đọc nhiệt độ toàn bộ
void readAllNonBlocking() {
    static unsigned long lastMillis = 0;
    
    if (readAllEnabled == 1) {
        if (!readAllRelayFlag) {
            turnOffAllRelays();
            relayOn(relaySteps[relayStepAll]);
            //bật cờ để lấy nhiệt độ
            readAllRelayFlag = true;
            lastMillis = millis(); 
        } else {
            if (millis() - lastMillis >= 300) {  
                sendTempStatus(getTemperature());                              
                // Chuyển sang relay tiếp theo
                relayStepAll++;
                if (relayStepAll >= relayNumSteps) relayStepAll = 0; // relayStep quá 13 thì reset về 0 để đọc lại
                readAllRelayFlag = false; // Tắt cờ để bật kênh khác
            }
        }
    }
}

// đọc nhiệt độ các kênh được chọn
void readRelayNonBlocking() {
    if (readEnabled == 1){
        // Tìm relay đầu tiên còn trạng thái = 1 lúc mới bắt đầu
        if (!relayReadFlag) {
            int findStep = relayStep;
            bool found = false;
            for (int i = 0; i < relayNumSteps; ++i) {
                if (*relayStates[findStep] == 1) {
                    found = true;
                    break;
                }
                findStep = (findStep + 1) % relayNumSteps;
            }
            if (!found) {
                // Không còn relay nào cần đọc, reset trạng thái và dừng lại
                relayStep = 0;
                readEnabled = 0;    // Kết thúc  
                return;
            }
            relayStep = findStep;
    
            turnOffAllRelays();
            relayOn(relaySteps[relayStep]);
            relayReadFlag = true;
            lastMillisRelay = millis();
        } else {
            if (millis() - lastMillisRelay >= 300) {
                // Đợi đủ 200ms, lấy nhiệt độ & gửi
                sendTempStatus(getTemperature());
                *relayStates[relayStep] = 0; // Đọc xong thì clear trạng thái kênh
    
                // Kiểm tra còn relay nào cần đọc không
                int nextStep = (relayStep + 1) % relayNumSteps;
                bool found = false;
                for (int i = 0; i < relayNumSteps; ++i) {
                    if (*relayStates[nextStep] == 1) {
                        relayStep = nextStep;
                        found = true;
                        break;
                    }
                    nextStep = (nextStep + 1) % relayNumSteps;
                }
                relayReadFlag = false; 
    
                if (!found) {
                    // Nếu không còn relay nào cần đọc, out hàm
                    readEnabled = 0;
                }
            }
        }
    }
}
//lấy nhiệt độ cao nhất
float getMaxTemperature() {
  float temps[] = {tempRelay1, tempRelay2, tempRelay3, tempRelay4, tempRelay5, tempRelay6, tempRelay7, tempRelay8, tempRelay9, tempRelay10, tempRelay11, tempRelay12, tempRelay13};
  maxTemp = 0;
  for (int i = 0; i < 13; i++) {
    if (temps[i] > 0 && temps[i] <= 150 && temps[i] > maxTemp) {
      maxTemp = temps[i];
    }
  }
  return maxTemp;
}
//lấy nhiệt độ thấp nhất
float getMinTemperature() {
  float temps[] = {tempRelay1, tempRelay2, tempRelay3, tempRelay4, tempRelay5, tempRelay6, tempRelay7, tempRelay8, tempRelay9, tempRelay10, tempRelay11, tempRelay12, tempRelay13};
  minTemp = 151;
  for (int i = 0; i < 13; i++) {
    if (temps[i] > 0 && temps[i] <= 150 && temps[i] < minTemp) {
      minTemp = temps[i];
    }
  }
  return minTemp;
}
//tắt hết relay
void turnOffAllRelays() {
  digitalWrite(RELAY_1, LOW);
  digitalWrite(RELAY_2, LOW);
  digitalWrite(RELAY_3, LOW);
  PCF.write(0, LOW);
  PCF.write(1, LOW);
  PCF.write(2, LOW);
  PCF.write(3, LOW);
  PCF.write(4, LOW);
  digitalWrite(RELAY_9, LOW);
  digitalWrite(RELAY_10, LOW);
  digitalWrite(RELAY_11, LOW);
  digitalWrite(RELAY_12, LOW);
  digitalWrite(RELAY_13, LOW);
}

void relayOn(int relayPin) {
    if (relayPin < 100)
        digitalWrite(relayPin, HIGH);
    else
        PCF.write(relayPin - 100, HIGH);
}

void relayOff(int relayPin) {
    if (relayPin < 100)
        digitalWrite(relayPin, LOW);
    else
        PCF.write(relayPin - 100, LOW);
}

//gửi nhiệt độ
void sendTempStatus(float tempX) {
  if (digitalRead(RELAY_1) == 1) {
    tempRelay1 = tempX;
    if (tempRelay1 < 0 || tempRelay1 > 150) {
      tempRelay1 = 0;
    }
    ERa.virtualWrite(V101, tempRelay1);
  }

  if (digitalRead(RELAY_2) == 1) {
    tempRelay2 = tempX;
    if (tempRelay2 < 0 || tempRelay2 > 150) {
      tempRelay2 = 0;
    }
    ERa.virtualWrite(V102, tempRelay2);
  }

  if (digitalRead(RELAY_3) == 1) {
    tempRelay3 = tempX;
    if (tempRelay3 < 0 || tempRelay3 > 150) {
      tempRelay3 = 0;
    }
    ERa.virtualWrite(V103, tempRelay3);
  }

  if (PCF.read(0) == 1) {
    tempRelay4 = tempX;
    if (tempRelay4 < 0 || tempRelay4 > 150) {
      tempRelay4 = 0;
    }
    ERa.virtualWrite(V104, tempRelay4);
  }

  if (PCF.read(1) == 1) {
    tempRelay5 = tempX;
    if (tempRelay5 < 0 || tempRelay5 > 150) {
      tempRelay5 = 0;
    }
    ERa.virtualWrite(V105, tempRelay5);
  }

  if (PCF.read(2) == 1) {
    tempRelay6 = tempX;
    if (tempRelay6 < 0 || tempRelay6 > 150) {
      tempRelay6 = 0;
    }
    ERa.virtualWrite(V106, tempRelay6);
  }

  if (PCF.read(3) == 1) {
    tempRelay7 = tempX;
    if (tempRelay7 < 0 || tempRelay7 > 150) {
      tempRelay7 = 0;
    }
    ERa.virtualWrite(V107, tempRelay7);
  }

  if (PCF.read(4) == 1) {
    tempRelay8 = tempX;
    if (tempRelay8 < 0 || tempRelay8 > 150) {
      tempRelay8 = 0;
    }
    ERa.virtualWrite(V108, tempRelay8);
  }

  if (digitalRead(RELAY_9) == 1) {
    tempRelay9 = tempX;
    if (tempRelay9 < 0 || tempRelay9 > 150) {
      tempRelay9 = 0;
    }
    ERa.virtualWrite(V109, tempRelay9);
  }

  if (digitalRead(RELAY_10) == 1) {
    tempRelay10 = tempX;
    if (tempRelay10 < 0 || tempRelay10 > 150) {
      tempRelay10 = 0;
    }
    ERa.virtualWrite(V110, tempRelay10);
  }

  if (digitalRead(RELAY_11) == 1) {
    tempRelay11 = tempX;
    if (tempRelay11 < 0 || tempRelay11 > 150) {
      tempRelay11 = 0;
    }
    ERa.virtualWrite(V111, tempRelay11);
  }

  if (digitalRead(RELAY_12) == 1) {
    tempRelay12 = tempX;
    if (tempRelay12 < 0 || tempRelay12 > 150) {
      tempRelay12 = 0;
    }
    ERa.virtualWrite(V112, tempRelay12);
  }

  if (digitalRead(RELAY_13) == 1) {
    tempRelay13 = tempX;
    if (tempRelay13 < 0 || tempRelay13 > 150) {
      tempRelay13 = 0;
    }
    ERa.virtualWrite(V113, tempRelay13);
  }

  maxTemp = getMaxTemperature();
  minTemp = getMinTemperature();
  ERa.virtualWrite(V31, maxTemp);  // Gửi nhiệt độ lớn nhất
  ERa.virtualWrite(V32, minTemp);  // Gửi nhiệt độ nhỏ nhất
}
float getTemperature() {
    float sumTemp = 0;
    int count = 0;
    int maxTry = 10;
    while (count < 3 && maxTry > 0) {
        thermoCouple.read();
        float temp = thermoCouple.getCelsius() - 9;
        if (temp > 0 && temp < 150) {
            sumTemp += temp;
            count++;
            
        }
        delay(50); // Đợi cảm biến cập nhật
        maxTry--;
    }

    if (count == 0) return 0;

    return sumTemp / count;
}

ERA_WRITE(V1) {
  uint8_t value = param.getInt();
  Relay1 = value;
}
ERA_WRITE(V2) {
  uint8_t value = param.getInt();
  Relay2 = value;
}
ERA_WRITE(V3) {
  uint8_t value = param.getInt();
  Relay3 = value;
}
ERA_WRITE(V4) {
  uint8_t value = param.getInt();
  Relay4 = value;
}
ERA_WRITE(V5) {
  uint8_t value = param.getInt();
  Relay5 = value;
}
ERA_WRITE(V6) {
  uint8_t value = param.getInt();
  Relay6 = value;
}
ERA_WRITE(V7) {
  uint8_t value = param.getInt();
  Relay7 = value;
}
ERA_WRITE(V8) {
  uint8_t value = param.getInt();
  Relay8 = value;
}
ERA_WRITE(V9) {
  uint8_t value = param.getInt();
  Relay9 = value;
}
ERA_WRITE(V10) {
  uint8_t value = param.getInt();
  Relay10 = value;
}
ERA_WRITE(V11) {
  uint8_t value = param.getInt();
  Relay11 = value;
}
ERA_WRITE(V12) {
  uint8_t value = param.getInt();
  Relay12 = value;
}
ERA_WRITE(V13) {
  uint8_t value = param.getInt();
  Relay13 = value;
}
ERA_WRITE(V14) {
  uint8_t value = param.getInt();
  if (value == HIGH) {
    digitalWrite(RELAY_1, LOW);
    digitalWrite(RELAY_2, LOW);
    digitalWrite(RELAY_3, LOW);
    digitalWrite(RELAY_9, LOW);
    digitalWrite(RELAY_10, LOW);
    digitalWrite(RELAY_11, LOW);
    digitalWrite(RELAY_12, LOW);
    digitalWrite(RELAY_13, LOW);
  }
}
ERA_WRITE(V20) {
  uint8_t value = param.getInt();  
  readAllEnabled = param.getInt();
}
ERA_WRITE(V21) {
  uint8_t value = param.getInt();
  readEnabled = param.getInt();

}
ERA_WRITE(V31) {
}

ERA_WRITE(V32) {
}
// gửi trạng thái relay
void sendRelayStatus() {
  ERa.virtualWrite(V1, Relay1);
  ERa.virtualWrite(V2, Relay2);
  ERa.virtualWrite(V3, Relay3);
  ERa.virtualWrite(V4, Relay4);
  ERa.virtualWrite(V5, Relay5);
  ERa.virtualWrite(V6, Relay6);
  ERa.virtualWrite(V7, Relay7);
  ERa.virtualWrite(V8, Relay8);
  ERa.virtualWrite(V9, Relay9);
  ERa.virtualWrite(V10, Relay10);
  ERa.virtualWrite(V11, Relay11);
  ERa.virtualWrite(V12, Relay12);
  ERa.virtualWrite(V13, Relay13);
  ERa.virtualWrite(V21, readEnabled);
  ERa.virtualWrite(V20, readAllEnabled);
}

/* This function is triggered whenever an SMS is sent */
ERA_WRITE_SMS() {
  ERA_LOG("ERa", "Write SMS to %s: %s", to, message);
  return true;
}

/* This function will run every time ERa is connected */
ERA_CONNECTED() {
  ERA_LOG("ERa", "ERa connected!");
}

/* This function will run every time ERa is disconnected */
ERA_DISCONNECTED() {
  ERA_LOG("ERa", "ERa disconnected!");
}
void timerEvent() {
  sendRelayStatus();
  maxTemp = getMaxTemperature();
  minTemp = getMinTemperature();
  ERa.virtualWrite(V31, maxTemp);  // Gửi nhiệt độ lớn nhất
  ERa.virtualWrite(V32, minTemp);  // Gửi nhiệt độ nhỏ nhất
  float temp = thermoCouple.getCelsius();
  int status = thermoCouple.read();
  ERA_LOG("Status", "R1=%d R2=%d R3=%d R10=%d R11=%d R12=%d R13=%d TEMP=%f readALL=%d",
          digitalRead(RELAY_1),
          digitalRead(RELAY_2),
          digitalRead(RELAY_3),
          digitalRead(RELAY_9),
          digitalRead(RELAY_10),
          digitalRead(RELAY_11),
          digitalRead(RELAY_12),
          digitalRead(RELAY_13),
          temp,
          readAllEnabled);
  ERA_LOG("TEMP", "T1=%.2f T2=%.2f T3=%.2f T10=%.2f T11=%.2f T12=%.2f T13=%.2f | MAX=%.2f MIN=%.2f",
          tempRelay1,
          tempRelay2,
          tempRelay3,
          tempRelay9,
          tempRelay10,
          tempRelay11,
          tempRelay12,
          tempRelay13,
          maxTemp,
          minTemp
         );
  ERA_LOG("Timer", "Uptime: %d", ERaMillis() / 1000L);

}

void setup() {
  Wire.begin(32 , 33);
  PCF.begin();
  SPI.begin();

  thermoCouple.begin();
  thermoCouple.setSPIspeed(4000000);
  pinMode(RELAY_1, OUTPUT);
  pinMode(RELAY_2, OUTPUT);
  pinMode(RELAY_3, OUTPUT);
  pinMode(RELAY_9, OUTPUT);
  pinMode(RELAY_10, OUTPUT);
  pinMode(RELAY_11, OUTPUT);
  pinMode(RELAY_12, OUTPUT);
  pinMode(RELAY_13, OUTPUT);
  pinMode(RELAY_14, OUTPUT);

  PCF.write(0, LOW);
  PCF.write(1, LOW);
  PCF.write(2, LOW);
  PCF.write(3, LOW);
  PCF.write(4, LOW);

  /* Setup debug console */
#if defined(ERA_DEBUG)
  Serial.begin(115200);
#endif

  /* Setup pin mode led pin */
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);

  /* Set board id */
  // ERa.setBoardID("Board_1");
  
  /* Set API task size. If this function is enabled,
     the core API will run on a separate task after disconnecting from the server
     (suitable for edge automation).*/
  // ERa.setTaskSize(ERA_API_TASK_SIZE, true);

  /* Set scan WiFi. If activated, the board will scan
     and connect to the best quality WiFi. */
  ERa.setScanWiFi(true);

  /* Initializing the ERa library. */
  ERa.begin(ssid, pass);

  /* Setup timer called function every second */
  ERa.addInterval(1000L, timerEvent);
  ERa.addInterval(100L, readAllNonBlocking);
  ERa.addInterval(100L, readRelayNonBlocking); 
}
void loop() {
  ERa.run();
}

1/ Cài đặt thư viện ERA và các thư viện liên quan khác.

1.1 Thư viện Era

Mở Arduino IDE Tool  Manage Libraries

Tìm “Era EoH

Cài đặt thư viện Era của EoH.

image.png

·      Tương tự cài đặt các thư viện khác:

  •  Thư viện MAX6675 của Adafruit.
  • Thư viện PCF8574 của Rob Tillaart.

2/ Thiết lập kết nối với Era

2.1 Thiết lập Gateway trên Era

Truy cập vào trang https://e-ra.io/index.html trên trình duyệt

image.png

Click vào Sign In để đăng nhập/tạo tài khoản trên Era

image.png

Tạo một Unit mới trên Era: "Tất cả các bảng điều khiển Units" -> "Tạo địa điểm"

image.png

Chọn "Thiết lập thủ công" -> "Tiếp theo"

image.png

Tạo “Tên địa điểm”, “chọn vị trí” cho địa điểm -> “Tạo

image.pngVào mục "Tất cả gateways" -> "Tạo gateway mới

image.png

Tạo “Tên Gateway” (Ví dụ: 12 Channel Datalogger)
Chọn “Bo mạch” là ESP32

Chọn "Địa điểm" là địa điểm vừa tạo ở "Tất cả bảng điều khiển Units"

image.png

Click "Bước kế tiếp" để tới trang "Cài đặt thư viện"

image.png

Click "Bước kế tiếp" để tới trang "Mã nguồn"

image.png

Click vào “Sao chép token” để lấy token
Quay lại Arduino IDE, tìm dòng #define ERA_AUTH_TOKEN

Dán mã token đã copy ở trang ERA vào

image.png

Nhập wifi và mật khẩu vào 
const char ssid[] = "<tên wifi>";

const char pass[] = "<mật khẩu>";

image.png

Chọn board “ESP32-WROOM-DA Module

Chọn cổng COM, nạp Code trên Arduino IDE

image.png

Sau khi flash code thành công, quay lại trang ERA, nhấn “Bước kế tiếp” để đến với trang “Kích hoạt Gateway"

Sau khi Gateway kết nối thành công, chọn "Đi đến Gateway"

image.png

2.2/ Cấu hình trên ERa

Chuyển qua tab "Pin Input/Output", chọn "Setup"

image.png

Chọn "Thiết bị Input/Output mới"

image.png

Tạo tên thiết bị

image.png

image.png

Tạo chân ảo

image.png

image.png

Tương tự các chân ảo khác

image.png

Sau khi tạo chân ảo, click vào "hành động"

image.png

Cấu hình cho hành động mới

image.png

image.png

Cấu hình tương tự cho các chân ảo khác

image.png

Tiếp tục tạo "thiết bị Input/Output" mới

image.png

Tạo chân ảo cho thiết bị “TEMP

image.png

Sau khi hoàn tất

image.png

Chọn "quay về dashboard" để sửa bảng điều khiển

image.png

2.3/ Cấu hình thành phần để hiển thị nhiệt độ

Kéo Widget "Display" vào bảng điều khiển, chọn "Cài đặt widget"

image.png

Đặt "Tên widget", chọn "Khu vực" là khu vực đã tạo trên 

image.png

Click vào "Thêm" ở mục "Thiết bị hiển thị" 

Cấu hình thiết bị hiển thị mới như bên dưới:

image.png

Chọn "Data stream" là thiết bị vừa tạo để hiển thị data của thiết bị (Vd: TEMP RELAY 1)

image.png

Tương tự cho các kênh còn lại

image.png

2.4/ Cấu hình phần điều khiển

2.4.1/ Cấu hình chọn kênh

Kéo Widget "State grid" vào bảng điều khiển, chọn "Cài đặt widget"

image.png

Thay đổi thiết bị hiển thị như trong ảnh

image.png

"Cài đặt action"

image.png

Tương tự cho 12 kênh còn lại

2.4.2/ Cấu hình hành động lấy mẫu dữ liệu

Dùng 2 Widget "Control" vào bảng điều khiển để chọn chế độ.

image.png

Cấu hình cho 2 button như sau

image.png

image.png

Có thể cấu hình thêm history chart và thêm 2 value box để hiển nhiệt độ cao nhất và thấp nhất.

Chọn "Xong"

Trên bảng điều khiển sẽ xuất hiện Widget để điều khiển thiết bị vừa tạo 

image.png