WI-FI Neopixel Display









Wi-Fi Controlled RGB 8x8 NeoPixel LED
Supplies

ESP8266 - https://www.aliexpress.com/item/4001225134179.html?spm=a2g0o.productlist.main.43.7949b69dJsZ41z&algo_pvid=4c66820b-aaca-4b36-a41d-0f78a92dce78&algo_exp_id=4c66820b-aaca-4b36-a41d-0f78a92dce78-42&pdp_ext_f=%7B%22order%22%3A%222%22%2C%22eval%22%3A%221%22%7D&pdp_npi=6%40dis%21EUR%211.76%211.53%21%21%2114.39%2112.52%21%40211b804117553497614417631e4445%2110000015358288483%21sea%21SK%210%21ABX%211%210%21n_tag%3A-29910%3Bm03_new_user%3A-29895&curPageLogUid=PQnsJiGqa0h9&utparam-url=scene%3Asearch%7Cquery_from%3A%7Cx_object_id%3A4001225134179%7C_p_origin_prod%3A
NEOPIXEL 8x8 - https://www.aliexpress.com/item/1005008088871285.html?spm=a2g0o.productlist.main.9.2f0423d1frHLyT&algo_pvid=746b6185-6075-45e6-bad8-86f14113aabf&algo_exp_id=746b6185-6075-45e6-bad8-86f14113aabf-8&pdp_ext_f=%7B%22order%22%3A%2263%22%2C%22eval%22%3A%221%22%7D&pdp_npi=6%40dis%21EUR%212.00%210.87%21%21%2116.36%217.13%21%40211b431017553498167338603e21b5%2112000043651874505%21sea%21SK%210%21ABX%211%210%21m03_new_user%3A-29895%3Bn_tag%3A-29910%3BpisId%3A5000000174216173&curPageLogUid=Z0NuTi7Zhdn2&utparam-url=scene%3Asearch%7Cquery_from%3A%7Cx_object_id%3A1005008088871285%7C_p_origin_prod%3A
USB C -https://www.aliexpress.com/item/1005007457909582.html?spm=a2g0o.detail.pcDetailTopMoreOtherSeller.5.43dasuqJsuqJFF&gps-id=pcDetailTopMoreOtherSeller&scm=1007.40050.354490.0&scm_id=1007.40050.354490.0&scm-url=1007.40050.354490.0&pvid=498dd464-7067-4023-9276-a278e1c68e65&_t=gps-id:pcDetailTopMoreOtherSeller,scm-url:1007.40050.354490.0,pvid:498dd464-7067-4023-9276-a278e1c68e65,tpp_buckets:668%232846%238116%232002&pdp_ext_f=%7B%22order%22%3A%224494%22%2C%22eval%22%3A%221%22%2C%22sceneId%22%3A%2230050%22%7D&pdp_npi=6%40dis%21EUR%211.63%210.87%21%21%2113.33%217.12%21%40211b819117553501990968479e6d5a%2112000040837026422%21rec%21SK%21%21ABXZ%211%210%21m03_new_user%3A-29895%3Bn_tag%3A-29910%3BpisId%3A5000000174216174&utparam-url=scene%3ApcDetailTopMoreOtherSeller%7Cquery_from%3A%7Cx_object_id%3A1005007457909582%7C_p_origin_prod%3A
SOLDERING

V- (neopixel) solder to GND (ESP-8366) and - (USB C)
V+(neopixel) solder to VN (ESP-8266) and + (USB C)
IN (neopixel) solder to D4 (esp-8266)
Use double-sided tape to attach the ESP8266 to the bottom left side of the NeoPixel.
3D Printing
https://www.thingiverse.com/thing:7119396
ASSEMBLY

Secure the NeoPixel matrix to the grid with a 3D pen.
Next, place the diffuser onto the grid and seal the edges using a soldering iron (around 200 °C works best).
nsert the USB connector into the enclosure and secure it with epoxy.
Close the enclosure with the diffuser.
PROGRAMING
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <ESPAsyncTCP.h>
#include <Adafruit_NeoPixel.h>
// ======= KONFIGURÁCIA =======
#define LED_PIN 2 // GPIO2 (D4 na NodeMCU)
#define WIDTH 8
#define HEIGHT 8
#define LED_COUNT (WIDTH * HEIGHT)
#define SERPENTINE true // true = hadovité (riadkovo opačné smery), false = každý riadok zľava doprava
const char* ssid = "WIFI NAME";
const char* password = "PASSWORLD";
// ===========================
// NeoPixel
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// WebServer a WebSocket
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
// --- mapovanie (x,y) -> index do pásu/panela ---
// --- mapovanie (x,y) -> index do pásu/panela (hadovitá 8x8) ---
int xyToIndex(int x, int y) {
// Jednoduché riadkové mapovanie: 0,0 = ľavý horný roh
return y * WIDTH + x;
}
// HTML stránka – 8×8 mriežka, väčšie políčka, kreslenie ťahaním (Pointer Events)
const char htmlPage[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<style>
:root { --px: 36px; } /* veľkosť jedného políčka */
body { font-family: Arial, sans-serif; background:#111; color:#fff; margin:0; padding:18px; text-align:center; }
h2 { margin: 0 0 12px 0; font-weight:600; }
.toolbar { margin: 10px auto 16px auto; display:flex; gap:12px; justify-content:center; align-items:center; flex-wrap:wrap; }
#colorPicker { width: 44px; height: 44px; padding:0; border:none; background:transparent; }
#grid {
margin: 0 auto;
display: grid;
grid-template-columns: repeat(8, var(--px));
grid-template-rows: repeat(8, var(--px));
gap: 2px;
touch-action: none; /* dôležité pre plynulé ťahanie na mobile */
user-select: none;
}
.pixel {
width: var(--px);
height: var(--px);
background: #000;
border: 1px solid #333;
border-radius: 4px;
}
button {
padding: 8px 12px; border: 0; border-radius: 8px; background:#2b2b2b; color:#fff; cursor:pointer;
}
button:hover { filter: brightness(1.15); }
</style>
</head>
<body>
<h2>NeoPixel 8×8 – kreslenie</h2>
<div class="toolbar">
<input type="color" id="colorPicker" value="#ff0000" />
<button id="clearBtn">Vyčistiť</button>
<button id="fillBtn">Vyplniť</button>
</div>
<div id="grid"></div>
<script>
const W = 8, H = 8;
let ws;
let drawing = false;
let color = "#ff0000";
function connectWS() {
ws = new WebSocket(`ws://${location.host}/ws`);
ws.onclose = () => setTimeout(connectWS, 1000);
}
connectWS();
const picker = document.getElementById('colorPicker');
picker.addEventListener('input', e => color = e.target.value);
const grid = document.getElementById('grid');
// posielanie farby na (x,y)
function sendXY(x, y, hex) {
const r = parseInt(hex.substr(1,2),16);
const g = parseInt(hex.substr(3,2),16);
const b = parseInt(hex.substr(5,2),16);
if (ws && ws.readyState === 1) {
// formát: "XY,x,y,r,g,b"
ws.send(`XY,${x},${y},${r},${g},${b}`);
}
}
// vytvor mriežku 8×8, ulož x/y do datasetu
for (let y = 0; y < H; y++) {
for (let x = 0; x < W; x++) {
const d = document.createElement('div');
d.className = 'pixel';
d.dataset.x = x;
d.dataset.y = y;
grid.appendChild(d);
}
}
// zafarbiť políčko lokálne aj poslať na ESP
function paintCell(el) {
if (!el || !el.classList.contains('pixel')) return;
el.style.background = color;
const x = parseInt(el.dataset.x, 10);
const y = parseInt(el.dataset.y, 10);
sendXY(x, y, color);
}
// podpora myš/touch/pero cez Pointer Events
grid.addEventListener('pointerdown', e => {
drawing = true;
paintCell(e.target);
});
grid.addEventListener('pointermove', e => {
if (!drawing) return;
const el = document.elementFromPoint(e.clientX, e.clientY);
paintCell(el);
});
window.addEventListener('pointerup', () => drawing = false);
window.addEventListener('pointercancel', () => drawing = false);
window.addEventListener('pointerleave', () => drawing = false);
// Vyčistenie a vyplnenie
document.getElementById('clearBtn').onclick = () => {
[...grid.children].forEach(c => c.style.background = '#000000');
if (ws && ws.readyState === 1) ws.send('CLR');
};
document.getElementById('fillBtn').onclick = () => {
[...grid.children].forEach(c => c.style.background = color);
if (ws && ws.readyState === 1) {
const r = parseInt(color.substr(1,2),16);
const g = parseInt(color.substr(3,2),16);
const b = parseInt(color.substr(5,2),16);
ws.send(`FILL,${r},${g},${b}`);
}
};
</script>
</body>
</html>
)rawliteral";
// --- spracovanie WebSocket správ ---
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
// očakávané formáty:
// "XY,x,y,r,g,b"
// "CLR"
// "FILL,r,g,b"
String msg = String((char*)data);
if (msg.startsWith("XY")) {
// XY,x,y,r,g,b
int x, y, r, g, b;
char cmd[3];
if (sscanf(msg.c_str(), "%2[^,],%d,%d,%d,%d,%d", cmd, &x, &y, &r, &g, &b) == 6) {
if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT) {
int idx = xyToIndex(x, y);
strip.setPixelColor(idx, strip.Color(r, g, b));
strip.show();
}
}
} else if (msg.startsWith("CLR")) {
for (int i = 0; i < LED_COUNT; i++) strip.setPixelColor(i, 0);
strip.show();
} else if (msg.startsWith("FILL")) {
int r, g, b;
char cmd[5];
if (sscanf(msg.c_str(), "%4[^,],%d,%d,%d", cmd, &r, &g, &b) == 4) {
uint32_t c = strip.Color(r, g, b);
for (int i = 0; i < LED_COUNT; i++) strip.setPixelColor(i, c);
strip.show();
}
}
}
}
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
void *arg, uint8_t *data, size_t len) {
if (type == WS_EVT_DATA) {
handleWebSocketMessage(arg, data, len);
}
}
void setup() {
strip.begin();
strip.show(); // všetko na čierno
Serial.begin(115200);
Serial.println();
Serial.println("Startujem Wi-Fi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(400);
Serial.print(".");
}
Serial.println("\nWiFi pripojené!");
Serial.print("IP adresa: ");
Serial.println(WiFi.localIP());
ws.onEvent(onEvent);
server.addHandler(&ws);
// hlavná stránka
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", htmlPage);
});
// tiché favicon (aby nezahlcovalo log)
server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(204);
});
server.begin();
Serial.println("Webserver beží.");
}
void loop() {
ws.cleanupClients();
}
Replace "WIFI NAME" with your Wi-Fi network name and "PASSWORD" with your Wi-Fi password!!!
CONECTING TO PHONE

in Serial Monitor will display something like: IP: 123.456.7.890
Open this number (IP address) on a device connected to the same Wi-Fi network.
(RED wheel is collor picker) ("Vyčistiť" is clean) (Vyplniť is Color the entire)