Compare commits

..

3 Commits

Author SHA1 Message Date
6b9b2f5f83 forgot the tiny pp :( 2025-12-20 13:34:16 +01:00
c48ed23155 helper webservice 2025-12-20 13:33:59 +01:00
73c6f13b0e actual fahrplan 2025-12-20 13:33:42 +01:00
5 changed files with 164 additions and 25 deletions

View File

@ -21,3 +21,7 @@ lib_deps =
fastled/FastLED fastled/FastLED
https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA.git https://github.com/mrfaptastic/ESP32-HUB75-MatrixPanel-I2S-DMA.git
bitbank2/AnimatedGIF bitbank2/AnimatedGIF
bblanchon/ArduinoJson
build_flags =
-DS3_LCD_DIV_NUM=20

40
fahrplan/schedhelp.py Normal file
View File

@ -0,0 +1,40 @@
#!/usr/bin/env python3
import json
import datetime
import requests
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
def run():
schedule = requests.get('https://api.events.ccc.de/congress/2025/schedule.json').json() # json.load(open('schedule.json', 'r'))
talks = []
for day in schedule['schedule']['conference']['days']:
for room in ['Zero', 'One', 'Fuse', 'Ground']: #day['rooms']:
talks += day['rooms'][room]
talks.sort(key=lambda talk: talk['date'])
now = datetime.datetime.now()
upcoming = []
for talk in talks:
if datetime.datetime.fromisoformat(talk['date']).timestamp() < now.timestamp():
continue
upcoming.append({'start': talk['start'], 'room': talk['room'], 'title': talk['title']})
return json.dumps(upcoming)
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
data = run().encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
if __name__ == '__main__':
server = ThreadingHTTPServer(("0.0.0.0", 8000), Handler)
server.serve_forever()

View File

@ -1,4 +1,3 @@
// config.h // config.h
#define WIFI_SSID PankiNet #define DISPLAY_DURATION 10000
#define WIFI_PSK foo

View File

@ -1,4 +1,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <ArduinoJson.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include "xtensa/core-macros.h" #include "xtensa/core-macros.h"
#include <ESP32-HUB75-MatrixPanel-I2S-DMA.h> #include <ESP32-HUB75-MatrixPanel-I2S-DMA.h>
@ -15,12 +18,22 @@
MatrixPanel_I2S_DMA *matrix = nullptr; MatrixPanel_I2S_DMA *matrix = nullptr;
AnimatedGIF gif; AnimatedGIF gif;
WiFiClientSecure client;
HTTPClient http;
DynamicJsonDocument doc(32768);
long lastrefresh = 0;
bool firstRequest = true;
int current_index = 0;
long last_index_change = 0;
void setup(){ void setup(){
Serial.begin(BAUD_RATE); Serial.begin(BAUD_RATE);
//while (!Serial) { // while (!Serial) {
// ; // wait for serial port to connect. Needed for native USB // ; // wait for serial port to connect. Needed for native USB
//} // }
pinMode(ONBOARD_LED, OUTPUT); pinMode(ONBOARD_LED, OUTPUT);
// redefine pins if required // redefine pins if required
@ -42,6 +55,26 @@ void setup(){
matrix->fillScreenRGB888(0, 0, 0); matrix->fillScreenRGB888(0, 0, 0);
gif.begin(LITTLE_ENDIAN_PIXELS); gif.begin(LITTLE_ENDIAN_PIXELS);
matrix->setCursor(0, 0);
char ssid[] = "YOUR_SSID";
char pw[] = "YOUR_PSK";
matrix->print(ssid);
//Serial.print("Connecting");
WiFi.begin(ssid, pw);
// WiFi.begin(ssid, NULL);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
matrix->fillScreenRGB888(0, 0, 0);
matrix->setCursor(0, 0);
matrix->print(WiFi.status());
//Serial.print(".");
}
matrix->fillScreenRGB888(0, 0, 0);
client.setInsecure();
http.useHTTP10(true);
http.begin(client, "https://www.theresno.cloud/schedhelp");
} }
void draw_title(const std::string& title, uint16_t color) { void draw_title(const std::string& title, uint16_t color) {
@ -60,10 +93,17 @@ void draw_title(const std::string& title, uint16_t color) {
} else { } else {
x_pos += 6; // char width + 1px gap x_pos += 6; // char width + 1px gap
} }
} else { } else if (x_pos != 0) {
x_pos += 2; // small space x_pos += 2; // small space
} }
if (y_pos >= 16 && x_pos >= (63 - 6)) {
// ellipsis
matrix->drawPixel(x_pos, y_pos + 6, color);
matrix->drawPixel(x_pos + 2, y_pos + 6, color);
matrix->drawPixel(x_pos + 4, y_pos + 6, color);
break;
}
if (x_pos > (63 - 5)) { // new line if (x_pos > (63 - 5)) { // new line
if (current != ' ') { if (current != ' ') {
matrix->drawChar(x_pos, y_pos, '-', color, matrix->color444(0,0,0), 1); matrix->drawChar(x_pos, y_pos, '-', color, matrix->color444(0,0,0), 1);
@ -76,7 +116,7 @@ void draw_title(const std::string& title, uint16_t color) {
void draw_time(const std::string &time) { void draw_time(const std::string &time) {
int x_pos = 0; int x_pos = 0;
int y_pos = 32 - 8; int y_pos = 32 - 7;
for (size_t idx = 0; idx < time.size(); idx++) { for (size_t idx = 0; idx < time.size(); idx++) {
char current = time[idx]; char current = time[idx];
@ -87,12 +127,12 @@ void draw_time(const std::string &time) {
break; break;
} }
if (current == ':') { if (current == ':') {
matrix->drawPixel(x_pos, y_pos + 2, matrix->color444(1,1,1)); matrix->drawPixel(x_pos, y_pos + 2, matrix->color565(255,255,255));
matrix->drawPixel(x_pos, y_pos + 4, matrix->color444(1,1,1)); matrix->drawPixel(x_pos, y_pos + 4, matrix->color565(255,255,255));
x_pos = x_pos + 2; x_pos = x_pos + 2;
} else { } else {
x_pos = x_pos - offset; x_pos = x_pos - offset;
matrix->drawChar(x_pos, y_pos, current, matrix->color444(1,1,1), matrix->color444(0,0,0), 1); matrix->drawChar(x_pos, y_pos, current, matrix->color565(255,255,255), matrix->color444(0,0,0), 1);
x_pos = x_pos + (6 - offset); x_pos = x_pos + (6 - offset);
} }
} }
@ -100,7 +140,7 @@ void draw_time(const std::string &time) {
void draw_location(const std::string &location) { void draw_location(const std::string &location) {
int pixel_size = (location.size() * 6) - 1; int pixel_size = (location.size() * 6) - 1;
matrix->setCursor(63 - pixel_size, 32 - 8); matrix->setCursor(63 - pixel_size, 32 - 7);
matrix->print(location.c_str()); matrix->print(location.c_str());
} }
@ -125,7 +165,7 @@ void GIFDraw(GIFDRAW *pDraw) {
// Apply the new pixels to the main image // Apply the new pixels to the main image
if (pDraw->ucHasTransparency) // if transparency used if (pDraw->ucHasTransparency) // if transparency used
{ {
Serial.println("Transparency!"); // Serial.println("Transparency!");
uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent; uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent;
int x, iCount; int x, iCount;
pEnd = s + pDraw->iWidth; pEnd = s + pDraw->iWidth;
@ -153,7 +193,7 @@ void GIFDraw(GIFDRAW *pDraw) {
{ {
for (int xOffset = 0; xOffset < iCount; xOffset++) for (int xOffset = 0; xOffset < iCount; xOffset++)
{ {
matrix->drawPixel(28 + x + xOffset, 25 + y, usTemp[xOffset]); matrix->drawPixel(28 + x + xOffset, 27 + y, usTemp[xOffset]);
} }
x += iCount; x += iCount;
iCount = 0; iCount = 0;
@ -181,23 +221,49 @@ void GIFDraw(GIFDRAW *pDraw) {
// Translate the 8-bit pixels through the RGB565 palette (already byte reversed) // Translate the 8-bit pixels through the RGB565 palette (already byte reversed)
for (x = 0; x < pDraw->iWidth; x++) for (x = 0; x < pDraw->iWidth; x++)
{ {
matrix->drawPixel(28 + x, 25 + y, usPalette[*s++]); matrix->drawPixel(28 + x, 27 + y, usPalette[*s++]);
} }
} }
} }
void loop() { void loop() {
std::string title = "THE ART OF TEXT RENDERING"; if(firstRequest || (millis() - lastrefresh > 120000)) {
std::string time = "11:00"; http.GET();
std::string loc = "GRND";
draw_title(title, matrix->color565(249, 176, 0)); DeserializationError error = deserializeJson(doc, http.getStream());
if (error) {
//Serial.print(F("deserializeJson() failed: "));
//Serial.println(error.f_str());
return;
}
lastrefresh = millis();
firstRequest = false;
}
const char *j_title = doc[current_index]["title"];
const char *j_time = doc[current_index]["start"];
const char *j_room = doc[current_index]["room"];
std::string title(j_title); //= "THE ART OF TEXT RE:NDERING";
std::string time(j_time); // = "11:00";
std::string loc(j_room); //= "GRND";
//draw_title(title, matrix->color565(249, 176, 0));
draw_title(title, matrix->color565(doc[current_index]["r"], doc[current_index]["g"], doc[current_index]["b"]));
draw_time(time); draw_time(time);
draw_location(loc); draw_location(loc);
if (gif.open((uint8_t *)reallytinypp, sizeof(reallytinypp), GIFDraw)) { if (gif.open((uint8_t *)reallytinypp, sizeof(reallytinypp), GIFDraw)) {
while (gif.playFrame(true, NULL)) { while (gif.playFrame(true, NULL)) {
; ;
} }
gif.close(); gif.close();
} }
if (millis() - last_index_change > DISPLAY_DURATION ) {
current_index++;
if (current_index > 4) {
current_index = 0;
}
last_index_change = millis();
matrix->fillScreenRGB888(0, 0, 0);
}
} }

30
fahrplan/src/pp.h Normal file
View File

@ -0,0 +1,30 @@
// Created with image_to_c
// https://github.com/bitbank2/image_to_c
//
// reallytinypp
// Data size = 251 bytes
//
// GIF, Compression=LZW, Size: 7 x 5, 3-Bpp
// 4 frames
//
// for non-Arduino builds...
#ifndef PROGMEM
#define PROGMEM
#endif
const uint8_t reallytinypp[] PROGMEM = {
0x47,0x49,0x46,0x38,0x39,0x61,0x07,0x00,0x05,0x00,0xf2,0x00,0x00,0x00,0x00,0x00,
0x20,0x67,0x2c,0xee,0x00,0x00,0x06,0x94,0x20,0x1b,0x4c,0x85,0x1f,0x54,0x90,0xff,
0xff,0xff,0x00,0x00,0x00,0x21,0xff,0x0b,0x4e,0x45,0x54,0x53,0x43,0x41,0x50,0x45,
0x32,0x2e,0x30,0x03,0x01,0x00,0x00,0x00,0x21,0xfe,0x27,0x47,0x49,0x46,0x20,0x63,
0x72,0x6f,0x70,0x70,0x65,0x64,0x20,0x77,0x69,0x74,0x68,0x20,0x68,0x74,0x74,0x70,
0x73,0x3a,0x2f,0x2f,0x65,0x7a,0x67,0x69,0x66,0x2e,0x63,0x6f,0x6d,0x2f,0x63,0x72,
0x6f,0x70,0x00,0x21,0xf9,0x04,0x05,0x1e,0x00,0x07,0x00,0x2c,0x00,0x00,0x00,0x00,
0x07,0x00,0x05,0x00,0x00,0x03,0x0f,0x78,0x17,0xb3,0x1d,0x06,0x8c,0xc8,0x84,0xb5,
0x6c,0x68,0x4d,0x8a,0xff,0x09,0x00,0x21,0xf9,0x04,0x05,0x0a,0x00,0x00,0x00,0x2c,
0x00,0x00,0x00,0x00,0x07,0x00,0x05,0x00,0x00,0x02,0x07,0x84,0x7f,0x33,0xa0,0xe8,
0xff,0x0a,0x00,0x21,0xf9,0x04,0x05,0x1e,0x00,0x01,0x00,0x2c,0x00,0x00,0x00,0x00,
0x07,0x00,0x05,0x00,0x00,0x03,0x08,0x18,0xba,0x0b,0x16,0x8e,0xc9,0x39,0x13,0x00,
0x21,0xf9,0x04,0x05,0x46,0x00,0x01,0x00,0x2c,0x00,0x00,0x00,0x00,0x07,0x00,0x05,
0x00,0x00,0x03,0x08,0x18,0xba,0x6b,0x10,0x8e,0xc9,0x39,0x13,0x00,0x21,0xf9,0x04,
0x05,0x32,0x00,0x01,0x00,0x2c,0x00,0x00,0x00,0x00,0x07,0x00,0x05,0x00,0x00,0x03,
0x08,0x18,0xba,0x0b,0x16,0x8e,0xc9,0x39,0x13,0x00,0x3b};