Skip to content

Commit db047f8

Browse files
committed
Adds test screen
1 parent 40cd5f5 commit db047f8

File tree

6 files changed

+152
-12
lines changed

6 files changed

+152
-12
lines changed

firmware/src/Files/Files.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ enum class StorageType {
257257
class IFiles
258258
{
259259
public:
260-
virtual bool isAvailable() = 0;
260+
virtual bool isAvailable(StorageType storageType = StorageType::AUTO) = 0;
261261
virtual bool getSpace(uint64_t &total, uint64_t &used, StorageType storageType = StorageType::AUTO) = 0;
262262
virtual bool createDirectory(const char *folder) = 0;
263263
virtual FILE *open(const char *filename, const char *mode) = 0;
@@ -279,7 +279,7 @@ class FilesImplementation: public IFiles
279279
{
280280
}
281281

282-
bool isAvailable()
282+
bool isAvailable(StorageType storageType = StorageType::AUTO)
283283
{
284284
return fileSystem && fileSystem->isMounted();
285285
}
@@ -461,8 +461,14 @@ class UnifiedStorage : public IFiles {
461461
UnifiedStorage(IFiles* flash, IFiles* sd)
462462
: flashFiles(flash), sdFiles(sd) {}
463463

464-
bool isAvailable() override {
465-
return (flashFiles->isAvailable()) || (sdFiles->isAvailable());
464+
bool isAvailable(StorageType storageType = StorageType::AUTO) override {
465+
if (storageType == StorageType::FLASH) {
466+
return flashFiles->isAvailable();
467+
} else if (storageType == StorageType::SD) {
468+
return sdFiles->isAvailable();
469+
} else {
470+
return (flashFiles->isAvailable()) || (sdFiles->isAvailable());
471+
}
466472
}
467473

468474
bool getSpace(uint64_t &total, uint64_t &used, StorageType storageType = StorageType::AUTO) override {

firmware/src/Screens/AboutScreen.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "version_info.h"
99
#include "images/rainbow_image.h"
1010
#include "../Files/Files.h"
11+
#include "TestScreen.h"
1112

1213
class AboutScreen : public Screen
1314
{
@@ -124,6 +125,12 @@ class AboutScreen : public Screen
124125

125126
void pressKey(SpecKeys key) override
126127
{
127-
m_navigationStack->pop();
128+
// If the 't' key is pressed, go to the test screen
129+
if (key == SPECKEY_T) {
130+
TestScreen *testScreen = new TestScreen(m_tft, m_hdmiDisplay, m_audioOutput, m_files);
131+
m_navigationStack->push(testScreen);
132+
} else {
133+
m_navigationStack->pop();
134+
}
128135
}
129136
};

firmware/src/Screens/MainMenuScreen.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "VideoFilePickerScreen.h"
99
#include "GameFilePickerScreen.h"
1010
#include "AboutScreen.h"
11+
#include "TestScreen.h"
1112

1213
static const std::vector<std::string> gameValidExtensions = {".z80", ".sna", ".tap", ".tzx"};
1314
static const std::vector<std::string> videoValidExtensions = {".avi"};

firmware/src/Screens/TestScreen.h

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#pragma once
2+
3+
#include "Screen.h"
4+
#include "fonts/GillSans_25_vlw.h"
5+
#include <vector>
6+
#include <cmath>
7+
8+
class TestScreen : public Screen {
9+
private:
10+
// HSV to RGB conversion (returns 8-bit per channel)
11+
void hsvToRgb(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
12+
float c = v * s;
13+
float x = c * (1 - fabs(fmod(h / 60.0, 2) - 1));
14+
float m = v - c;
15+
float r1, g1, b1;
16+
if (h < 60) { r1 = c; g1 = x; b1 = 0; }
17+
else if (h < 120) { r1 = x; g1 = c; b1 = 0; }
18+
else if (h < 180) { r1 = 0; g1 = c; b1 = x; }
19+
else if (h < 240) { r1 = 0; g1 = x; b1 = c; }
20+
else if (h < 300) { r1 = x; g1 = 0; b1 = c; }
21+
else { r1 = c; g1 = 0; b1 = x; }
22+
r = (uint8_t)((r1 + m) * 255);
23+
g = (uint8_t)((g1 + m) * 255);
24+
b = (uint8_t)((b1 + m) * 255);
25+
}
26+
27+
void drawHSVGradient() {
28+
m_tft.startWrite();
29+
int w = m_tft.width();
30+
int h = m_tft.height();
31+
std::vector<uint16_t> rowBuf(w);
32+
for (int y = 0; y < h; ++y) {
33+
float v = 1.0f;
34+
float s = 1.0f - (float)y / (h - 1);
35+
for (int x = 0; x < w; ++x) {
36+
float hue = 360.0f * x / (w - 1);
37+
uint8_t r, g, b;
38+
hsvToRgb(hue, s, v, r, g, b);
39+
rowBuf[x] = Display::swapBytes(Display::color565(r, g, b));
40+
}
41+
m_tft.setWindow(0, y, w - 1, y);
42+
m_tft.pushPixels(rowBuf.data(), w);
43+
}
44+
m_tft.endWrite();
45+
}
46+
47+
// Generate a square wave buffer for a note (frequency in Hz, duration in ms)
48+
std::vector<uint8_t> generateNoteBuffer(float freq, int durationMs, int sampleRate = 15600) {
49+
int samples = (sampleRate * durationMs) / 1000;
50+
std::vector<uint8_t> buf(samples);
51+
int period = (int)(sampleRate / freq / 2);
52+
for (int i = 0; i < samples; ++i) {
53+
buf[i] = (i / period) % 2 ? 255 : 0;
54+
}
55+
return buf;
56+
}
57+
58+
// Play a sequence of notes: 'a', 'b', 'c', 'd'
59+
void playNotes() {
60+
struct Note { float freq; int duration; };
61+
// Frequencies for notes a, b, c, d (approximate, in Hz)
62+
Note notes[] = {
63+
{440.0f, 180}, // A4
64+
{493.88f, 180}, // B4
65+
{523.25f, 180}, // C5
66+
{587.33f, 180} // D5
67+
};
68+
if (!m_files->isAvailable(StorageType::SD)) {
69+
// Play notes in reverse order if SD card is not mounted
70+
for (int i = 3; i >= 0; --i) {
71+
auto buf = generateNoteBuffer(notes[i].freq, notes[i].duration);
72+
if (m_audioOutput) m_audioOutput->write(buf.data(), buf.size());
73+
}
74+
} else {
75+
// Play notes in normal order if SD card is mounted
76+
for (int i = 0; i < 4; ++i) {
77+
auto buf = generateNoteBuffer(notes[i].freq, notes[i].duration);
78+
if (m_audioOutput) m_audioOutput->write(buf.data(), buf.size());
79+
}
80+
}
81+
}
82+
83+
public:
84+
TestScreen(Display &tft, HDMIDisplay *hdmiDisplay, AudioOutput *audioOutput, IFiles *files)
85+
: Screen(tft, hdmiDisplay, audioOutput, files) {}
86+
87+
void didAppear() override {
88+
drawHSVGradient();
89+
m_tft.loadFont(GillSans_25_vlw);
90+
std::string msg = "Test Screen and Audio";
91+
auto textSize = m_tft.measureString(msg.c_str());
92+
int textX = (m_tft.width() - textSize.x) / 2;
93+
int textY = 20;
94+
m_tft.fillRect(textX-5, textY-5, textSize.x+10, textSize.y+10, TFT_BLACK);
95+
m_tft.setTextColor(TFT_WHITE, TFT_BLACK);
96+
m_tft.drawString(msg.c_str(), textX, textY);
97+
98+
// SD card status
99+
std::string sdMsg;
100+
uint16_t sdColor;
101+
if (m_files && m_files->isAvailable(StorageType::SD)) {
102+
sdMsg = "SD Card: Mounted";
103+
sdColor = TFT_GREEN;
104+
} else {
105+
sdMsg = "SD Card: Not Mounted";
106+
sdColor = TFT_RED;
107+
}
108+
auto sdSize = m_tft.measureString(sdMsg.c_str());
109+
int sdX = (m_tft.width() - sdSize.x) / 2;
110+
int sdY = m_tft.height()/2 - 20;
111+
m_tft.fillRect(sdX-5, sdY-5, sdSize.x+10, sdSize.y+10, TFT_BLACK);
112+
m_tft.setTextColor(sdColor, TFT_BLACK);
113+
m_tft.drawString(sdMsg.c_str(), sdX, sdY);
114+
115+
std::string footer = "Press any key to return";
116+
auto footerSize = m_tft.measureString(footer.c_str());
117+
int footerX = (m_tft.width() - footerSize.x) / 2;
118+
int footerY = m_tft.height() - 40;
119+
m_tft.fillRect(footerX-5, footerY-5, footerSize.x+10, footerSize.y+10, TFT_BLACK);
120+
m_tft.setTextColor(TFT_WHITE, TFT_BLACK);
121+
m_tft.drawString(footer.c_str(), footerX, footerY);
122+
playNotes();
123+
}
124+
125+
void pressKey(SpecKeys key) override {
126+
m_navigationStack->pop();
127+
}
128+
};

firmware/src/TFT/Display.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,6 @@
55
#include <algorithm>
66
#include <vector>
77

8-
// helper functions
9-
inline uint16_t swapBytes(uint16_t val)
10-
{
11-
return (val >> 8) | (val << 8);
12-
}
13-
148
uint32_t swapEndian32(uint32_t val)
159
{
1610
return ((val >> 24) & 0x000000FF) |

firmware/src/TFT/Display.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ class Display
4141
{
4242
public:
4343
Display(int width, int height) : _width(width), _height(height) {}
44+
static inline uint16_t swapBytes(uint16_t val)
45+
{
46+
return (val >> 8) | (val << 8);
47+
}
4448
virtual void startWrite()
4549
{
4650
// nothing to do
@@ -84,6 +88,7 @@ class Display
8488
virtual void saveScreenshot(uint16_t borderWidth = 20, uint16_t borderColor = TFT_BLACK) {
8589
// not implemented here - maybe somewhere else?
8690
}
91+
virtual void drawPixel(uint16_t color, int x, int y);
8792
protected:
8893
int _width;
8994
int _height;
@@ -94,7 +99,6 @@ class Display
9499

95100
// Text rendering
96101
virtual Glyph getGlyphData(uint32_t unicode);
97-
virtual void drawPixel(uint16_t color, int x, int y);
98102
virtual void drawGlyph(const Glyph &glyph, int x, int y);
99103

100104
uint16_t textcolor;

0 commit comments

Comments
 (0)