initial commit

This commit is contained in:
Gwendolyn 2022-03-12 12:33:52 +01:00
commit ee867edb51
30 changed files with 3862 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.pio
.idea
CMakeLists*.txt
cmake-*

BIN
cyber.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
cyber96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
gradient.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

178
imgtoc.py Normal file
View File

@ -0,0 +1,178 @@
#!/usr/bin/env python3
# ##### ## ## #### ####### #### ####
# # # # # # # # # # # # #
# # # # # # # # # #
# # # # # ### # # # #
# # # # # # # # # # #
# ##### # # #### # #### ####
#
# Converts a bitmap image to c source.
import os
import typer
from PIL import Image
class PixelList:
byte_align_rows = 0
align_pixels = 0
depth = 0
current_row = []
rows = []
current_byte = 0
current_byte_length = 0
num_row_bytes = 0
def __init__(self, depth: int, align_pixels: int, byte_align_rows: bool):
if depth not in [1, 2, 4, 8]:
raise 'depth must be 1, 2, 4 or 8'
elif align_pixels % depth != 0:
raise 'align_pixels must be a multiple of depth'
elif align_pixels not in [1, 2, 4, 8]:
raise 'align_pixels must be 1, 2, 4 or 8'
self.depth = depth
self.byte_align_rows = byte_align_rows
self.align_pixels = align_pixels
def append_pixel(self, pixel: int):
self.current_byte = (self.current_byte << self.align_pixels) | (pixel & ((1 << self.depth) - 1))
self.current_byte_length += self.align_pixels
if self.current_byte_length == 8:
self.current_row.append(self.current_byte)
self.num_row_bytes += 1
self.current_byte = 0
self.current_byte_length = 0
pass
def end_row(self):
if self.current_byte_length > 0 and self.byte_align_rows:
byte = self.current_byte << (8 - self.current_byte_length)
self.current_row.append(byte)
self.num_row_bytes += 1
self.current_byte = 0
self.current_byte_length = 0
self.rows.append(self.current_row)
self.current_row = []
def end_image(self):
if self.current_byte_length > 0:
byte = self.current_byte << (8 - self.current_byte_length)
self.rows.append([byte])
def get_rows(self):
return self.rows
def img_to_c(im: Image, fmt: str, invert: bool, depth: int, align_pixels: int, byte_align_rows: bool):
pixels = PixelList(depth, align_pixels, byte_align_rows)
for iy in range(0, im.size[1]): # iterate rows
for ix in range(0, im.size[0]): # iterate columns
pix = im.getpixel((ix, iy))
pixels.append_pixel(pix)
pixels.end_row()
pixels.end_image()
src_c = ''
for row in pixels.get_rows(): # iterate rows
src_c += ' '
for byte in row:
if invert:
byte = 0xff - byte
if fmt == 'hex':
src_c += '0x' + format(byte, '02x')
elif fmt == 'bin':
src_c += 'B' + format(byte, '08b')
elif fmt == 'dec':
src_c += f'{byte:>3}'
src_c += ', '
src_c += '\n'
return src_c
def make_palette(depth: int):
num_colors = 1 << depth
colors = []
color_step = 255 / (num_colors - 1)
for i in range(0, num_colors - 1):
colors.append(int(color_step * i))
colors.append(255)
return colors
def find_closest_palette_color(color: int, palette: [int]) -> (int, int):
return min(enumerate(palette), key=lambda x: abs(x[1] - color))
def dither(original: Image, palette: [int]):
im = Image.new('L', original.size)
im.paste(original)
for iy in range(0, im.size[1]): # iterate rows
for ix in range(0, im.size[0]): # iterate columns
oldpix = im.getpixel((ix, iy))
closest = find_closest_palette_color(oldpix, palette)
im.putpixel((ix, iy), closest[0])
quant_error = oldpix - closest[1]
if ix < im.size[0] - 1:
im.putpixel((ix + 1, iy), int(im.getpixel((ix + 1, iy)) + quant_error * 7 / 16))
if ix > 0 and iy < im.size[1] - 1:
im.putpixel((ix - 1, iy + 1), int(im.getpixel((ix - 1, iy + 1)) + quant_error * 3 / 16))
if iy < im.size[1] - 1:
im.putpixel((ix, iy + 1), int(im.getpixel((ix, iy + 1)) + quant_error * 5 / 16))
if ix < im.size[0] - 1 and iy < im.size[1] - 1:
im.putpixel((ix + 1, iy + 1), int(im.getpixel((ix + 1, iy + 1)) + quant_error * 1 / 16))
return im
def main(input_file: str,
out: str = typer.Option(None, help='File to write to, defaults to standard output'),
name: str = typer.Option(None, help='Name of the bitmap variable in generated source, defaults to the '
'filename without extension.'),
fmt: str = typer.Option('hex', help='Format to generate image data in (hex or bin).'),
invert: bool = typer.Option(False, help='Invert brightness.'),
depth: int = typer.Option(1, help='Bit depth of the output, can be 1, 2, 4 or 8.'),
pixel_align: int = typer.Option(None, help='Bit-alignment of the pixels, defaults to the depth, can be 1, 2, 4 or 8.'),
no_row_align: bool = typer.Option(False, help='Disable byte alignment for rows.'),
):
im: Image = Image.open(input_file)
if im.mode != 'L':
im = im.convert(mode='L')
im = dither(im, make_palette(depth))
if name is None:
name = os.path.splitext(os.path.basename(input_file))[0]
if pixel_align is None:
pixel_align = depth
row_align_val = 0 if no_row_align else 1
ctext = f'// Image definition of: {input_file}\n'
ctext += 'static PROGMEM bitmap_t ' + name + ' { \n'
ctext += f' .width = {im.size[0]},\n'
ctext += f' .height = {im.size[1]},\n'
ctext += f' .depth = {depth},\n'
ctext += f' .pixel_align = {pixel_align},\n'
ctext += f' .row_align = {row_align_val},\n'
ctext += ' .data = (PROGMEM uint8_t[]) {\n'
ctext += img_to_c(im, fmt, invert, depth, pixel_align, not no_row_align)
ctext += ' }\n};\n'
if out:
with open(out, mode='w') as f:
f.write(ctext)
f.close()
else:
print(ctext)
if __name__ == "__main__":
typer.run(main)

302
include/FixedPoint.h Normal file
View File

@ -0,0 +1,302 @@
template<int ScalingFactor>
class FixedPoint {
int64_t val_;
public:
constexpr FixedPoint() : val_(0) {}
constexpr FixedPoint(int64_t v) : val_(v * ScalingFactor) {}
constexpr FixedPoint(int32_t v) : val_(v * ScalingFactor) {}
constexpr FixedPoint(int16_t v) : val_(v * ScalingFactor) {}
constexpr FixedPoint(int8_t v) : val_(v * ScalingFactor) {}
constexpr FixedPoint(uint64_t v) : val_(v * ScalingFactor) {}
constexpr FixedPoint(uint32_t v) : val_(v * ScalingFactor) {}
constexpr FixedPoint(uint16_t v) : val_(v * ScalingFactor) {}
constexpr FixedPoint(uint8_t v) : val_(v * ScalingFactor) {}
constexpr FixedPoint(float v) : val_(static_cast<int64_t>(v * ScalingFactor)) {}
constexpr FixedPoint(double v) : val_(static_cast<int64_t>(v * ScalingFactor)) {}
constexpr FixedPoint(long double v) : val_(static_cast<int64_t>(v * ScalingFactor)) {}
constexpr double toDouble() {
return static_cast<double>(val_) / ScalingFactor;
}
constexpr int64_t toInt() {
return val_ / ScalingFactor;
}
constexpr int GetScalingFactor() {
return ScalingFactor;
}
constexpr int64_t GetUnderlyingValue() {
return val_;
}
constexpr FixedPoint<ScalingFactor> abs() const {
FixedPoint<ScalingFactor> res;
res.val_ = std::abs(val_);
return res;
}
constexpr FixedPoint<ScalingFactor> exp10(int exp) {
FixedPoint<ScalingFactor> res = *this;
while(exp > 0) {
res *= 10;
exp--;
}
while (exp < 0) {
res *= 10;
exp++;
}
return res;
}
String toString(int min_decimal_digits = 0, bool zero_prefix_if_less_than_one = true) {
if (val_ == 0) {
return "0";
}
constexpr int max_len = std::numeric_limits<int64_t>::max_digits10 + 2; // max digits plus decimal point and sign
char c[max_len];
int len = 0;
int64_t val = val_;
bool negative = false;
if (val < 0) {
negative = true;
val = -val;
}
int64_t decimal_part = val % ScalingFactor;
if (decimal_part != 0) {
while (decimal_part % 10 == 0) {
decimal_part /= 10;
}
do {
len += 1;
c[max_len - len] = '0' + decimal_part % 10;
decimal_part /= 10;
} while (decimal_part > 0);
len += 1;
c[max_len - len] = '.';
}
val /= ScalingFactor;
do {
len += 1;
c[max_len - len] = '0' + val % 10;
val /= 10;
} while (val > 0);
if (negative) {
len += 1;
c[max_len - len] = '-';
}
return String(&c[max_len - len], len);
}
constexpr FixedPoint<ScalingFactor> operator-() {
FixedPoint<ScalingFactor> res;
res.val_ = -val_;
return res;
}
constexpr FixedPoint<ScalingFactor> &operator+=(const FixedPoint<ScalingFactor> &rhs) {
val_ += rhs.val_;
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator+=(int rhs) {
*this += FixedPoint<ScalingFactor>(rhs);
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator+=(double rhs) {
*this += FixedPoint<ScalingFactor>(rhs);
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator-=(const FixedPoint<ScalingFactor> &rhs) {
val_ -= rhs.val_;
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator-=(int rhs) {
*this -= FixedPoint<ScalingFactor>(rhs);
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator-=(double rhs) {
*this -= FixedPoint<ScalingFactor>(rhs);
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator*=(const FixedPoint<ScalingFactor> &rhs) {
val_ = val_ * rhs.val_ / ScalingFactor;
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator*=(int rhs) {
*this *= FixedPoint<ScalingFactor>(rhs);
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator*=(double rhs) {
*this *= FixedPoint<ScalingFactor>(rhs);
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator/=(const FixedPoint<ScalingFactor> &rhs) {
val_ = val_ * ScalingFactor / rhs.val_;
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator/=(int rhs) {
*this /= FixedPoint<ScalingFactor>(rhs);
return *this;
}
constexpr FixedPoint<ScalingFactor> &operator/=(double rhs) {
*this /= FixedPoint<ScalingFactor>(rhs);
return *this;
}
constexpr friend FixedPoint<ScalingFactor> operator+(FixedPoint<ScalingFactor> lhs,
const FixedPoint<ScalingFactor> &rhs) {
lhs += rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator-(FixedPoint<ScalingFactor> lhs,
const FixedPoint<ScalingFactor> &rhs) {
lhs -= rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator*(FixedPoint<ScalingFactor> lhs,
const FixedPoint<ScalingFactor> &rhs) {
lhs *= rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator/(FixedPoint<ScalingFactor> lhs,
const FixedPoint<ScalingFactor> &rhs) {
lhs /= rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator+(FixedPoint<ScalingFactor> lhs, int rhs) {
lhs += rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator+(int lhs, FixedPoint<ScalingFactor> rhs) {
rhs += lhs;
return rhs;
}
constexpr friend FixedPoint<ScalingFactor> operator+(FixedPoint<ScalingFactor> lhs, double rhs) {
lhs += rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator+(double lhs, FixedPoint<ScalingFactor> rhs) {
rhs += lhs;
return rhs;
}
constexpr friend FixedPoint<ScalingFactor> operator-(FixedPoint<ScalingFactor> lhs, int rhs) {
lhs -= rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator-(int lhs, FixedPoint<ScalingFactor> rhs) {
return FixedPoint<ScalingFactor>(lhs) - rhs;
}
constexpr friend FixedPoint<ScalingFactor> operator-(FixedPoint<ScalingFactor> lhs, double rhs) {
lhs -= rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator-(double lhs, FixedPoint<ScalingFactor> rhs) {
return FixedPoint<ScalingFactor>(lhs) - rhs;
}
constexpr friend FixedPoint<ScalingFactor> operator*(FixedPoint<ScalingFactor> lhs, int rhs) {
lhs *= rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator*(int lhs, FixedPoint<ScalingFactor> rhs) {
rhs *= lhs;
return rhs;
}
constexpr friend FixedPoint<ScalingFactor> operator*(FixedPoint<ScalingFactor> lhs, double rhs) {
lhs *= rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator*(double lhs, FixedPoint<ScalingFactor> rhs) {
rhs *= lhs;
return rhs;
}
constexpr friend FixedPoint<ScalingFactor> operator/(FixedPoint<ScalingFactor> lhs, int rhs) {
lhs /= rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator/(int lhs, FixedPoint<ScalingFactor> rhs) {
return FixedPoint<ScalingFactor>(lhs) / rhs;
}
constexpr friend FixedPoint<ScalingFactor> operator/(FixedPoint<ScalingFactor> lhs, double rhs) {
lhs /= rhs;
return lhs;
}
constexpr friend FixedPoint<ScalingFactor> operator/(double lhs, FixedPoint<ScalingFactor> rhs) {
return FixedPoint<ScalingFactor>(lhs) / rhs;
}
constexpr friend bool operator<(FixedPoint<ScalingFactor> lhs, const FixedPoint<ScalingFactor> &rhs) {
return lhs.val_ < rhs.val_;
}
constexpr friend bool operator>(FixedPoint<ScalingFactor> lhs, const FixedPoint<ScalingFactor> &rhs) {
return rhs < lhs;
}
constexpr friend bool operator<=(FixedPoint<ScalingFactor> lhs, const FixedPoint<ScalingFactor> &rhs) {
return !(lhs > rhs);
}
constexpr friend bool operator>=(FixedPoint<ScalingFactor> lhs, const FixedPoint<ScalingFactor> &rhs) {
return !(lhs < rhs);
}
constexpr friend bool operator==(FixedPoint<ScalingFactor> lhs, const FixedPoint<ScalingFactor> &rhs) {
return lhs.val_ == rhs.val_;
}
constexpr friend bool operator!=(FixedPoint<ScalingFactor> lhs, const FixedPoint<ScalingFactor> &rhs) {
return !(lhs == rhs);
}
};

39
include/README Normal file
View File

@ -0,0 +1,39 @@
This directory is intended for project header files.
A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.
```src/main.c
#include "header.h"
int main (void)
{
...
}
```
Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.
In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.
Read more about using header files in official GCC documentation:
* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

36
include/bitmaps/cyber96.h Normal file
View File

@ -0,0 +1,36 @@
// Image definition of: cyber96.png
static PROGMEM bitmap_t cyber96 {
.width = 96,
.height = 25,
.depth = 8,
.pixel_align = 8,
.row_align = 1,
.data = (PROGMEM uint8_t[]) {
0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x90, 0xde, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe8, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xda, 0xf0, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xeb, 0xe3, 0x4f, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xe3, 0x9c, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xd7, 0x39, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xed, 0xa8, 0x2f, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x38, 0xee, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfb, 0x19, 0x00, 0x00, 0x00, 0xe4, 0xfc, 0xff, 0xff, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0xfe, 0xfe, 0xfd, 0xf7, 0x0e, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfd, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xe2, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfb, 0x5a, 0x00,
0x00, 0x00, 0x00, 0x0e, 0xe5, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x35, 0x00, 0x00, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0xff, 0xff, 0xff, 0xff, 0x32, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x85, 0x00, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x05, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfa, 0x2c,
0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xd6, 0x06, 0x00, 0x00, 0x11, 0xff, 0xff, 0xff, 0xff, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0xff, 0xff, 0xff, 0xff, 0x34, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0x37, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xa6, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xff, 0xff, 0xfc, 0xa4,
0x00, 0x00, 0x0e, 0xf0, 0xfe, 0xff, 0xff, 0xfc, 0x97, 0x88, 0x88, 0x88, 0x88, 0x88, 0x74, 0x10, 0x00, 0x00, 0x00, 0x11, 0xff, 0xff, 0xff, 0xfd, 0xc3, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xfe, 0xff, 0xff, 0xff, 0x34, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xb3, 0x88, 0x88, 0x88, 0x88, 0x97, 0xe5, 0xfe, 0xff, 0xff, 0xfc, 0xb0, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xab, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x64, 0x05, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0xa3, 0x88, 0x88, 0x88, 0x88, 0x88, 0x8c, 0xea, 0xfc, 0xff, 0xff, 0xea,
0x00, 0x00, 0x79, 0xff, 0xff, 0xff, 0xfd, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0xf8, 0xfe, 0xff, 0xff, 0xff, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x6d, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x27, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0xd9, 0xfc, 0xff, 0xff, 0xf8, 0x03, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0xfe, 0xff, 0xff, 0xfd,
0x00, 0x0b, 0xec, 0xfe, 0xff, 0xff, 0xfb, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x52, 0x00, 0x00, 0x36, 0xfb, 0xfe, 0xff, 0xff, 0xff, 0x86, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff,
0x00, 0x73, 0xff, 0xff, 0xff, 0xfd, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9f, 0xfe, 0xff, 0xff, 0xfd, 0xf2, 0x23, 0x11, 0xe1, 0xfc, 0xff, 0xff, 0xfd, 0xc0, 0x04, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, 0xff, 0x23, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x7c, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0xcf, 0xfc, 0xff, 0xff, 0xf2,
0x08, 0xe8, 0xfd, 0xff, 0xff, 0xfb, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xd4, 0xfc, 0xff, 0xff, 0xfc, 0xd1, 0xb7, 0xfd, 0xff, 0xff, 0xfc, 0xe8, 0x17, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xfe, 0xff, 0xff, 0xff, 0x13, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xfc, 0xb7,
0x5e, 0xff, 0xff, 0xff, 0xfd, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0xf3, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfb, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0xef, 0xfd, 0xff, 0xff, 0xed, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x45,
0xb7, 0xfc, 0xff, 0xff, 0xfb, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xcf, 0xcf, 0xcf, 0xcf, 0xda, 0xfd, 0xfc, 0xff, 0xff, 0xfd, 0x96, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xdd, 0xcf, 0xcf, 0xcf, 0xab, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0x88, 0x00,
0xe5, 0xff, 0xff, 0xfc, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xab, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xf6, 0x1d, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xe1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x60, 0x00, 0x00,
0xfa, 0xff, 0xff, 0xfe, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc6, 0xfd, 0xff, 0xff, 0xfc, 0xdb, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x6e, 0xc4, 0xfc, 0xff, 0xff, 0xff, 0xa8, 0x27, 0x01, 0x00, 0x00, 0x00,
0xe4, 0xff, 0xff, 0xfc, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xff, 0xff, 0xff, 0xff, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xf6, 0x1e, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xe0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x30, 0xfd, 0xff, 0xff, 0xfe, 0xf5, 0x1a, 0x00, 0x00, 0x00, 0x00,
0xb6, 0xfc, 0xff, 0xff, 0xfb, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xd3, 0xbb, 0xbb, 0xbb, 0xbb, 0xcc, 0xfb, 0xfc, 0xff, 0xff, 0xfd, 0x97, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xcf, 0xbb, 0xbb, 0xbb, 0xa2, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x9a, 0xfe, 0xff, 0xff, 0xfd, 0xa0, 0x00, 0x00, 0x00, 0x00,
0x5e, 0xff, 0xff, 0xff, 0xfd, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xed, 0xfd, 0xff, 0xff, 0xee, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x16, 0xf2, 0xfe, 0xff, 0xff, 0xfe, 0x36, 0x00, 0x00, 0x00,
0x08, 0xe7, 0xfd, 0xff, 0xff, 0xfb, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0xfe, 0xff, 0xff, 0xff, 0x13, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x00, 0x74, 0xff, 0xff, 0xff, 0xfc, 0xc8, 0x01, 0x00, 0x00,
0x00, 0x71, 0xff, 0xff, 0xff, 0xfd, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0xff, 0xff, 0xff, 0xff, 0x23, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x00, 0x06, 0xdc, 0xfc, 0xff, 0xff, 0xff, 0x59, 0x00, 0x00,
0x00, 0x08, 0xe8, 0xfd, 0xff, 0xff, 0xfb, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x00, 0x00, 0x4d, 0xff, 0xff, 0xff, 0xfd, 0xe6, 0x0b, 0x00,
0x00, 0x00, 0x73, 0xff, 0xff, 0xff, 0xfd, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xd9, 0xfc, 0xff, 0xff, 0xf7, 0x03, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x00, 0x00, 0x00, 0xb9, 0xfc, 0xff, 0xff, 0xff, 0x80, 0x00,
0x00, 0x00, 0x0a, 0xec, 0xfe, 0xff, 0xff, 0xfc, 0x97, 0x88, 0x88, 0x88, 0x88, 0x88, 0x75, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xb3, 0x88, 0x88, 0x88, 0x88, 0x97, 0xe6, 0xfe, 0xff, 0xff, 0xfc, 0xaf, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xab, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x65, 0x05, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x29, 0xfb, 0xff, 0xff, 0xff, 0xf8, 0x1f,
0x00, 0x00, 0x00, 0x75, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xd8, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xfe, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0x36, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xa8, 0x00, 0x00, 0x00, 0x45, 0xff, 0xff, 0xff, 0xff, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0xfe, 0xff, 0xff, 0xfd, 0xa0,
0x00, 0x00, 0x00, 0x09, 0xdb, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xff, 0xff, 0xff, 0xff, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x84, 0x00, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x06, 0x00, 0x00, 0x43, 0xff, 0xff, 0xff, 0xff, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xf0, 0xfe, 0xff, 0xff, 0xef,
0x00, 0x00, 0x00, 0x00, 0x2b, 0xe6, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfa, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0xf5, 0xfd, 0xfe, 0xfc, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xfd, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xe0, 0x00, 0x00, 0x00, 0x1a, 0xfb, 0xfe, 0xfe, 0xf7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xff, 0xff, 0xfc, 0xcf,
0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x89, 0xdb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe7, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xe2, 0xe9, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xe1, 0x9a, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xd6, 0x38, 0x00, 0x00, 0x00, 0x00, 0x5f, 0xe9, 0xe4, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x87, 0xed, 0xd0, 0x2e,
}
};

View File

@ -0,0 +1,71 @@
// Image definition of: gradient.png
static PROGMEM bitmap_t gradient {
.width = 60,
.height = 60,
.depth = 8,
.pixel_align = 8,
.row_align = 1,
.data = (PROGMEM uint8_t[]) {
0x07, 0x0b, 0x0e, 0x12, 0x15, 0x19, 0x1c, 0x20, 0x23, 0x26, 0x2a, 0x2e, 0x31, 0x34, 0x38, 0x3c, 0x3f, 0x43, 0x46, 0x49, 0x4d, 0x50, 0x54, 0x58, 0x5b, 0x5f, 0x63, 0x66, 0x6a, 0x6d, 0x71, 0x75, 0x78, 0x7c, 0x7f, 0x82, 0x86, 0x8a, 0x8d, 0x91, 0x94, 0x98, 0x9b, 0x9f, 0xa2, 0xa5, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xbb, 0xbe, 0xc1, 0xc5, 0xc9, 0xcc, 0xd0, 0xd3, 0xd7,
0x07, 0x0b, 0x0e, 0x11, 0x15, 0x18, 0x1c, 0x20, 0x23, 0x27, 0x2a, 0x2e, 0x31, 0x34, 0x38, 0x3c, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x50, 0x55, 0x58, 0x5b, 0x5e, 0x62, 0x66, 0x6a, 0x6d, 0x71, 0x75, 0x78, 0x7c, 0x7f, 0x82, 0x86, 0x8a, 0x8d, 0x91, 0x94, 0x98, 0x9b, 0x9f, 0xa2, 0xa6, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xbb, 0xbe, 0xc2, 0xc6, 0xc9, 0xcc, 0xd0, 0xd3, 0xd7,
0x07, 0x0b, 0x0e, 0x12, 0x15, 0x19, 0x1c, 0x20, 0x23, 0x27, 0x2a, 0x2d, 0x31, 0x34, 0x38, 0x3c, 0x3f, 0x43, 0x46, 0x49, 0x4d, 0x50, 0x54, 0x57, 0x5b, 0x5e, 0x62, 0x66, 0x69, 0x6d, 0x70, 0x74, 0x78, 0x7c, 0x7f, 0x82, 0x86, 0x89, 0x8d, 0x91, 0x94, 0x98, 0x9c, 0x9f, 0xa2, 0xa6, 0xa9, 0xac, 0xb0, 0xb3, 0xb7, 0xba, 0xbe, 0xc2, 0xc5, 0xc9, 0xcc, 0xd0, 0xd3, 0xd7,
0x07, 0x0a, 0x0e, 0x12, 0x15, 0x19, 0x1c, 0x20, 0x23, 0x27, 0x2a, 0x2e, 0x31, 0x35, 0x38, 0x3b, 0x3f, 0x42, 0x46, 0x49, 0x4d, 0x51, 0x54, 0x57, 0x5b, 0x5f, 0x62, 0x66, 0x69, 0x6d, 0x70, 0x74, 0x77, 0x7b, 0x7f, 0x83, 0x86, 0x8a, 0x8d, 0x90, 0x94, 0x97, 0x9b, 0x9f, 0xa2, 0xa6, 0xa9, 0xad, 0xb0, 0xb3, 0xb7, 0xbb, 0xbe, 0xc1, 0xc5, 0xc8, 0xcc, 0xd0, 0xd3, 0xd7,
0x06, 0x0a, 0x0d, 0x12, 0x15, 0x18, 0x1c, 0x1f, 0x23, 0x27, 0x2a, 0x2d, 0x31, 0x35, 0x38, 0x3c, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x66, 0x69, 0x6d, 0x70, 0x74, 0x77, 0x7b, 0x7e, 0x82, 0x85, 0x8a, 0x8d, 0x90, 0x94, 0x97, 0x9b, 0x9f, 0xa2, 0xa6, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xba, 0xbe, 0xc2, 0xc5, 0xc8, 0xcc, 0xd0, 0xd3, 0xd7,
0x06, 0x0a, 0x0d, 0x11, 0x14, 0x18, 0x1c, 0x1f, 0x23, 0x26, 0x2a, 0x2e, 0x31, 0x35, 0x38, 0x3b, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x65, 0x69, 0x6c, 0x70, 0x74, 0x77, 0x7b, 0x7e, 0x81, 0x85, 0x88, 0x8d, 0x91, 0x94, 0x98, 0x9b, 0x9e, 0xa2, 0xa6, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xbb, 0xbe, 0xc2, 0xc5, 0xc8, 0xcc, 0xcf, 0xd3, 0xd7,
0x06, 0x0a, 0x0d, 0x10, 0x14, 0x18, 0x1b, 0x1f, 0x23, 0x26, 0x2a, 0x2e, 0x31, 0x35, 0x38, 0x3c, 0x3f, 0x42, 0x46, 0x4a, 0x4d, 0x50, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x65, 0x69, 0x6c, 0x70, 0x73, 0x77, 0x7a, 0x7e, 0x81, 0x85, 0x89, 0x8c, 0x8f, 0x93, 0x97, 0x9b, 0x9f, 0xa2, 0xa5, 0xa9, 0xac, 0xb0, 0xb4, 0xb7, 0xbb, 0xbe, 0xc1, 0xc5, 0xc9, 0xcc, 0xd0, 0xd3, 0xd7,
0x06, 0x0a, 0x0d, 0x11, 0x14, 0x18, 0x1b, 0x1f, 0x22, 0x26, 0x29, 0x2e, 0x31, 0x35, 0x38, 0x3c, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x50, 0x54, 0x58, 0x5b, 0x5e, 0x62, 0x66, 0x69, 0x6d, 0x70, 0x74, 0x77, 0x7b, 0x7e, 0x81, 0x85, 0x89, 0x8c, 0x90, 0x93, 0x97, 0x9a, 0x9e, 0xa1, 0xa6, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xbb, 0xbe, 0xc2, 0xc5, 0xc9, 0xcc, 0xd0, 0xd3, 0xd7,
0x06, 0x0a, 0x0d, 0x11, 0x14, 0x18, 0x1b, 0x1f, 0x22, 0x26, 0x29, 0x2d, 0x30, 0x34, 0x38, 0x3b, 0x3f, 0x43, 0x46, 0x49, 0x4d, 0x51, 0x54, 0x58, 0x5c, 0x5e, 0x62, 0x66, 0x69, 0x6d, 0x70, 0x74, 0x77, 0x7b, 0x7e, 0x82, 0x85, 0x89, 0x8c, 0x8f, 0x93, 0x97, 0x9a, 0x9e, 0xa2, 0xa5, 0xa8, 0xad, 0xb1, 0xb4, 0xb7, 0xbb, 0xbe, 0xc2, 0xc5, 0xc9, 0xcc, 0xd0, 0xd3, 0xd7,
0x06, 0x0a, 0x0d, 0x11, 0x14, 0x17, 0x1b, 0x1f, 0x22, 0x26, 0x29, 0x2d, 0x30, 0x34, 0x38, 0x3a, 0x3e, 0x43, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x66, 0x69, 0x6d, 0x70, 0x74, 0x77, 0x7b, 0x7e, 0x82, 0x85, 0x89, 0x8c, 0x8f, 0x93, 0x97, 0x9a, 0x9e, 0xa1, 0xa4, 0xa8, 0xab, 0xaf, 0xb2, 0xb7, 0xbb, 0xbe, 0xc1, 0xc5, 0xc9, 0xcc, 0xd0, 0xd3, 0xd7,
0x06, 0x0a, 0x0d, 0x10, 0x14, 0x17, 0x1b, 0x1f, 0x22, 0x26, 0x29, 0x2c, 0x30, 0x34, 0x37, 0x3a, 0x3f, 0x42, 0x45, 0x49, 0x4d, 0x50, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x66, 0x69, 0x6c, 0x70, 0x74, 0x77, 0x7a, 0x7e, 0x82, 0x85, 0x89, 0x8c, 0x90, 0x93, 0x97, 0x9a, 0x9e, 0xa1, 0xa5, 0xa8, 0xac, 0xaf, 0xb3, 0xb6, 0xba, 0xbd, 0xc1, 0xc5, 0xc9, 0xcc, 0xd0, 0xd3, 0xd7,
0x06, 0x0a, 0x0d, 0x10, 0x14, 0x18, 0x1b, 0x1f, 0x22, 0x25, 0x29, 0x2d, 0x30, 0x34, 0x37, 0x3b, 0x3e, 0x41, 0x46, 0x49, 0x4c, 0x50, 0x54, 0x57, 0x5b, 0x5f, 0x62, 0x65, 0x69, 0x6c, 0x70, 0x74, 0x77, 0x7b, 0x7e, 0x82, 0x85, 0x89, 0x8c, 0x8f, 0x93, 0x97, 0x9a, 0x9e, 0xa1, 0xa5, 0xa8, 0xac, 0xaf, 0xb3, 0xb6, 0xba, 0xbd, 0xc1, 0xc4, 0xc8, 0xcc, 0xd0, 0xd3, 0xd7,
0x06, 0x0a, 0x0d, 0x11, 0x14, 0x18, 0x1b, 0x1f, 0x22, 0x26, 0x29, 0x2d, 0x30, 0x34, 0x37, 0x3a, 0x3e, 0x42, 0x45, 0x49, 0x4c, 0x50, 0x53, 0x56, 0x5a, 0x5f, 0x62, 0x66, 0x69, 0x6c, 0x70, 0x74, 0x77, 0x7b, 0x7e, 0x82, 0x85, 0x89, 0x8c, 0x90, 0x93, 0x97, 0x9a, 0x9e, 0xa1, 0xa4, 0xa8, 0xab, 0xaf, 0xb3, 0xb6, 0xba, 0xbe, 0xc1, 0xc4, 0xc8, 0xcb, 0xcf, 0xd3, 0xd7,
0x06, 0x0a, 0x0d, 0x11, 0x14, 0x18, 0x1b, 0x1f, 0x22, 0x25, 0x2a, 0x2d, 0x30, 0x34, 0x37, 0x3a, 0x3e, 0x42, 0x45, 0x49, 0x4c, 0x50, 0x53, 0x57, 0x5a, 0x5e, 0x61, 0x65, 0x69, 0x6d, 0x70, 0x74, 0x77, 0x7a, 0x7e, 0x81, 0x85, 0x89, 0x8c, 0x90, 0x93, 0x96, 0x9a, 0x9e, 0xa1, 0xa5, 0xa8, 0xac, 0xaf, 0xb3, 0xb6, 0xba, 0xbd, 0xc1, 0xc4, 0xc7, 0xcb, 0xcf, 0xd2, 0xd6,
0x06, 0x0a, 0x0d, 0x10, 0x14, 0x18, 0x1b, 0x1e, 0x22, 0x26, 0x29, 0x2c, 0x30, 0x34, 0x38, 0x3a, 0x3e, 0x42, 0x45, 0x49, 0x4c, 0x50, 0x54, 0x57, 0x5a, 0x5d, 0x61, 0x65, 0x68, 0x6c, 0x70, 0x74, 0x77, 0x7b, 0x7e, 0x81, 0x86, 0x89, 0x8c, 0x90, 0x93, 0x97, 0x9a, 0x9e, 0xa1, 0xa5, 0xa9, 0xac, 0xb0, 0xb3, 0xb6, 0xba, 0xbe, 0xc1, 0xc4, 0xc7, 0xcc, 0xcf, 0xd2, 0xd6,
0x06, 0x0a, 0x0d, 0x10, 0x14, 0x18, 0x1b, 0x1f, 0x22, 0x26, 0x29, 0x2d, 0x30, 0x34, 0x37, 0x3b, 0x3e, 0x42, 0x45, 0x49, 0x4c, 0x50, 0x54, 0x57, 0x5a, 0x5e, 0x61, 0x65, 0x68, 0x6c, 0x6f, 0x73, 0x77, 0x7b, 0x7e, 0x82, 0x85, 0x89, 0x8c, 0x90, 0x94, 0x97, 0x9a, 0x9e, 0xa1, 0xa5, 0xa8, 0xac, 0xaf, 0xb3, 0xb6, 0xba, 0xbd, 0xc1, 0xc4, 0xc8, 0xcb, 0xcf, 0xd2, 0xd6,
0x05, 0x09, 0x0d, 0x10, 0x14, 0x17, 0x1b, 0x1f, 0x22, 0x26, 0x2a, 0x2c, 0x30, 0x34, 0x37, 0x3b, 0x3f, 0x42, 0x45, 0x49, 0x4c, 0x50, 0x53, 0x56, 0x5a, 0x5e, 0x61, 0x65, 0x68, 0x6c, 0x6f, 0x73, 0x76, 0x7a, 0x7d, 0x81, 0x85, 0x89, 0x8c, 0x8f, 0x93, 0x96, 0x9a, 0x9e, 0xa1, 0xa5, 0xa8, 0xac, 0xaf, 0xb3, 0xb7, 0xba, 0xbd, 0xc1, 0xc4, 0xc8, 0xcb, 0xcf, 0xd2, 0xd6,
0x05, 0x09, 0x0c, 0x10, 0x13, 0x18, 0x1b, 0x1f, 0x22, 0x26, 0x29, 0x2d, 0x30, 0x34, 0x38, 0x3b, 0x3e, 0x42, 0x45, 0x48, 0x4c, 0x50, 0x54, 0x57, 0x5a, 0x5e, 0x61, 0x65, 0x68, 0x6c, 0x6f, 0x73, 0x77, 0x7a, 0x7d, 0x81, 0x85, 0x88, 0x8c, 0x8f, 0x93, 0x97, 0x9a, 0x9e, 0xa1, 0xa4, 0xa8, 0xac, 0xaf, 0xb3, 0xb6, 0xba, 0xbd, 0xc1, 0xc4, 0xc8, 0xcb, 0xce, 0xd2, 0xd6,
0x05, 0x09, 0x0c, 0x0f, 0x13, 0x17, 0x1b, 0x1e, 0x22, 0x26, 0x29, 0x2d, 0x30, 0x34, 0x37, 0x3b, 0x3f, 0x41, 0x45, 0x49, 0x4d, 0x50, 0x53, 0x57, 0x5a, 0x5e, 0x62, 0x65, 0x69, 0x6c, 0x70, 0x73, 0x76, 0x79, 0x7e, 0x81, 0x84, 0x88, 0x8b, 0x8f, 0x92, 0x97, 0x9a, 0x9e, 0xa1, 0xa5, 0xa8, 0xab, 0xaf, 0xb3, 0xb6, 0xba, 0xbd, 0xc1, 0xc4, 0xc8, 0xcb, 0xce, 0xd2, 0xd6,
0x05, 0x09, 0x0c, 0x0f, 0x13, 0x17, 0x1a, 0x1e, 0x21, 0x25, 0x29, 0x2d, 0x30, 0x34, 0x37, 0x3b, 0x3e, 0x41, 0x45, 0x49, 0x4c, 0x50, 0x53, 0x57, 0x5a, 0x5e, 0x61, 0x65, 0x68, 0x6c, 0x6f, 0x73, 0x76, 0x7a, 0x7d, 0x81, 0x84, 0x88, 0x8b, 0x8f, 0x93, 0x96, 0x99, 0x9d, 0xa1, 0xa5, 0xa9, 0xab, 0xb0, 0xb3, 0xb6, 0xb9, 0xbe, 0xc1, 0xc4, 0xc8, 0xcb, 0xcf, 0xd3, 0xd6,
0x05, 0x09, 0x0c, 0x10, 0x13, 0x17, 0x1a, 0x1e, 0x21, 0x25, 0x28, 0x2c, 0x2f, 0x33, 0x37, 0x3b, 0x3e, 0x42, 0x45, 0x49, 0x4c, 0x50, 0x53, 0x57, 0x5a, 0x5e, 0x61, 0x65, 0x69, 0x6b, 0x6f, 0x73, 0x76, 0x7a, 0x7d, 0x81, 0x84, 0x88, 0x8b, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa0, 0xa4, 0xa7, 0xac, 0xb0, 0xb3, 0xb6, 0xba, 0xbd, 0xc1, 0xc4, 0xc8, 0xcb, 0xcf, 0xd2, 0xd6,
0x05, 0x09, 0x0c, 0x10, 0x14, 0x17, 0x1a, 0x1e, 0x22, 0x25, 0x28, 0x2b, 0x2f, 0x33, 0x36, 0x3a, 0x3e, 0x42, 0x45, 0x49, 0x4c, 0x50, 0x53, 0x56, 0x5a, 0x5e, 0x61, 0x65, 0x69, 0x6c, 0x6f, 0x73, 0x76, 0x7a, 0x7e, 0x81, 0x84, 0x88, 0x8c, 0x8f, 0x93, 0x96, 0x99, 0x9d, 0xa1, 0xa4, 0xa8, 0xab, 0xaf, 0xb3, 0xb6, 0xb9, 0xbd, 0xc1, 0xc4, 0xc8, 0xcb, 0xcf, 0xd3, 0xd6,
0x05, 0x09, 0x0c, 0x10, 0x13, 0x17, 0x1a, 0x1e, 0x21, 0x25, 0x28, 0x2c, 0x2f, 0x32, 0x37, 0x3a, 0x3e, 0x41, 0x44, 0x49, 0x4c, 0x50, 0x54, 0x57, 0x5a, 0x5e, 0x61, 0x65, 0x69, 0x6c, 0x6f, 0x73, 0x76, 0x7a, 0x7d, 0x81, 0x84, 0x88, 0x8b, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa1, 0xa4, 0xa7, 0xab, 0xae, 0xb2, 0xb5, 0xb8, 0xbd, 0xc1, 0xc4, 0xc8, 0xcc, 0xcf, 0xd2, 0xd6,
0x06, 0x09, 0x0c, 0x10, 0x13, 0x16, 0x1b, 0x1e, 0x21, 0x25, 0x29, 0x2c, 0x30, 0x33, 0x37, 0x3a, 0x3e, 0x41, 0x44, 0x48, 0x4b, 0x50, 0x53, 0x56, 0x5a, 0x5e, 0x61, 0x65, 0x68, 0x6c, 0x6f, 0x73, 0x76, 0x7a, 0x7e, 0x81, 0x84, 0x88, 0x8b, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa0, 0xa4, 0xa7, 0xab, 0xae, 0xb2, 0xb6, 0xb9, 0xbc, 0xc0, 0xc3, 0xc7, 0xcb, 0xcf, 0xd2, 0xd6,
0x05, 0x09, 0x0c, 0x10, 0x13, 0x16, 0x1a, 0x1e, 0x22, 0x25, 0x29, 0x2c, 0x2f, 0x32, 0x36, 0x39, 0x3d, 0x41, 0x45, 0x48, 0x4b, 0x4f, 0x52, 0x56, 0x5a, 0x5d, 0x61, 0x65, 0x68, 0x6c, 0x6f, 0x72, 0x76, 0x79, 0x7d, 0x81, 0x84, 0x88, 0x8b, 0x8f, 0x93, 0x96, 0x99, 0x9d, 0xa1, 0xa4, 0xa7, 0xab, 0xaf, 0xb2, 0xb5, 0xb9, 0xbd, 0xc0, 0xc3, 0xc7, 0xca, 0xcf, 0xd2, 0xd6,
0x05, 0x09, 0x0c, 0x10, 0x13, 0x17, 0x1a, 0x1e, 0x21, 0x25, 0x28, 0x2c, 0x2f, 0x33, 0x36, 0x3a, 0x3d, 0x41, 0x44, 0x48, 0x4b, 0x4f, 0x52, 0x56, 0x5a, 0x5d, 0x61, 0x65, 0x69, 0x6c, 0x70, 0x73, 0x77, 0x7a, 0x7d, 0x81, 0x84, 0x88, 0x8b, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa0, 0xa4, 0xa8, 0xab, 0xae, 0xb2, 0xb5, 0xb9, 0xbc, 0xc0, 0xc3, 0xc7, 0xca, 0xce, 0xd1, 0xd5,
0x05, 0x09, 0x0c, 0x10, 0x13, 0x17, 0x1a, 0x1d, 0x21, 0x25, 0x29, 0x2c, 0x2f, 0x33, 0x36, 0x3a, 0x3d, 0x41, 0x44, 0x48, 0x4b, 0x4f, 0x52, 0x56, 0x59, 0x5d, 0x61, 0x64, 0x68, 0x6b, 0x6f, 0x73, 0x77, 0x79, 0x7d, 0x81, 0x84, 0x88, 0x8b, 0x8e, 0x93, 0x96, 0x99, 0x9d, 0xa0, 0xa4, 0xa7, 0xab, 0xaf, 0xb1, 0xb6, 0xb9, 0xbd, 0xc0, 0xc3, 0xc7, 0xca, 0xce, 0xd1, 0xd5,
0x05, 0x09, 0x0c, 0x10, 0x13, 0x17, 0x1b, 0x1e, 0x21, 0x25, 0x28, 0x2c, 0x2f, 0x33, 0x37, 0x3a, 0x3d, 0x41, 0x44, 0x48, 0x4b, 0x4f, 0x52, 0x56, 0x59, 0x5d, 0x60, 0x64, 0x67, 0x6b, 0x6e, 0x72, 0x77, 0x79, 0x7e, 0x80, 0x84, 0x88, 0x8b, 0x8f, 0x93, 0x96, 0x99, 0x9d, 0xa0, 0xa4, 0xa7, 0xab, 0xaf, 0xb2, 0xb5, 0xb9, 0xbc, 0xc0, 0xc3, 0xc7, 0xcb, 0xce, 0xd1, 0xd5,
0x04, 0x08, 0x0c, 0x10, 0x13, 0x17, 0x1a, 0x1e, 0x21, 0x25, 0x28, 0x2c, 0x2f, 0x33, 0x36, 0x3a, 0x3e, 0x41, 0x44, 0x48, 0x4b, 0x4f, 0x52, 0x56, 0x59, 0x5d, 0x60, 0x64, 0x67, 0x6b, 0x6e, 0x72, 0x75, 0x79, 0x7c, 0x81, 0x84, 0x88, 0x8c, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa0, 0xa4, 0xa7, 0xab, 0xaf, 0xb2, 0xb5, 0xb9, 0xbd, 0xc0, 0xc3, 0xc7, 0xca, 0xce, 0xd1, 0xd5,
0x05, 0x08, 0x0b, 0x0f, 0x13, 0x16, 0x1a, 0x1e, 0x21, 0x25, 0x28, 0x2b, 0x30, 0x33, 0x37, 0x3a, 0x3d, 0x40, 0x45, 0x48, 0x4b, 0x4f, 0x52, 0x56, 0x59, 0x5d, 0x60, 0x64, 0x68, 0x6b, 0x6e, 0x72, 0x76, 0x79, 0x7c, 0x80, 0x84, 0x87, 0x8b, 0x8f, 0x92, 0x96, 0x99, 0x9d, 0xa1, 0xa4, 0xa7, 0xab, 0xae, 0xb2, 0xb5, 0xb9, 0xbc, 0xc0, 0xc4, 0xc7, 0xcb, 0xce, 0xd1, 0xd5,
0x04, 0x08, 0x0c, 0x0e, 0x12, 0x16, 0x19, 0x1e, 0x21, 0x25, 0x28, 0x2c, 0x2f, 0x33, 0x36, 0x39, 0x3e, 0x41, 0x44, 0x48, 0x4c, 0x4f, 0x52, 0x56, 0x59, 0x5d, 0x61, 0x64, 0x67, 0x6b, 0x6f, 0x72, 0x76, 0x79, 0x7c, 0x80, 0x84, 0x87, 0x8a, 0x8e, 0x92, 0x96, 0x99, 0x9d, 0xa0, 0xa3, 0xa7, 0xab, 0xaf, 0xb2, 0xb5, 0xb9, 0xbc, 0xc0, 0xc3, 0xc7, 0xca, 0xce, 0xd2, 0xd5,
0x04, 0x08, 0x0c, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x20, 0x25, 0x28, 0x2c, 0x30, 0x33, 0x36, 0x3a, 0x3d, 0x41, 0x45, 0x48, 0x4b, 0x4f, 0x53, 0x56, 0x59, 0x5d, 0x61, 0x64, 0x67, 0x6b, 0x6f, 0x72, 0x75, 0x79, 0x7d, 0x80, 0x83, 0x87, 0x8a, 0x8e, 0x91, 0x95, 0x99, 0x9d, 0xa0, 0xa4, 0xa8, 0xab, 0xaf, 0xb2, 0xb5, 0xb9, 0xbc, 0xc0, 0xc3, 0xc7, 0xcb, 0xce, 0xd2, 0xd5,
0x04, 0x08, 0x0b, 0x0f, 0x12, 0x16, 0x19, 0x1d, 0x21, 0x24, 0x27, 0x2b, 0x2f, 0x33, 0x36, 0x3a, 0x3d, 0x41, 0x44, 0x48, 0x4b, 0x4f, 0x52, 0x56, 0x59, 0x5d, 0x60, 0x64, 0x67, 0x6b, 0x6e, 0x72, 0x76, 0x79, 0x7d, 0x80, 0x83, 0x87, 0x8b, 0x8e, 0x92, 0x95, 0x98, 0x9c, 0xa0, 0xa3, 0xa7, 0xab, 0xae, 0xb2, 0xb5, 0xb9, 0xbc, 0xc0, 0xc4, 0xc7, 0xcb, 0xce, 0xd1, 0xd4,
0x04, 0x08, 0x0b, 0x0f, 0x12, 0x16, 0x1a, 0x1d, 0x20, 0x24, 0x27, 0x2b, 0x2e, 0x32, 0x35, 0x3a, 0x3d, 0x41, 0x45, 0x48, 0x4b, 0x4f, 0x52, 0x56, 0x59, 0x5d, 0x60, 0x64, 0x68, 0x6b, 0x6f, 0x72, 0x75, 0x79, 0x7c, 0x80, 0x83, 0x87, 0x8a, 0x8e, 0x92, 0x95, 0x98, 0x9c, 0x9f, 0xa3, 0xa7, 0xaa, 0xaf, 0xb2, 0xb5, 0xb9, 0xbc, 0xc0, 0xc3, 0xc7, 0xca, 0xce, 0xd2, 0xd5,
0x04, 0x08, 0x0b, 0x0f, 0x12, 0x16, 0x19, 0x1d, 0x21, 0x24, 0x28, 0x2b, 0x2e, 0x32, 0x35, 0x39, 0x3d, 0x40, 0x45, 0x48, 0x4b, 0x4f, 0x52, 0x55, 0x59, 0x5d, 0x61, 0x64, 0x67, 0x6b, 0x6e, 0x72, 0x75, 0x79, 0x7c, 0x80, 0x83, 0x87, 0x8b, 0x8e, 0x92, 0x95, 0x99, 0x9c, 0x9f, 0xa3, 0xa7, 0xaa, 0xad, 0xb1, 0xb5, 0xb9, 0xbd, 0xc0, 0xc4, 0xc7, 0xca, 0xce, 0xd1, 0xd5,
0x04, 0x08, 0x0c, 0x0f, 0x12, 0x16, 0x1a, 0x1d, 0x21, 0x24, 0x28, 0x2b, 0x2e, 0x32, 0x35, 0x39, 0x3c, 0x40, 0x44, 0x47, 0x4c, 0x4f, 0x52, 0x56, 0x59, 0x5d, 0x61, 0x64, 0x68, 0x6b, 0x6f, 0x72, 0x75, 0x79, 0x7d, 0x80, 0x84, 0x87, 0x8a, 0x8e, 0x91, 0x95, 0x99, 0x9c, 0xa0, 0xa3, 0xa6, 0xaa, 0xad, 0xb1, 0xb4, 0xb8, 0xbb, 0xbf, 0xc4, 0xc7, 0xca, 0xce, 0xd1, 0xd5,
0x04, 0x08, 0x0b, 0x0f, 0x12, 0x16, 0x19, 0x1d, 0x20, 0x24, 0x27, 0x2b, 0x2e, 0x32, 0x36, 0x39, 0x3d, 0x40, 0x43, 0x47, 0x4a, 0x4e, 0x52, 0x56, 0x59, 0x5d, 0x60, 0x64, 0x68, 0x6b, 0x6e, 0x72, 0x75, 0x79, 0x7c, 0x80, 0x83, 0x87, 0x8b, 0x8e, 0x92, 0x95, 0x98, 0x9c, 0xa0, 0xa3, 0xa7, 0xaa, 0xae, 0xb1, 0xb5, 0xb8, 0xbc, 0xbf, 0xc2, 0xc6, 0xca, 0xce, 0xd1, 0xd5,
0x04, 0x08, 0x0b, 0x0f, 0x13, 0x16, 0x19, 0x1d, 0x21, 0x24, 0x28, 0x2b, 0x2f, 0x32, 0x35, 0x39, 0x3d, 0x40, 0x43, 0x47, 0x4b, 0x4e, 0x52, 0x55, 0x58, 0x5c, 0x60, 0x64, 0x67, 0x6b, 0x6e, 0x72, 0x75, 0x79, 0x7d, 0x80, 0x83, 0x87, 0x8a, 0x8e, 0x92, 0x95, 0x99, 0x9c, 0xa0, 0xa3, 0xa6, 0xaa, 0xad, 0xb1, 0xb5, 0xb8, 0xbb, 0xbf, 0xc3, 0xc6, 0xc9, 0xcd, 0xd1, 0xd5,
0x04, 0x08, 0x0b, 0x0f, 0x12, 0x16, 0x1a, 0x1d, 0x21, 0x24, 0x28, 0x2b, 0x2e, 0x32, 0x35, 0x39, 0x3c, 0x40, 0x44, 0x47, 0x4b, 0x4e, 0x52, 0x55, 0x59, 0x5c, 0x60, 0x63, 0x66, 0x6b, 0x6e, 0x72, 0x75, 0x79, 0x7c, 0x80, 0x83, 0x87, 0x8a, 0x8e, 0x91, 0x95, 0x99, 0x9c, 0x9f, 0xa3, 0xa7, 0xaa, 0xad, 0xb1, 0xb5, 0xb8, 0xbc, 0xbf, 0xc2, 0xc6, 0xc9, 0xcd, 0xd1, 0xd4,
0x04, 0x08, 0x0b, 0x0f, 0x12, 0x16, 0x19, 0x1d, 0x20, 0x24, 0x27, 0x2b, 0x2e, 0x32, 0x36, 0x39, 0x3c, 0x40, 0x43, 0x47, 0x4a, 0x4e, 0x52, 0x55, 0x58, 0x5c, 0x60, 0x63, 0x67, 0x6a, 0x6e, 0x72, 0x75, 0x79, 0x7d, 0x80, 0x84, 0x87, 0x8b, 0x8e, 0x92, 0x95, 0x99, 0x9c, 0x9f, 0xa3, 0xa6, 0xaa, 0xae, 0xb1, 0xb4, 0xb8, 0xbb, 0xbf, 0xc2, 0xc6, 0xca, 0xcd, 0xd1, 0xd4,
0x04, 0x08, 0x0b, 0x0f, 0x12, 0x16, 0x19, 0x1d, 0x20, 0x24, 0x27, 0x2b, 0x2e, 0x32, 0x35, 0x39, 0x3d, 0x40, 0x43, 0x47, 0x4a, 0x4e, 0x52, 0x55, 0x59, 0x5c, 0x5f, 0x63, 0x67, 0x6a, 0x6e, 0x71, 0x74, 0x78, 0x7c, 0x80, 0x84, 0x87, 0x8b, 0x8e, 0x91, 0x95, 0x98, 0x9c, 0x9f, 0xa3, 0xa7, 0xaa, 0xad, 0xb1, 0xb4, 0xb8, 0xbb, 0xbf, 0xc3, 0xc6, 0xca, 0xcd, 0xd0, 0xd4,
0x03, 0x07, 0x0a, 0x0f, 0x12, 0x16, 0x19, 0x1d, 0x20, 0x24, 0x28, 0x2b, 0x2e, 0x32, 0x36, 0x39, 0x3c, 0x40, 0x43, 0x47, 0x4b, 0x4e, 0x51, 0x55, 0x58, 0x5c, 0x60, 0x63, 0x67, 0x6a, 0x6e, 0x71, 0x75, 0x78, 0x7c, 0x7f, 0x82, 0x87, 0x8b, 0x8e, 0x91, 0x95, 0x99, 0x9c, 0xa0, 0xa3, 0xa6, 0xaa, 0xae, 0xb1, 0xb4, 0xb8, 0xbc, 0xbf, 0xc3, 0xc6, 0xca, 0xcd, 0xd1, 0xd4,
0x04, 0x07, 0x0b, 0x0e, 0x12, 0x15, 0x19, 0x1d, 0x21, 0x24, 0x27, 0x2b, 0x2f, 0x32, 0x35, 0x39, 0x3c, 0x40, 0x43, 0x47, 0x4b, 0x4e, 0x52, 0x55, 0x59, 0x5c, 0x60, 0x63, 0x66, 0x6a, 0x6d, 0x71, 0x74, 0x78, 0x7b, 0x7f, 0x82, 0x86, 0x8a, 0x8e, 0x92, 0x95, 0x99, 0x9c, 0x9f, 0xa3, 0xa7, 0xaa, 0xae, 0xb1, 0xb4, 0xb8, 0xbc, 0xbf, 0xc3, 0xc6, 0xca, 0xcd, 0xd1, 0xd4,
0x03, 0x07, 0x0b, 0x0e, 0x12, 0x15, 0x18, 0x1c, 0x1f, 0x24, 0x28, 0x2b, 0x2e, 0x32, 0x35, 0x39, 0x3d, 0x40, 0x44, 0x47, 0x4a, 0x4e, 0x52, 0x55, 0x58, 0x5c, 0x5f, 0x63, 0x67, 0x6a, 0x6d, 0x71, 0x74, 0x78, 0x7b, 0x7f, 0x83, 0x86, 0x8a, 0x8d, 0x90, 0x94, 0x99, 0x9c, 0xa0, 0xa3, 0xa6, 0xaa, 0xad, 0xb1, 0xb4, 0xb8, 0xbb, 0xbf, 0xc3, 0xc6, 0xca, 0xcd, 0xd0, 0xd4,
0x04, 0x07, 0x0b, 0x0e, 0x11, 0x15, 0x18, 0x1c, 0x20, 0x23, 0x27, 0x2b, 0x2f, 0x32, 0x35, 0x39, 0x3c, 0x40, 0x44, 0x47, 0x4a, 0x4e, 0x52, 0x55, 0x59, 0x5c, 0x5f, 0x63, 0x67, 0x6a, 0x6d, 0x71, 0x75, 0x78, 0x7b, 0x7f, 0x83, 0x86, 0x8a, 0x8d, 0x91, 0x94, 0x98, 0x9b, 0x9f, 0xa3, 0xa7, 0xaa, 0xae, 0xb1, 0xb5, 0xb8, 0xbb, 0xbf, 0xc3, 0xc6, 0xc9, 0xcd, 0xd1, 0xd4,
0x04, 0x07, 0x0a, 0x0e, 0x11, 0x15, 0x19, 0x1c, 0x20, 0x23, 0x26, 0x2a, 0x2e, 0x31, 0x35, 0x39, 0x3d, 0x40, 0x43, 0x47, 0x4b, 0x4e, 0x52, 0x55, 0x59, 0x5c, 0x5f, 0x63, 0x67, 0x6a, 0x6e, 0x71, 0x74, 0x78, 0x7b, 0x7f, 0x82, 0x86, 0x89, 0x8d, 0x91, 0x94, 0x98, 0x9b, 0x9e, 0xa2, 0xa5, 0xa9, 0xae, 0xb1, 0xb5, 0xb8, 0xbc, 0xbf, 0xc3, 0xc6, 0xc9, 0xcd, 0xd1, 0xd4,
0x04, 0x07, 0x0b, 0x0e, 0x11, 0x15, 0x19, 0x1c, 0x1f, 0x23, 0x26, 0x2a, 0x2e, 0x31, 0x35, 0x38, 0x3c, 0x40, 0x43, 0x47, 0x4a, 0x4e, 0x52, 0x55, 0x58, 0x5c, 0x60, 0x63, 0x66, 0x6a, 0x6d, 0x71, 0x74, 0x78, 0x7c, 0x7f, 0x83, 0x86, 0x89, 0x8d, 0x90, 0x94, 0x97, 0x9b, 0x9f, 0xa2, 0xa6, 0xa9, 0xad, 0xb0, 0xb5, 0xb8, 0xbb, 0xbf, 0xc2, 0xc6, 0xc9, 0xcd, 0xd0, 0xd4,
0x04, 0x07, 0x0a, 0x0e, 0x12, 0x15, 0x19, 0x1c, 0x20, 0x23, 0x27, 0x2a, 0x2d, 0x31, 0x34, 0x38, 0x3c, 0x3f, 0x43, 0x46, 0x4b, 0x4e, 0x52, 0x55, 0x59, 0x5c, 0x5f, 0x63, 0x66, 0x6a, 0x6e, 0x71, 0x74, 0x78, 0x7c, 0x7f, 0x83, 0x86, 0x89, 0x8d, 0x91, 0x94, 0x97, 0x9b, 0x9f, 0xa2, 0xa5, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xba, 0xbf, 0xc3, 0xc6, 0xca, 0xcd, 0xd1, 0xd4,
0x03, 0x07, 0x0b, 0x0e, 0x12, 0x15, 0x19, 0x1c, 0x1f, 0x23, 0x26, 0x2a, 0x2e, 0x31, 0x34, 0x38, 0x3c, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x52, 0x55, 0x58, 0x5c, 0x60, 0x63, 0x67, 0x6a, 0x6e, 0x71, 0x74, 0x78, 0x7b, 0x7f, 0x82, 0x86, 0x89, 0x8d, 0x91, 0x94, 0x98, 0x9b, 0x9f, 0xa2, 0xa6, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xbb, 0xbe, 0xc1, 0xc5, 0xca, 0xcd, 0xd1, 0xd4,
0x03, 0x07, 0x0a, 0x0e, 0x12, 0x15, 0x19, 0x1c, 0x20, 0x23, 0x26, 0x2a, 0x2e, 0x31, 0x34, 0x38, 0x3c, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x50, 0x54, 0x58, 0x5c, 0x60, 0x63, 0x66, 0x6a, 0x6e, 0x71, 0x75, 0x78, 0x7c, 0x7f, 0x83, 0x86, 0x8a, 0x8d, 0x91, 0x94, 0x98, 0x9b, 0x9e, 0xa2, 0xa6, 0xa9, 0xac, 0xb0, 0xb4, 0xb7, 0xbb, 0xbe, 0xc1, 0xc5, 0xc9, 0xcc, 0xd1, 0xd4,
0x03, 0x07, 0x0a, 0x0e, 0x12, 0x15, 0x19, 0x1c, 0x1f, 0x23, 0x27, 0x2a, 0x2e, 0x31, 0x35, 0x38, 0x3c, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x57, 0x5b, 0x5e, 0x62, 0x67, 0x6a, 0x6d, 0x71, 0x75, 0x78, 0x7c, 0x7f, 0x82, 0x86, 0x89, 0x8d, 0x90, 0x94, 0x97, 0x9b, 0x9f, 0xa3, 0xa6, 0xa9, 0xac, 0xb0, 0xb4, 0xb8, 0xbb, 0xbe, 0xc2, 0xc5, 0xc9, 0xcc, 0xcf, 0xd3,
0x04, 0x07, 0x0a, 0x0e, 0x11, 0x15, 0x18, 0x1c, 0x20, 0x23, 0x27, 0x2a, 0x2d, 0x31, 0x34, 0x38, 0x3b, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5e, 0x62, 0x66, 0x69, 0x6d, 0x71, 0x75, 0x78, 0x7c, 0x7f, 0x83, 0x86, 0x8a, 0x8d, 0x91, 0x94, 0x97, 0x9b, 0x9f, 0xa2, 0xa6, 0xa9, 0xac, 0xb0, 0xb4, 0xb7, 0xba, 0xbe, 0xc2, 0xc5, 0xc9, 0xcc, 0xd0, 0xd3,
0x03, 0x07, 0x0b, 0x0e, 0x12, 0x15, 0x19, 0x1c, 0x1f, 0x23, 0x27, 0x2a, 0x2e, 0x31, 0x35, 0x38, 0x3b, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x66, 0x69, 0x6c, 0x70, 0x74, 0x78, 0x7b, 0x7f, 0x82, 0x86, 0x8a, 0x8d, 0x91, 0x94, 0x98, 0x9b, 0x9f, 0xa2, 0xa5, 0xa9, 0xac, 0xb0, 0xb4, 0xb7, 0xba, 0xbe, 0xc1, 0xc5, 0xc9, 0xcc, 0xd0, 0xd3,
0x02, 0x06, 0x0a, 0x0e, 0x11, 0x15, 0x19, 0x1c, 0x20, 0x23, 0x27, 0x2a, 0x2d, 0x31, 0x35, 0x38, 0x3c, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x65, 0x69, 0x6d, 0x70, 0x73, 0x77, 0x7b, 0x7e, 0x83, 0x86, 0x8a, 0x8d, 0x90, 0x95, 0x97, 0x9b, 0x9f, 0xa2, 0xa6, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xbb, 0xbf, 0xc2, 0xc5, 0xc8, 0xcc, 0xd0, 0xd3,
0x03, 0x06, 0x09, 0x0d, 0x11, 0x15, 0x18, 0x1c, 0x1f, 0x23, 0x26, 0x2a, 0x2e, 0x31, 0x34, 0x38, 0x3b, 0x3f, 0x42, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5e, 0x62, 0x66, 0x69, 0x6d, 0x70, 0x74, 0x77, 0x7a, 0x7e, 0x81, 0x85, 0x89, 0x8d, 0x91, 0x94, 0x97, 0x9b, 0x9f, 0xa2, 0xa6, 0xa9, 0xac, 0xb0, 0xb4, 0xb7, 0xba, 0xbe, 0xc2, 0xc5, 0xc9, 0xcc, 0xd0, 0xd3,
0x02, 0x06, 0x0a, 0x0d, 0x11, 0x14, 0x18, 0x1b, 0x1f, 0x23, 0x27, 0x2a, 0x2d, 0x31, 0x35, 0x38, 0x3b, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x66, 0x69, 0x6c, 0x71, 0x74, 0x77, 0x7b, 0x7e, 0x82, 0x85, 0x89, 0x8c, 0x90, 0x94, 0x98, 0x9b, 0x9e, 0xa2, 0xa6, 0xa9, 0xad, 0xb0, 0xb3, 0xb7, 0xbb, 0xbe, 0xc1, 0xc5, 0xc9, 0xcd, 0xd0, 0xd3,
0x03, 0x06, 0x0a, 0x0d, 0x11, 0x14, 0x17, 0x1b, 0x1f, 0x22, 0x26, 0x2a, 0x2d, 0x31, 0x34, 0x38, 0x3b, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x57, 0x5b, 0x5e, 0x62, 0x65, 0x69, 0x6d, 0x70, 0x74, 0x77, 0x7a, 0x7e, 0x81, 0x85, 0x89, 0x8c, 0x90, 0x93, 0x97, 0x9a, 0x9e, 0xa2, 0xa5, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xba, 0xbe, 0xc1, 0xc5, 0xc8, 0xcc, 0xcf, 0xd3,
0x03, 0x06, 0x0a, 0x0d, 0x11, 0x14, 0x17, 0x1b, 0x1e, 0x22, 0x26, 0x29, 0x2d, 0x31, 0x34, 0x38, 0x3b, 0x3f, 0x42, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x66, 0x69, 0x6d, 0x70, 0x73, 0x77, 0x7b, 0x7e, 0x82, 0x85, 0x89, 0x8c, 0x90, 0x94, 0x97, 0x9a, 0x9e, 0xa2, 0xa5, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xbb, 0xbe, 0xc2, 0xc5, 0xc9, 0xcd, 0xd0, 0xd3,
0x03, 0x06, 0x0a, 0x0d, 0x11, 0x14, 0x18, 0x1b, 0x1e, 0x22, 0x26, 0x29, 0x2d, 0x30, 0x33, 0x37, 0x3c, 0x3f, 0x43, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x66, 0x69, 0x6d, 0x70, 0x74, 0x77, 0x7a, 0x7e, 0x82, 0x85, 0x89, 0x8c, 0x90, 0x93, 0x96, 0x9b, 0x9e, 0xa1, 0xa5, 0xa8, 0xac, 0xb0, 0xb4, 0xb7, 0xbb, 0xbe, 0xc2, 0xc5, 0xc9, 0xcc, 0xcf, 0xd3,
0x03, 0x06, 0x0a, 0x0e, 0x11, 0x14, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x29, 0x2d, 0x30, 0x33, 0x37, 0x3b, 0x3e, 0x42, 0x46, 0x4a, 0x4d, 0x51, 0x54, 0x58, 0x5b, 0x5f, 0x62, 0x66, 0x69, 0x6d, 0x70, 0x74, 0x77, 0x7b, 0x7e, 0x82, 0x85, 0x88, 0x8c, 0x8f, 0x93, 0x97, 0x9a, 0x9e, 0xa1, 0xa5, 0xa8, 0xac, 0xaf, 0xb3, 0xb7, 0xba, 0xbe, 0xc2, 0xc5, 0xc8, 0xcc, 0xcf, 0xd3,
}
};

252
include/demos.cpp Normal file
View File

@ -0,0 +1,252 @@
float p = 3.1415926;
typedef struct {
uint8_t width;
uint8_t height;
const uint8_t *data;
} bitmap_t;
/*const uint8_t PROGMEM dvdlogo_data[] = {
B00001111, B11111111, B11111100, B00000011, B11111111, B11000000,
B00001111, B11111111, B11111100, B00000011, B11111111, B11111000,
B00000000, B00111111, B11111100, B00000111, B11100001, B11111110,
B00000000, B00000111, B11011110, B00001111, B00000000, B00111110,
B00011110, B00000011, B11011110, B00011110, B01111000, B00011111,
B00011110, B00000011, B11011110, B00111100, B01111000, B00011111,
B00011110, B00000011, B11001111, B00111100, B11111000, B00011110,
B00011110, B00000111, B11001111, B01111000, B11110000, B00011110,
B00111110, B00001111, B10000111, B11110000, B11110000, B00111100,
B00111110, B00111111, B00000111, B11100000, B11110000, B11111000,
B00111111, B11111100, B00000111, B11000000, B11111111, B11110000,
B00111111, B11110000, B00000011, B10000001, B11111111, B10000000,
B00000000, B00000000, B00000011, B10000000, B00000000, B00000000,
B00000000, B00000000, B00000011, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
B00000001, B11111111, B11111111, B11111111, B11111100, B00000000,
B00111111, B11111111, B11100000, B01111111, B11111111, B11100000,
B11111111, B11111111, B10000000, B00011111, B11111111, B11111000,
B01111111, B11111111, B10000000, B00111111, B11111111, B11110000,
B00000111, B11111111, B11111111, B11111111, B11111111, B00000000,
B00000000, B00000011, B11111111, B11111110, B00000000, B00000000 };*/
/* DVD logo */
static PROGMEM bitmap_t dvdlogo = {
.width = 48,
.height = 22,
.data = (PROGMEM uint8_t[]) {
B00001111, B11111111, B11111100, B00000011, B11111111, B11000000,
B00001111, B11111111, B11111100, B00000011, B11111111, B11111000,
B00000000, B00111111, B11111100, B00000111, B11100001, B11111110,
B00000000, B00000111, B11011110, B00001111, B00000000, B00111110,
B00011110, B00000011, B11011110, B00011110, B01111000, B00011111,
B00011110, B00000011, B11011110, B00111100, B01111000, B00011111,
B00011110, B00000011, B11001111, B00111100, B11111000, B00011110,
B00011110, B00000111, B11001111, B01111000, B11110000, B00011110,
B00111110, B00001111, B10000111, B11110000, B11110000, B00111100,
B00111110, B00111111, B00000111, B11100000, B11110000, B11111000,
B00111111, B11111100, B00000111, B11000000, B11111111, B11110000,
B00111111, B11110000, B00000011, B10000001, B11111111, B10000000,
B00000000, B00000000, B00000011, B10000000, B00000000, B00000000,
B00000000, B00000000, B00000011, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
B00000000, B00000000, B00000000, B00000000, B00000000, B00000000,
B00000001, B11111111, B11111111, B11111111, B11111100, B00000000,
B00111111, B11111111, B11100000, B01111111, B11111111, B11100000,
B11111111, B11111111, B10000000, B00011111, B11111111, B11111000,
B01111111, B11111111, B10000000, B00111111, B11111111, B11110000,
B00000111, B11111111, B11111111, B11111111, B11111111, B00000000,
B00000000, B00000011, B11111111, B11111110, B00000000, B00000000
}
};
void testlines(uint16_t color) {
screen.fillScreen(BLACK);
for (uint16_t x = 0; x < screen.width() - 1; x += 6) {
screen.drawLine(0, 0, x, screen.height() - 1, color);
}
for (uint16_t y = 0; y < screen.height() - 1; y += 6) {
screen.drawLine(0, 0, screen.width() - 1, y, color);
}
screen.fillScreen(BLACK);
for (uint16_t x = 0; x < screen.width() - 1; x += 6) {
screen.drawLine(screen.width() - 1, 0, x, screen.height() - 1, color);
}
for (uint16_t y = 0; y < screen.height() - 1; y += 6) {
screen.drawLine(screen.width() - 1, 0, 0, y, color);
}
screen.fillScreen(BLACK);
for (uint16_t x = 0; x < screen.width() - 1; x += 6) {
screen.drawLine(0, screen.height() - 1, x, 0, color);
}
for (uint16_t y = 0; y < screen.height() - 1; y += 6) {
screen.drawLine(0, screen.height() - 1, screen.width() - 1, y, color);
}
screen.fillScreen(BLACK);
for (uint16_t x = 0; x < screen.width() - 1; x += 6) {
screen.drawLine(screen.width() - 1, screen.height() - 1, x, 0, color);
}
for (uint16_t y = 0; y < screen.height() - 1; y += 6) {
screen.drawLine(screen.width() - 1, screen.height() - 1, 0, y, color);
}
}
void testdrawtext(const char *text, uint16_t color) {
screen.setCursor(0, 0);
screen.setTextColor(color);
screen.print(text);
}
void testfastlines(uint16_t color1, uint16_t color2) {
screen.fillScreen(BLACK);
for (uint16_t y = 0; y < screen.height() - 1; y += 5) {
screen.drawFastHLine(0, y, screen.width() - 1, color1);
}
for (uint16_t x = 0; x < screen.width() - 1; x += 5) {
screen.drawFastVLine(x, 0, screen.height() - 1, color2);
}
}
void testdrawrects(uint16_t color) {
screen.fillScreen(BLACK);
for (uint16_t x = 0; x < screen.height() - 1; x += 6) {
screen.drawRect((screen.width() - 1) / 2 - x / 2, (screen.height() - 1) / 2 - x / 2, x, x, color);
}
}
void testfillrects(uint16_t color1, uint16_t color2) {
screen.fillScreen(BLACK);
for (uint16_t x = screen.height() - 1; x > 6; x -= 6) {
screen.fillRect((screen.width() - 1) / 2 - x / 2, (screen.height() - 1) / 2 - x / 2, x, x, color1);
screen.drawRect((screen.width() - 1) / 2 - x / 2, (screen.height() - 1) / 2 - x / 2, x, x, color2);
}
}
void testfillcircles(uint8_t radius, uint16_t color) {
for (uint8_t x = radius; x < screen.width() - 1; x += radius * 2) {
for (uint8_t y = radius; y < screen.height() - 1; y += radius * 2) {
screen.fillCircle(x, y, radius, color);
}
}
}
void testdrawcircles(uint8_t radius, uint16_t color) {
for (uint8_t x = 0; x < screen.width() - 1 + radius; x += radius * 2) {
for (uint8_t y = 0; y < screen.height() - 1 + radius; y += radius * 2) {
screen.drawCircle(x, y, radius, color);
}
}
}
void testtriangles() {
screen.fillScreen(BLACK);
int color = 0xF800;
int t;
int w = screen.width() / 2;
int x = screen.height();
int y = 0;
int z = screen.width();
for (t = 0; t <= 15; t += 1) {
screen.drawTriangle(w, y, y, x, z, x, color);
x -= 4;
y += 4;
z -= 4;
color += 100;
}
}
void testroundrects() {
screen.fillScreen(BLACK);
int color = 100;
int x = 0;
int y = 0;
int w = screen.width();
int h = screen.height();
for (int i = 0; i <= 24; i++) {
screen.drawRoundRect(x, y, w, h, 5, color);
x += 2;
y += 3;
w -= 4;
h -= 6;
color += 1100;
Serial.println(i);
}
}
void oledPrintTest() {
screen.fillScreen(BLACK);
screen.setCursor(0, 5);
screen.setTextColor(RED);
screen.setTextSize(1);
screen.println("Hello World!");
screen.setTextColor(YELLOW);
screen.setTextSize(2);
screen.println("Hello World!");
screen.setTextColor(BLUE);
screen.setTextSize(3);
screen.print(1234.567);
delay(1500);
screen.setCursor(0, 5);
screen.fillScreen(BLACK);
screen.setTextColor(WHITE);
screen.setTextSize(0);
screen.println("Hello World!");
screen.setTextSize(1);
screen.setTextColor(GREEN);
screen.print(p, 6);
screen.println(" Want pi?");
screen.println(" ");
screen.print(8675309, HEX); // print 8,675,309 out in HEX!
screen.println(" Print HEX!");
screen.println(" ");
screen.setTextColor(WHITE);
screen.println("Sketch has been");
screen.println("running for: ");
screen.setTextColor(MAGENTA);
screen.print(millis() / 1000);
screen.setTextColor(WHITE);
screen.print(" seconds.");
}
void mediabuttons() {
// play
screen.fillScreen(BLACK);
screen.fillRoundRect(25, 10, 78, 60, 8, WHITE);
screen.fillTriangle(42, 20, 42, 60, 90, 40, RED);
delay(500);
// pause
screen.fillRoundRect(25, 90, 78, 60, 8, WHITE);
screen.fillRoundRect(39, 98, 20, 45, 5, GREEN);
screen.fillRoundRect(69, 98, 20, 45, 5, GREEN);
delay(500);
// play color
screen.fillTriangle(42, 20, 42, 60, 90, 40, BLUE);
delay(50);
// pause color
screen.fillRoundRect(39, 98, 20, 45, 5, RED);
screen.fillRoundRect(69, 98, 20, 45, 5, RED);
// play color
screen.fillTriangle(42, 20, 42, 60, 90, 40, GREEN);
}
/**************************************************************************/
/*!
@brief Renders a simple test pattern on the screen
*/
/**************************************************************************/
void lcdTestPattern(void) {
static const uint16_t PROGMEM colors[] =
{RED, YELLOW, GREEN, CYAN, BLUE, MAGENTA, BLACK, WHITE};
for (uint8_t c = 0; c < 8; c++) {
screen.fillRect(0, screen.height() * c / 8, screen.width(), screen.height() / 8,
pgm_read_word(&colors[c]));
}
}

42
include/fptrig.h Normal file
View File

@ -0,0 +1,42 @@
#include <cstdint>
/*
Implements the 5-order polynomial approximation to sin(x).
@param i angle (with 2^15 units/circle)
@return 16 bit fixed point Sine value (4.12) (ie: +4096 = +1 & -4096 = -1)
The result is accurate to within +- 1 count. ie: +/-2.44e-4.
*/
int16_t fpsin(int16_t i)
{
/* Convert (signed) input to a value between 0 and 8192. (8192 is pi/2, which is the region of the curve fit). */
/* ------------------------------------------------------------------- */
i <<= 1;
uint8_t c = i<0; //set carry for output pos/neg
if(i == (i|0x4000)) // flip input value to corresponding value in range [0..8192)
i = (1<<15) - i;
i = (i & 0x7FFF) >> 1;
/* ------------------------------------------------------------------- */
/* The following section implements the formula:
= y * 2^-n * ( A1 - 2^(q-p)* y * 2^-n * y * 2^-n * [B1 - 2^-r * y * 2^-n * C1 * y]) * 2^(a-q)
Where the constants are defined as follows:
*/
enum {A1=3370945099UL, B1=2746362156UL, C1=292421UL};
enum {n=13, p=32, q=31, r=3, a=12};
uint32_t y = (C1*((uint32_t)i))>>n;
y = B1 - (((uint32_t)i*y)>>r);
y = (uint32_t)i * (y>>n);
y = (uint32_t)i * (y>>n);
y = A1 - (y>>(p-q));
y = (uint32_t)i * (y>>n);
y = (y+(1UL<<(q-a-1)))>>(q-a); // Rounding
return c ? -y : y;
}
//Cos(x) = sin(x + pi/2)
#define fpcos(i) fpsin((int16_t)(((uint16_t)(i)) + 8192U))

15
include/screen-settings.h Normal file
View File

@ -0,0 +1,15 @@
// Screen dimensions
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 128 // Change this to 96 for 1.27" OLED.
// You can use any (4 or) 5 pins
// #define SCLK_PIN 14
// #define MOSI_PIN 13
#define DC_PIN 26
#define CS_PIN 27
#define RST_PIN 25
#define HSPI_MISO 12
#define HSPI_MOSI 13
#define HSPI_SCLK 14
#define HSPI_SS 15

View File

@ -0,0 +1,3 @@
#include "Commander.h"
void registerWifiCommands(Commander &commander);

382
lib/Commander/Commander.cpp Normal file
View File

@ -0,0 +1,382 @@
#include <new>
#include <HardwareSerial.h>
#include "Commander.h"
Commander *Commander::serial_instance_ = nullptr;
void Commander::EnableSerial() {
assert(!running_);
assert(serial_instance_ == nullptr);
serial_instance_ = this;
enable_serial_ = true;
}
void Commander::SetFallbackHandler(const std::function<void(const std::vector<CmdString> &,
CommanderClient *)> &handler) {
assert(!running_);
handler_ = handler;
}
void Commander::Begin() {
assert(!running_);
running_ = true;
if (enable_serial_) {
serial_client_ = new SerialCommanderClient(this);
serial_client_->Begin();
serial_instance_ = this;
}
if (enable_wifi_) {
assert(port_ > 0);
tcp_server_ = new AsyncServer(port_);
tcp_server_->onClient(Commander::WifiOnClientHandler, this);
xTaskCreate(Commander::TaskFn, "Commander", 10000, this, 1, &this->task_handle_);
}
}
void Commander::Stop() {
assert(running_);
vTaskDelete(task_handle_);
// todo: remove the serial handler, disconnect all wifi tcp clients, stop the wifi tcp server
running_ = false;
}
void Commander::WifiOnClient(AsyncClient *client) {
wifi_clients_.emplace_back(this, client);
}
void Commander::WifiOnClientHandler(void *thisarg, AsyncClient *client) {
((Commander *)thisarg)->WifiOnClient(client);
}
void Commander::EnableWifi(uint16_t port) {
assert(!running_);
enable_wifi_ = true;
port_ = port;
}
void Command::SetPrompt(const String& prompt) {
prompt_ = prompt;
}
void Commander::TaskFn(void *thisarg) {
((Commander*)thisarg)->Task();
}
[[noreturn]] void Commander::Task() {
while(true) {
ulTaskNotifyTake(pdTRUE, 1000); // todo: number of ticks, can it be made infinite?
// todo: something something queue of newly connected wifi clients, actually use a queue here maybe instead of a notification
}
}
void CommanderClient::DipatchCurrentCommandLine() {
assert(!current_line_.empty());
// todo: somehow enter into new shell if a shell command was called without arguments
const Command* shell = shell_stack_.back()->Dispatch(current_line_, this);
if (shell != nullptr) {
shell_stack_.push_back(shell);
}
current_line_.clear();
}
void CommanderClient::ProcessCommandLine() {
if (current_line_.empty()) {
current_line_.emplace_back("");
}
size_t pos = 0;
for (char c : buffer_) {
auto &current = current_line_.back();
switch (c) {
case ' ':
if (in_quotes_ || in_escape_) {
current.concat(' ');
} else {
if (current.length() > 0) {
current_line_.emplace_back("");
}
}
break;
case '"':
if (in_escape_) {
current.concat('"');
in_escape_ = false;
} else {
if (in_quotes_) {
in_quotes_ = false;
current_line_.emplace_back("");
} else {
in_quotes_ = true;
if (current.length() > 0) {
current_line_.emplace_back("");
}
}
}
break;
case '\\':
if (in_escape_) {
current.concat('\\');
in_escape_ = false;
} else {
in_escape_ = true;
}
break;
case '\n':
if (in_escape_ || in_quotes_) {
current.concat('\n');
print("> "); // line continuation prompt
if (in_escape_) {
in_escape_ = false;
}
} else {
goto line_completed;
}
break;
default:
if (c >= 0x20) { // ignore control characters etc (also ignores tab)
current.concat(c);
}
break;
}
pos += 1;
}
goto end;
line_completed:
pos += 1;
DipatchCurrentCommandLine();
end:
buffer_.remove(0, pos);
}
const String &CommanderClient::GetPrompt() {
return shell_stack_.back()->prompt_;
}
const String &CommanderClient::GetWelcomeMessage() {
return shell_stack_.back()->welcome_message_;
}
void CommanderClient::BufferAppend(const String &str) {
buffer_.concat(str);
}
void CommanderClient::BufferAppend(char ch) {
buffer_.concat(ch);
}
/*
template<typename T>
bool CommanderClient::ReadLine(T *out) {
static_assert(!std::is_same<T, int64_t>::value, "64-bit not supported");
static_assert(!std::is_same<T, uint64_t>::value, "64-bit not supported");
static_assert(std::is_integral<T>::value, "integer type required");
CmdString line = ReadLine();
if (line.isEmpty()) return false;
int64_t res;
int i = 0;
if (!std::is_unsigned<T>::value) {
bool negative = false;
if (line[0] == '-') {
i = 1;
negative = true;
if (line.length() < 2) return false;
}
char ch = line[i];
if (ch >= '0' && ch <= '9') {
res = ch - '0';
if (negative) {
res = -res;
}
} else {
return false;
};
i += 1;
}
for (; i < line.length(); i++) {
char ch = line[i];
if (ch >= '0' && ch <= '9') {
uint8_t digit = ch - '0';
res *= 10;
res += digit;
if (res > std::numeric_limits<T>::max() || res < std::numeric_limits<T>::min()) {
return false;
}
} else {
return false;
}
}
*out = res;
return true;
}*/
CmdString CommanderClient::ReadLine() {
CmdString ret;
int c = read();
while (c != '\n') {
if (c >= 0x20) { // ignore control chars, todo: make this better I guess? line editing capabilities?
write(c);
ret += (char)c;
}
c = read();
}
write('\n');
return ret;
}
SerialCommanderClient *SerialCommanderClient::single_instance_ = nullptr;
size_t SerialCommanderClient::write(uint8_t byte) { return Serial.write(byte); }
int SerialCommanderClient::available() { return Serial.available(); }
int SerialCommanderClient::read() { return Serial.read(); }
int SerialCommanderClient::peek() { return Serial.peek(); }
void SerialCommanderClient::flush() { Serial.flush(); }
void SerialCommanderClient::OnReceive() {
assert(task_handle_ != nullptr);
vTaskNotifyGiveFromISR(task_handle_, nullptr);
}
SerialCommanderClient::SerialCommanderClient(Commander *commander) : CommanderClient(commander) {
assert(single_instance_ == nullptr);
single_instance_ = this;
println(GetWelcomeMessage());
println("");
print(GetPrompt());
}
void SerialCommanderClient::Task() {
while(true) {
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(10000)); // todo: number of ticks, possible to wait infinitely long?
if (in_command_) continue;
in_command_ = true;
while(Serial.available()) {
int ch = Serial.read();
if (ch < 0) {
break;
}
Serial.write(ch); // echo the typed character so the user sees it
BufferAppend((char)ch);
if (ch == '\n') {
ProcessCommandLine();
print(GetPrompt());
}
}
in_command_ = false;
}
}
void WifiCommanderClient::OnData(void *data, size_t len) {
// todo
}
size_t WifiCommanderClient::write(uint8_t byte) {
// todo: also implement a write(const char*) or something like it
}
int WifiCommanderClient::available() {
// todo
}
int WifiCommanderClient::read() {
// todo
}
int WifiCommanderClient::peek() {
// todo
}
void WifiCommanderClient::flush() {
// todo
}
void WifiCommanderClient::OnDataHandler(void *thisarg, AsyncClient *, void *data, size_t len) {
((WifiCommanderClient *)thisarg)->OnData(data, len);
}
WifiCommanderClient::WifiCommanderClient(Commander *commander, AsyncClient *client)
: CommanderClient(commander), async_client_(client) {
async_client_->onData(WifiCommanderClient::OnDataHandler, this);
}
Command *Command::RegisterCommand(const String &name,
const std::function<void(const std::vector<CmdString> &, CommanderClient *)> &handler) {
commands_.push_back(Command(name, handler, false));
return &commands_[commands_.size() - 1];
}
Command *Command::RegisterCommandWithShell(const String &name) {
commands_.push_back(Command(name, nullptr, true));
return &commands_[commands_.size() - 1];
}
const Command* Command::Dispatch(std::vector<CmdString> cmdline, CommanderClient *client) const {
if (!cmdline.empty()) {
auto &first = cmdline.front();
for (const auto &item : commands_) {
if (item.name_.equals(first)) {
if (item.shell_) {
return &item;
}
item.Dispatch(std::vector<CmdString>(cmdline.begin() + 1, cmdline.end()),
client); // todo maybe optimize this vector
return nullptr;
}
}
if (first.equals("?")) {
DefaultHelpHandler(client);
return nullptr;
}
}
if (handler_ == nullptr) {
DefaultHandler(cmdline, client);
} else {
handler_(cmdline, client);
}
}
void Command::DefaultHandler(const std::vector<CmdString> &cmdline, CommanderClient *client) const {
if (commands_.empty()) {
client->println("this command is not implemented");
} else {
if (cmdline.empty()) {
DefaultHelpHandler(client);
} else {
client->print("unknown sub-command: ");
client->println(cmdline.front());
}
}
}
void Command::DefaultHelpHandler(CommanderClient *client) const {
client->println("the following commands are available: ");
for (const auto &item : commands_) {
client->print(" ");
client->print(item.name_);
client->print(" ");
client->print(item.description_);
client->println();
}
}
void Command::SetWelcomeMessage(const String &message) {
welcome_message_ = message;
}

318
lib/Commander/Commander.h Normal file
View File

@ -0,0 +1,318 @@
#ifndef ESP32DISPLAYTEST_LIB_COMMANDER_COMMANDER_H_
#define ESP32DISPLAYTEST_LIB_COMMANDER_COMMANDER_H_
#include <functional>
#include <utility>
#include <vector>
#include <WString.h>
#include <WiFiServer.h>
#include <AsyncTCP.h>
#include <Stream.h>
class CmdString : public String {
public:
CmdString(const char *cstr = "") : String(cstr){}
CmdString(const String& str) : String(str){}
template<typename T>
bool ReadInto(T *out) const {
static_assert(!std::is_same<T, int64_t>::value, "64-bit not supported");
static_assert(!std::is_same<T, uint64_t>::value, "64-bit not supported");
static_assert(std::is_integral<T>::value, "integer type required");
if (isEmpty()) return false;
int64_t res;
int i = 0;
if (!std::is_unsigned<T>::value) {
bool negative = false;
if (this->charAt(0) == '-') {
i = 1;
negative = true;
if (length() < 2) return false;
}
char ch = this->charAt(i);
if (ch >= '0' && ch <= '9') {
res = ch - '0';
if (negative) {
res = -res;
}
} else {
return false;
};
i += 1;
}
for (; i < length(); i++) {
char ch = this->charAt(i);
if (ch >= '0' && ch <= '9') {
uint8_t digit = ch - '0';
res *= 10;
res += digit;
if (res > std::numeric_limits<T>::max() || res < std::numeric_limits<T>::min()) {
return false;
}
} else {
return false;
}
}
*out = res;
return true;
}
};
class Commander;
class Command;
class CommanderClient;
class SerialCommanderClient;
class WifiCommanderClient;
class Command {
friend class CommanderClient;
protected:
String name_;
String description_;
std::function<void(std::vector<CmdString>, CommanderClient *)> handler_;
std::vector<Command> commands_;
bool shell_;
String prompt_ = "> ";
String welcome_message_ = "";
Command(const String &name, std::function<void(std::vector<CmdString>, CommanderClient *)> handler, bool shell)
: handler_(std::move(handler)), shell_(shell) {
int space_index = name.indexOf(' ');
if (space_index == -1) {
name_ = name;
description_ = "";
} else {
name_ = name.substring(0, space_index);
description_ = name.substring(space_index);
}
}
const Command* Dispatch(std::vector<CmdString> cmdline, CommanderClient *client) const;
void DefaultHandler(const std::vector<CmdString> &cmdline, CommanderClient *client) const;
void DefaultHelpHandler(CommanderClient *client) const;
public:
Command *RegisterCommand(const String &name,
const std::function<void(const std::vector<CmdString> &,
CommanderClient *)> &handler);
Command *RegisterCommandWithShell(const String &name);
void SetPrompt(const String &prompt);
void SetWelcomeMessage(const String &message);
};
class Commander : public Command {
friend CommanderClient;
private:
bool enable_wifi_ = false;
bool enable_serial_ = false;
uint16_t port_ = 0;
AsyncServer *tcp_server_{};
std::vector<WifiCommanderClient> wifi_clients_;
SerialCommanderClient *serial_client_{};
bool running_ = false;
static Commander *serial_instance_;
TaskHandle_t task_handle_{};
public:
Commander() : Command("", Commander::DefaultFallbackHandler, false) {}
using Command::RegisterCommand;
using Command::RegisterCommandWithShell;
void EnableSerial();
void EnableWifi(uint16_t port);
void SetFallbackHandler(const std::function<void(const std::vector<CmdString> &,
CommanderClient *)> &handler);
void Begin();
void Stop();
private:
void WifiOnClient(AsyncClient *client);
void SerialReceive();
[[noreturn]] void Task();
static void SerialReceiveHandler();
static void WifiOnClientHandler(void *thisarg, AsyncClient *client);
[[noreturn]] static void TaskFn(void *thisarg);
static void DefaultFallbackHandler(std::vector<CmdString> cmd, CommanderClient *client) {
}
};
class CommanderClient : public Stream {
protected:
std::vector<const Command *> shell_stack_;
bool in_command_ = false;
String buffer_ = "";
std::vector<CmdString> current_line_;
bool in_quotes_ = false;
bool in_escape_ = false;
bool previous_was_cr_ = false;
TaskHandle_t task_handle_ = nullptr;
void BufferAppend(const String &str);
void BufferAppend(char ch);
void DipatchCurrentCommandLine();
void ProcessCommandLine();
const String &GetPrompt();
const String &GetWelcomeMessage();
virtual void Task() = 0;
static void TaskFn(void *thisarg) {
((CommanderClient *)thisarg)->Task();
}
public:
explicit CommanderClient(Commander *commander) {
shell_stack_.push_back(commander);
}
virtual void Begin() {
xTaskCreate(CommanderClient::TaskFn, "commander client", 10000, this, 1, &this->task_handle_);
}
CmdString ReadLine();
template<typename T>
bool ReadLine(T *out) {
static_assert(!std::is_same<T, int64_t>::value, "64-bit not supported");
static_assert(!std::is_same<T, uint64_t>::value, "64-bit not supported");
static_assert(std::is_integral<T>::value, "integer type required");
CmdString line = ReadLine();
if (line.isEmpty()) return false;
int64_t res;
int i = 0;
if (!std::is_unsigned<T>::value) {
bool negative = false;
if (line[0] == '-') {
i = 1;
negative = true;
if (line.length() < 2) return false;
}
char ch = line[i];
if (ch >= '0' && ch <= '9') {
res = ch - '0';
if (negative) {
res = -res;
}
} else {
return false;
};
i += 1;
}
for (; i < line.length(); i++) {
char ch = line[i];
if (ch >= '0' && ch <= '9') {
uint8_t digit = ch - '0';
res *= 10;
res += digit;
if (res > std::numeric_limits<T>::max() || res < std::numeric_limits<T>::min()) {
return false;
}
} else {
return false;
}
}
*out = res;
return true;
}
};
class SerialCommanderClient : public CommanderClient {
static SerialCommanderClient *single_instance_;
public:
explicit SerialCommanderClient(Commander *commander);
void OnReceive();
size_t write(uint8_t byte) override;
int available() override;
int read() override;
int peek() override;
void flush() override;
void Begin() override {
CommanderClient::Begin();
Serial.onReceive(SerialCommanderClient::OnReceiveHandler);
}
[[noreturn]] void Task() override;
static void OnReceiveHandler() {
single_instance_->OnReceive();
}
};
class WifiCommanderClient : public CommanderClient {
AsyncClient *async_client_;
public:
explicit WifiCommanderClient(Commander *commander, AsyncClient *client);
void OnData(void *data, size_t len);
size_t write(uint8_t byte) override;
int available() override;
int read() override;
int peek() override;
void flush() override;
void Task() {};
private:
static void OnDataHandler(void *thisarg, AsyncClient *, void *data, size_t len);
};
#endif //ESP32DISPLAYTEST_LIB_COMMANDER_COMMANDER_H_

View File

@ -0,0 +1,57 @@
#include "ExtendedScreen.h"
#include <algorithm>
#include <cstdint>
void
ExtendedScreen::drawTransparentBitmap(int16_t x, int16_t y, const bitmap_t& bitmap, const RGB& foreground, const RGB& background) {
_screen->startWrite();
for (int16_t j = 0; j < bitmap.height; j++, y++) {
for (int16_t i = 0; i < bitmap.width; i++) {
uint8_t alpha = bitmap.data[j*bitmap.width+i]; // todo: allow for less than 8 bit per pixel
uint16_t blended_color = foreground.alpha_blend_over_to565(background, alpha);
_screen->writePixel(x + i, y, blended_color);
}
}
_screen->endWrite();
}
void
ExtendedScreen::drawTransparentBitmap(int16_t x, int16_t y, const bitmap_t& bitmap, const RGB& foreground, const RGB& background, const rect_t& rect) {
auto background565 = background.to_565();
_screen->startWrite();
int16_t max_image_width = std::min(static_cast<int16_t>(rect.x + rect.width), static_cast<int16_t>(bitmap.width));
int16_t max_image_height = std::min(static_cast<int16_t>(rect.y + rect.height), static_cast<int16_t>(bitmap.height));
int16_t j = rect.y;
for(; j < 0; j++) {
for (int16_t i = rect.x; i < rect.x + rect.width; i++) {
_screen->writePixel(x + i - rect.x, y + j - rect.y, background565);
}
}
for (; j < max_image_height; j++) {
int16_t i = rect.x;
for (; i < 0; i++) {
_screen->writePixel(x + i - rect.x, y + j - rect.y, background565);
}
for (; i < max_image_width; i++) {
uint8_t alpha = bitmap.data[j*bitmap.width+i]; // todo: allow for less than 8 bit per pixel
uint16_t blended_color = foreground.alpha_blend_over_to565(background, alpha);
_screen->writePixel(x + i - rect.x, y + j - rect.y, blended_color);
}
for (; i < rect.x + rect.width; i++) {
_screen->writePixel(x + i - rect.x, y + j - rect.y, background565);
}
}
for(; j < rect.y + rect.height; j++) {
for (int16_t i = rect.x; i < rect.x + rect.width; i++) {
_screen->writePixel(x + i - rect.x, y + j - rect.y, background565);
}
}
_screen->endWrite();
}

View File

@ -0,0 +1,76 @@
#ifndef ESP32DISPLAYTEST_EXTENDEDSCREEN_H
#define ESP32DISPLAYTEST_EXTENDEDSCREEN_H
#include "Adafruit_GFX.h"
#include "colors.h"
#include "bitmap.h"
#include <cstdint>
class ExtendedScreen : public Adafruit_GFX {
private:
Adafruit_GFX *_screen;
void drawPixel(int16_t x, int16_t y, uint16_t color) override {
_screen->drawPixel(x, y, color);
}
public:
explicit ExtendedScreen(Adafruit_GFX *actualScreen) : Adafruit_GFX(actualScreen->width(), actualScreen->height()) {
this->_screen = actualScreen;
}
void drawTransparentBitmap(int16_t x, int16_t y, const bitmap_t& bitmap, const RGB& foreground, const RGB& background);
void drawTransparentBitmap(int16_t x, int16_t y, const bitmap_t& bitmap, const RGB& foreground, const RGB& background, const rect_t& rect);
// forward possibly-overridden virtual methods to the actual screen class
void startWrite() override { _screen->startWrite(); };
void writePixel(int16_t x, int16_t y, uint16_t color) override { _screen->writePixel(x, y, color); };
void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color) override { _screen->writeFillRect(x, y, w, h, color); };
void writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) override {
_screen->writeFastVLine(x, y, h, color);
};
void writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) override {
_screen->writeFastHLine(x, y, w, color);
};
void writeLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
uint16_t color) override { _screen->writeLine(x0, y0, x1, y1, color); };
void endWrite() override { _screen->endWrite(); };
void setRotation(uint8_t r) override { _screen->setRotation(r); };
void invertDisplay(bool i) override { _screen->invertDisplay(i); };
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color) override {
_screen->drawFastVLine(x, y, h, color);
};
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color) override {
_screen->drawFastHLine(x, y, w, color);
};
void fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color) override { _screen->fillRect(x, y, w, h, color); };
void fillScreen(uint16_t color) override { _screen->fillScreen(color); };
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1,
uint16_t color) override { _screen->drawLine(x0, y0, x1, y1, color); };
void drawRect(int16_t x, int16_t y, int16_t w, int16_t h,
uint16_t color) override { _screen->drawRect(x, y, w, h, color); };
};
#endif //ESP32DISPLAYTEST_EXTENDEDSCREEN_H

46
lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

162
lib/Util/HSL.h Normal file
View File

@ -0,0 +1,162 @@
#ifndef ESP32DISPLAYTEST_HSL_H
#define ESP32DISPLAYTEST_HSL_H
#include <algorithm>
#include <cstdint>
#include "RGB.h"
class HSL {
static constexpr HSL from_rgb(const RGB &rgb) {
constexpr uint32_t unit = 255 << 8;
const uint32_t r = rgb.red << 8;
const uint32_t g = rgb.green << 8;
const uint32_t b = rgb.blue << 8;
const uint32_t max = std::max(r, std::max(g, b));
const uint32_t min = std::min(r, std::max(g, b));
uint32_t h = 0;
uint32_t s = 0;
uint32_t l = (max + min) / 2;
if (max == min) {
h = 0;
s = 0;
} else {
uint32_t d = max - min;
if (l > unit / 2) {
s = d / (unit * 2 - max - min);
} else {
s = d / (max + min);
}
if (max == r) {
h = (g - b) / 2;
if (g < b) {
h += unit * 6;
}
} else if (max == g) {
h = (b - r) / d + unit * 2;
} else {
h = (r - g) / d + unit * 4;
}
h = h / 6;
}
return HSL(h, s, l);
}
public:
const uint16_t hue = 0;
const uint16_t saturation = 0;
const uint16_t lightness = 0;
constexpr HSL() : hue(0), saturation(0), lightness(0) {}
explicit constexpr HSL(const RGB &rgb) : HSL(from_rgb(rgb)) {}
constexpr HSL(uint16_t h, uint16_t s, uint16_t l) : hue(h), saturation(s), lightness(l) {}
constexpr RGB toRGB() const {
const int32_t scale = 1 << 15;
int32_t c_ = 2 * scale * lightness / 100 - scale;
int32_t c = (scale - (c_ >= 0 ? c_ : -c_)) * saturation / 100;
int32_t x_ = ((hue * scale / 60 % (2 * scale)) - scale);
int32_t x = c * (scale - (x_ >= 0 ? x_ : -x_)) / scale;
int32_t m = lightness * scale / 100 - c / 2;
uint32_t r = 0;
uint32_t g = 0;
uint32_t b = 0;
if (0 <= hue && hue < 60) {
r = c;
g = x;
b = 0;
} else if (60 <= hue && hue < 120) {
r = x;
g = c;
b = 0;
} else if (120 <= hue && hue < 180) {
r = 0;
g = c;
b = x;
} else if (180 <= hue && hue < 240) {
r = 0;
g = x;
b = c;
} else if (240 <= hue && hue < 300) {
r = x;
g = 0;
b = c;
} else if (300 <= hue && hue < 360) {
r = c;
g = 0;
b = x;
}
r = (r + m) * 255 / scale;
g = (g + m) * 255 / scale;
b = (b + m) * 255 / scale;
return {static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b)};
}
constexpr uint16_t to_565() const {
const int32_t scale = 1 << 15;
int32_t c_ = 2 * scale * lightness / 100 - scale;
int32_t c = (scale - (c_ >= 0 ? c_ : -c_)) * saturation / 100;
int32_t x_ = ((hue * scale / 60 % (2 * scale)) - scale);
int32_t x = c * (scale - (x_ >= 0 ? x_ : -x_)) / scale;
int32_t m = lightness * scale / 100 - c / 2;
uint32_t r = 0;
uint32_t g = 0;
uint32_t b = 0;
if (0 <= hue && hue < 60) {
r = c;
g = x;
b = 0;
} else if (60 <= hue && hue < 120) {
r = x;
g = c;
b = 0;
} else if (120 <= hue && hue < 180) {
r = 0;
g = c;
b = x;
} else if (180 <= hue && hue < 240) {
r = 0;
g = x;
b = c;
} else if (240 <= hue && hue < 300) {
r = x;
g = 0;
b = c;
} else if (300 <= hue && hue < 360) {
r = c;
g = 0;
b = x;
}
r = (r + m) * 31 / scale;
g = (g + m) * 63 / scale;
b = (b + m) * 31 / scale;
assert(r <= 31);
assert(g <= 63);
assert(b <= 31);
return (r << 11)
| (g << 5)
| b;
}
};
#endif //ESP32DISPLAYTEST_HSL_H

153
lib/Util/RGB.h Normal file
View File

@ -0,0 +1,153 @@
#ifndef ESP32DISPLAYTEST_RGB_H
#define ESP32DISPLAYTEST_RGB_H
//#include "HSL.h"
#include <cstdint>
#include <cstddef>
#include <cassert>
#include <vector>
#include <Print.h>
class RGB {
static constexpr uint8_t hex_char_to_num(char ch) {
assert(
(ch >= '0' && ch <= '9') ||
(ch >= 'a' && ch <= 'f') ||
(ch >= 'A' && ch <= 'F')
);
if (ch >= '0' && ch <= '9') return ch - '0';
if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
return -1;
}
static constexpr size_t const_strlen(const char *str) {
size_t len = 0;
while (*(str + len) != '\0') len++;
return len;
}
static constexpr uint32_t alpha_blend(uint8_t top, uint8_t bottom, uint8_t alpha) {
return ((uint32_t)top) * ((uint32_t)alpha) + ((uint32_t)bottom) * (255 - (uint32_t)alpha);
}
static constexpr uint32_t hex_str_to_num(const char *hex) {
size_t len = const_strlen(hex);
assert(len >= 6);
if (hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X')) {
assert(len == 8);
hex += 2;
} else {
assert(len == 6);
}
uint32_t res = 0;
for (size_t i = 0; i < 6; ++i) {
res = (res << 4) | hex_char_to_num(hex[i]);
}
return res;
}
static constexpr uint32_t hex_str_to_num(const char *hex, size_t len) {
assert(len >= 6);
if (hex[0] == '0' && (hex[1] == 'x' || hex[1] == 'X')) {
assert(len == 8);
hex += 2;
} else {
assert(len == 6);
}
uint32_t res = 0;
for (size_t i = 0; i < 6; ++i) {
res = (res << 4) | hex_char_to_num(hex[i]);
}
return res;
}
explicit constexpr RGB(uint32_t num) : red(static_cast<uint8_t>((num >> 16) & 0xFF)),
green(static_cast<uint8_t>((num >> 8) & 0xFF)),
blue(static_cast<uint8_t>((num) & 0xFF)) {
}
public:
/*const*/ uint8_t red = 0;
/*const*/ uint8_t green = 0;
/*const*/ uint8_t blue = 0;
constexpr RGB() : red(0), green(0), blue(0) {}
constexpr RGB(const RGB &other) = default;
constexpr RGB(uint8_t r, uint8_t g, uint8_t b) : red(r), green(g), blue(b) {
}
explicit constexpr RGB(const char *hex) : RGB(hex_str_to_num(hex)) {
}
explicit constexpr RGB(const char *hex, size_t len) : RGB(hex_str_to_num(hex, len)) {
}
constexpr uint16_t
to_565() const {
uint8_t r = this->red >> 3;
uint8_t g = this->green >> 2;
uint8_t b = this->blue >> 3;
return (r << 11)
| (g << 5)
| b;
}
constexpr RGB alpha_blend_over(const RGB &other, uint8_t alpha) const {
uint32_t r = alpha_blend(this->red, other.red, alpha);
uint32_t g = alpha_blend(this->green, other.green, alpha);
uint32_t b = alpha_blend(this->blue, other.blue, alpha);
return {static_cast<uint8_t>(r / 255), static_cast<uint8_t>(g / 255), static_cast<uint8_t>(b / 255)};
}
constexpr uint16_t alpha_blend_over_to565(const RGB &other, uint8_t alpha) const {
uint8_t r = alpha_blend(this->red, other.red, alpha) * 31 / 255 / 255;
uint8_t g = alpha_blend(this->green, other.green, alpha) * 31 / 255 / 255;
uint8_t b = alpha_blend(this->blue, other.blue, alpha) * 31 / 255 / 255;
assert(r <= 31);
assert(g <= 63);
assert(b <= 31);
return (r << 11)
| (g << 5)
| b;
}
static RGB blend(const std::vector<RGB> &colors) {
if (colors.empty()) {
return {0, 0, 0};
}
uint64_t r = 0;
uint64_t g = 0;
uint64_t b = 0;
for (const auto &c : colors) {
r += c.red;
g += c.green;
b += c.blue;
}
r /= colors.size();
g /= colors.size();
b /= colors.size();
return {static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b)};
}
size_t printTo(Print &p) const {
return p.printf("RGB(%d,%d,%d)", red, green, blue);
}
};
constexpr RGB operator "" _rgb(const char *lit) {
return RGB(lit);
}
constexpr RGB operator "" _rgb(const char *lit, size_t len) {
return RGB(lit, len);
}
#endif //ESP32DISPLAYTEST_RGB_H

24
lib/Util/bitmap.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef ESP32DISPLAYTEST_BITMAP_H
#define ESP32DISPLAYTEST_BITMAP_H
#include <cstdint>
typedef struct {
uint16_t width;
uint16_t height;
uint8_t depth;
uint8_t pixel_align;
uint8_t row_align;
const uint8_t *data;
} bitmap_t;
typedef struct {
int16_t x;
int16_t y;
uint16_t width;
uint16_t height;
} rect_t;
#endif //ESP32DISPLAYTEST_BITMAP_H

18
lib/Util/colors.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef ESP32DISPLAYTEST_COLORS_H
#define ESP32DISPLAYTEST_COLORS_H
#include "HSL.h"
#include "RGB.h"
#include <cstdint>
constexpr uint16_t operator "" _rgb565(const char *lit) {
return RGB(lit).to_565();
}
constexpr uint16_t operator "" _rgb565(const char *lit, size_t len) {
return RGB(lit, len).to_565();
}
#endif //ESP32DISPLAYTEST_COLORS_H

View File

@ -0,0 +1,149 @@
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import os
import re
import subprocess
import sys
from platformio.compat import path_to_unicode, WINDOWS, PY2
from platformio.project.exception import PlatformioException
from platformio.project.helpers import load_project_ide_data
from platformio.commands.device import DeviceMonitorFilter
# By design, __init__ is called inside miniterm and we can't pass context to it.
# pylint: disable=attribute-defined-outside-init
class Esp32BetterExceptionDecoder(DeviceMonitorFilter):
NAME = "esp32_better_exception_decoder"
def __call__(self):
self.buffer = ""
self.backtrace_re = re.compile(
r"^Backtrace: ?((0x[0-9a-fA-F]+:0x[0-9a-fA-F]+ ?)+)\s*$"
)
self.firmware_path = None
self.addr2line_path = None
self.enabled = self.setup_paths()
if self.config.get("env:" + self.environment, "build_type") != "debug":
print(
"""
Please build project in debug configuration to get more details about an exception.
See https://docs.platformio.org/page/projectconf/build_configurations.html
"""
)
return self
def setup_paths(self):
self.project_dir = path_to_unicode(os.path.abspath(self.project_dir))
try:
data = load_project_ide_data(self.project_dir, self.environment)
self.firmware_path = data["prog_path"]
if not os.path.isfile(self.firmware_path):
sys.stderr.write(
"%s: firmware at %s does not exist, rebuild the project?\n"
% (self.__class__.__name__, self.firmware_path)
)
return False
cc_path = data.get("cc_path", "")
if "-gcc" in cc_path:
path = cc_path.replace("-gcc", "-addr2line")
if os.path.isfile(path):
self.addr2line_path = path
return True
except PlatformioException as e:
sys.stderr.write(
"%s: disabling, exception while looking for addr2line: %s\n"
% (self.__class__.__name__, e)
)
return False
sys.stderr.write(
"%s: disabling, failed to find addr2line.\n" % self.__class__.__name__
)
return False
def rx(self, text):
if not self.enabled:
return text
last = 0
while True:
idx = text.find("\n", last)
if idx == -1:
if len(self.buffer) < 4096:
self.buffer += text[last:]
break
line = text[last:idx]
if self.buffer:
line = self.buffer + line
self.buffer = ""
last = idx + 1
m = self.backtrace_re.match(line)
if m is None:
continue
trace = self.get_backtrace(m)
if len(trace) != "":
text = text[: idx + 1] + trace + text[idx + 1 :]
last += len(trace)
return text
def get_backtrace(self, match):
addrs_parts = [part.strip() for part in match.group(1).split("0x") if part.strip() != ""]
addrs_parts = ["0x" + part for part in addrs_parts]
addrs_parts = [part + " " if not part.endswith(":") else part for part in addrs_parts]
addrs = [addr.strip() for addr in "".join(addrs_parts).split(" ") if addr.strip() != ""]
trace = ""
enc = "mbcs" if WINDOWS else "utf-8"
args = [self.addr2line_path, u"-fipC", u"-e", self.firmware_path]
if PY2:
args = [a.encode(enc) for a in args]
try:
for i, addr in enumerate(addrs):
if PY2:
addr = addr.encode(enc)
output = (
subprocess.check_output(args + [addr])
.decode(enc)
.strip()
)
output = output.replace(
"\n", "\n "
) # newlines happen with inlined methods
output = self.strip_project_dir(output)
trace += " #%-2d in %s\n" % (i, output)
except subprocess.CalledProcessError as e:
sys.stderr.write(
"%s: failed to call %s: %s\n"
% (self.__class__.__name__, self.addr2line_path, e)
)
return trace
def strip_project_dir(self, trace):
while True:
idx = trace.find(self.project_dir)
if idx == -1:
break
trace = trace[:idx] + trace[idx + len(self.project_dir) + 1 :]
return trace

54
platformio.ini Normal file
View File

@ -0,0 +1,54 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[platformio]
default_envs = graphs
[env]
build_unflags = -std=gnu++11
build_flags =
-Wfatal-errors
-std=gnu++14
;platform = espressif32
platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream
framework = arduino
board = esp32dev
monitor_speed = 9600
monitor_filters = esp32_better_exception_decoder
build_type = debug
lib_deps =
adafruit/Adafruit GFX Library@^1.10.13
adafruit/Adafruit SSD1351 library@^1.2.7
adafruit/Adafruit BusIO@^1.11.1
Wire
SPI
WiFi
AsyncTCP
[env:native]
platform = native
lib_deps =
adafruit/Adafruit GFX Library@^1.10.13
src_filter = +<native.cpp>
[env:test]
src_filter = +<test.cpp>
[env:pride_flags]
src_filter = +<pride_flags.cpp>
[env:wifi]
src_filter =
+<wifi.cpp>
+<wifi/*.cpp>
[env:graphs]
src_filter = +<graphs.cpp>

781
src/graphs.cpp Normal file
View File

@ -0,0 +1,781 @@
//#include "Commander.h"
#include "fptrig.h"
#include <Arduino.h>
#include <WiFi.h>
#include <Adafruit_SSD1351.h>
#include <SPI.h>
#include <vector>
#include "screen-settings.h"
#include "colors.h"
#include "FixedPoint.h"
typedef FixedPoint<1000000> Decimal;
constexpr Decimal operator "" _d(long double v) {
return v;
}
constexpr Decimal operator "" _d(unsigned long long int v) {
return v;
}
static_assert(1_d == 1.0_d, "1 == 1.0");
static_assert(1_d == 1_d, "1 == 1");
static_assert(1_d + 1_d == 2_d, "1 + 1 == 2");
static_assert(2_d * 3_d == 6_d, "2 * 3 == 6");
static_assert(1.25_d * 2.3_d == 2.875_d, "1.25 * 2.3 == 2.875");
static_assert(.9_d / 3_d == .3_d, ".9 / 3 == .3");
SPIClass hspi(HSPI);
Adafruit_SSD1351 screen(SCREEN_WIDTH, SCREEN_HEIGHT, &hspi, CS_PIN, DC_PIN, RST_PIN);
//Commander commander;
struct Point {
Decimal x;
Decimal y;
};
enum InterpolationMethod {
NEAREST_NEIGHBOR,
LINEAR,
COSINE,
CUBIC_HERMITE,
MONOTONIC_CUBIC_HERMITE,
};
enum Visualisation {
LINE_DRAW, // todo: this is just for debugging
BAR,
LINE,
AREA,
AREA_WITH_LINE,
};
struct CubicCurve {
Decimal a;
Decimal b;
Decimal c;
Decimal d;
Decimal GetValueAt(Decimal x) const {
return a * x * x * x
+ b * x * x
+ c * x
+ d;
}
Decimal GetSlopeAt(Decimal x) const {
return 3_d * a * x * x
+ 2_d * b * x
+ c;
}
bool const IsEmpty() const {
return a == 0 && b == 0 && c == 0 && d == 0;
}
static constexpr CubicCurve make(Decimal x1, Decimal y1, Decimal m1, Decimal x2, Decimal y2, Decimal m2) {
Decimal a = (m1 + m2 - 2 * (y2 - y1) / (x2 - x1)) / ((x1 - x2) * (x1 - x2));
Decimal b = (m2 - m1) / (2 * (x2 - x1)) - 3 / 2 * (x1 + x2) * a;
Decimal c = m1 - 3 * x1 * x1 * a - 2 * x1 * b;
Decimal d = y1 - x1 * x1 * x1 * a - x1 * x1 * b - x1 * c;
return CubicCurve{a, b, c, d};
}
static constexpr CubicCurve makeLeftEnd(Decimal x1, Decimal y1, Decimal x2, Decimal y2, Decimal m2) {
// set second derivative to 0 at first point
// todo: actually implement this
/*
* f(x) = ax^3 + bx^2 + cx + d
* f'(x) = 3ax^2 + 2bx + c
* f''(x) = 6ax + 2b
*
y1 = a*x1^3 + b*x1^2 + c*x1 + d
y2 = a*x2^3 + b*x2^2 + c*x2 + d
m2 = 3*a*x2^2 + 2*b*x2 + c
0 = 6*a*x1 + 2*b
*
*
*
*
*
*/
return make(x1, y1, 0, x2, y2, m2);
}
static constexpr CubicCurve makeRightEnd(Decimal x1, Decimal y1, Decimal m1, Decimal x2, Decimal y2) {
// set second derivative to 0 at second point
// todo: actually implement this
return make(x1, y1, m1, x2, y2, 0);
}
};
struct Series {
RGB color;
String label;
Visualisation visualisation;
InterpolationMethod interpolation; // does not apply to "bar" visualisation
// point x and y values are scaled with 1000
std::vector<Point> points;
mutable CubicCurve cached_cubic_curve;
mutable Decimal cached_cubic_curve_from;
mutable Decimal cached_cubic_curve_to;
std::tuple<Decimal, bool> Interpolate(Decimal at) const {
if (points.size() < 2) {
return std::make_tuple(0, false);
}
if (at < points.front().x || at > points.back().x) {
return std::make_tuple(0, false);
}
switch (interpolation) {
case NEAREST_NEIGHBOR: return InterpolateNearestNeighbour(at);
case LINEAR: return InterpolateLinear(at);
case COSINE: return InterpolateCosine(at);
case CUBIC_HERMITE: return InterpolateCubicHermite(at);
case MONOTONIC_CUBIC_HERMITE: return InterpolateMonotonicCubicHermite(at);
}
return std::make_tuple(0, false);
}
std::tuple<Decimal, bool> InterpolateNearestNeighbour(Decimal at) const {
assert(points.size() >= 2);
if (at == points.front().x) {
return std::make_tuple(points.front().y, true);
}
for (auto current = points.begin() + 1; current < points.end(); current++) {
if (at == current->x) {
return std::make_tuple(current->y, true);
}
auto prev = current - 1;
if (at > prev->x && at < current->x) {
if (at - prev->x < current->x - at) {
return std::make_tuple(prev->y, true);
} else {
return std::make_tuple(current->y, true);
}
}
}
assert(false);
}
std::tuple<Decimal, bool> InterpolateLinear(Decimal at) const {
assert(points.size() >= 2);
if (at == points.front().x) {
return std::make_tuple(points.front().y, true);
}
for (auto current = points.begin() + 1; current < points.end(); current++) {
if (at == current->x) {
return std::make_tuple(current->y, true);
}
auto prev = current - 1;
if (at > prev->x && at < current->x) {
Decimal dist = current->x - prev->x;
Decimal dist_val = at - prev->x;
Decimal val = (current->y * dist_val + prev->y * (dist - dist_val)) / dist;
return std::make_tuple(val, true);
}
}
assert(false);
}
std::tuple<Decimal, bool> InterpolateCosine(Decimal at) const {
assert(points.size() >= 2);
if (at == points.front().x) {
return std::make_tuple(points.front().y, true);
}
for (auto current = points.begin() + 1; current < points.end(); current++) {
if (at == current->x) {
return std::make_tuple(current->y, true);
}
auto prev = current - 1;
if (at > prev->x && at < current->x) {
Decimal dist = current->x - prev->x;
Decimal dist_val = at - prev->x;
Decimal mu =
0; // todo: rework this to work with the FixedPoint class (dist - fpcos((1 << 14) * dist_val / dist) * dist / 8192 / 2);
if (mu > dist) {
Serial.print("mu = ");
Serial.println(mu.toString().c_str());
Serial.print("dist = ");
Serial.println(dist.toString().c_str());
for (;;);
}
assert(mu <= dist);
Decimal val = (prev->y * (dist - mu) + current->y * mu) / dist;
return std::make_tuple(val, true);
}
}
assert(false);
}
CubicCurve &GetCubicCurve(Decimal at, bool monotonic) const {
if (!cached_cubic_curve.IsEmpty()) {
if (at >= cached_cubic_curve_from && at <= cached_cubic_curve_to) {
return cached_cubic_curve;
}
}
for (auto p2 = points.begin() + 1; p2 < points.end(); p2++) {
auto p1 = p2 - 1;
if (at >= p1->x && at <= p2->x) {
cached_cubic_curve_from = p1->x;
cached_cubic_curve_to = p2->x;
Decimal m_after_p1 = (p2->y - p1->y) / (p2->x - p1->x);
Decimal m_before_p2 = (p2->y - p1->y) / (p2->x - p1->x);
Decimal m_before_p1, m_after_p2, m1, m2;
auto p0 = p1 - 1;
auto p3 = p2 + 1;
bool is_beginning = p0 < points.begin();
bool is_end = p3 >= points.end();
if (!is_beginning) {
m_before_p1 = (p1->y - p0->y) / (p1->x - p0->x);
if (monotonic &&
(p0->y == p1->y
|| p1->y == p2->y
|| (p0->y < p1->y && p2->y < p1->y)
|| (p0->y > p1->y && p2->y > p1->y)
)) {
m1 = 0;
} else {
m1 = (m_before_p1 + m_after_p1) / 2; // todo: use a mean that is weighted towards 0?
}
}
if (!is_end) {
m_after_p2 = (p3->y - p2->y) / (p3->x - p2->x);
if (monotonic &&
(p1->y == p2->y
|| p2->y == p3->y
|| (p1->y < p2->y && p3->y < p2->y)
|| (p1->y > p2->y && p3->y > p2->y)
)) {
m1 = 0;
} else {
m2 = (m_before_p2 + m_after_p2) / 2; // todo: use a mean that is weighted towards 0?
}
}
if (is_beginning) {
cached_cubic_curve = CubicCurve::makeLeftEnd(p1->x, p1->y, p2->x, p2->y, m2);
} else if (is_end) {
cached_cubic_curve = CubicCurve::makeRightEnd(p1->x, p1->y, m1, p2->x, p2->y);
} else {
cached_cubic_curve = CubicCurve::make(p1->x, p1->y, m1, p2->x, p2->y, m2);
}
return cached_cubic_curve;
}
}
assert(false);
}
std::tuple<Decimal, bool> InterpolateCubicHermite(Decimal at) const {
assert(points.size() >= 2);
if (points.size() == 2) {
return InterpolateLinear(at);
}
if (at < points.front().x || at > points.back().x) {
return std::make_tuple(0, false);
}
CubicCurve curve = GetCubicCurve(at, false);
return std::make_tuple(curve.GetValueAt(at), true);
}
std::tuple<Decimal, bool> InterpolateMonotonicCubicHermite(Decimal at) const {
assert(points.size() >= 2);
if (points.size() == 2) {
return InterpolateLinear(at);
}
if (at < points.front().x || at > points.back().x) {
return std::make_tuple(0, false);
}
CubicCurve curve = GetCubicCurve(at, true);
return std::make_tuple(curve.GetValueAt(at), true);
}
};
#define PADDING_BETWEEN_NUMBERS_AND_GRAPH 5
#define PADDING_BETWEEN_NUMBERS 5
[[gnu::pure]] Decimal find_step(Decimal step) {
int exp = 0;
while (step < 1) {
step *= 10;
exp--;
}
while (step >= 10) {
step /= 10;
exp++;
}
if (step > 5) {
step = 10;
} else if (step > 2.5) {
step = 5;
} else if (step > 2) {
step = 2.5;
} else if (step > 1) {
step = 2;
} else {
step = 1;
}
return step.exp10(exp);
}
struct Chart {
RGB background_color;
RGB foreground_color;
bool show_axes;
bool show_labels;
bool show_numbers;
bool auto_range_ordinate;
bool auto_range_abscissa;
Decimal ordinate_from;
Decimal ordinate_to;
Decimal abscissa_from;
Decimal abscissa_to;
std::vector<Series> series;
std::tuple<Decimal, Decimal> GetAbscissaRange() const {
Decimal from = abscissa_from;
Decimal to = abscissa_to;
if (auto_range_abscissa) {
from = INT32_MAX;
to = INT32_MIN;
for (const auto &s : series) {
for (const auto &p : s.points) {
if (p.x < from) {
from = p.x;
}
if (p.x > to) {
to = p.x;
}
}
}
}
return std::make_tuple(from, to);
}
std::tuple<Decimal, Decimal> GetOrdinateRange() const {
Decimal from = ordinate_from;
Decimal to = ordinate_to;
if (auto_range_ordinate) {
from = INT32_MAX;
to = INT32_MIN;
for (const auto &s : series) {
for (const auto &p : s.points) {
if (p.y < from) {
from = p.y;
}
if (p.y > to) {
to = p.y;
}
}
}
}
return std::make_tuple(from, to);
}
void Draw(Adafruit_GFX &target,
int16_t rect_x, int16_t rect_y, uint16_t rect_w, uint16_t rect_h) {
target.fillRect(rect_x, rect_y, rect_w, rect_h, background_color.to_565());
target.setTextSize(1);
int16_t canvas_x = rect_x;
int16_t canvas_y = rect_y;
uint16_t canvas_width = rect_w;
uint16_t canvas_height = rect_h;
Decimal ord_min, ord_max, abs_min, abs_max;
std::tie(ord_min, ord_max) = GetOrdinateRange();
std::tie(abs_min, abs_max) = GetAbscissaRange();
if (show_labels) {
// todo: draw labels, change canvas size accordingly
}
if (show_numbers) {
DrawNumbers(target, canvas_x, canvas_y, canvas_width, canvas_height);
}
DrawGraph(target, canvas_x, canvas_y, canvas_width, canvas_height);
if (show_axes) {
int16_t ord_pos = val_to_screen(0, canvas_x, canvas_width, abs_min, abs_max, false);
target.drawLine(ord_pos, canvas_y, ord_pos, canvas_y + canvas_height, foreground_color.to_565());
int16_t abs_pos = val_to_screen(0, canvas_y, canvas_height, ord_min, ord_max, true);
target.drawLine(canvas_x, abs_pos, canvas_x + canvas_width, abs_pos, foreground_color.to_565());
}
}
void DrawGraph(Adafruit_GFX &target,
int16_t &canvas_x,
int16_t &canvas_y,
uint16_t &canvas_width,
uint16_t &canvas_height) {
Decimal ord_min, ord_max, abs_min, abs_max;
std::tie(ord_min, ord_max) = GetOrdinateRange();
std::tie(abs_min, abs_max) = GetAbscissaRange();
std::vector<Series *> normal_series;
for (auto &s : series) {
if (s.visualisation == Visualisation::LINE_DRAW || s.visualisation == Visualisation::BAR) {
continue;
}
normal_series.push_back(&s);
}
enum PixelType {
NONE,
LINE,
AREA,
};
std::vector<std::vector<std::tuple<PixelType, const Series *>>> pixels(canvas_height);
assert(pixels.size() == canvas_height);
std::vector<std::tuple<int16_t, const Series *>> values;
if (!normal_series.empty()) {
for (uint16_t x = 0; x < canvas_width; x++) {
Decimal abs = (abs_min * (canvas_width - 1 - x) + abs_max * x) / (canvas_width - 1);
values.clear();
for (auto &p : pixels) {
p.clear();
}
for (auto s : normal_series) {
Decimal val;
bool success;
std::tie(val, success) = s->Interpolate(abs);
if (!success) continue;
int16_t y = static_cast<int16_t>(((val - ord_min) * (canvas_height - 1) / (ord_max - ord_min)).toInt());
values.emplace_back(y, s);
}
for (const auto &item : values) {
// todo: combine this loop with the previous
int16_t y;
const Series *s;
std::tie(y, s) = item;
assert(y >= 0 && y < canvas_height);
if (s->visualisation == Visualisation::LINE || s->visualisation == Visualisation::AREA_WITH_LINE) {
pixels[y].emplace_back(PixelType::LINE, s);
// todo: this leaves gaps between line segments
}
if (s->visualisation == Visualisation::AREA || s->visualisation == Visualisation::AREA_WITH_LINE) {
for (size_t i = 0; i <= y; ++i) {
pixels[i].emplace_back(PixelType::AREA, s);
}
}
}
std::vector<RGB> line_pixels;
std::vector<RGB> area_pixels;
const Series *s;
PixelType t;
for (int16_t y = 0; y < canvas_height; ++y) {
line_pixels.clear();
area_pixels.clear();
auto pixel = pixels[y];
for (const auto &item : pixel) {
std::tie(t, s) = item;
if (t == PixelType::LINE) {
line_pixels.push_back(s->color);
} else if (t == PixelType::AREA) {
area_pixels.push_back(s->color);
}
}
int16_t screen_x = canvas_x + x;
int16_t screen_y = canvas_y + canvas_height - y;
if (!line_pixels.empty()) {
// draw line pixel
RGB color = RGB::blend(line_pixels);
target.drawPixel(screen_x, screen_y, color.to_565());
} else if (!area_pixels.empty()) {
// draw area pixel
RGB color = RGB::blend(area_pixels);
uint16_t c2 = color.alpha_blend_over_to565(background_color, 100); // TODO: make configurable
target.drawPixel(screen_x, screen_y, c2);
} else {
// draw background pixel
target.drawPixel(screen_x, screen_y, background_color.to_565());
}
}
}
}
for (const auto &s : series) {
if (s.visualisation == Visualisation::LINE_DRAW) {
const Point *last = nullptr;
for (const auto &p : s.points) {
if (last != nullptr) {
int16_t tox = val_to_screen(p.x, canvas_x, canvas_width, abs_min, abs_max, false);
int16_t toy = val_to_screen(p.y, canvas_y, canvas_height, ord_min, ord_max, true);
int16_t fromx = val_to_screen(last->x, canvas_x, canvas_width, abs_min, abs_max, false);
int16_t fromy = val_to_screen(last->y, canvas_y, canvas_height, ord_min, ord_max, true);
target.drawLine(fromx, fromy, tox, toy, s.color.to_565());
}
last = &p;
}
} else if (s.visualisation == Visualisation::BAR) {
// todo: draw bar series
}
}
/*for (const auto &s : series) {
if (s.visualisation != Visualisation::BAR) {
for (const auto &p : s.points) {
int16_t x = val_to_screen(p.x, canvas_x, canvas_width, abs_min, abs_max, false);
int16_t y = val_to_screen(p.y, canvas_y, canvas_height, ord_min, ord_max, true);
target.drawCircle(x, y, 1, s.color.to_565());
}
}
}*/
}
void DrawNumbers(Adafruit_GFX &target,
int16_t &canvas_x,
int16_t &canvas_y,
uint16_t &canvas_width,
uint16_t &canvas_height) const {
Decimal ord_min, ord_max, abs_min, abs_max;
std::tie(ord_min, ord_max) = GetOrdinateRange();
std::tie(abs_min, abs_max) = GetAbscissaRange();
int16_t dummy_x, dummy_y;
uint16_t text_width, text_height;
target.getTextBounds("0", 0, 0, &dummy_x, &dummy_y, &text_width, &text_height);
uint16_t text_y_offset = (text_height + 1) / 2; // rounded-up half height of text
canvas_height -= text_height + PADDING_BETWEEN_NUMBERS_AND_GRAPH;
canvas_height -= 2 * text_y_offset;
canvas_y += text_y_offset;
Decimal ord_max_num = ord_max;
Decimal ord_min_num = ord_min;
uint8_t max_num_ord_numbers = canvas_height / (text_height + PADDING_BETWEEN_NUMBERS);
Decimal ord_num_step = (ord_max_num - ord_min_num) / max_num_ord_numbers;
ord_num_step = find_step(ord_num_step);
if (ord_max_num > 0) {
ord_max_num = (ord_max_num / ord_num_step) * ord_num_step;
} else {
ord_max_num = ((ord_max_num - ord_num_step) / ord_num_step) * ord_num_step;
}
if (ord_min_num > 0) {
ord_min_num = ((ord_min_num + ord_num_step) / ord_num_step) * ord_num_step;
} else {
ord_min_num = (ord_min_num / ord_num_step) * ord_num_step;
}
String str;
// value text string width
std::vector<std::tuple<Decimal, String, uint16_t>> ord_steps;
uint16_t max_width = 0;
for (Decimal step = ord_min_num; step <= ord_max_num; step += ord_num_step) {
str =
step.toString(2, ord_min_num.abs() < 1000 && ord_max_num.abs() < 1000); // todo: use digits of ord_num_step
target.getTextBounds(str, 0, 0, &dummy_x, &dummy_y, &text_width, &text_height);
ord_steps.emplace_back(step, str, text_width);
max_width = std::max(max_width, text_width);
}
canvas_x += max_width + PADDING_BETWEEN_NUMBERS_AND_GRAPH;
canvas_width -= max_width + PADDING_BETWEEN_NUMBERS_AND_GRAPH;
target.setTextColor(foreground_color.to_565());
for (const auto &step : ord_steps) {
Decimal val;
String text;
uint16_t width;
std::tie(val, text, width) = step;
int16_t posx = canvas_x - width - PADDING_BETWEEN_NUMBERS_AND_GRAPH;
int16_t posy = val_to_screen(val, canvas_y, canvas_height, ord_min, ord_max, true);
target.setCursor(posx, posy - text_y_offset);
target.print(text);
target.drawLine(canvas_x - 1, posy, canvas_x + 1, posy, foreground_color.to_565());
}
Decimal abs_max_num = abs_max;
Decimal abs_min_num = abs_min;
}
[[gnu::pure]] static int16_t val_to_screen(Decimal val,
int16_t canvas_start,
uint16_t canvas_size,
Decimal val_min,
Decimal val_max,
bool reverse) {
int16_t canvas_val = static_cast<int16_t>(((val - val_min) * canvas_size / (val_max - val_min)).toInt());
if (reverse) {
return canvas_start + canvas_size - canvas_val;
} else {
return canvas_start + canvas_val;
}
}
};
RGB color_white = "FFFFFF"_rgb;
RGB color_black = "000000"_rgb;
std::vector<Point> v1 = {{0_d, 10_d}, {5_d, 20_d}, {10_d, 16_d}, {15_d, 50_d},
{20_d, 0_d}, {25_d, 40_d}};
std::vector<Point>
v2 = {{0_d, 5_d}, {3_d, 10_d}, {7_d, 20_d}, {11_d, 6_d}, {15_d, 22_d}, {22_d, 36_d}, {25_d, 50_d}};
std::vector<Point>
v3 = {{-10_d, 100000_d}, {-5_d, 2000_d}, {0_d, 100000_d}, {5_d, 2000_d}, {10_d, -16000_d}, {15_d, 50000_d},
{20_d, 1000_d}, {25_d, 40000_d}};
std::vector<Point> v4 =
{{0, 12374_d}, {3_d, 43563_d}, {7_d, 6512_d}, {11_d, 14434_d}, {15_d, 43143_d}, {22_d, 298_d}, {25_d, 93834_d}};
Series s1{
"00FFFF"_rgb,
"CYAN",
Visualisation::AREA_WITH_LINE,
InterpolationMethod::MONOTONIC_CUBIC_HERMITE,
v1
};
Series s2{
"FF0000"_rgb,
"RED",
Visualisation::AREA_WITH_LINE,
InterpolationMethod::MONOTONIC_CUBIC_HERMITE,
v2
};
std::vector<Series> series{s1, s2};
Chart chart1 = {color_black, color_white,
true, true, true,
true, true, 0, 0, 0, 0,
series};
Series s3{
"00FFFF"_rgb,
"CYAN",
Visualisation::AREA_WITH_LINE,
InterpolationMethod::CUBIC_HERMITE,
v1
};
Series s4{
"FF0000"_rgb,
"RED",
Visualisation::AREA_WITH_LINE,
InterpolationMethod::CUBIC_HERMITE,
v2
};
std::vector<Series> series2{s3, s4};
Chart chart2 = {color_white, color_black,
true, true, true,
true, false, 0, 0, 0_d, 5_d,
series2};
void setup() {
Serial.begin(9600);
screen.begin(32000000);
// commander.EnableSerial();
// commander.SetPrompt("graph> ");
// commander.Begin();
screen.fillScreen("555555"_rgb565);
chart1.Draw(screen, 5, 5, screen.width() - 10, screen.height() / 2 - 10);
chart2.Draw(screen, 5, screen.height() / 2 + 5, screen.width() - 10, screen.height() / 2 - 10);
}
Decimal chart2_offset = 0_d;
Decimal chart2_step = 0.2;
bool chart2_direction = true;
void loop() {
if (chart2_direction) {
chart2_offset += chart2_step;
if (chart2_offset > 20) {
chart2_offset = 20;
chart2_direction = false;
}
} else {
chart2_offset -= chart2_step;
if (chart2_offset < 0) {
chart2_offset = 0;
chart2_direction = true;
}
}
chart2.abscissa_from = chart2_offset;
chart2.abscissa_to = chart2_offset + 5;
chart2.Draw(screen, 5, screen.height() / 2 + 5, screen.width() - 10, screen.height() / 2 - 10);
delay(100);
}

276
src/pride_flags.cpp Normal file
View File

@ -0,0 +1,276 @@
#include "ExtendedScreen.h"
#include "bitmap.h"
#include "colors.h"
#include <Arduino.h>
#include <Adafruit_SSD1351.h>
#include <SPI.h>
#include "screen-settings.h"
SPIClass hspi(HSPI);
Adafruit_SSD1351 hardware_screen(SCREEN_WIDTH, SCREEN_HEIGHT, &hspi, CS_PIN, DC_PIN, RST_PIN);
ExtendedScreen screen(&hardware_screen);
rect_t draw_striped_bitmap(bitmap_t &image,
uint16_t x, uint16_t y,
int num_stripes, const RGB stripes[],
RGB background,
rect_t old_rect);
rect_t
draw_bouncy_striped_bitmap(bitmap_t &image, uint16_t num_stripes, const RGB stripes[], RGB background);
RGB black = RGB{0, 0, 0};
void setup() {
Serial.begin(9600);
hardware_screen.begin(32000000);
screen.fillScreen(black.to_565());
}
uint16_t rainbow_saturation = 100;
uint16_t rainbow_lightness = 50;
const int16_t rainbow_flag[] = {0, 60, 120, 180, 240, 300};
const uint16_t rainbow_length = sizeof(rainbow_flag) / sizeof(rainbow_flag[0]);
#define hue_step 1 << 7
#define start_x 20
#define start_y 90
#define x_step 7 << 5
#define y_step 9 << 5
constexpr RGB dark(50, 50, 50);
//RGB blue(0, 200, 255);
constexpr RGB blue(0x00, 0xC8, 0xFF);
//RGB trans_pink(220, 0, 255);
constexpr RGB trans_pink(0xDC, 0x00, 0xFF);
//RGB white(255, 255, 255);
constexpr RGB white(0xFF, 0xFF, 0xFF);
const RGB trans_flag[] = {blue, trans_pink, white, trans_pink, blue};
const uint16_t trans_flag_length = sizeof(trans_flag) / sizeof(trans_flag[0]);
RGB gray(150, 150, 150);
RGB green(150, 255, 45);
const RGB agender_flag[] = {dark, gray, white, green, white, gray, dark};
const uint16_t agender_flag_length = sizeof(agender_flag) / sizeof(agender_flag[0]);
RGB red(214, 41, 0);
RGB orange(255, 155, 85);
RGB lesbian_pink(212, 97, 166);
RGB lesbian_purple(165, 0, 98);
const RGB lesbian_flag[] = {red, orange, white, lesbian_pink, lesbian_purple};
const uint16_t lesbian_flag_length = sizeof(lesbian_flag) / sizeof(lesbian_flag[0]);
const RGB enby_yellow(255, 255, 0);
const RGB enby_purple(150, 20, 255);
const RGB enby_flag[] = {enby_yellow, white, enby_purple, dark};
const uint16_t enby_flag_length = sizeof(enby_flag) / sizeof(enby_flag[0]);
#include "bitmaps/cyber96.h"
int32_t hue_time = 0;
int32_t toggle_count = 0;
void loop() {
int selector = (toggle_count / 100) % 5;
RGB rainbow_stripes[rainbow_length];
switch (selector) {
case 0:
for (size_t i = 0; i < rainbow_length; ++i) {
uint16_t hue = (int16_t) ((rainbow_flag[i] + (hue_time >> 8)) % 360);
rainbow_stripes[i] = HSL(hue, rainbow_saturation, rainbow_lightness).toRGB();
}
draw_bouncy_striped_bitmap(cyber96, rainbow_length, rainbow_stripes, black);
hue_time = (int32_t) (((360 << 8) + hue_time + hue_step) % (360 << 8));
break;
case 1:
draw_bouncy_striped_bitmap(cyber96, trans_flag_length, trans_flag, black);
break;
case 2:
draw_bouncy_striped_bitmap(cyber96, agender_flag_length, agender_flag, black);
break;
case 3:
draw_bouncy_striped_bitmap(cyber96, lesbian_flag_length, lesbian_flag, black);
break;
case 4:
draw_bouncy_striped_bitmap(cyber96, enby_flag_length, enby_flag, black);
break;
}
toggle_count += 1;
delay(20);
}
/**
* draws a bitmap in colored horizontal stripes
* colors are in RGB65
* note: the width of the image MUST be a multiple of 8
* returns the rect in which the image was drawn
*/
rect_t draw_striped_bitmap(bitmap_t &image,
uint16_t x, uint16_t y,
int num_stripes, const RGB stripes[],
RGB background,
rect_t old_rect) {
int row_height = image.height / num_stripes;
int leftover = image.height % num_stripes;
bool even = num_stripes % 2 == 0;
int middle_row_height_1;
int middle_row_height_2;
if (even) {
middle_row_height_1 = row_height + leftover / 2;
middle_row_height_2 = middle_row_height_1 + (leftover % 2);
} else {
middle_row_height_1 = row_height + leftover;
middle_row_height_2 = 0;
}
uint16_t offset_y = 0;
int16_t rect_x_offset = 0;
uint16_t rect_width_offset = 0;
if (old_rect.width > 0) {
if (old_rect.x < x) {
rect_width_offset = x - old_rect.x;
rect_x_offset = -rect_width_offset;
} else {
rect_x_offset = 0;
rect_width_offset = old_rect.x - x;
}
}
for (int i = 0; i < num_stripes; i++) {
RGB current_color = stripes[i];
uint16_t offset_data = offset_y * image.width / 8;
uint16_t current_row_height = row_height;
if (even) {
if (i == num_stripes / 2) {
current_row_height = middle_row_height_1;
} else if (i == num_stripes / 2 - 1) {
current_row_height = middle_row_height_2;
}
} else {
if (i == num_stripes / 2) {
current_row_height = middle_row_height_1;
}
}
rect_t stripe_rect = {
.x = rect_x_offset, .y = static_cast<int16_t>(offset_y),
.width = static_cast<uint16_t>(image.width + rect_width_offset), .height = current_row_height,
};
int16_t rect_y_offset = 0;
uint16_t rect_height_offset = 0;
if (old_rect.height > 0) {
if (i == 0 && old_rect.y < y) {
rect_height_offset = y - old_rect.y;
rect_y_offset = -rect_height_offset;
} else if (i == num_stripes - 1 && old_rect.y > y) {
rect_height_offset = (old_rect.y + old_rect.height) - (y + offset_y + current_row_height);
}
}
stripe_rect.height += rect_height_offset;
stripe_rect.y += rect_y_offset;
screen.drawTransparentBitmap(x + rect_x_offset, y + offset_y + rect_y_offset, image, current_color, background,
stripe_rect);
offset_y += current_row_height;
}
return rect_t{static_cast<int16_t>(x), static_cast<int16_t>(y), image.width, image.height};
}
/**
* draws a bouncy image with looping stripes, each time this function is called the image moves and the colors of the stripes are shifted
* colors are in HSL
* start positions are screen coordinates
* steps for position and hue are scaled with 255
* returns the rect in which the image was drawn
*/
rect_t draw_bouncy_striped_bitmap(bitmap_t &image,
uint16_t num_stripes, const RGB stripes[],
RGB background
) {
static int16_t x = start_x;
static int16_t y = start_y;
static int32_t scaled_x = start_x << 8;
static int32_t scaled_y = start_y << 8;
static int32_t move_x = x_step;
static int32_t move_y = y_step;
static rect_t rect = {};
const int32_t max_scaled_x = (SCREEN_WIDTH - image.width - 1) << 8;
const int32_t max_scaled_y = (SCREEN_HEIGHT - image.height - 1) << 8;
if (x_step >= max_scaled_x || x_step <= -max_scaled_x) {
screen.setCursor(0, 5);
screen.setTextColor(RGB(255, 0, 0).to_565());
screen.setTextSize(1);
screen.println("x_step is too large");
return rect_t{};
}
if (y_step >= max_scaled_y || y_step <= -max_scaled_y) {
screen.setCursor(0, 5);
screen.setTextColor(RGB(255, 0, 0).to_565());
screen.setTextSize(1);
screen.println("y_step is too large");
return rect_t{};
}
rect = draw_striped_bitmap(image, x, y, num_stripes, stripes, background, rect);
scaled_x = scaled_x + move_x;
scaled_y = scaled_y + move_y;
if (scaled_x < 0) {
scaled_x = -scaled_x;
move_x = -move_x;
} else if (scaled_x > max_scaled_x) {
scaled_x = 2 * max_scaled_x - scaled_x;
move_x = -move_x;
}
if (scaled_y < 0) {
scaled_y = -scaled_y;
move_y = -move_y;
} else if (scaled_y > max_scaled_y) {
scaled_y = 2 * max_scaled_y - scaled_y;
move_y = -move_y;
}
x = (int16_t) (scaled_x >> 8);
y = (int16_t) (scaled_y >> 8);
return rect;
}

27
src/test.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "colors.h"
#include <Arduino.h>
#include <Adafruit_SSD1351.h>
#include <SPI.h>
#include "screen-settings.h"
SPIClass hspi(HSPI);
Adafruit_SSD1351 screen(SCREEN_WIDTH, SCREEN_HEIGHT, &hspi, CS_PIN, DC_PIN, RST_PIN);
void setup() {
screen.begin(32000000);
screen.fillScreen(0xFF4444_rgb565);
}
int hue = 0;
void loop() {
uint16_t color = HSL(hue, 100, 50).toRGB().alpha_blend_over_to565(RGB(00,50,00), 50);
screen.fillScreen(color);
hue += 1;
if (hue >= 360) hue -= 360;
delay(50);
}

179
src/wifi.cpp Normal file
View File

@ -0,0 +1,179 @@
#include "Commander.h"
#include "wifi/wifi_command.h"
#include <Arduino.h>
#include <WiFi.h>
#include <Adafruit_SSD1351.h>
#include <SPI.h>
#include "screen-settings.h"
#include "colors.h"
SPIClass hspi(HSPI);
Adafruit_SSD1351 screen(SCREEN_WIDTH, SCREEN_HEIGHT, &hspi, CS_PIN, DC_PIN, RST_PIN);
Commander commander;
RGB global_color;
RGB parse_color(const std::vector<CmdString> &params) {
// todo: rgb and hsl colors
return RGB(params.front().c_str());
}
void draw_set_color(const std::vector<CmdString> &params, CommanderClient *client) {
if (params.empty()) {
client->println("no color provided");
return;
}
global_color = parse_color(params);
}
void draw_fill_screen(const std::vector<CmdString> &params, CommanderClient *client) {
RGB color;
if (params.empty()) {
color = global_color;
} else {
color = parse_color(params);
}
screen.fillScreen(color.to_565());
}
// <x> <y> <width> <height> [color]
void draw_fill_rect(const std::vector<CmdString> &params, CommanderClient *client) {
if (params.size() < 4) {
client->println("missing parameters");
return;
}
int16_t x, y, width, height;
if (!params[0].ReadInto(&x)) {
client->println("parameter x is not valid");
return;
}
if (!params[1].ReadInto(&y)) {
client->println("parameter y is not valid");
return;
}
if (!params[2].ReadInto(&width)) {
client->println("parameter width is not valid");
return;
}
if (!params[3].ReadInto(&height)) {
client->println("parameter height is not valid");
return;
}
RGB color;
if (params.size() < 5) {
color = global_color;
} else {
color = parse_color(std::vector<CmdString>(params.begin()+4, params.end()));
}
screen.fillRect(x, y, width, height, color.to_565());
}
// <x1> <y1> <x2> <y2> [color]
void draw_draw_line(const std::vector<CmdString> &params, CommanderClient *client) {
if (params.size() < 4) {
client->println("missing parameters");
return;
}
int16_t x1, y1, x2, y2;
if (!params[0].ReadInto(&x1)) {
client->println("parameter x1 is not valid");
return;
}
if (!params[1].ReadInto(&y1)) {
client->println("parameter y1 is not valid");
return;
}
if (!params[2].ReadInto(&x2)) {
client->println("parameter x2 is not valid");
return;
}
if (!params[3].ReadInto(&y2)) {
client->println("parameter y2 is not valid");
return;
}
RGB color;
if (params.size() < 5) {
color = global_color;
} else {
color = parse_color(std::vector<CmdString>(params.begin()+4, params.end()));
}
screen.drawLine(x1, y1, x2, y2, color.to_565());
}
void setup() {
Serial.begin(9600);
screen.begin(32000000);
//
// WiFi.mode(WIFI_AP);
// WiFi.softAP("esp32", "very gay");
// todo: automatically enable AP if configured
/*Serial.print("connecting to wifi...");
if (WiFi.begin() != WL_CONNECT_FAILED) {
while (WiFi.status() != WL_CONNECTED && WiFi.status() != WL_CONNECT_FAILED) {
vTaskDelay(pdMS_TO_TICKS(500));
Serial.print('.');
}
Serial.println();
if (WiFi.status() == WL_CONNECT_FAILED) {
Serial.println("connection failed");
WiFi.enableSTA(false);
} else {
Serial.println("connected");
}
}*/
auto hello_cmd = commander.RegisterCommand("hello", [](const std::vector<CmdString> &params, CommanderClient *client) {
client->println("world");
});
auto draw_cmd = commander.RegisterCommandWithShell("draw");
draw_cmd->SetPrompt("esp32/draw> ");
draw_cmd->RegisterCommand("set-color (<hex-color> | HSL <hue> <saturation> <lightness> | RGB <red> <green> <blue>)",
draw_set_color);
draw_cmd->RegisterCommand("fill-screen [color]", draw_fill_screen);
draw_cmd->RegisterCommand("fill-rect <x> <y> <width> <height> [color]", draw_fill_rect);
draw_cmd->RegisterCommand("draw-line <x1> <y1> <x2> <y2> [color]", draw_draw_line);
registerWifiCommands(commander);
commander.EnableSerial();
// commander.EnableWifi(5000);
commander.SetPrompt("esp32> ");
commander.Begin();
}
void loop() {
// WiFiClient client = server.available();
// if (client) {
// Serial.println("[client connected]");
// while (client.connected()) {
// if (client.available()) {
// char c = client.read();
// Serial.write(c);
// }
// }
// client.stop();
// Serial.println("[client disconnected]");
// }
}

200
src/wifi/wifi_command.cpp Normal file
View File

@ -0,0 +1,200 @@
#include <WiFi.h>
#include "Commander.h"
void wifi_autoconnect(const std::vector<CmdString> &params, CommanderClient *client) {
WiFi.enableSTA(true);
WiFi.begin();
WiFi.setAutoConnect(true);
WiFi.setAutoReconnect(true);
client->print("connecting to network...");
while (WiFi.status() != WL_CONNECTED && WiFi.status() != WL_CONNECT_FAILED) {
vTaskDelay(pdMS_TO_TICKS(500));
client->print('.');
}
client->println();
if (WiFi.status() == WL_CONNECTED) {
client->print("connected to ");
client->println(WiFi.SSID());
} else {
client->println("connection failed");
}
}
void wifi_connect(const std::vector<CmdString> &params, CommanderClient *client) {
WiFi.enableSTA(true);
client->println("scanning...");
int16_t count = WiFi.scanNetworks();
client->println("available networks:");
for (int16_t i = 0; i < count; ++i) {
client->print(i);
client->print(") ");
client->print(WiFi.SSID(i));
client->print("\t signal: ");
client->print(WiFi.RSSI(i));
client->print("\t encryption: ");
switch (WiFi.encryptionType(i)) {
case WIFI_AUTH_OPEN: client->print("none");
break;
case WIFI_AUTH_WEP: client->print("WEP");
break;
case WIFI_AUTH_WPA_PSK: client->print("WPA PSK");
break;
case WIFI_AUTH_WPA2_PSK: client->print("WPA2 PSK");
break;
case WIFI_AUTH_WPA2_ENTERPRISE: client->print("WPA2 ENTERPRISE");
break;
case WIFI_AUTH_WPA_WPA2_PSK: client->print("WPA/WPA2 PSK");
break;
default: client->print("unknown");
}
client->println();
}
int16_t selected_network;
do {
client->print("please select which network to connect to: ");
} while (!client->ReadLine(&selected_network) && selected_network < count);
CmdString wifi_name = WiFi.SSID(selected_network);
switch (WiFi.encryptionType(selected_network)) {
case WIFI_AUTH_OPEN: WiFi.begin(wifi_name.c_str());
break;
case WIFI_AUTH_WEP:
case WIFI_AUTH_WPA_PSK:
case WIFI_AUTH_WPA2_PSK:
case WIFI_AUTH_WPA_WPA2_PSK:
case WIFI_AUTH_WPA2_ENTERPRISE: {
client->print("wifi password: ");
CmdString key = client->ReadLine();
WiFi.begin(wifi_name.c_str(), key.c_str());
break;
}
default: client->println("error: unknown encryption type");
return;
}
WiFi.setAutoConnect(true);
WiFi.setAutoReconnect(true);
client->print("connecting to network...");
while (WiFi.status() != WL_CONNECTED && WiFi.status() != WL_CONNECT_FAILED) {
vTaskDelay(pdMS_TO_TICKS(500));
client->print('.');
}
client->println();
if (WiFi.status() == WL_CONNECTED) {
client->print("connected to ");
client->println(WiFi.SSID());
} else {
client->println("connection failed");
}
}
void wifi_disconnect(const std::vector<CmdString> &params, CommanderClient *client) {
WiFi.disconnect(true);
client->print("disconnecting...");
while (WiFi.status() != WL_DISCONNECTED) {
client->print('.');
vTaskDelay(pdMS_TO_TICKS(500));
}
client->println();
client->println("disconnected from wifi network");
}
void wifi_status(const std::vector<CmdString> &params, CommanderClient *client) {
if (!WiFi.isConnected()) {
client->println("wifi is not connected");
return;
}
client->println("wifi is connected");
client->print("SSID: ");
client->println(WiFi.SSID());
client->print("RSSI: ");
client->println(WiFi.RSSI());
client->print("IPv4: ");
client->println(WiFi.localIP());
client->print("IPv6: ");
client->println(WiFi.localIPv6());
client->print("MAC: ");
client->println(WiFi.macAddress());
}
void wifi_ap_enable(const std::vector<CmdString> &params, CommanderClient *client) {
WiFi.enableAP(true);
// todo: anything to do here?
}
void wifi_ap_disable(const std::vector<CmdString> &params, CommanderClient *client) {
WiFi.softAPdisconnect(true);
}
void wifi_ap_setup(const std::vector<CmdString> &params, CommanderClient *client) {
client->print("SSID for AP: ");
CmdString ssid = client->ReadLine();
client->print("password for AP: ");
CmdString passphrase = client->ReadLine();
WiFi.softAP(ssid.c_str(), passphrase.c_str());
// todo: WiFi.softAPConfig, WiFi.softAPsetHostname, WiFi.softAPmacAddress
}
void wifi_ap_status(const std::vector<CmdString> &params, CommanderClient *client) {
if ((WiFi.getMode() & WIFI_MODE_AP) == 0) { // AP is disabled
client->println("AP is disabled");
return;
}
client->print("SSID: ");
client->println(WiFi.softAPSSID());
client->print("MAC: ");
client->println(WiFi.softAPmacAddress());
client->print("IPv4: ");
client->println(WiFi.softAPIP());
client->print("IPv6: ");
client->println(WiFi.softAPIPv6());
client->print("network ID: ");
client->println(WiFi.softAPNetworkID());
client->print("broadcast IP: ");
client->println(WiFi.softAPBroadcastIP());
client->print("subnet: ");
client->println(WiFi.softAPSubnetCIDR());
client->print("hostname: ");
client->println(WiFi.softAPgetHostname());
client->print("connected clients: ");
client->println(WiFi.softAPgetStationNum());
}
void registerWifiCommands(Commander &commander) {
auto wifi_cmd = commander.RegisterCommand("wifi", nullptr);
wifi_cmd->RegisterCommand("autoconnect", wifi_connect);
wifi_cmd->RegisterCommand("connect", wifi_connect);
wifi_cmd->RegisterCommand("disconnect", wifi_disconnect);
wifi_cmd->RegisterCommand("status", wifi_status);
auto wifi_ap_cmd = wifi_cmd->RegisterCommand("ap", nullptr);
wifi_ap_cmd->RegisterCommand("enable", wifi_ap_enable);
wifi_ap_cmd->RegisterCommand("disable", wifi_ap_disable);
wifi_ap_cmd->RegisterCommand("setup", wifi_ap_setup);
wifi_ap_cmd->RegisterCommand("status", wifi_ap_status);
}

18
test/README Normal file
View File

@ -0,0 +1,18 @@
This directory is intended for PlatformIO Unit Testing and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html
lib_deps =
adafruit/Adafruit GFX Library@^1.10.13
adafruit/Adafruit SSD1351 library@^1.2.7
adafruit/Adafruit BusIO@^1.11.1