/*
* ====================================================================
* ZKOUŠECKA RJ45 KABELU (STM32 + NOKIA 5110)
* PROVEDENÍ: GRAFICKÝ WIREMAP (FLUKE / KLEIN TOOLS)
* ====================================================================
*/
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>
// Piny Nokia 5110: (CLK, DIN, DC, CE, RST)
#define PIN_CLK PA9 // A9
#define PIN_DIN PB15 // B15
#define PIN_DC PB14 // B14
#define PIN_CE PB13 // B13
#define PIN_RST PB12 // B12
Adafruit_PCD8544 display = Adafruit_PCD8544(PIN_CLK, PIN_DIN, PIN_DC, PIN_CE, PIN_RST);
#define BUTTON_PIN PB9 // B9
// Piny RJ45: TX (A0..A7) a RX (B0, B1, B10, B11, B5, B6, B7, B8)
const uint8_t TX_PINS[8] = {PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7};
const uint8_t RX_PINS[8] = {PB0, PB1, PB10, PB11, PB5, PB6, PB7, PB8};
struct WireResult {
int connectedRx;
bool isShort;
int shortWith;
};
WireResult results[8];
WireResult prevResults[8];
bool firstRun = true;
bool allConnected = true;
bool isStraight = true;
bool isCrossover = true;
bool anyShort = false;
bool hasMiswire = false;
int openCount = 0;
unsigned long lastTestTime = 0;
// Vnitřní vyrovnávací paměť knihovny (84 x 48 bodů = 504 bajtů)
extern uint8_t pcd8544_buffer[];
// Precizní bitový přenos s mikrosekundovým taktem hodin
void spiWriteByte(uint8_t data) {
for (uint8_t i = 0; i < 8; i++) {
digitalWrite(PIN_DIN, (data & 0x80) ? HIGH : LOW);
data <<= 1;
digitalWrite(PIN_CLK, HIGH);
delayMicroseconds(1);
digitalWrite(PIN_CLK, LOW);
delayMicroseconds(1);
}
}
// 100% stabilní vykreslení: řádek po řádku s absolutním ukotvením souřadnic (Y=page, X=0)
// DŮLEŽITÉ: Nikdy se neposílá 0x21, takže řadič Wokwi zůstává v základním režimu
// a bezchybně akceptuje nastavení adresy pro každý řádek (nulový posun do stran).
void renderScreen() {
// 1. Ujistit se, že řadič je v základním režimu (H=0) a v normálním režimu zobrazení
digitalWrite(PIN_DC, LOW);
digitalWrite(PIN_CE, LOW);
delayMicroseconds(2);
spiWriteByte(0x20); // Základní instrukce: H=0, PD=0
spiWriteByte(0x0C); // Režim zobrazení: NORMAL (D=1, E=0)
delayMicroseconds(2);
digitalWrite(PIN_CE, HIGH);
delayMicroseconds(2);
// 2. Přenos po jednotlivých řádcích s explicitním ukotvením kurzoru na Y=page a X=0
for (uint8_t page = 0; page < 6; page++) {
// Příkazy pro nastavení pozice kurzoru: Y = page (0..5), X = 0
digitalWrite(PIN_DC, LOW);
digitalWrite(PIN_CE, LOW);
delayMicroseconds(2);
spiWriteByte(0x40 | page); // Nastav Y adresu (0x40 .. 0x45)
spiWriteByte(0x80); // Nastav X adresu na 0 (0x80)
delayMicroseconds(2);
digitalWrite(PIN_CE, HIGH);
delayMicroseconds(2);
// Přenos 84 grafických bajtů daného řádku
digitalWrite(PIN_DC, HIGH);
digitalWrite(PIN_CE, LOW);
delayMicroseconds(2);
for (uint8_t col = 0; col < 84; col++) {
spiWriteByte(pcd8544_buffer[page * 84 + col]);
}
delayMicroseconds(2);
digitalWrite(PIN_CE, HIGH);
delayMicroseconds(2);
}
}
void showSchoolIntro() {
display.clearDisplay();
display.setTextColor(BLACK);
// Vnější rámeček
display.drawRect(0, 0, 84, 48, BLACK);
// Vycentrované texty školy a katedry
display.setCursor(15, 3);
display.print("JU v C.B.");
display.setCursor(12, 12);
display.print("PF - KFTCH"); // Pedagogická fakulta - Katedra fyziky, techniky a chemie
display.drawLine(4, 21, 80, 21, BLACK); // Dělící linka
display.setCursor(12, 25);
display.print("LAN TESTER");
display.setCursor(9, 35);
display.print("v1.0 (2026)");
renderScreen();
delay(1800); // Zobrazí se staticky na 1.8 sekundy
}
void showQuickScanAnimation() {
// Blesková animace načítání (projede všech 8 žil za cca 0.16 s)
for (int w = 0; w < 8; w++) {
display.clearDisplay();
display.setTextColor(BLACK);
display.setCursor(8, 4);
display.print("RJ45 TESTER");
display.setCursor(6, 16);
display.print("SKENOVANI...");
// Rychlý progress bar
display.drawRect(4, 28, 76, 8, BLACK);
display.fillRect(6, 30, (w + 1) * 9, 4, BLACK);
display.setCursor(18, 38);
display.print("ZILA ");
display.print(w + 1);
display.print(" / 8");
renderScreen();
delay(20); // 20 ms * 8 = 160 ms
}
}
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
for (int i = 0; i < 8; i++) {
pinMode(RX_PINS[i], INPUT_PULLDOWN);
pinMode(TX_PINS[i], INPUT);
}
// 1. Nastavení pinů displeje Nokia 5110 jako výstupní
pinMode(PIN_RST, OUTPUT);
pinMode(PIN_CE, OUTPUT);
pinMode(PIN_DC, OUTPUT);
pinMode(PIN_DIN, OUTPUT);
pinMode(PIN_CLK, OUTPUT);
digitalWrite(PIN_CE, HIGH);
digitalWrite(PIN_DC, HIGH);
digitalWrite(PIN_CLK, LOW);
digitalWrite(PIN_DIN, LOW);
// 2. Hardwarový reset řadiče PCD8544
// Tento reset přepne řadič Wokwi do základního režimu (H=0, X=0, Y=0)
digitalWrite(PIN_RST, LOW);
delay(20);
digitalWrite(PIN_RST, HIGH);
delay(20);
// 3. Inicializace v základním režimu (H=0)
// DŮLEŽITÉ: Nikdy neposílat 0x21! Ve Wokwi by to způsobilo uvíznutí v rozšířeném režimu.
digitalWrite(PIN_DC, LOW);
digitalWrite(PIN_CE, LOW);
delayMicroseconds(2);
spiWriteByte(0x20); // Základní instrukční sada (H=0, PD=0)
spiWriteByte(0x0C); // Režim zobrazení: NORMAL (D=1, E=0)
delayMicroseconds(2);
digitalWrite(PIN_CE, HIGH);
delay(10);
// Inicializace grafického bufferu Adafruit GFX
display.setTextWrap(false);
display.clearDisplay();
// 4. Úvodní obrazovka fakulty a katedry (JU PF KFTCH)
showSchoolIntro();
// 5. Bleskové načtení testu
showQuickScanAnimation();
performCableTest();
saveCurrentResults();
drawResultsScreen();
}
void performCableTest() {
allConnected = true;
isStraight = true;
anyShort = false;
hasMiswire = false;
openCount = 0;
for (int tx = 0; tx < 8; tx++) {
for (int k = 0; k < 8; k++) {
pinMode(TX_PINS[k], INPUT);
}
pinMode(TX_PINS[tx], OUTPUT);
digitalWrite(TX_PINS[tx], HIGH);
delayMicroseconds(60);
int activeRxCount = 0;
int firstRx = -1;
int secondRx = -1;
for (int rx = 0; rx < 8; rx++) {
if (digitalRead(RX_PINS[rx]) == HIGH) {
activeRxCount++;
if (firstRx == -1) firstRx = rx;
else secondRx = rx;
}
}
if (activeRxCount == 0) {
results[tx].connectedRx = -1;
results[tx].isShort = false;
results[tx].shortWith = -1;
allConnected = false;
isStraight = false;
openCount++;
} else if (activeRxCount == 1) {
results[tx].connectedRx = firstRx;
results[tx].isShort = false;
results[tx].shortWith = -1;
if (firstRx != tx) isStraight = false;
} else {
results[tx].connectedRx = firstRx;
results[tx].isShort = true;
results[tx].shortWith = secondRx;
anyShort = true;
isStraight = false;
}
pinMode(TX_PINS[tx], INPUT);
}
isCrossover = false;
if (!anyShort && openCount == 0) {
if (results[0].connectedRx == 2 && results[2].connectedRx == 0 &&
results[1].connectedRx == 5 && results[5].connectedRx == 1 &&
results[3].connectedRx == 3 && results[4].connectedRx == 4 &&
results[6].connectedRx == 6 && results[7].connectedRx == 7) {
isCrossover = true;
}
}
// Detekce přehozených pinů (miswire)
if (!isCrossover) {
for (int i = 0; i < 8; i++) {
if (results[i].connectedRx != -1 && results[i].connectedRx != i) {
hasMiswire = true;
break;
}
}
}
}
// ====================================================================
// VARIANTA 1: GRAFICKÝ WIREMAP DISPLEJ (FLUKE / PROFESSIONAL STYLE)
// ====================================================================
void drawResultsScreen() {
display.clearDisplay();
display.setTextWrap(false);
// 1. Analýza stavu a diagnostika propustnosti (100 Mbps vs 1 Gbps)
char banner[16];
char detail[16];
// Fast Ethernet (100 Mbps) vyžaduje pouze páry 1-2 a 3-6
bool is100MOk = (!anyShort && results[0].connectedRx == 0 && results[1].connectedRx == 1 &&
results[2].connectedRx == 2 && results[5].connectedRx == 5);
if (openCount == 8) {
strcpy(banner, "KABEL ODPOJEN");
strcpy(detail, "ZAPOJTE KABEL");
} else if (anyShort) {
strcpy(banner, "FAIL: ZKRAT");
strcpy(detail, "ZKRAT V KABELU");
} else if (isStraight && allConnected) {
strcpy(banner, "PASS: 1Gbps OK");
strcpy(detail, "PRIMY (T568B)");
} else if (isCrossover) {
strcpy(banner, "PASS: CROSS OK");
strcpy(detail, "CROSS (T568A)");
} else if (is100MOk) {
strcpy(banner, "PASS: 100 Mbps");
strcpy(detail, "POUZE 100M (P4/7)");
} else if (hasMiswire) {
strcpy(banner, "FAIL: PROHOZENO");
strcpy(detail, "PREHOZENE PINY");
} else if (openCount > 0) {
strcpy(banner, "FAIL: PRERUSENO");
int count = 0;
char pins[10] = "";
for (int i = 0; i < 8; i++) {
if (results[i].connectedRx == -1) {
if (count > 0 && count < 3) strcat(pins, ",");
if (count < 3) {
char pNum[2];
pNum[0] = '1' + i;
pNum[1] = '\0';
strcat(pins, pNum);
}
count++;
}
}
snprintf(detail, 16, "VADA PIN: %s", pins);
} else {
strcpy(banner, "FAIL: CHYBA");
strcpy(detail, "NEZNAMY STAV");
}
// 2. Horní inverzní stavový banner (y = 0..8) po vzoru přístrojů Fluke
display.fillRect(0, 0, 84, 9, BLACK);
display.setTextColor(WHITE, BLACK);
int bx = (84 - strlen(banner) * 6) / 2;
if (bx < 0) bx = 0;
display.setCursor(bx, 1);
display.print(banner);
// 3. Grafický Wiremap (y = 10..35)
display.setTextColor(BLACK);
// Štítky TX a RX
display.setCursor(1, 10);
display.print("TX");
display.setCursor(1, 28);
display.print("RX");
// Dělící svislá linka za štítky
display.drawFastVLine(15, 10, 26, BLACK);
// Vykreslení jednotlivých žil 1..8
for (int i = 0; i < 8; i++) {
int cx = 21 + i * 8; // Přesný střed sloupce žíly (21, 29, 37, 45, 53, 61, 69, 77)
// Číslo vysílače TX nahoře (y = 10)
display.setCursor(cx - 2, 10);
display.print(i + 1);
// Spodní řádek RX (y = 28)
display.setCursor(cx - 2, 28);
if (results[i].isShort) {
display.print("S");
} else if (results[i].connectedRx == -1) {
display.print("-");
} else {
display.print(results[i].connectedRx + 1);
}
// Grafické propojení mezi TX a RX (y = 18..26):
if (results[i].isShort) {
// Zkrat: plná svislá linka + vodorovný můstek k sousední zkratované žíle
display.drawFastVLine(cx, 18, 9, BLACK);
if (results[i].shortWith != -1) {
int other_cx = 21 + results[i].shortWith * 8;
int xMin = cx < other_cx ? cx : other_cx;
int w = abs(other_cx - cx) + 1;
display.drawFastHLine(xMin, 22, w, BLACK);
}
} else if (results[i].connectedRx == -1) {
// Přerušená žíla: pahýly nahoře i dole a křížek X uprostřed
display.drawFastVLine(cx, 18, 2, BLACK);
display.drawFastVLine(cx, 25, 2, BLACK);
display.drawLine(cx - 2, 21, cx + 2, 24, BLACK);
display.drawLine(cx - 2, 24, cx + 2, 21, BLACK);
} else if (results[i].connectedRx == i) {
// Přímé 100% spojení: plná svislá čára
display.drawFastVLine(cx, 18, 9, BLACK);
} else {
// Křížené / přehozené spojení: šikmá spojnice k reálnému RX pinu
int rx_cx = 21 + results[i].connectedRx * 8;
display.drawLine(cx, 18, rx_cx, 26, BLACK);
}
}
// 4. Spodní dělící linka (y = 36)
display.drawFastHLine(0, 36, 84, BLACK);
// 5. Spodní textový řádek s detailní diagnostikou (y = 39)
int dx = (84 - strlen(detail) * 6) / 2;
if (dx < 0) dx = 0;
display.setCursor(dx, 39);
display.print(detail);
renderScreen();
}
bool hasResultsChanged() {
if (firstRun) {
firstRun = false;
return true;
}
for (int i = 0; i < 8; i++) {
if (results[i].connectedRx != prevResults[i].connectedRx ||
results[i].isShort != prevResults[i].isShort ||
results[i].shortWith != prevResults[i].shortWith) {
return true;
}
}
return false;
}
void saveCurrentResults() {
for (int i = 0; i < 8; i++) {
prevResults[i] = results[i];
}
}
void loop() {
unsigned long now = millis();
// Průběžný test kabelu každých 150 ms (okamžitá odezva při přepnutí DIP přepínače)
if (now - lastTestTime >= 150) {
lastTestTime = now;
performCableTest();
if (hasResultsChanged()) {
saveCurrentResults();
drawResultsScreen();
}
}
// Tlačítko pro nový manuální test s rychlým načtením
if (digitalRead(BUTTON_PIN) == LOW) {
showQuickScanAnimation();
performCableTest();
saveCurrentResults();
drawResultsScreen();
delay(200);
}
delay(20); // Stabilní oddech procesoru: 100% spolehlivost běhu
}
Loading
stm32-bluepill
stm32-bluepill
Loading
nokia-5110
nokia-5110