Display output example programs for Unit PoE-P4 used with AddOn Display Out For PoE-P4.
AddOn Display Out For PoE-P4 module. First confirm that the 24P FPC cable is connected correctly, then connect the module's HDMI port to an external display. The current driver only supports two output timings: 1280 x 720@60Hz and 1920 x 1080@30Hz.M5UnitPoEP4HDMI driver is already included in M5GFX and does not need to be installed separately.1280 x 720@60Hz.#include <M5GFX.h>
#include <M5UnitPoEP4HDMI.h>
namespace
{
M5UnitPoEP4HDMI::config_t makeDisplayConfig()
{
M5UnitPoEP4HDMI::config_t cfg;
// Unit PoE-P4 HDMI only supports 1280x720@60 or 1920x1080@30.
// Set one of the supported timings explicitly here.
cfg.width = 1280;
cfg.height = 720;
cfg.refresh_rate = 60;
// Switch to 1080p30 by uncommenting the three lines below.
// cfg.width = 1920;
// cfg.height = 1080;
// cfg.refresh_rate = 30;
return cfg;
}
}
// Start the display with the selected HDMI timing.
M5UnitPoEP4HDMI display(makeDisplayConfig());
static constexpr size_t BAR_COUNT = 64;
static int max_y[BAR_COUNT];
static int prev_y[BAR_COUNT];
static uint32_t colors[BAR_COUNT];
void setup(void)
{
display.init();
display.startWrite();
display.fillScreen(TFT_BLACK);
if (display.isEPD())
{
display.setEpdMode(epd_mode_t::epd_fastest);
}
if (display.width() < display.height())
{
display.setRotation(display.getRotation() ^ 1);
}
for (int x = 0; x < BAR_COUNT; ++x)
{
prev_y[x] = display.height();
max_y[x] = display.height();
int r=0,g=0,b=0;
switch (x >> 4)
{
case 0:
b = 255;
g = x*0x11;
break;
case 1:
b = 255 - (x&15)*0x11;
g = 255;
break;
case 2:
g = 255;
r = (x&15)*0x11;
break;
case 3:
r = 255;
g = 255 - (x&15)*0x11;
break;
}
colors[x] = display.color888(r,g,b);
}
}
void loop(void)
{
int h = display.height();
static int i;
++i;
display.waitDisplay();
for (int x = 0; x < BAR_COUNT; ++x)
{
int y = (h>>1) - (sinf((float)((x-24)*i) / 3210.0f) + sinf((float)((x-40)*i) / 1234.0f)) * (h>>2);
int xpos = x * display.width() / BAR_COUNT;
int w = ((x+1) * display.width() / BAR_COUNT) - xpos - 1;
if (max_y[x]+1 >= y) { max_y[x] = y-1; }
else
{
if ((i & 3) ==0 )
{
display.fillRect(xpos, max_y[x]-3, w, 1, TFT_BLACK);
max_y[x]++;
}
}
display.fillRect(xpos, max_y[x]-3, w, 3, TFT_WHITE);
if (prev_y[x] < y)
{
display.fillRect(xpos, prev_y[x], w, y - prev_y[x], TFT_BLACK);
}
else
{
display.fillRect(xpos, y, w, prev_y[x] - y, colors[x]);
}
prev_y[x] = y;
}
display.display();
}This program initializes the HDMI display output of AddOn Display Out For PoE-P4 and draws a dynamic colorful waveform bar chart on the external display.
#include "FS.h"
#include "SD_MMC.h"
#include <M5Unified.h>
#include <M5GFX.h>
#include <M5UnitPoEP4HDMI.h>
namespace
{
M5UnitPoEP4HDMI::config_t makeDisplayConfig()
{
M5UnitPoEP4HDMI::config_t cfg;
// Unit PoE-P4 HDMI only supports 1280x720@60 or 1920x1080@30.
// Set one of the supported timings explicitly here.
cfg.width = 1280;
cfg.height = 720;
cfg.refresh_rate = 60;
// Switch to 1080p30 by uncommenting the three lines below.
// cfg.width = 1920;
// cfg.height = 1080;
// cfg.refresh_rate = 30;
return cfg;
}
}
// Start the display with the selected HDMI timing.
M5UnitPoEP4HDMI display(makeDisplayConfig());
void setup()
{
Serial.begin(115200);
display.begin();
if (display.width() < display.height()) {
display.setRotation(display.getRotation() ^ 1);
}
if (!SD_MMC.setPins(SDMMC_CLK, SDMMC_CMD, SDMMC_D0, SDMMC_D1, SDMMC_D2, SDMMC_D3)) {
Serial.println("SD_MMC pin change failed");
return;
}
if (!SD_MMC.begin()) {
Serial.println("SD_MMC begin failed");
return;
}
Serial.println("SD_MMC begin success");
uint8_t cardType = SD_MMC.cardType();
if (cardType == CARD_NONE) {
Serial.println("No SD card");
return;
}
Serial.printf("Card size: %llu MB\n", SD_MMC.cardSize() / 1024 / 1024);
display.drawJpgFile(SD_MMC, "/U221_AddOn_Display_Out_For_PoE-P4_Arduino_test_picture.jpg", 0, 0);
}
void loop()
{
delay(1000);
}After the program runs successfully, Unit PoE-P4 reads the image file from the microSD card and displays it on the external display.
#include <ETH.h>
#include <HTTPClient.h>
#include <M5GFX.h>
#include <M5UnitPoEP4HDMI.h>
#define ETH_PHY_TYPE ETH_PHY_IP101
#define ETH_PHY_ADDR 1
#define ETH_PHY_MDC 31
#define ETH_PHY_MDIO 52
#define ETH_PHY_POWER 51
#define ETH_CLK_MODE EMAC_CLK_EXT_IN
namespace
{
M5UnitPoEP4HDMI::config_t makeDisplayConfig()
{
M5UnitPoEP4HDMI::config_t cfg;
// Unit PoE-P4 HDMI only supports 1280x720@60 or 1920x1080@30.
// Set one of the supported timings explicitly here.
cfg.width = 1280;
cfg.height = 720;
cfg.refresh_rate = 60;
// Switch to 1080p30 by uncommenting the three lines below.
// cfg.width = 1920;
// cfg.height = 1080;
// cfg.refresh_rate = 30;
return cfg;
}
}
// Start the display with the selected HDMI timing.
M5UnitPoEP4HDMI display(makeDisplayConfig());
static const char *IMAGE_URL = "https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1253/U221_AddOn_Display_Out_For_PoE-P4_Arduino_test_picture.jpg";
static bool eth_connected = false;
void onEvent(arduino_event_id_t event)
{
switch (event)
{
case ARDUINO_EVENT_ETH_START:
Serial.println("[ETH] Started");
ETH.setHostname("unit-poe-p4");
break;
case ARDUINO_EVENT_ETH_CONNECTED:
Serial.println("[ETH] Connected");
break;
case ARDUINO_EVENT_ETH_GOT_IP:
Serial.println("[ETH] Got IP");
Serial.println(ETH);
eth_connected = true;
break;
case ARDUINO_EVENT_ETH_LOST_IP:
Serial.println("[ETH] Lost IP");
eth_connected = false;
break;
case ARDUINO_EVENT_ETH_DISCONNECTED:
Serial.println("[ETH] Disconnected");
eth_connected = false;
break;
case ARDUINO_EVENT_ETH_STOP:
Serial.println("[ETH] Stopped");
eth_connected = false;
break;
default:
break;
}
}
bool waitEthernet(uint32_t timeout_ms)
{
uint32_t start = millis();
while (!eth_connected && (millis() - start < timeout_ms))
{
Serial.printf(
"[ETH] phyAddr=%lu, linkUp=%d, speed=%d Mbps, fullDuplex=%d, IP=%s\n",
ETH.phyAddr(),
ETH.linkUp(),
ETH.linkSpeed(),
ETH.fullDuplex(),
ETH.localIP().toString().c_str());
delay(1000);
}
return eth_connected;
}
void setup()
{
Serial.begin(115200);
delay(1000);
// ---- 1. Display initialization ----
Serial.println("[DISP] Initializing HDMI display...");
display.begin();
if (display.width() < display.height())
{
display.setRotation(display.getRotation() ^ 1);
}
display.fillScreen(TFT_BLACK);
display.setTextColor(TFT_WHITE, TFT_BLACK);
display.setTextSize(2);
display.setTextDatum(datum_t::middle_center);
display.drawString("Starting Ethernet...", display.width() / 2, display.height() / 2);
// ---- 2. Ethernet initialization ----
Network.onEvent(onEvent);
Serial.println("[ETH] Initializing Ethernet...");
bool eth_ok = ETH.begin(
ETH_PHY_TYPE,
ETH_PHY_ADDR,
ETH_PHY_MDC,
ETH_PHY_MDIO,
ETH_PHY_POWER,
ETH_CLK_MODE);
Serial.printf("[ETH] ETH.begin result: %s\n", eth_ok ? "OK" : "FAILED");
Serial.printf("[ETH] PHY addr: %lu\n", ETH.phyAddr());
if (!eth_ok)
{
Serial.println("[ETH] ETH.begin() failed!");
display.fillScreen(TFT_BLACK);
display.setTextColor(TFT_RED, TFT_BLACK);
display.drawString("ETH.begin Failed!", display.width() / 2, display.height() / 2);
return;
}
display.fillScreen(TFT_BLACK);
display.drawString("Waiting for IP...", display.width() / 2, display.height() / 2);
bool network_ready = waitEthernet(20000);
// ---- 3. Download and display JPEG ----
if (network_ready)
{
Serial.printf("[ETH] Network ready. IP: %s\n", ETH.localIP().toString().c_str());
display.fillScreen(TFT_BLACK);
display.setTextColor(TFT_WHITE, TFT_BLACK);
display.drawString("Downloading...", display.width() / 2, display.height() / 2);
Serial.printf("[HTTP] Downloading: %s\n", IMAGE_URL);
unsigned long dl_start = millis();
bool ok = display.drawJpgUrl(IMAGE_URL, 0, 0);
unsigned long dl_elapsed = millis() - dl_start;
if (ok)
{
Serial.printf("[HTTP] Image displayed OK in %lu ms\n", dl_elapsed);
}
else
{
Serial.printf("[HTTP] Failed to download/decode after %lu ms\n", dl_elapsed);
display.fillScreen(TFT_BLACK);
display.drawString("Download Failed!", display.width() / 2, display.height() / 2);
}
}
else
{
Serial.println("[ETH] Network timeout!");
display.fillScreen(TFT_BLACK);
display.setTextColor(TFT_RED, TFT_BLACK);
display.drawString("Ethernet Timeout!", display.width() / 2, display.height() / 2);
}
}
void loop(void)
{
delay(1000);
}After the program runs successfully, Unit PoE-P4 connects to the Internet via Ethernet, downloads the JPG image file from the specified URL, and displays the image on the external display. The actual effect is the same as the example image shown in the SD card image display example.
Serial output example:
[ETH] Initializing Ethernet...
[ETH] Started
[ETH] Connected
[ETH] ETH.begin result: OK
[ETH] PHY addr: 1
[ETH] phyAddr=1, linkUp=1, speed=100 Mbps, fullDuplex=1, IP=0.0.0.0
[ETH] phyAddr=1, linkUp=1, speed=100 Mbps, fullDuplex=1, IP=0.0.0.0
[ETH] phyAddr=1, linkUp=1, speed=100 Mbps, fullDuplex=1, IP=0.0.0.0
[ETH] phyAddr=1, linkUp=1, speed=100 Mbps, fullDuplex=1, IP=0.0.0.0
[ETH] phyAddr=1, linkUp=1, speed=100 Mbps, fullDuplex=1, IP=0.0.0.0
[ETH] Got IP
*eth0: <UP,100M,FULL_DUPLEX,AUTO,ADDR:0x1> (DHCPC,GARP,IP_MOD) PRIO: 50
ether 80:F1:B2:D1:77:CC
inet 192.168.20.91 netmask 255.255.255.0 broadcast 192.168.20.255
gateway 192.168.20.1 dns 223.5.5.5
[ETH] Network ready. IP: 192.168.20.91
[HTTP] Downloading: https://m5stack-doc.oss-cn-shenzhen.aliyuncs.com/1253/U221_AddOn_Display_Out_For_PoE-P4_Arduino_test_picture.jpg
[HTTP] Image displayed OK in 1156 ms