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.
· 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
Click vào Sign In để đăng nhập/tạo tài khoản trên Era
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"
Chọn "Thiết lập thủ công" -> "Tiếp theo"
Tạo “Tên địa điểm”, “chọn vị trí” cho địa điểm -> “Tạo”
Vào mục "Tất cả gateways" -> "Tạo gateway mới"
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"
Click "Bước kế tiếp" để tới trang "Cài đặt thư viện"
Click "Bước kế tiếp" để tới trang "Mã nguồn"
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
Nhập wifi và mật khẩu vào
const char ssid[] = "<tên wifi>";
const char pass[] = "<mật khẩu>";
Chọn board “ESP32-WROOM-DA Module”
Chọn cổng COM, nạp Code trên Arduino IDE
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"
2.2/ Cấu hình trên ERa
Chuyển qua tab "Pin Input/Output", chọn "Setup"
Chọn "Thiết bị Input/Output mới"
Tạo tên thiết bị
Tạo chân ảo
Tương tự các chân ảo khác
Sau khi tạo chân ảo, click vào "hành động"
Cấu hình cho hành động mới
Cấu hình tương tự cho các chân ảo khác
Tiếp tục tạo "thiết bị Input/Output" mới
Tạo chân ảo cho thiết bị “TEMP”
Sau khi hoàn tất
Chọn "quay về dashboard" để sửa bảng điều khiển
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"
Đặt "Tên widget", chọn "Khu vực" là khu vực đã tạo trên
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:
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)
Tương tự cho các kênh còn lại
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"
Thay đổi thiết bị hiển thị như trong ảnh
"Cài đặt action"
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ế độ.
Cấu hình cho 2 button như sau
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