initial commit
This commit is contained in:
commit
ee867edb51
|
@ -0,0 +1,4 @@
|
||||||
|
.pio
|
||||||
|
.idea
|
||||||
|
CMakeLists*.txt
|
||||||
|
cmake-*
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
|
@ -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)
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
|
@ -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
|
|
@ -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,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -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]));
|
||||||
|
}
|
||||||
|
}
|
|
@ -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))
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include "Commander.h"
|
||||||
|
|
||||||
|
void registerWifiCommands(Commander &commander);
|
|
@ -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 ¤t = 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;
|
||||||
|
}
|
|
@ -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_
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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> ¶ms) {
|
||||||
|
// todo: rgb and hsl colors
|
||||||
|
return RGB(params.front().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_set_color(const std::vector<CmdString> ¶ms, CommanderClient *client) {
|
||||||
|
if (params.empty()) {
|
||||||
|
client->println("no color provided");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
global_color = parse_color(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw_fill_screen(const std::vector<CmdString> ¶ms, 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> ¶ms, 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> ¶ms, 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> ¶ms, 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]");
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
#include <WiFi.h>
|
||||||
|
#include "Commander.h"
|
||||||
|
|
||||||
|
|
||||||
|
void wifi_autoconnect(const std::vector<CmdString> ¶ms, 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> ¶ms, 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> ¶ms, 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> ¶ms, 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> ¶ms, CommanderClient *client) {
|
||||||
|
WiFi.enableAP(true);
|
||||||
|
// todo: anything to do here?
|
||||||
|
}
|
||||||
|
|
||||||
|
void wifi_ap_disable(const std::vector<CmdString> ¶ms, CommanderClient *client) {
|
||||||
|
WiFi.softAPdisconnect(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wifi_ap_setup(const std::vector<CmdString> ¶ms, 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> ¶ms, 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);
|
||||||
|
}
|
|
@ -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
|
Loading…
Reference in New Issue