Public Access
Send only the changed dots of a frame to the Nano
Frames were sent as F0 plus one s command per yellow dot (thousands of characters). During scrolling that overran the Nano's serial buffer, and half-received frames redrew the whole screen. showBitmap() now sends two B commands over the changed rectangle (dots off, dots on), or one B command for a full frame, and the pacing counts the characters sent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
89001ddbc6
commit
47ef65ee7d
8 files changed
+262
-25
No files matched your search
@@ -8,8 +8,11 @@
|
||||
#include "Arduino.h"
|
||||
#include "BROSE9323.h"
|
||||
#include "EEPROM.h"
|
||||
#include "frame_commands.h" // the real one, from lib/BROSE9323
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// From src/nano/main.cpp
|
||||
@@ -190,6 +193,101 @@ void test_full_screen_bitmap_is_streamed()
|
||||
TEST_ASSERT_EQUAL(84 * 19, display.count("pixel"));
|
||||
}
|
||||
|
||||
// The commands the ESP32 driver sends for a frame (lib/BROSE9323/frame_commands.h), run
|
||||
// through this parser: the drawn dots must give the wanted image.
|
||||
static const int IMG_BYTES = 11 * 19;
|
||||
|
||||
static std::string frame(const uint8_t *wanted, const uint8_t *shown, bool instant)
|
||||
{
|
||||
std::string text;
|
||||
frameCommands(wanted, shown, 84, 19, instant, [&text](const char *t) { text += t; });
|
||||
return text;
|
||||
}
|
||||
|
||||
static void setDot(uint8_t *img, int x, int y, bool on)
|
||||
{
|
||||
if (on)
|
||||
img[y * 11 + x / 8] |= 0x80 >> (x & 7);
|
||||
else
|
||||
img[y * 11 + x / 8] &= ~(0x80 >> (x & 7));
|
||||
}
|
||||
|
||||
// The recorded fill and pixel calls applied to an image
|
||||
static void applyCalls(uint8_t *img)
|
||||
{
|
||||
for (const DisplayCall &call : display.calls)
|
||||
if (call.what == "fill")
|
||||
memset(img, call.a ? 0xFF : 0, IMG_BYTES);
|
||||
else if (call.what == "pixel")
|
||||
setDot(img, call.a, call.b, call.c);
|
||||
}
|
||||
|
||||
static void pattern(uint8_t *img, int seed)
|
||||
{
|
||||
for (int i = 0; i < IMG_BYTES; i++)
|
||||
img[i] = (uint8_t)(i * 37 + seed * 11);
|
||||
for (int y = 0; y < 19; y++) // nothing past the 84th dot
|
||||
img[y * 11 + 10] &= 0xF0;
|
||||
}
|
||||
|
||||
void test_frame_commands_send_only_the_changed_dots()
|
||||
{
|
||||
uint8_t shown[IMG_BYTES], wanted[IMG_BYTES];
|
||||
pattern(shown, 1);
|
||||
memcpy(wanted, shown, IMG_BYTES);
|
||||
int changed = 0;
|
||||
for (int x = 30; x < 50; x++) // a scroll window, 6 rows high
|
||||
for (int y = 12; y < 18; y++)
|
||||
if ((x + y) % 3 == 0)
|
||||
{
|
||||
bool now = (shown[y * 11 + x / 8] >> (7 - (x & 7))) & 1;
|
||||
setDot(wanted, x, y, !now);
|
||||
changed++;
|
||||
}
|
||||
|
||||
std::string text = frame(wanted, shown, true);
|
||||
TEST_ASSERT_TRUE(text.size() < 150); // instead of the whole screen
|
||||
send(text);
|
||||
TEST_ASSERT_FALSE(replied("ERR"));
|
||||
TEST_ASSERT_EQUAL(0, display.count("fill"));
|
||||
TEST_ASSERT_EQUAL(changed, display.count("pixel")); // only the changed dots
|
||||
uint8_t result[IMG_BYTES];
|
||||
memcpy(result, shown, IMG_BYTES);
|
||||
applyCalls(result);
|
||||
TEST_ASSERT_EQUAL_MEMORY(wanted, result, IMG_BYTES);
|
||||
TEST_ASSERT_EQUAL(1, display.count("display"));
|
||||
TEST_ASSERT_EQUAL(1, display.last("display")->b); // D1: instant
|
||||
}
|
||||
|
||||
void test_frame_commands_full_frame()
|
||||
{
|
||||
uint8_t wanted[IMG_BYTES];
|
||||
pattern(wanted, 2);
|
||||
std::string text = frame(wanted, nullptr, false);
|
||||
TEST_ASSERT_TRUE(text.size() < 420);
|
||||
send(text);
|
||||
TEST_ASSERT_FALSE(replied("ERR"));
|
||||
TEST_ASSERT_EQUAL(1, display.count("fill"));
|
||||
uint8_t result[IMG_BYTES];
|
||||
memset(result, 0xAA, IMG_BYTES);
|
||||
applyCalls(result);
|
||||
TEST_ASSERT_EQUAL_MEMORY(wanted, result, IMG_BYTES);
|
||||
TEST_ASSERT_EQUAL(0, display.last("display")->b); // D: with the effect
|
||||
}
|
||||
|
||||
void test_frame_commands_edge_and_nothing_changed()
|
||||
{
|
||||
uint8_t shown[IMG_BYTES] = {}, wanted[IMG_BYTES] = {};
|
||||
TEST_ASSERT_EQUAL_STRING("", frame(wanted, shown, true).c_str());
|
||||
setDot(wanted, 83, 18, true); // the last dot: the B width (4) goes past the edge
|
||||
std::string text = frame(wanted, shown, false);
|
||||
send(text);
|
||||
TEST_ASSERT_FALSE(replied("ERR"));
|
||||
TEST_ASSERT_EQUAL(1, display.count("pixel"));
|
||||
TEST_ASSERT_EQUAL(83, display.last("pixel")->a);
|
||||
TEST_ASSERT_EQUAL(18, display.last("pixel")->b);
|
||||
}
|
||||
|
||||
void test_invalid_bitmap_data_is_not_run_as_commands()
|
||||
{
|
||||
// width 5 is invalid; the data contains the command letters B, D and F
|
||||
@@ -528,6 +626,9 @@ int main()
|
||||
|
||||
RUN_TEST(test_bitmap_pixels);
|
||||
RUN_TEST(test_full_screen_bitmap_is_streamed);
|
||||
RUN_TEST(test_frame_commands_send_only_the_changed_dots);
|
||||
RUN_TEST(test_frame_commands_full_frame);
|
||||
RUN_TEST(test_frame_commands_edge_and_nothing_changed);
|
||||
RUN_TEST(test_invalid_bitmap_data_is_not_run_as_commands);
|
||||
RUN_TEST(test_incomplete_bitmap_header);
|
||||
|
||||
|
||||
Reference in new issue
Block a user