

COM and NO. Connect the wiring according to the actual load and power supply specifications. Disconnect the power supply before wiring, disconnecting wires, or replacing the load.
This tutorial uses CoreS3 with Module13.2 4Relay v1.1. The module communicates via I2C and uses the default address 0x26. The I2C pins connected to CoreS3 are G12 (SDA) and G11 (SCL). The CoreS3 touchscreen controls the relays: tap STEP to switch the current channel, tap ALL ON to turn on all channels, and tap ALL OFF to turn off all channels.
#include <M5Unified.h>
#include "Module_4RELAY.h"
MODULE_4RELAY relay;
TwoWire *relayWire = &Wire1;
uint8_t currentChannel = 0;
constexpr uint8_t RELAY_SDA = 12;
constexpr uint8_t RELAY_SCL = 11;
constexpr int16_t TOUCH_BUTTON_Y = 176;
constexpr int16_t TOUCH_BUTTON_HEIGHT = 58;
constexpr int16_t TOUCH_BUTTON_WIDTH = 100;
constexpr int16_t TOUCH_BUTTON_GAP = 6;
int16_t getButtonX(uint8_t index)
{
return TOUCH_BUTTON_GAP + index * (TOUCH_BUTTON_WIDTH + TOUCH_BUTTON_GAP);
}
int8_t getTouchedButton(int16_t x, int16_t y)
{
if (y < TOUCH_BUTTON_Y || y >= TOUCH_BUTTON_Y + TOUCH_BUTTON_HEIGHT) {
return -1;
}
for (uint8_t index = 0; index < 3; ++index) {
const int16_t buttonX = getButtonX(index);
if (x >= buttonX && x < buttonX + TOUCH_BUTTON_WIDTH) {
return index;
}
}
return -1;
}
void drawTouchButton(uint8_t index, const char *label)
{
const int16_t x = getButtonX(index);
M5.Display.fillRoundRect(x, TOUCH_BUTTON_Y, TOUCH_BUTTON_WIDTH,
TOUCH_BUTTON_HEIGHT, 5, TFT_DARKGREY);
M5.Display.drawRoundRect(x, TOUCH_BUTTON_Y, TOUCH_BUTTON_WIDTH,
TOUCH_BUTTON_HEIGHT, 5, TFT_ORANGE);
M5.Display.setTextDatum(MC_DATUM);
M5.Display.setTextColor(TFT_WHITE, TFT_DARKGREY);
M5.Display.drawString(label, x + TOUCH_BUTTON_WIDTH / 2,
TOUCH_BUTTON_Y + TOUCH_BUTTON_HEIGHT / 2);
}
void showStatus(const char *action)
{
const uint8_t state = relay.getAllRelayState();
M5.Display.fillScreen(TFT_BLACK);
M5.Display.setTextColor(TFT_WHITE, TFT_BLACK);
M5.Display.setTextSize(2);
M5.Display.setTextDatum(TL_DATUM);
M5.Display.drawString("Module13.2 4Relay", 8, 8);
M5.Display.drawString(action, 8, 32);
for (uint8_t index = 0; index < 4; ++index) {
const bool isOn = (state >> index) & 0x01;
M5.Display.setCursor(8, 60 + index * 26);
M5.Display.printf("CH%u: %s%s", index + 1, isOn ? "ON" : "OFF",
index == currentChannel ? " <" : "");
}
Serial.printf("%s | relay state: 0x%02X\n", action, state);
drawTouchButton(0, "STEP");
drawTouchButton(1, "ALL ON");
drawTouchButton(2, "ALL OFF");
}
void setup()
{
M5.begin();
Serial.begin(115200);
while (!relay.begin(relayWire, MODULE_4RELAY_ADDR, RELAY_SDA, RELAY_SCL,
200000L)) {
Serial.println("4RELAY INIT ERROR");
M5.Display.fillScreen(TFT_BLACK);
M5.Display.setTextColor(TFT_RED, TFT_BLACK);
M5.Display.setTextSize(2);
M5.Display.setTextDatum(TL_DATUM);
M5.Display.drawString("4RELAY INIT ERROR", 8, 8);
delay(1000);
}
Serial.println("4RELAY INIT SUCCESS");
relay.setAllRelay(false);
showStatus("Ready");
}
void loop()
{
M5.update();
const auto touch = M5.Touch.getDetail();
if (touch.wasClicked()) {
switch (getTouchedButton(touch.x, touch.y)) {
case 0: {
const bool nextState = !relay.getRelayState(currentChannel);
const bool success = relay.setRelay(currentChannel, nextState);
currentChannel = (currentChannel + 1) % 4;
showStatus(success ? "Channel updated" : "Channel update error");
break;
}
case 1: {
const bool success = relay.setAllRelay(true);
showStatus(success ? "All ON" : "All ON error");
break;
}
case 2: {
const bool success = relay.setAllRelay(false);
showStatus(success ? "All OFF" : "All OFF error");
break;
}
default:
break;
}
}
delay(20);
}
After the program starts, the CoreS3 screen displays the status of the 4 relays. The < symbol indicates the channel selected for the next toggle.
STEP: Toggle the current channel, then select the next channel automatically.ALL ON: Turn on all 4 relays.ALL OFF: Turn off all 4 relays.Use a multimeter or a low-voltage DC load that meets the specifications to verify the continuity between the COM and NO terminals before connecting the actual load.
