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
@@ -147,7 +147,10 @@ See README.md for the overview.
|
|||||||
old hardcoded clock (also the default and the HA "Clock" option).
|
old hardcoded clock (also the default and the HA "Clock" option).
|
||||||
- Moving pages: `renderPage()` returns when the page changes next (scroll step, second);
|
- Moving pages: `renderPage()` returns when the page changes next (scroll step, second);
|
||||||
`FlipdotFrames` sends those frames as `D1` (no effect) and paces them by the estimated
|
`FlipdotFrames` sends those frames as `D1` (no effect) and paces them by the estimated
|
||||||
flip time, because the Nano has no acknowledgement. Character 127 is the degree sign in
|
receive and flip time, because the Nano has no acknowledgement. Full screen frames go
|
||||||
|
through `BROSE9323::showBitmap()`: only the changed dots, as two `B` commands over the
|
||||||
|
changed rectangle (`lib/BROSE9323/frame_commands.h`, tested with the Nano parser in
|
||||||
|
`test_nano`). Don't send frames pixel by pixel (`s` commands are 7 characters per dot). Character 127 is the degree sign in
|
||||||
page texts and fonts (UTF-8 ° in JSON).
|
page texts and fonts (UTF-8 ° in JSON).
|
||||||
- Fonts use the "FDF1" format of `logic/font.h`; the web page writes the same format.
|
- Fonts use the "FDF1" format of `logic/font.h`; the web page writes the same format.
|
||||||
`logic/builtin_fonts.cpp` is generated: change `tools/make_builtin_fonts.py` and rerun it.
|
`logic/builtin_fonts.cpp` is generated: change `tools/make_builtin_fonts.py` and rerun it.
|
||||||
|
|||||||
@@ -23,6 +23,38 @@ void BROSE9323::begin(Stream *s)
|
|||||||
{
|
{
|
||||||
stream = s;
|
stream = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t BROSE9323::showBitmap(const uint8_t *bitmap, bool changes_only, bool instant)
|
||||||
|
{
|
||||||
|
// What the Nano shows now, in drawBitmap format (the buffers keep the bits the other
|
||||||
|
// way around)
|
||||||
|
uint8_t *shown = (uint8_t *)malloc(_buffer_size);
|
||||||
|
if (!shown)
|
||||||
|
return 0;
|
||||||
|
for (uint16_t i = 0; i < _buffer_size; i++)
|
||||||
|
{
|
||||||
|
uint8_t b = _old_buffer[i], r = 0;
|
||||||
|
for (uint8_t k = 0; k < 8; k++)
|
||||||
|
if (b & (1 << k))
|
||||||
|
r |= 0x80 >> k;
|
||||||
|
shown[i] = r;
|
||||||
|
}
|
||||||
|
size_t sent = frameCommands(bitmap, changes_only ? shown : nullptr, width(), height(), instant,
|
||||||
|
[this](const char *text) { stream->print(text); });
|
||||||
|
free(shown);
|
||||||
|
|
||||||
|
for (uint8_t y = 0; y < height(); y++)
|
||||||
|
for (uint8_t x = 0; x < width(); x++)
|
||||||
|
{
|
||||||
|
uint8_t &byte = _new_buffer[y * _buffer_width + x / 8];
|
||||||
|
if (bitmap[y * _buffer_width + x / 8] & (0x80 >> (x & 7)))
|
||||||
|
byte |= 1 << (x & 7);
|
||||||
|
else
|
||||||
|
byte &= ~(1 << (x & 7));
|
||||||
|
}
|
||||||
|
memcpy(_old_buffer, _new_buffer, _buffer_size);
|
||||||
|
return sent;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
void BROSE9323::begin(void)
|
void BROSE9323::begin(void)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
#include <Adafruit_GFX.h>
|
#include <Adafruit_GFX.h>
|
||||||
|
|
||||||
#include "dot_sequence.h"
|
#include "dot_sequence.h"
|
||||||
|
#ifdef ESPSERIAL
|
||||||
|
#include "frame_commands.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
//#define FLIPDOT_PLCC_ADAPTER
|
//#define FLIPDOT_PLCC_ADAPTER
|
||||||
// ESPSERIAL is set per target via build_flags in platformio.ini (-D ESPSERIAL for the ESP32)
|
// ESPSERIAL is set per target via build_flags in platformio.ini (-D ESPSERIAL for the ESP32)
|
||||||
@@ -103,6 +106,11 @@ public:
|
|||||||
BROSE9323(uint8_t, uint8_t, uint8_t, uint16_t ft = 280);
|
BROSE9323(uint8_t, uint8_t, uint8_t, uint16_t ft = 280);
|
||||||
#ifdef ESPSERIAL
|
#ifdef ESPSERIAL
|
||||||
void begin(Stream * = &Serial);
|
void begin(Stream * = &Serial);
|
||||||
|
// Shows a full screen bitmap (drawBitmap format) and ends the frame (D, or D1 when
|
||||||
|
// instant). changes_only: only the dots that differ from the last frame are sent
|
||||||
|
// (frame_commands.h); otherwise the whole screen. Returns the characters sent (0:
|
||||||
|
// nothing changed, nothing sent).
|
||||||
|
size_t showBitmap(const uint8_t *bitmap, bool changes_only, bool instant = false);
|
||||||
#endif
|
#endif
|
||||||
void begin(void);
|
void begin(void);
|
||||||
// Shows the buffer: the changed dots, all with force. instant: without the refresh
|
// Shows the buffer: the changed dots, all with force. instant: without the refresh
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// The serial commands (src/nano/README.md) that bring the Nano from the image `shown` to
|
||||||
|
// `wanted`: over the rectangle that changed, one B command that switches dots off and one
|
||||||
|
// that switches dots on, then D (or D1 when instant). Only the changed dots are in them,
|
||||||
|
// so the Nano only touches those and the line stays short (scrolling text: a small
|
||||||
|
// window). Without `shown` (nullptr): F0 and the whole image as one B command.
|
||||||
|
//
|
||||||
|
// Bitmaps in drawBitmap format: rows of (width + 7) / 8 bytes, the most significant bit
|
||||||
|
// is the leftmost dot. `out(const char *)` gets the text in pieces. Returns the number of
|
||||||
|
// characters sent, 0 when nothing changed (then nothing is sent, not even D).
|
||||||
|
// Header only, so it is used by the ESP32 driver and tested with the Nano's parser.
|
||||||
|
template <typename Out>
|
||||||
|
size_t frameCommands(const uint8_t *wanted, const uint8_t *shown, uint8_t width, uint8_t height, bool instant,
|
||||||
|
Out out)
|
||||||
|
{
|
||||||
|
const int row_bytes = (width + 7) / 8;
|
||||||
|
auto dot = [row_bytes](const uint8_t *bitmap, int x, int y) {
|
||||||
|
return (bitmap[y * row_bytes + x / 8] >> (7 - (x & 7))) & 1;
|
||||||
|
};
|
||||||
|
size_t sent = 0;
|
||||||
|
auto emit = [&](const char *text) {
|
||||||
|
out(text);
|
||||||
|
sent += strlen(text);
|
||||||
|
};
|
||||||
|
|
||||||
|
int x0 = width, x1 = -1, y0 = height, y1 = -1;
|
||||||
|
if (shown)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
for (int x = 0; x < width; x++)
|
||||||
|
if (dot(wanted, x, y) != dot(shown, x, y))
|
||||||
|
{
|
||||||
|
x0 = x < x0 ? x : x0;
|
||||||
|
x1 = x > x1 ? x : x1;
|
||||||
|
y0 = y < y0 ? y : y0;
|
||||||
|
y1 = y > y1 ? y : y1;
|
||||||
|
}
|
||||||
|
if (x1 < 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
x0 = 0;
|
||||||
|
y0 = 0;
|
||||||
|
x1 = width - 1;
|
||||||
|
y1 = height - 1;
|
||||||
|
emit("F0\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// B needs a width that is a multiple of 4; dots past the right edge are never set
|
||||||
|
int w = (x1 - x0 + 4) / 4 * 4, h = y1 - y0 + 1;
|
||||||
|
// Whether a dot is in the command for this color
|
||||||
|
auto in = [&](int x, int y, int color) {
|
||||||
|
if (x >= width || dot(wanted, x, y) != color)
|
||||||
|
return false;
|
||||||
|
return !shown || dot(shown, x, y) != color;
|
||||||
|
};
|
||||||
|
for (int color = shown ? 0 : 1; color <= 1; color++)
|
||||||
|
{
|
||||||
|
bool any = false;
|
||||||
|
for (int y = y0; y <= y1 && !any; y++)
|
||||||
|
for (int x = x0; x <= x1 && !any; x++)
|
||||||
|
any = in(x, y, color);
|
||||||
|
if (!any)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char text[24];
|
||||||
|
snprintf(text, sizeof(text), "B%02x%02x%02x%02x%d", x0, y0, w, h, color);
|
||||||
|
emit(text);
|
||||||
|
for (int y = y0; y <= y1; y++)
|
||||||
|
{
|
||||||
|
char row[64 + 1]; // up to 256 dots
|
||||||
|
int n = 0;
|
||||||
|
for (int i = 0; i < w / 4; i++)
|
||||||
|
{
|
||||||
|
int nibble = 0;
|
||||||
|
for (int k = 0; k < 4; k++)
|
||||||
|
if (in(x0 + i * 4 + k, y, color))
|
||||||
|
nibble |= 8 >> k;
|
||||||
|
row[n++] = "0123456789ABCDEF"[nibble];
|
||||||
|
}
|
||||||
|
row[n] = '\0';
|
||||||
|
emit(row);
|
||||||
|
}
|
||||||
|
emit("\n");
|
||||||
|
}
|
||||||
|
emit(instant ? "D1\n" : "D\n");
|
||||||
|
return sent;
|
||||||
|
}
|
||||||
+4
-3
@@ -49,9 +49,10 @@ const uint32_t POWER_OFF_DELAY = 30000;
|
|||||||
// timeout (a read blocks loop() for at most about twice this)
|
// timeout (a read blocks loop() for at most about twice this)
|
||||||
const uint32_t HA_POLL_INTERVAL = 60000;
|
const uint32_t HA_POLL_INTERVAL = 60000;
|
||||||
const uint32_t HA_HTTP_TIMEOUT = 1500;
|
const uint32_t HA_HTTP_TIMEOUT = 1500;
|
||||||
// Moving dynamic pages (scrolling, seconds): sending a frame to the Nano takes about this
|
// Moving dynamic pages (scrolling, seconds): the next frame waits until the Nano received
|
||||||
// long (a bitmap at 115200 baud), plus the flip time of the changed dots
|
// this one (characters per ms at NANO_BAUD) and flipped its changed dots, plus a margin
|
||||||
const uint32_t FRAME_SEND_TIME = 60;
|
const uint32_t NANO_CHARS_PER_MS = NANO_BAUD / 10000;
|
||||||
|
const uint32_t FRAME_MARGIN = 20;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Flashing the Nano over WiFi (see README.md, pio run -e nano_ota -t upload)
|
// Flashing the Nano over WiFi (see README.md, pio run -e nano_ota -t upload)
|
||||||
|
|||||||
@@ -10,18 +10,15 @@ FlipdotFrames::FlipdotFrames(BROSE9323 &display, PageResources &resources) : _di
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only the dots that changed are sent (unless redraw: the Nano's image is unknown)
|
||||||
uint32_t FlipdotFrames::dynamicPage(const LocalTime &time, uint32_t now, bool redraw)
|
uint32_t FlipdotFrames::dynamicPage(const LocalTime &time, uint32_t now, bool redraw)
|
||||||
{
|
{
|
||||||
uint8_t bitmap[BITMAP_BYTES];
|
uint8_t bitmap[BITMAP_BYTES];
|
||||||
uint32_t wait = renderPage(_dynamic, time, now, _resources, bitmap);
|
uint32_t wait = renderPage(_dynamic, time, now, _resources, bitmap);
|
||||||
bool moving = wait != 0;
|
bool moving = wait != 0;
|
||||||
if (!redraw && memcmp(bitmap, _last_dynamic, BITMAP_BYTES) == 0)
|
// The Nano has no acknowledgement: the next frame waits until it has received this one
|
||||||
return wait;
|
// and flipped its dots, or the next frame overflows its serial buffer
|
||||||
// The Nano has no acknowledgement: wait until it has flipped the dots of this frame,
|
uint32_t busy = _show(bitmap, !redraw, moving);
|
||||||
// or the next frame overflows its serial buffer
|
|
||||||
uint32_t busy = _flipMs(bitmap, redraw);
|
|
||||||
_drawFullScreen(bitmap, moving);
|
|
||||||
memcpy(_last_dynamic, bitmap, BITMAP_BYTES);
|
|
||||||
return moving && wait < busy ? busy : wait;
|
return moving && wait < busy ? busy : wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,14 +27,17 @@ void FlipdotFrames::setDrive(uint16_t flip_time, const DriveSettings &drive)
|
|||||||
_dot_us = (uint32_t)drive.pulses * (flip_time + drive.flip_gap);
|
_dot_us = (uint32_t)drive.pulses * (flip_time + drive.flip_gap);
|
||||||
}
|
}
|
||||||
|
|
||||||
// About how long the Nano takes to receive and show the bitmap (all: every dot flips)
|
// Sends the bitmap; returns about how long the Nano takes to receive and show it in ms
|
||||||
uint32_t FlipdotFrames::_flipMs(const uint8_t *bitmap, bool all) const
|
uint32_t FlipdotFrames::_show(const uint8_t *bitmap, bool changes_only, bool instant)
|
||||||
{
|
{
|
||||||
uint32_t dots = 0;
|
uint32_t dots = 0;
|
||||||
for (int y = 0; y < BITMAP_HEIGHT; y++)
|
for (int y = 0; y < BITMAP_HEIGHT; y++)
|
||||||
for (int x = 0; x < BITMAP_WIDTH; x++)
|
for (int x = 0; x < BITMAP_WIDTH; x++)
|
||||||
dots += all || bitmapPixel(bitmap, x, y) != _display.shownPixel(x, y);
|
dots += bitmapPixel(bitmap, x, y) != _display.shownPixel(x, y);
|
||||||
return FRAME_SEND_TIME + dots * _dot_us / 1000;
|
size_t chars = _display.showBitmap(bitmap, changes_only, instant);
|
||||||
|
if (chars == 0)
|
||||||
|
return 0;
|
||||||
|
return chars / NANO_CHARS_PER_MS + dots * _dot_us / 1000 + FRAME_MARGIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlipdotFrames::noWifi(int hour, int minute)
|
void FlipdotFrames::noWifi(int hour, int minute)
|
||||||
@@ -86,9 +86,7 @@ void FlipdotFrames::setOffBitmap(const uint8_t *bitmap)
|
|||||||
memset(_off, 0, BITMAP_BYTES);
|
memset(_off, 0, BITMAP_BYTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FlipdotFrames::_drawFullScreen(const uint8_t *bitmap, bool instant)
|
void FlipdotFrames::_drawFullScreen(const uint8_t *bitmap)
|
||||||
{
|
{
|
||||||
_display.fillScreen(0);
|
_show(bitmap, false, false);
|
||||||
_display.drawBitmap(0, 0, bitmap, BITMAP_WIDTH, BITMAP_HEIGHT, 1);
|
|
||||||
_display.display(false, instant);
|
|
||||||
}
|
}
|
||||||
@@ -12,9 +12,9 @@ class FlipdotFrames : public Frames
|
|||||||
public:
|
public:
|
||||||
FlipdotFrames(BROSE9323 &display, PageResources &resources);
|
FlipdotFrames(BROSE9323 &display, PageResources &resources);
|
||||||
|
|
||||||
// The page set with setDynamicPage(), drawn for this time. Not sent when it looks the
|
// The page set with setDynamicPage(), drawn for this time. Only the changed dots are
|
||||||
// same as the last frame (unless redraw). Moving pages are sent without the refresh
|
// sent (nothing when it looks the same), unless redraw. Moving pages are sent without
|
||||||
// effect, and not faster than the Nano can flip the dots.
|
// the refresh effect, and not faster than the Nano can receive and flip them.
|
||||||
uint32_t dynamicPage(const LocalTime &time, uint32_t now, bool redraw) override;
|
uint32_t dynamicPage(const LocalTime &time, uint32_t now, bool redraw) override;
|
||||||
// "No WiFi" with the time below it
|
// "No WiFi" with the time below it
|
||||||
void noWifi(int hour, int minute) override;
|
void noWifi(int hour, int minute) override;
|
||||||
@@ -37,12 +37,11 @@ private:
|
|||||||
BROSE9323 &_display;
|
BROSE9323 &_display;
|
||||||
PageResources &_resources;
|
PageResources &_resources;
|
||||||
DynamicPage _dynamic;
|
DynamicPage _dynamic;
|
||||||
uint8_t _last_dynamic[BITMAP_BYTES] = {};
|
|
||||||
uint32_t _dot_us = 1000; // per changed dot: pulses x (flip time + gap)
|
uint32_t _dot_us = 1000; // per changed dot: pulses x (flip time + gap)
|
||||||
uint8_t _page[BITMAP_BYTES] = {};
|
uint8_t _page[BITMAP_BYTES] = {};
|
||||||
uint8_t _live[BITMAP_BYTES] = {};
|
uint8_t _live[BITMAP_BYTES] = {};
|
||||||
uint8_t _off[BITMAP_BYTES] = {}; // all zero: blank
|
uint8_t _off[BITMAP_BYTES] = {}; // all zero: blank
|
||||||
|
|
||||||
void _drawFullScreen(const uint8_t *bitmap, bool instant = false);
|
void _drawFullScreen(const uint8_t *bitmap);
|
||||||
uint32_t _flipMs(const uint8_t *bitmap, bool all) const;
|
uint32_t _show(const uint8_t *bitmap, bool changes_only, bool instant);
|
||||||
};
|
};
|
||||||
@@ -8,8 +8,11 @@
|
|||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include "BROSE9323.h"
|
#include "BROSE9323.h"
|
||||||
#include "EEPROM.h"
|
#include "EEPROM.h"
|
||||||
|
#include "frame_commands.h" // the real one, from lib/BROSE9323
|
||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// From src/nano/main.cpp
|
// From src/nano/main.cpp
|
||||||
@@ -190,6 +193,101 @@ void test_full_screen_bitmap_is_streamed()
|
|||||||
TEST_ASSERT_EQUAL(84 * 19, display.count("pixel"));
|
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()
|
void test_invalid_bitmap_data_is_not_run_as_commands()
|
||||||
{
|
{
|
||||||
// width 5 is invalid; the data contains the command letters B, D and F
|
// 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_bitmap_pixels);
|
||||||
RUN_TEST(test_full_screen_bitmap_is_streamed);
|
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_invalid_bitmap_data_is_not_run_as_commands);
|
||||||
RUN_TEST(test_incomplete_bitmap_header);
|
RUN_TEST(test_incomplete_bitmap_header);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user