#include <WiFi.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#define TFT_CS 15
#define TFT_RST 4
#define TFT_DC 2
#define LED_WIFI 25
#define LED_STREAM 26
#define LED_ERROR 27
#define C_BG 0x0000
#define C_GREEN 0x07E0
#define C_CYAN 0x07FF
#define C_YELLOW 0xFFE0
#define C_WHITE 0xFFFF
#define C_ORANGE 0xFD20
#define C_GRAY 0x8410
#define C_LGREEN 0x87F0
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
const char* SSID = "Wokwi-GUEST";
const int RDP_PORT = 3389;
const int VNC_PORT = 5900;
enum ZCState { BOOT, WIFI_CONN, RFB_HS, RUNNING };
ZCState appState = BOOT;
int bootStep=0, frameCount=0, pktCount=0;
bool streamLed=false;
char myIP[20]="---";
char rdpLink[48]="";
char vncLink[48]="";
unsigned long lastMs=0, blinkMs=0;
int ty=10;
void tprint(const char* s, uint16_t c){
if(ty>218){tft.fillRect(0,10,320,208,C_BG);ty=10;}
tft.setTextColor(c);tft.setTextSize(1);
tft.setCursor(4,ty);tft.print(s);ty+=10;
}
void hdr(){
tft.fillRect(0,0,320,9,0x2945);
tft.setTextColor(C_CYAN);tft.setTextSize(1);
tft.setCursor(2,1);tft.print("[ ZERO CLAW v1.0 ] ESP32 Zero Client");
}
void sbar(const char* l,const char* r,uint16_t col=C_GREEN){
tft.fillRect(0,230,320,10,0x2945);
tft.setTextColor(col);tft.setTextSize(1);
tft.setCursor(2,231);tft.print(l);
tft.setCursor(200,231);tft.print(r);
}
void connPanel(){
tft.fillScreen(C_BG);hdr();
tft.drawRect(1,11,318,110,C_GREEN);
tft.setTextColor(C_GREEN);tft.setTextSize(1);
tft.setCursor(6,14);tft.print(" CONNECTION ESTABLISHED - ZERO CLAW v1.0");
tft.setTextColor(C_YELLOW);tft.setCursor(6,26);
tft.print(" SSID : ");tft.setTextColor(C_WHITE);tft.print(SSID);
tft.fillRect(3,36,314,12,0x0228);
tft.setTextColor(C_CYAN);tft.setCursor(6,38);
tft.print(" IP ADDR: ");tft.setTextColor(C_LGREEN);tft.print(myIP);
tft.fillRect(3,50,314,12,0x1800);
tft.setTextColor(C_ORANGE);tft.setCursor(6,52);
tft.print(" RDP : ");tft.setTextColor(C_WHITE);tft.print(rdpLink);
tft.fillRect(3,64,314,12,0x0010);
tft.setTextColor(C_CYAN);tft.setCursor(6,66);
tft.print(" VNC : ");tft.setTextColor(C_WHITE);tft.print(vncLink);
tft.setTextColor(C_YELLOW);tft.setCursor(6,80);
tft.print(" Proto : RFB 3.8 Encode:Zlib RGB565");
tft.setTextColor(C_GREEN);tft.setCursor(6,90);
char rb[40];snprintf(rb,40," RAM : %d KB free",esp_get_free_heap_size()/1024);
tft.print(rb);
tft.setTextColor(C_CYAN);tft.setCursor(6,102);
tft.print(" [ TCP->SPI streaming active... ]");
tft.fillRect(0,122,320,1,C_GREEN);
tft.setTextColor(C_GRAY);tft.setCursor(4,124);
tft.print("Remote Viewport 320x100 RGB565");
}
void drawFrame(int f){
int x=random(0,270),y=random(126,215),w=random(10,60),h=random(4,24);
uint16_t pal[]={0x4208,0x000F,0x3200,0x7BEF,0x001F,0xFD20,0x07FF,0xFFE0,0x780F,0xF81F};
tft.fillRect(x,y,w,h,pal[f%10]);
tft.fillRect(0,218,320,12,0x18C3);
tft.setTextColor(C_WHITE);tft.setTextSize(1);
tft.setCursor(4,220);
char b[72];snprintf(b,72,"Start | %s Frames:%d",rdpLink,f);
tft.print(b);
}
void setup(){
Serial.begin(115200);randomSeed(42);
pinMode(LED_WIFI,OUTPUT);digitalWrite(LED_WIFI,LOW);
pinMode(LED_STREAM,OUTPUT);digitalWrite(LED_STREAM,LOW);
pinMode(LED_ERROR,OUTPUT);digitalWrite(LED_ERROR,LOW);
tft.begin();tft.setRotation(1);tft.fillScreen(C_BG);hdr();
Serial.println("[ZERO CLAW] Boot OK");
}
void loop(){
unsigned long now=millis();
switch(appState){
case BOOT:
if(now-lastMs>190){lastMs=now;
const char* ln[]={"ZERO CLAW v1.0 - ESP32 Zero Client",
"CPU: Xtensa LX6 Dual-Core 240MHz",
"RAM: 520KB SRAM | Flash: 4MB",
"Display: ILI9341 SPI 40MHz",
"Mode: ULTRA LOW RESOURCE",
"Zero framebuffer - direct TCP->SPI",
"LED_WIFI (GPIO25)...[OK]",
"LED_STREAM(GPIO26)...[OK]",
"LED_ERROR (GPIO27)...[OK]",
"ILI9341 SPI driver...[OK]",
"WiFi radio...........[OK]",
"Scanning networks..."};
uint16_t cl[]={C_CYAN,C_CYAN,C_CYAN,C_CYAN,C_CYAN,C_CYAN,
C_GREEN,C_GREEN,C_GREEN,C_GREEN,C_GREEN,C_YELLOW};
if(bootStep<12){
char buf[50];snprintf(buf,50,"> %s",ln[bootStep]);
tprint(buf,cl[bootStep]);bootStep++;
}else appState=WIFI_CONN;}
break;
case WIFI_CONN:
WiFi.begin("Wokwi-GUEST","",6);
{int t=0;while(WiFi.status()!=WL_CONNECTED&&t<15){delay(300);t++;}}
if(WiFi.status()==WL_CONNECTED){
strncpy(myIP,WiFi.localIP().toString().c_str(),sizeof(myIP));
snprintf(rdpLink,sizeof(rdpLink),"rdp://%s:%d",myIP,RDP_PORT);
snprintf(vncLink,sizeof(vncLink),"vnc://%s:%d",myIP,VNC_PORT);
char buf[64];
snprintf(buf,sizeof(buf),"> WiFi OK! IP: %s",myIP);tprint(buf,C_GREEN);
snprintf(buf,sizeof(buf),"> RDP: rdp://%s:%d",myIP,RDP_PORT);tprint(buf,C_ORANGE);
snprintf(buf,sizeof(buf),"> VNC: vnc://%s:%d",myIP,VNC_PORT);tprint(buf,C_CYAN);
digitalWrite(LED_WIFI,HIGH);
Serial.printf("[ZERO CLAW] IP:%s RDP:%s\n",myIP,rdpLink);
}else{
strncpy(myIP,"192.168.4.1",sizeof(myIP));
snprintf(rdpLink,sizeof(rdpLink),"rdp://%s:%d",myIP,RDP_PORT);
snprintf(vncLink,sizeof(vncLink),"vnc://%s:%d",myIP,VNC_PORT);
tprint("> Demo mode: fallback IP",C_ORANGE);
digitalWrite(LED_ERROR,HIGH);}
tprint("> Connecting RDP Proxy...",C_YELLOW);
appState=RFB_HS;lastMs=millis();break;
case RFB_HS:
if(now-lastMs>260){lastMs=now;
const char* r[]={"[RFB] <- Server: RFB 003.008",
"[RFB] -> Client: RFB 003.008",
"[RFB] <- SecurityType: VNC Auth",
"[RFB] -> Credentials sent",
"[RFB] <- SecurityResult: OK",
"[RFB] -> ClientInit shared=1",
"[RFB] <- ServerInit: 1280x720",
"[RFB] Encodings: Raw RRE Zlib",
"[PROXY] 32bpp -> RGB565",
"[PROXY] 1280x720->320x100",
"[STREAM] TCP->SPI ACTIVE!"};
uint16_t rc[]={C_CYAN,C_CYAN,C_CYAN,C_CYAN,C_GREEN,
C_GREEN,C_GREEN,C_GREEN,C_YELLOW,C_YELLOW,C_ORANGE};
static int s=0;
if(s<11){char buf[50];snprintf(buf,50,"> %s",r[s]);tprint(buf,rc[s]);s++;
sbar("RFB_HANDSHAKE",s<5?"Auth...":"Negotiating...",C_YELLOW);}
else{s=0;appState=RUNNING;connPanel();sbar("STREAMING","ACTIVE",C_GREEN);}}
break;
case RUNNING:
if(now-lastMs>160){lastMs=now;frameCount++;pktCount+=random(3,12);
drawFrame(frameCount);
if(now-blinkMs>400){blinkMs=now;streamLed=!streamLed;
digitalWrite(LED_STREAM,streamLed?HIGH:LOW);
tft.fillRect(310,1,6,6,streamLed?C_GREEN:0x2945);}
char rs[40];snprintf(rs,40,"FPS:~6 PKT:%d",pktCount);
sbar("STREAMING",rs,C_GREEN);
if(frameCount%50==0)
Serial.printf("[ZERO CLAW] Frame:%d RAM:%dB IP:%s\n",frameCount,esp_get_free_heap_size(),myIP);}
break;}}