Hardware components | ||||||
![]() |
| × | 3 | |||
| × | 1 | ||||
| × | 1 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
![]() |
| |||||
Hand tools and fabrication machines | ||||||
![]() |
|
1.Software Development
Read more- Use version 2.3.6 of ArduinoIDE.
- This time, we will use three M5Stacks.
- The two M5Stacks read the sensor values and transmit them via ESP-NOW.(S1, S2)
- The remaining one is the receiving side.(R1)
- First, let's talk about the sender's side.
- Use a Grove cable to connect the ENVⅣ unit to S1. This unit can measure temperature and humidity. S1 and S2 sends data once every 10 seconds.
- Use a Grove cable to connect the ADT7410 to S2. This unit can measure surface temperature of object. The ADT7410 uses different calculation methods depending on the measurement mode. It took me a month to notice this. In addition, Grove cables will also need to be reconfigured.
- Receives data from S1 and S2 and automatically calculates the risk of condensation. Connect a 4channel relay and control the Patlite from the relay. There is one thing to note. This relay only operates on DC, so it is necessary to convert AC to DC.
- Create a box and insert R1 and Patlite. (I'm still finding my crafting sense)
- Two thresholds are set for this relay. If it is 85% or less, it will light up green. If it is between 85.1% and 95%, it will light up yellow. If it is 95.1% or more, it will light up red. If the risk exceeds 100%, condensation will occur as the saturated vapor content is exceeded. By the way, the text on the M5stack screen also changes color in sync with the Patlite.
1 / 8 • M5stack+ENVⅣ
M5stack + ADT7410
C/C++The surface temperature is measured once every 10 seconds.Communicate with the receiving side using ESP-NOW.
#include <M5Stack.h>
#include <Wire.h>
#include <WiFi.h>
#include <esp_now.h>
#include <esp_wifi.h>
#include "skADT7410.h"
#define WIFI_CHANNEL 1
#define ADT_ADDRESS 0x48
skADT7410 adt(ADT_ADDRESS);
typedef struct __attribute__((packed)) {
uint8_t id;
float sftemp;
} PacketADT;
uint8_t receiverMAC[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
void onSendStatus(const uint8_t *mac, esp_now_send_status_t status) {
Serial.printf("Send to %02X:%02X:%02X:%02X:%02X:%02X %s\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
status == ESP_NOW_SEND_SUCCESS ? "OK" : "FAILED");
}
void setup() {
M5.begin();
Serial.begin(115200);
Wire.begin();
if (adt.Begin() != 0) {
Serial.println("ADT7410 init failed");
while (true)
;
}
adt.ActionMode(ADT_MODE_CONTINUE);
WiFi.mode(WIFI_STA);
esp_wifi_set_channel(WIFI_CHANNEL, WIFI_SECOND_CHAN_NONE);
if (esp_now_init() != ESP_OK) {
Serial.println("ESP-NOW init error");
while (true)
;
}
esp_now_peer_info_t peer = {};
memcpy(peer.peer_addr, receiverMAC, 6);
peer.channel = WIFI_CHANNEL;
peer.encrypt = false;
esp_now_add_peer(&peer);
esp_now_register_send_cb(onSendStatus);
}
void loop() {
uint16_t data;
float sftemp = 0;
Wire.requestFrom(ADT_ADDRESS, 2); //2byte要求
data = Wire.read() << 8; //8bit上へ
data |= Wire.read();
data >>= 3; //3bit右に
if (data & 0x1000) { //13bitの計算式
sftemp = (float)(data - 8192) / 16.0; //負の場合
} else {
sftemp = (float)data / 16.0; //正の場合
}
Serial.print(sftemp);
PacketADT pkt;
pkt.id = 2;
pkt.sftemp = sftemp;
esp_now_send(receiverMAC, (uint8_t *)&pkt, sizeof(pkt));
Serial.printf("Sent[ADT] ID:%d sfTemp:%.2f C\n", pkt.id, pkt.sftemp);
delay(10000);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0, 0);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.printf("sfTemp keisoku");
M5.Lcd.setCursor(250, 210);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(PINK);
M5.Lcd.printf("MJ");
M5.Lcd.drawLine(5, 25, 310, 25, RED);
M5.Lcd.drawLine(5, 200, 310, 200, RED);
M5.Lcd.setCursor(0, 80);
M5.Lcd.setTextSize(3);
M5.Lcd.setTextColor(CYAN);
M5.Lcd.print("sfTemp: ");
M5.Lcd.print(sftemp);
M5.Lcd.println(" C");
}
M5stack + ENVⅣ
C/C++Temperature and humidity are retrieved from the ENV unit once every 10 seconds.Communicate with the receiving side using ESP-NOW.
#include <M5Stack.h>
#include <Wire.h>
#include <WiFi.h>
#include <esp_now.h>
#include <esp_wifi.h>
#include <SensirionI2cSht4x.h>
#define WIFI_CHANNEL 1
#ifdef NO_ERROR
#undef NO_ERROR
#endif
#define NO_ERROR 0
SensirionI2cSht4x sensor;
static char errorMessage[64];
static int16_t error;
typedef struct __attribute__((packed)){
uint8_t id;
float temp;
float hum;
}PacketENV;
uint8_t receiverMAC[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
void onSendStatus(const uint8_t *mac, esp_now_send_status_t status) {
Serial.printf("Send to %02X:%02X:%02X:%02X:%02X:%02X %s\n",
mac[0],mac[1],mac[2],mac[3],mac[4],mac[5],
status==ESP_NOW_SEND_SUCCESS?"OK":"FAILED");
}
void setup(){
M5.begin();
Serial.begin(115200);
Wire.begin();
sensor.begin(Wire, SHT40_I2C_ADDR_44);
sensor.softReset();
delay(10);
WiFi.mode(WIFI_STA);
esp_wifi_set_channel(WIFI_CHANNEL, WIFI_SECOND_CHAN_NONE);
if(esp_now_init() != ESP_OK){
Serial.println("ESP-NOW init error");
while(true);
}
esp_now_peer_info_t peer = {};
memcpy(peer.peer_addr, receiverMAC, 6);
peer.channel = WIFI_CHANNEL;
peer.encrypt = false;
esp_now_add_peer(&peer);
esp_now_register_send_cb(onSendStatus);
}
void loop(){
uint16_t data;
float temp;
float hum;
error = sensor.measureLowestPrecision(temp, hum);
if (error != NO_ERROR) {
Serial.print("Error trying to execute measureLowestPrecision(): ");
errorToString(error, errorMessage, sizeof errorMessage);
Serial.println(errorMessage);
return;
}
PacketENV pkt;
pkt.id =1;
pkt.temp = temp;
pkt.hum = hum;
esp_now_send(receiverMAC, (uint8_t*)&pkt, sizeof(pkt));
Serial.printf("Sent[ENV] ID:%d Temp:%.2f C\n Hum:%.2f\n " , pkt.id, pkt.temp, pkt.hum);
delay(10000);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setCursor(0,0);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.printf("TempHum keisoku");
M5.Lcd.setCursor(250,210);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(PINK);
M5.Lcd.printf("MJ");
M5.Lcd.drawLine(5, 25, 310, 25, RED);
M5.Lcd.drawLine(5, 200, 310, 200, RED);
M5.Lcd.setCursor(0, 80);
M5.Lcd.setTextSize(3);
M5.Lcd.setTextColor(CYAN);
M5.Lcd.print("Temp: ");
M5.Lcd.print(temp);
M5.Lcd.println(" C");
M5.Lcd.setCursor(0,110);
M5.Lcd.setTextSize(3);
M5.Lcd.setTextColor(CYAN);
M5.Lcd.print("Hum: ");
M5.Lcd.print(hum);
M5.Lcd.println(" %");
}
M5stack + relay + Patlite
C/C++Calculates the received data and displays the condensation risk. Controls the relay based on the condensation risk.
#include <M5Stack.h>
#include <WiFi.h>
#include <esp_wifi.h>
#include <esp_now.h>
#include "Module_4RELAY.h"
esp_now_peer_info_t slave;
typedef struct __attribute__((packed)) {
uint8_t id;
float temp;
float hum;
} PacketBase;
typedef struct __attribute__((packed)) {
uint8_t id;
float sftemp;
} PacketADT;
const int WIFI_CHANNEL = 1;
float latestTemp = 0.0;
float latestHum = 0.0;
float latestSftemp = 0.0;
bool envReceived = false;
bool adtReceived = false;
const float THRESHOLD1 = 85.0; //閾値1
const float THRESHOLD2 = 95.0; //閾値2
MODULE_4RELAY RELAY;
bool setRelayState(uint8_t relayMask) {
Wire.beginTransmission(0x26);
Wire.write(relayMask & 0x10);
uint8_t err = Wire.endTransmission();
if (err != 0) {
Serial.printf("Relay I2C error: %d\n", err);
return false;
}
return true;
}
float satVaporPressure(float T) { //飽和水蒸気の計算
return 6.112 * exp(17.62 * T / (243.12 + T));
}
void onDataRecv(const uint8_t *mac, const uint8_t *buf, int len) {
if (len == sizeof(PacketBase)) { //ENVのパケット
PacketBase envPkt;
memcpy(&envPkt, buf, sizeof(PacketBase));
latestTemp = envPkt.temp;
latestHum = envPkt.hum;
envReceived = true;
Serial.printf("[ENV] id=%d, T=%.2f, H=%.2f\n",
envPkt.id, envPkt.temp, envPkt.hum);
}
else if (len == sizeof(PacketADT)) { //ADTのパケット
PacketADT adtPkt;
memcpy(&adtPkt, buf, sizeof(PacketADT));
latestSftemp = adtPkt.sftemp;
adtReceived = true;
Serial.printf("[ADT] id=%d, sftemp=%.2f C\n",
adtPkt.id, adtPkt.sftemp);
}
if (envReceived && adtReceived) {
float e_air = satVaporPressure(latestTemp) * latestHum / 100.0; //水蒸気圧
float e_surf = satVaporPressure(latestSftemp); //表面温度の飽和水蒸気圧
float condRisk = e_air / e_surf * 100.0; //結露リスク
Serial.printf("e_air(hPa)=%.2f, e_surf(hPa)=%.2f, Risk=%.2f%%\n",
e_air, e_surf, condRisk);
M5.Lcd.fillScreen(BLACK); //画面更新わかりやすいようにあえてGFXは使わない
M5.Lcd.setTextSize(3);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setCursor(100, 60);
M5.Lcd.printf("CD RISK");
M5.Lcd.setCursor(250, 210);
M5.Lcd.setTextSize(2);
M5.Lcd.setTextColor(PINK);
M5.Lcd.printf("MJ");
M5.Lcd.drawLine(5, 25, 310, 25, RED);
M5.Lcd.drawLine(5, 200, 310, 200, RED);
M5.Lcd.setTextSize(5);
RELAY.setAllRelay(false);
if (condRisk < THRESHOLD1) {
M5.Lcd.setTextColor(GREEN);
RELAY.setRelay(0, true);
} else if (condRisk < THRESHOLD2) {
M5.Lcd.setTextColor(YELLOW);
RELAY.setRelay(1, true);
} else {
M5.Lcd.setTextColor(RED);
RELAY.setRelay(2, true);
}
M5.Lcd.setCursor(100, 140);
M5.Lcd.printf("%.1f%%", condRisk);
envReceived = adtReceived = false;
}
}
void setup() {
M5.begin(true, false, true, true);
while(!RELAY.begin(&Wire, MODULE_4RELAY_ADDR, 21, 22, 200000L)){
Serial.println("4RELAY INIT ERROR");
delay(1000);
}
Serial.begin(115200);
Wire.begin(21, 22);
Serial.println("I2C Scanner Start");
for (uint8_t addr = 1; addr < 127; ++addr) {
Wire.beginTransmission(addr);
if (Wire.endTransmission() == 0) {
Serial.printf("Found I2C device at 0x%02X\n", addr);
}
}
WiFi.mode(WIFI_STA);
WiFi.disconnect();
if (esp_now_init() != ESP_OK) {
Serial.println("ESPNow Init Failed");
ESP.restart();
}
Serial.println("ESPNow Init Success");
memset(&slave, 0, sizeof(slave));
memset(slave.peer_addr, 0xFF, 6);
if (esp_now_add_peer(&slave) == ESP_OK) {
Serial.println("Peer added");
}
esp_now_register_recv_cb(onDataRecv);
}
void loop() {
delay(1000);
}
Comments