Skip to content

Commit 6f7d871

Browse files
Added HUB75 display output
1 parent 63a8c0d commit 6f7d871

File tree

8 files changed

+695
-529
lines changed

8 files changed

+695
-529
lines changed

components/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Ignore everything in this directory
2+
*
3+
# Except this file
4+
!.gitignore

main/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ idf_component_register(SRCS
1515
INCLUDE_DIRS
1616
"include"
1717
"include/Shapes"
18-
REQUIRES)
18+
REQUIRES ESP32-HUB75-MatrixPanel-I2S-DMA
19+
PRIV_REQUIRES esp_timer)
1920

2021
target_compile_options(${COMPONENT_LIB} PRIVATE
2122
-Wno-narrowing

main/include/Utils.hpp

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#pragma once
2+
#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h"
23
#include <cstdint>
34
#include <vector>
45

@@ -26,5 +27,65 @@ using Pixels = std::vector<Pixel>;
2627

2728
class Texture;
2829

29-
// Note: loadTexture is omitted as it requires file system access
30-
// You'll need to implement this based on your texture loading needs
30+
namespace Colors {
31+
static const Color BLACK(0, 0, 0);
32+
static const Color WHITE(255, 255, 255);
33+
static const Color RED(255, 0, 0);
34+
static const Color GREEN(0, 255, 0);
35+
static const Color BLUE(0, 0, 255);
36+
static const Color YELLOW(255, 255, 0);
37+
static const Color MAGENTA(255, 0, 255);
38+
static const Color CYAN(0, 255, 255);
39+
} // namespace Colors
40+
41+
class HUB75Display {
42+
private:
43+
MatrixPanel_I2S_DMA *display;
44+
int width;
45+
int height;
46+
bool initialized;
47+
static const char *TAG;
48+
49+
HUB75_I2S_CFG::i2s_pins getDefaultPins() {
50+
return {
51+
.r1 = 4, // R1 - Red for upper half
52+
.g1 = 5, // G1 - Green for upper half
53+
.b1 = 6, // B1 - Blue for upper half
54+
.r2 = 7, // R2 - Red for lower half
55+
.g2 = 15, // G2 - Green for lower half
56+
.b2 = 16, // B2 - Blue for lower half
57+
.a = 18, // A - Address line
58+
.b = 8, // B - Address line
59+
.c = 3, // C - Address line
60+
.d = 42, // D - Address line
61+
.e = 17, // E - Address line (set to -1 for 32x32 panels)
62+
.lat = 40, // LAT - Latch pin
63+
.oe = 2, // OE - Output Enable pin
64+
.clk = 41 // CLK - Clock pin
65+
};
66+
}
67+
68+
public:
69+
HUB75Display(int panelWidth = 64, int panelHeight = 32,
70+
int chainLength = 1);
71+
72+
~HUB75Display();
73+
74+
void setPixel(int x, int y, const Color &color);
75+
76+
void setPixel(int x, int y, uint8_t r, uint8_t g, uint8_t b);
77+
78+
void setPixels(const Pixels &pixels);
79+
80+
void setBuffer(const Pixels &pixels);
81+
82+
void fill(const Color &color);
83+
84+
void clear();
85+
86+
void setBrightness(uint8_t brightness);
87+
88+
bool isInitialized() const;
89+
90+
bool isValidCoordinate(int x, int y) const;
91+
};

main/main.cpp

Lines changed: 67 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,116 +2,97 @@
22
#include "Shapes/Circle.hpp"
33
#include "Shapes/Collection.hpp"
44
#include "Shapes/Rectangle.hpp"
5+
#include "Utils.hpp"
6+
#include "esp_timer.h"
7+
#include "freertos/projdefs.h"
8+
#include <cstdint>
59
#include <cstdio>
610
#include <stdio.h>
711
#include <vector>
812

9-
// Function to generate a predictable test scene
10-
void generateTestScene(std::vector<Collection *> &collections) {
11-
// Simple test scene with known positions and colors
12-
Collection *testCollection =
13-
new Collection(ShapeParams{0, 0, Color(0, 0, 0, 1.0f), 0});
13+
void runSolarSystem() {
14+
const int width = 64;
15+
const int height = 64;
16+
Renderer renderer(width, height, Color(0, 0, 0, 1.0f));
1417

15-
// Add a red circle at known position
16-
Circle *circle1 =
17-
new Circle(CircleParams{10, 10, Color(155, 0, 0, 1.0f), 10, true});
18-
testCollection->addShape(circle1);
18+
std::vector<Collection *> collections;
1919

20-
// Add a blue rectangle
21-
Rectangle *rect1 = new Rectangle(
22-
RectangleParams{20, 20, Color(0, 0, 255, 1.0f), 8, 6, true});
23-
testCollection->addShape(rect1);
20+
Collection *sunCollection =
21+
new Collection(ShapeParams{32, 32, Color(0, 0, 0, 1.0f), 0});
22+
sunCollection->setPivot(32.0f, 32.0f);
23+
collections.push_back(sunCollection);
2424

25-
// Add a green circle
26-
Circle *circle2 =
27-
new Circle(CircleParams{35, 15, Color(0, 255, 0, 1.0f), 5, false});
28-
testCollection->addShape(circle2);
25+
Circle *sun =
26+
new Circle(CircleParams{32, 32, Color(255, 204, 0, 1.0f), 8, true});
27+
sunCollection->addShape(sun);
2928

30-
collections.push_back(testCollection);
31-
}
29+
Collection *earthCollection =
30+
new Collection(ShapeParams{32, 32, Color(0, 0, 0, 1.0f), 0});
31+
earthCollection->setPivot(32.0f, 32.0f);
3232

33-
// Function to output pixels in a consistent format
34-
void outputPixelsForComparison(const Pixels &pixels, const char *testName) {
35-
printf("=== %s ===\n", testName);
36-
printf("Total pixels: %zu\n", pixels.size());
37-
38-
// Sort pixels for consistent output (x, then y)
39-
Pixels sorted = pixels;
40-
// Simple bubble sort (good enough for testing)
41-
for (size_t i = 0; i < sorted.size(); ++i) {
42-
for (size_t j = i + 1; j < sorted.size(); ++j) {
43-
if (sorted[j].x < sorted[i].x ||
44-
(sorted[j].x == sorted[i].x && sorted[j].y < sorted[i].y)) {
45-
std::swap(sorted[i], sorted[j]);
46-
}
47-
}
48-
}
33+
Circle *earth = new Circle(
34+
CircleParams{32 + 20, 32, Color(0, 100, 255, 1.0f), 4, true});
35+
earthCollection->addShape(earth);
4936

50-
// Output first 50 pixels (or all if less)
51-
size_t maxOutput = (sorted.size() > 50) ? 50 : sorted.size();
52-
printf("First %zu pixels:\n", maxOutput);
37+
Circle *alien = new Circle(
38+
CircleParams{32 - 20, 32, Color(0, 255, 100, 1.0f), 4, true});
39+
earthCollection->addShape(alien);
5340

54-
for (size_t i = 0; i < maxOutput; i++) {
55-
const Pixel &p = sorted[i];
56-
printf("Pixel[%zu]: (%d,%d) RGBA(%d,%d,%d,%.3f)\n", i, p.x, p.y,
57-
p.color.r, p.color.g, p.color.b, p.color.a);
58-
}
41+
Collection *moonCollection =
42+
new Collection(ShapeParams{32 + 20, 32, Color(0, 0, 0, 1.0f), 0});
43+
Circle *moon = new Circle(
44+
CircleParams{32 + 20 + 8, 32, Color(200, 200, 200, 1.0f), 2, true});
45+
moonCollection->addShape(moon);
5946

60-
if (sorted.size() > maxOutput) {
61-
printf("... and %zu more pixels\n", sorted.size() - maxOutput);
62-
}
63-
printf("Checksum: %zu pixels\n\n", sorted.size());
64-
}
47+
earthCollection->addShape(moonCollection);
48+
sunCollection->addShape(earthCollection);
6549

66-
uint32_t calculatePixelChecksum(const Pixels &pixels) {
67-
uint32_t sum = 0;
68-
for (const auto &pixel : pixels) {
69-
sum += pixel.x;
70-
sum += pixel.y;
71-
sum += pixel.color.r;
72-
sum += pixel.color.g;
73-
sum += pixel.color.b;
74-
sum += static_cast<uint32_t>(pixel.color.a * 1000.0f);
75-
}
76-
return sum;
77-
}
78-
void runComparisonTest() {
79-
printf("Starting C++/TypeScript Comparison Test\n");
80-
printf("=======================================\n");
81-
82-
const int width = 64;
83-
const int height = 64;
84-
Renderer renderer(width, height, Color(255, 255, 255, 1.0f));
50+
Circle *earthOrbit =
51+
new Circle(CircleParams{32, 32, Color(100, 100, 100, 0.3f), 20, false});
52+
Circle *moonOrbit = new Circle(
53+
CircleParams{32 + 20, 32, Color(100, 100, 100, 0.3f), 8, false});
54+
sunCollection->addShape(earthOrbit);
55+
earthCollection->addShape(moonOrbit);
8556

86-
// Test 1: Simple static scene
87-
printf("TEST 1: Simple Static Scene\n");
88-
std::vector<Collection *> scene1;
89-
generateTestScene(scene1);
57+
DrawOptions options = {width, height, true}; // Anti-aliased
9058

91-
DrawOptions options = {width, height, false}; // Aliased for consistency
92-
Pixels pixels1 = renderer.render(scene1, options);
93-
outputPixelsForComparison(pixels1, "Static Scene Aliased");
94-
printf("Calculated checksum: 0x%08lX\n", calculatePixelChecksum(pixels1));
59+
HUB75Display display(width, height);
60+
if (!display.isInitialized()) {
61+
printf("Display not initialized. Skipping display output.\n");
62+
return;
63+
}
9564

96-
// Test 2: Same scene with anti-aliasing
97-
printf("TEST 2: Anti-Aliased Version\n");
98-
options.antialias = true;
99-
Pixels pixels2 = renderer.render(scene1, options);
100-
outputPixelsForComparison(pixels2, "Static Scene Anti-Aliased");
101-
printf("Calculated checksum: 0x%08lX\n", calculatePixelChecksum(pixels2));
65+
display.clear();
66+
int counter = 0;
67+
uint64_t sumTime = 0;
68+
while (true) {
69+
earthCollection->rotate(1.5f);
70+
moonCollection->rotate(3.0f);
71+
uint64_t startTime = esp_timer_get_time();
72+
Pixels pixels = renderer.render(collections, options);
73+
uint64_t endTime = esp_timer_get_time();
74+
sumTime += (endTime - startTime);
75+
counter++;
76+
if (counter % 30 == 0) {
77+
float avgTimeMs = (sumTime / counter) / 1000.0f;
78+
printf("Average render time over %d frames: %.2f ms\n", counter,
79+
avgTimeMs);
80+
counter = 0;
81+
sumTime = 0;
82+
}
83+
display.setBuffer(pixels);
84+
vTaskDelay(pdMS_TO_TICKS(20));
85+
}
10286

10387
// Cleanup
104-
for (auto collection : scene1) {
88+
for (auto collection : collections) {
10589
delete collection;
10690
}
107-
108-
printf("Comparison test completed.\n");
109-
printf("Copy this output and compare with TypeScript version.\n");
11091
}
11192

11293
extern "C" void app_main(void) {
11394
printf("C++/TypeScript Output Comparison\n");
11495
printf("================================\n");
11596

116-
runComparisonTest();
97+
runSolarSystem();
11798
}

main/src/Utils.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#include "Utils.hpp"
2+
#include "ESP32-HUB75-MatrixPanel-I2S-DMA.h"
3+
4+
HUB75Display::HUB75Display(int panelWidth, int panelHeight, int chainLength)
5+
: display(nullptr), width(panelWidth * chainLength), height(panelHeight),
6+
initialized(false) {
7+
8+
ESP_LOGI(TAG, "Creating HUB75 display: %dx%d (chain: %d)", width, height,
9+
chainLength);
10+
11+
HUB75_I2S_CFG mxconfig(panelWidth, panelHeight, chainLength,
12+
getDefaultPins(), HUB75_I2S_CFG::SHIFTREG,
13+
HUB75_I2S_CFG::TYPE138,
14+
false, // double buffer
15+
HUB75_I2S_CFG::HZ_10M, DEFAULT_LAT_BLANKING,
16+
true, // clock phase
17+
120, // min refresh rate
18+
8 // pixel color depth
19+
);
20+
21+
// Create display instance
22+
display = new MatrixPanel_I2S_DMA(mxconfig);
23+
24+
// Initialize display
25+
if (display->begin()) {
26+
initialized = true;
27+
display->setBrightness8(90);
28+
display->clearScreen();
29+
ESP_LOGI(TAG, "Display initialized successfully");
30+
} else {
31+
ESP_LOGE(TAG, "Failed to initialize display");
32+
}
33+
}
34+
35+
HUB75Display::~HUB75Display() {
36+
if (display) {
37+
delete display;
38+
}
39+
}
40+
41+
// Set a pixel with Color struct (supports alpha blending)
42+
void HUB75Display::setPixel(int x, int y, const Color &color) {
43+
if (!initialized || !isValidCoordinate(x, y)) {
44+
return;
45+
}
46+
47+
display->drawPixelRGB888(x, y, color.r * color.a, color.g * color.a,
48+
color.b * color.a);
49+
}
50+
51+
// Set a pixel with RGB values (convenience method)
52+
void HUB75Display::setPixel(int x, int y, uint8_t r, uint8_t g, uint8_t b) {
53+
setPixel(x, y, Color(r, g, b));
54+
}
55+
56+
void HUB75Display::setPixels(const Pixels &pixels) {
57+
if (!initialized)
58+
return;
59+
60+
for (const auto &pixel : pixels) {
61+
setPixel(pixel.x, pixel.y, pixel.color);
62+
}
63+
}
64+
65+
void HUB75Display::setBuffer(const Pixels &pixels) {
66+
if (!initialized)
67+
return;
68+
display->clearScreen();
69+
for (const auto &pixel : pixels) {
70+
setPixel(pixel.x, pixel.y, pixel.color);
71+
}
72+
}
73+
74+
// Fill the entire display with a color
75+
void HUB75Display::fill(const Color &color) {
76+
if (!initialized)
77+
return;
78+
79+
for (int y = 0; y < height; y++) {
80+
for (int x = 0; x < width; x++) {
81+
setPixel(x, y, color);
82+
}
83+
}
84+
}
85+
86+
// Clear the display (fill with black)
87+
void HUB75Display::clear() {
88+
if (initialized) {
89+
display->clearScreen();
90+
}
91+
}
92+
93+
bool HUB75Display::isInitialized() const { return initialized; }
94+
95+
// Set display brightness (0-255)
96+
void HUB75Display::setBrightness(uint8_t brightness) {
97+
if (initialized) {
98+
display->setBrightness8(brightness);
99+
}
100+
}
101+
102+
bool HUB75Display::isValidCoordinate(int x, int y) const {
103+
return x >= 0 && x < width && y >= 0 && y < height;
104+
}
105+
106+
const char *HUB75Display::TAG = "HUB75Display";

0 commit comments

Comments
 (0)