top of page

Reliquary 002

Nov 27

5 min read

0

1

0




Working code for images:


ESP32 (Arduino):


/*

* ESP32 Fast Image Display

* Receives multiple pixels per request

*/


#include <WiFi.h>

#include <WebServer.h>

#include <TFT_eSPI.h>


#define DISPLAY_ID 1


const char* ssid = "sweetpiWIFI";

const char* password = "ihopethisWORKS";


TFT_eSPI tft = TFT_eSPI();

WebServer server(80);


void setup() {

Serial.begin(115200);

// Init display

tft.init();

tft.setRotation(0);

tft.fillScreen(TFT_BLACK);

// Show startup

tft.setTextColor(TFT_WHITE);

tft.setTextSize(2);

tft.setCursor(60, 140);

tft.print("Starting...");

// Connect WiFi

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {

delay(500);

Serial.print(".");

}

Serial.println("\nConnected!");

Serial.print("IP: ");

Serial.println(WiFi.localIP());

// Show ready

tft.fillScreen(TFT_BLACK);

tft.setCursor(80, 100);

tft.print("READY");

tft.setTextSize(1);

tft.setCursor(10, 200);

tft.print("IP: ");

tft.println(WiFi.localIP());

// Setup endpoints

server.on("/", handleRoot);

server.on("/test", handleTest);

server.on("/clear", handleClear);

server.on("/rect", handleRect); // Fast rectangle drawing

server.on("/row", HTTP_POST, handleRow); // Receive full row

server.begin();

}


void loop() {

server.handleClient();

}


void handleRoot() {

server.send(200, "text/plain", "OK");

}


void handleTest() {

// Fast test pattern using rectangles

tft.fillRect(0, 0, 80, 320, TFT_RED);

tft.fillRect(80, 0, 80, 320, TFT_GREEN);

tft.fillRect(160, 0, 80, 320, TFT_BLUE);

server.send(200, "text/plain", "OK");

}


void handleClear() {

tft.fillScreen(TFT_BLACK);

server.send(200, "text/plain", "OK");

}


void handleRect() {

// Draw filled rectangle (much faster than individual pixels)

if (server.hasArg("x") && server.hasArg("y") &&

server.hasArg("w") && server.hasArg("h") &&

server.hasArg("c")) {

int x = server.arg("x").toInt();

int y = server.arg("y").toInt();

int w = server.arg("w").toInt();

int h = server.arg("h").toInt();

uint16_t color = server.arg("c").toInt();

tft.fillRect(x, y, w, h, color);

}

server.send(200, "text/plain", "OK");

}


void handleRow() {

// Receive entire row of pixels at once

if (server.hasArg("y") && server.hasArg("plain")) {

int y = server.arg("y").toInt();

String data = server.arg("plain");

// Data format: pairs of bytes for RGB565 colors

int len = data.length();

for(int i = 0; i < len && i/2 < 240; i += 2) {

if(i+1 < len) {

uint16_t color = (data[i] << 8) | data[i+1];

tft.drawPixel(i/2, y, color);

}

}

}

server.send(200, "text/plain", "OK");

}


Raspberry Pi (Python)


cd ~/phone_controller


cat > fast_display.py << 'ENDOFFILE'

#!/usr/bin/env python3

"""

Fast Image Display for ESP32

Optimized for speed

"""


from flask import Flask, request, jsonify

from PIL import Image

import requests

import base64

import io

import time

import threading

from concurrent.futures import ThreadPoolExecutor


app = Flask(__name__)


ESP32_IP = "192.168.8.234"


def send_image_fast(image_data):

"""Send image using optimized methods"""

try:

# Decode image

if ',' in image_data:

image_data = image_data.split(',')[1]

image_bytes = base64.b64decode(image_data)

img = Image.open(io.BytesIO(image_bytes))

# Resize to fit display

img = img.resize((240, 320), Image.Resampling.LANCZOS)

# Convert to RGB

if img.mode != 'RGB':

img = img.convert('RGB')

# Clear display

requests.get(f"http://{ESP32_IP}/clear", timeout=1)

pixels = img.load()

print("Sending image fast...")

start_time = time.time()

# Method 1: Send rows (fastest)

for y in range(320):

row_data = bytearray()

for x in range(240):

r, g, b = pixels[x, y]

# Convert to RGB565

rgb565 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)

row_data.append((rgb565 >> 8) & 0xFF)

row_data.append(rgb565 & 0xFF)

# Send entire row at once

try:

requests.post(

f"http://{ESP32_IP}/row",

data=bytes(row_data),

params={'y': y},

timeout=0.5

)

except:

pass

# Show progress every 40 rows

if y % 40 == 0:

print(f"Progress: {y}/320")

elapsed = time.time() - start_time

print(f"Image sent in {elapsed:.1f} seconds!")

return True

except Exception as e:

print(f"Error: {e}")

return False


def send_image_compressed(image_data):

"""Send image as compressed blocks"""

try:

# Decode image

if ',' in image_data:

image_data = image_data.split(',')[1]

image_bytes = base64.b64decode(image_data)

img = Image.open(io.BytesIO(image_bytes))

# Resize

img = img.resize((240, 320), Image.Resampling.LANCZOS)

# Reduce colors for faster transfer

img = img.convert('P', palette=Image.ADAPTIVE, colors=64)

img = img.convert('RGB')

# Clear display

requests.get(f"http://{ESP32_IP}/clear", timeout=1)

pixels = img.load()

print("Sending compressed image...")

# Find and send blocks of same color (RLE-like)

for y in range(0, 320, 4): # Process 4 rows at a time

for x in range(0, 240, 4): # Process 4 pixels at a time

# Get average color of 4x4 block

r_sum = g_sum = b_sum = 0

count = 0

for dy in range(4):

for dx in range(4):

if x+dx < 240 and y+dy < 320:

r, g, b = pixels[x+dx, y+dy]

r_sum += r

g_sum += g

b_sum += b

count += 1

if count > 0:

r_avg = r_sum // count

g_avg = g_sum // count

b_avg = b_sum // count

# Convert to RGB565

color = ((r_avg & 0xF8) << 8) | ((g_avg & 0xFC) << 3) | (b_avg >> 3)

# Send rectangle

try:

requests.get(

f"http://{ESP32_IP}/rect",

params={'x': x, 'y': y, 'w': 4, 'h': 4, 'c': color},

timeout=0.2

)

except:

pass

print("Compressed image sent!")

return True

except Exception as e:

print(f"Error: {e}")

return False


@app.route('/')

def index():

return '''

<!DOCTYPE html>

<html>

<head>

<title>Fast ESP32 Display</title>

<meta name="viewport" content="width=device-width, initial-scale=1">

<style>

body {

font-family: Arial;

background: linear-gradient(135deg, #667eea, #764ba2);

color: white;

padding: 20px;

}

.container {

max-width: 400px;

margin: 0 auto;

background: rgba(255,255,255,0.1);

backdrop-filter: blur(10px);

border-radius: 20px;

padding: 30px;

}

h1 {

text-align: center;

font-size: 28px;

}

.btn {

width: 100%;

padding: 20px;

margin: 10px 0;

border: none;

border-radius: 15px;

font-size: 18px;

cursor: pointer;

color: white;

font-weight: bold;

transition: transform 0.2s;

}

.btn:active {

transform: scale(0.95);

}

.photo {

background: linear-gradient(135deg, #667eea, #764ba2);

}

.fast {

background: linear-gradient(135deg, #11998e, #38ef7d);

}

.compressed {

background: linear-gradient(135deg, #f2994a, #f2c94c);

}

.test {

background: linear-gradient(135deg, #ee0979, #ff6a00);

}

.clear {

background: linear-gradient(135deg, #8e2de2, #4a00e0);

}

input {

display: none;

}

#preview {

max-width: 100%;

border-radius: 15px;

margin: 20px 0;

display: none;

box-shadow: 0 10px 30px rgba(0,0,0,0.3);

}

#status {

text-align: center;

padding: 15px;

background: rgba(255,255,255,0.2);

border-radius: 15px;

margin: 15px 0;

display: none;

font-size: 16px;

}

.info {

background: rgba(255,255,255,0.1);

padding: 15px;

border-radius: 15px;

margin: 15px 0;

text-align: center;

font-size: 14px;

}

</style>

</head>

<body>

<div class="container">

<h1>⚡ Fast Display</h1>

<button class="btn photo" onclick="document.getElementById('file').click()">

📷 Select/Take Photo

<input type="file" id="file" accept="image/*">

</button>

<img id="preview">

<div id="status"></div>

<button class="btn fast" onclick="sendFast()">

🚀 Fast Send (~10 sec)

</button>

<button class="btn compressed" onclick="sendCompressed()">

⚡ Ultra Fast (~5 sec)

</button>

<div class="info">

Ultra Fast mode reduces quality slightly for 2x speed

</div>

<button class="btn test" onclick="test()">

🎨 Test Pattern

</button>

<button class="btn clear" onclick="clear()">

🔲 Clear

</button>

</div>

<script>

let imageData = null;

document.getElementById('file').onchange = function(e) {

const file = e.target.files[0];

if(file) {

const reader = new FileReader();

reader.onload = function(e) {

imageData = e.target.result;

document.getElementById('preview').src = imageData;

document.getElementById('preview').style.display = 'block';

showStatus('✅ Ready to send!');

};

reader.readAsDataURL(file);

}

};

function showStatus(msg) {

const status = document.getElementById('status');

status.textContent = msg;

status.style.display = 'block';

}

function sendFast() {

if(!imageData) {

showStatus('❌ Select an image first!');

return;

}

showStatus('⏳ Sending fast... (~10 seconds)');

fetch('/api/fast', {

method: 'POST',

headers: {'Content-Type': 'application/json'},

body: JSON.stringify({image: imageData})

})

.then(r => r.json())

.then(data => {

showStatus(data.success ? '✅ Image displayed!' : '❌ Failed');

});

}

function sendCompressed() {

if(!imageData) {

showStatus('❌ Select an image first!');

return;

}

showStatus('⚡ Sending ultra fast... (~5 seconds)');

fetch('/api/compressed', {

method: 'POST',

headers: {'Content-Type': 'application/json'},

body: JSON.stringify({image: imageData})

})

.then(r => r.json())

.then(data => {

showStatus(data.success ? '✅ Image displayed!' : '❌ Failed');

});

}

function test() {

fetch('/api/test', {method: 'POST'});

showStatus('🎨 Test pattern');

}

function clear() {

fetch('/api/clear', {method: 'POST'});

showStatus('🔲 Cleared');

}

</script>

</body>

</html>

'''


@app.route('/api/fast', methods=['POST'])

def fast():

data = request.get_json()

success = send_image_fast(data.get('image'))

return jsonify({'success': success})


@app.route('/api/compressed', methods=['POST'])

def compressed():

data = request.get_json()

success = send_image_compressed(data.get('image'))

return jsonify({'success': success})


@app.route('/api/test', methods=['POST'])

def test():

try:

requests.get(f"http://{ESP32_IP}/test", timeout=1)

return jsonify({'success': True})

except:

return jsonify({'success': False})


@app.route('/api/clear', methods=['POST'])

def clear():

try:

requests.get(f"http://{ESP32_IP}/clear", timeout=1)

return jsonify({'success': True})

except:

return jsonify({'success': False})


if __name__ == '__main__':

print("\n" + "="*40)

print("FAST ESP32 DISPLAY")

print("="*40)

print(f"ESP32 IP: {ESP32_IP}")

print("Access: http://192.168.8.241:5000")

print("\nSpeed options:")

print("- Fast mode: ~10 seconds (full quality)")

print("- Ultra fast: ~5 seconds (reduced quality)")

print("="*40 + "\n")

app.run(host='0.0.0.0', port=5000, debug=False)

ENDOFFILE


python3 fast_display.py



Nov 27

5 min read

0

1

0

Related Posts

Comments

Share Your ThoughtsBe the first to write a comment.
bottom of page