

#include <M5Unified.h>
#include <M5GFX.h>
#include <Wire.h>
#include "MODULE_2RELAY.h"
M5Canvas canvas(&M5.Display);
MODULE_2RELAY RELAY;
constexpr uint8_t RELAY_I2C_ADDRESS = MODULE_2RELAY_ADDR;
constexpr uint8_t RELAY_SDA = 12;
constexpr uint8_t RELAY_SCL = 11;
uint8_t stepChannel = 0;
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);
canvas.fillRoundRect(x, TOUCH_BUTTON_Y, TOUCH_BUTTON_WIDTH,
TOUCH_BUTTON_HEIGHT, 5, TFT_DARKGREY);
canvas.drawRoundRect(x, TOUCH_BUTTON_Y, TOUCH_BUTTON_WIDTH,
TOUCH_BUTTON_HEIGHT, 5, TFT_ORANGE);
canvas.drawString(label, x + TOUCH_BUTTON_WIDTH / 2,
TOUCH_BUTTON_Y + TOUCH_BUTTON_HEIGHT / 2);
}
void drawStatus(const char *action)
{
canvas.fillSprite(TFT_BLACK);
canvas.setTextDatum(MC_DATUM);
canvas.setTextColor(TFT_WHITE);
canvas.drawString("Module13.2 2Relay", 160, 12);
canvas.drawString(action, 160, 32);
for (uint8_t channel = 0; channel < 2; ++channel) {
const int16_t y = 50 + 42 * channel;
const bool state = RELAY.getRelayState(channel);
if (state) {
canvas.fillRect(12, y, 136, 32, TFT_GREEN);
} else {
canvas.drawRect(12, y, 136, 32, TFT_LIGHTGREY);
}
canvas.drawString("CH" + String(channel + 1) +
(state ? ": ON" : ": OFF"),
80, y + 16);
}
canvas.drawString("FW: " + String(RELAY.getVersion()),
160, 138);
drawTouchButton(0, "REVERSE");
drawTouchButton(1, "STEP");
drawTouchButton(2, "ALL");
canvas.pushSprite(0, 0);
}
void drawInitError()
{
canvas.fillSprite(TFT_BLACK);
canvas.setTextDatum(MC_DATUM);
canvas.setTextColor(TFT_RED);
canvas.drawString("Module13.2 2Relay ERROR", 160, 108);
canvas.setTextColor(TFT_WHITE);
canvas.drawString("Check address / wiring", 160, 136);
canvas.pushSprite(0, 0);
}
void setup()
{
M5.begin();
Serial.begin(115200);
canvas.setColorDepth(8);
canvas.setFont(&fonts::FreeMonoBold9pt7b);
canvas.setTextSize(1);
canvas.createSprite(M5.Display.width(), M5.Display.height());
// while(!RELAY.begin(&Wire1, RELAY_SDA, RELAY_SCL, RELAY_I2C_ADDRESS)){
while(!RELAY.begin(&M5.In_I2C, RELAY_I2C_ADDRESS)){
Serial.println("Module13.2 2Relay INIT ERROR");
drawInitError();
}
Serial.println("Module13.2 2Relay INIT SUCCESS");
drawStatus("Ready");
}
void loop()
{
M5.update();
const auto touch = M5.Touch.getDetail();
if (touch.wasClicked()) {
switch (getTouchedButton(touch.x, touch.y)) {
case 0:
drawStatus(RELAY.reverseRelay() ? "Reverse" : "Reverse Error");
break;
case 1: {
const bool currentState = RELAY.getRelayState(stepChannel);
if (RELAY.setRelay(stepChannel, !currentState)) {
stepChannel = (stepChannel + 1) % 2;
drawStatus("Step");
} else {
drawStatus("Step Error");
}
break;
}
case 2: {
const bool channel0 = RELAY.getRelayState(0);
const bool channel1 = RELAY.getRelayState(1);
const bool allOn = channel0 && channel1;
const bool success = RELAY.setAllRelay(!allOn);
drawStatus(success ? (allOn ? "All OFF" : "All ON")
: "All Error");
break;
}
default:
break;
}
}
delay(20);
}This example uses the CoreS3 touchscreen and setDeviceAddr() to switch the Module13.2 2Relay I2C address between 0x26 and 0x66. At startup, the program checks 0x26 and 0x66 in sequence: when 0x26 is detected, the button target address is 0x66; when 0x66 is detected, the button target address is 0x26. If neither address is detected, an error message is shown on the screen. The code supports both Wire1 and M5.In_I2C initialization methods. As in Section 3.1, select one by commenting out or uncommenting the corresponding begin() call. Tap the SET ADDRESS area on the screen to switch the address.
#include <M5Unified.h>
#include <M5GFX.h>
#include <Wire.h>
#include "MODULE_2RELAY.h"
MODULE_2RELAY RELAY;
constexpr uint8_t RELAY_ADDRESS_26 = 0x26;
constexpr uint8_t RELAY_ADDRESS_66 = 0x66;
constexpr uint8_t RELAY_SDA = 12;
constexpr uint8_t RELAY_SCL = 11;
constexpr int16_t ADDRESS_BUTTON_X = 60;
constexpr int16_t ADDRESS_BUTTON_Y = 110;
constexpr int16_t ADDRESS_BUTTON_WIDTH = 200;
constexpr int16_t ADDRESS_BUTTON_HEIGHT = 60;
bool moduleReady = false;
uint8_t currentRelayAddress = 0;
uint8_t newRelayAddress = 0;
void drawAddressScreen(const char *result = nullptr)
{
M5.Display.fillScreen(TFT_BLACK);
M5.Display.setFont(&fonts::FreeMonoBold9pt7b);
M5.Display.setTextSize(1);
M5.Display.setTextDatum(MC_DATUM);
M5.Display.setTextColor(TFT_WHITE);
M5.Display.drawString("Module13.2 2Relay", 160, 32);
const String currentAddress = moduleReady ? String(currentRelayAddress, HEX)
: String("--");
const String newAddress = moduleReady ? String(newRelayAddress, HEX)
: String("--");
M5.Display.drawString("Current: 0x" + currentAddress +
" -> New: 0x" + newAddress,
160, 75);
M5.Display.drawRoundRect(ADDRESS_BUTTON_X, ADDRESS_BUTTON_Y,
ADDRESS_BUTTON_WIDTH, ADDRESS_BUTTON_HEIGHT,
6, TFT_ORANGE);
M5.Display.drawString("SET ADDRESS", 160, 140);
if (result != nullptr) {
M5.Display.drawString(result, 160, 205);
}
}
void setup()
{
M5.begin();
Serial.begin(115200);
M5.Display.setFont(&fonts::FreeMonoBold9pt7b);
// moduleReady = RELAY.begin(&Wire1, RELAY_SDA, RELAY_SCL, RELAY_ADDRESS_26);
moduleReady = RELAY.begin(&M5.In_I2C, RELAY_ADDRESS_26);
if (moduleReady) {
currentRelayAddress = RELAY_ADDRESS_26;
newRelayAddress = RELAY_ADDRESS_66;
} else {
// moduleReady = RELAY.begin(&Wire1, RELAY_SDA, RELAY_SCL, RELAY_ADDRESS_66);
moduleReady = RELAY.begin(&M5.In_I2C, RELAY_ADDRESS_66);
if (moduleReady) {
currentRelayAddress = RELAY_ADDRESS_66;
newRelayAddress = RELAY_ADDRESS_26;
}
}
if (moduleReady) {
Serial.printf("Module13.2 2Relay found at 0x%02X\n",
currentRelayAddress);
drawAddressScreen();
} else {
Serial.println("Module13.2 2Relay INIT ERROR");
drawAddressScreen("Module13.2 2Relay ERROR");
}
}
void loop()
{
M5.update();
const auto touch = M5.Touch.getDetail();
if (touch.wasClicked() && touch.x >= ADDRESS_BUTTON_X &&
touch.x < ADDRESS_BUTTON_X + ADDRESS_BUTTON_WIDTH &&
touch.y >= ADDRESS_BUTTON_Y &&
touch.y < ADDRESS_BUTTON_Y + ADDRESS_BUTTON_HEIGHT) {
if (!moduleReady) {
drawAddressScreen("Module not found");
} else if (RELAY.setDeviceAddr(newRelayAddress)) {
currentRelayAddress = newRelayAddress;
newRelayAddress = (currentRelayAddress == RELAY_ADDRESS_26)
? RELAY_ADDRESS_66
: RELAY_ADDRESS_26;
Serial.printf("I2C address updated to 0x%02X\n",
currentRelayAddress);
drawAddressScreen("Success!");
} else {
Serial.println("I2C address update failed");
drawAddressScreen("Failed!");
}
}
delay(20);
}
REVERSE to reverse the states of both relays, STEP to switch the states of the two relays in sequence, or ALL to turn both relays on or off simultaneously.