add bitmap conversion from aseprite ico, add boot animation

This commit is contained in:
2020-01-05 16:46:53 +01:00
parent 6db58e14a7
commit d7cd0e9cfa
37 changed files with 593 additions and 81 deletions
+3
View File
@@ -6,10 +6,13 @@ set(COMPONENT_SRCS
"liquid/liquid.c"
"scenes/scene_root.c"
"scenes/scene_car.c"
"scenes/scene_demo.c"
"scenes/scene_bootanim.c"
"graphics/nokia.c"
"graphics/utf8.c"
"graphics/font.c"
"graphics/drawing.c"
"graphics/bitmaps.c"
)
set(COMPONENT_ADD_INCLUDEDIRS "." "liquid" "graphics")
+71
View File
@@ -0,0 +1,71 @@
#include "bitmaps.h"
#include <stddef.h>
#include <string.h>
/* 84x48: boot_logo */
// ····················································································
// ·····································································████████·······
// ························································█████████████····█···██·····
// ···················█·······························█████····█·███·····███······█····
// ······█·█····································██████···██·█████··███████·········█···
// ·······█······························███████····██·█████·█·······███··········█····
// ······█·█······················███████·····███·███████··██···················██·····
// ···························██████·██·███·███·███████████·████·············███·······
// ······················████████████·██··███··█████████·█████·············██·██·······
// ··················█████·██·████···█·████···███████·███·············█████···██·······
// ·············███████████·█████████████···██·······█············█████··█·█·█·██······
// ············██████████·██████████··███████·················█████··█·██·█···███······
// ·········██████··█··████·██·███···████·█···············████···█··███··█······██·····
// ······███·████████████████████··██················█████··█···█·█·█···········█·█····
// ···███████····················██··············████·█····█·····█················█····
// ····██··························█·············█···█····██······················█····
// ··██·····························█·······█·█·█···█····██·······················█····
// ·█······························█·······█·█·█···█·█····························█····
// ·█····························██·······█·█·█·····█····················█········█····
// ··██······················████·█·█·█··█·······························█······█··█···
// ····████···················█··█·█·█··█································█······█··█···
// ·······█···················██·█·██··························█·········█······█··█···
// ·······█····················█·██······█·····················█··········█·····█··█···
// ·······█····················█·█········█··········█··········█·········█·····███····
// ········█···················█··········█···········█·········█·····██████████·······
// ········█····················█·········█············█·········█·███·················
// ········█····················█··········█···········█·······████····················
// ········█·····················█·········█············█·█████························
// ········█······················█·········█·········████·····························
// ········█·······················█········█····█████···██················█·█·········
// ·········█······················█·········█·██···························█··········
// ·········█······················█·······████····························█·█·········
// ·········█······················█····███·····················█······················
// ·········█·······················████·······················███·····················
// ·········█·····················███···························█······················
// ·········█········██████████████····················································
// ··········█·██████·····························································█····
// ···········█····························································█······█····
// ··························································█············█·······█····
// ··████·································█··················█······██····█··█····█····
// ··█···█···██████···█████████····█·····███████···███████···█·····█··█····█·██····█···
// ··█···█··██·····█·█·█··········█·█······█····█·█·█·······█·····█····█···█··█····█···
// ··████····█·····██··█··········█·█······█····█···██████··█·····█····█···█··█····█···
// ··█···█···██████··███████·····█···█·····█···█··███·······█·····█···█·····█·██···█···
// ··█····█··██········█········███████····█··█·····█·······█······█·█······█·█·█·█····
// ··█···█···█·██······█·····██··█····█··█████······█········█████··█········█···█·····
// ██████····█···███····█████···█·····██············█··································
// ··········█·····█···································································
static const uint8_t G_BOOT_LOGO_BYTES[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x20, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0xc0, 0xc0, 0x40, 0xc0, 0xc0, 0x40, 0xc0, 0xa0, 0xa0, 0x20, 0xa0, 0xa0, 0xe0, 0x60, 0xd0, 0x90, 0xd0, 0xd0, 0xf0, 0xf0, 0xc8, 0xe8, 0xe8, 0xb8, 0xb8, 0x64, 0xd4, 0xb4, 0x94, 0x9c, 0x14, 0x0c, 0x0c, 0x1c, 0x14, 0x34, 0x34, 0x34, 0x12, 0x1a, 0x0a, 0x0a, 0x06, 0x82, 0x82, 0x82, 0x44, 0x44, 0x28, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xc0, 0xc0, 0x60, 0x60, 0x60, 0x50, 0x30, 0x30, 0x38, 0x3c, 0x3c, 0x2c, 0x2c, 0x3c, 0x2e, 0x2e, 0x3e, 0x3e, 0x37, 0x3d, 0x2b, 0x3f, 0x3d, 0x2f, 0x3f, 0x3f, 0x5f, 0x4d, 0xad, 0x25, 0x16, 0x1d, 0x1f, 0x1e, 0x0a, 0x1b, 0x09, 0x0d, 0x04, 0x02, 0x03, 0x03, 0xc3, 0x43, 0x43, 0x43, 0xa5, 0x63, 0x23, 0x22, 0x21, 0x91, 0xd1, 0x31, 0x11, 0x08, 0x08, 0x28, 0x58, 0x2c, 0x04, 0x34, 0x1c, 0x16, 0x0a, 0x0a, 0x16, 0x0a, 0x05, 0x01, 0x04, 0x0b, 0x0f, 0x3c, 0x10, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x09, 0x09, 0x10, 0x10, 0x10, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x38, 0xe8, 0x08, 0xf4, 0x4c, 0x32, 0x29, 0x10, 0x08, 0x00, 0x10, 0x48, 0x84, 0x02, 0x05, 0x02, 0x05, 0x02, 0x01, 0x00, 0x00, 0x02, 0x05, 0x82, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x60, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x80, 0x87, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0x08, 0x10, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x8c, 0xb0, 0xc0, 0x80, 0x40, 0x40, 0x20, 0x20, 0x20, 0x20, 0x20, 0x11, 0x16, 0x18, 0x30, 0x28, 0x08, 0x08, 0x08, 0x08, 0x04, 0x05, 0x06, 0x04, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0xa1, 0x41, 0xa1, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x0f, 0x10, 0x20, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0c, 0x05, 0x06, 0x02, 0x02, 0x02, 0x01, 0x01, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x02, 0x07, 0x02, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x20, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x7f, 0x44, 0x44, 0x44, 0x2b, 0x10, 0x00, 0x02, 0xff, 0x19, 0x29, 0x29, 0x49, 0x49, 0xc6, 0x04, 0x0a, 0x09, 0x3f, 0x49, 0x49, 0x49, 0x49, 0x41, 0x21, 0x21, 0x00, 0x50, 0x38, 0x16, 0x11, 0x16, 0x18, 0x70, 0x40, 0x00, 0x21, 0x21, 0x3f, 0x21, 0x21, 0x11, 0x09, 0x06, 0x00, 0x0a, 0x09, 0x7f, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x1e, 0x21, 0x20, 0x20, 0x20, 0x20, 0x0e, 0x11, 0x20, 0x10, 0x09, 0x06, 0x00, 0x00, 0x00, 0x07, 0x18, 0x21, 0x1f, 0x08, 0x10, 0x20, 0x10, 0x0f, 0x00, 0x00, 0x00 };
static const struct BitmapImage bitmaps[] = {
{ .name="boot_logo", .width=84, .height=48, .bytes=G_BOOT_LOGO_BYTES },
{}
};
const struct BitmapImage *Bitmap_Get(const char *name) {
const struct BitmapImage *ptr = &bitmaps[0];
while (ptr->name) {
if (0 == strcmp(ptr->name, name)) {
return ptr;
}
}
return NULL;
}
+21
View File
@@ -0,0 +1,21 @@
/**
* TODO file description
*
* Created on 2020/01/05.
*/
#ifndef BITMAPS_H
#define BITMAPS_H
#include <stdint.h>
struct BitmapImage {
const char *name;
uint8_t width;
uint8_t height;
const uint8_t* bytes;
};
const struct BitmapImage *Bitmap_Get(const char *name);
#endif //BITMAPS_H
+2 -4
View File
@@ -282,12 +282,10 @@ void LCD_setStrEx(const char *dString, int x, int y, enum Color bw, uint8_t styl
// This function will draw an array over the screen. (For now) the
// array must be the same size as the screen, covering the entirety
// of the display.
// Also, the array must reside in FLASH and declared with PROGMEM.
void LCD_setBitmap(const char *bitArray)
void LCD_setBitmap(const uint8_t *bitArray)
{
for (int i = 0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++) {
char c = bitArray[i];
LCD_displayMap[i] = c;
LCD_displayMap[i] = bitArray[i];
}
}
+5 -3
View File
@@ -4,8 +4,8 @@
* Created on 2020/01/05.
*/
#ifndef REFLOWER_DRAWING_H
#define REFLOWER_DRAWING_H
#ifndef LCD_DRAWING_H
#define LCD_DRAWING_H
#include <stdint.h>
#include <stdbool.h>
@@ -34,6 +34,8 @@ void LCD_setRect(int x0, int y0, int x1, int y1, bool fill, enum Color bw);
// thickness ranging from 1 to the radius of the circle.
void LCD_setCircle (int x0, int y0, int radius, enum Color bw, int lineThickness);
void LCD_setBitmap(const uint8_t *bitArray);
/*
FONT FUNCTIONS
size = 1 ... normal
@@ -72,4 +74,4 @@ void LCD_clearDisplay(enum Color bw);
/* Invert colors (hard change in data; does NOT send to display immediately) */
void LCD_invertDisplayData();
#endif //REFLOWER_DRAWING_H
#endif //LCD_DRAWING_H
+3 -3
View File
@@ -2,8 +2,8 @@
* Driver for Nokia 5110 SPI LCD display
*/
#ifndef GFX_NOKIA
#define GFX_NOKIA
#ifndef GFX_NOKIA_H
#define GFX_NOKIA_H
/* Pin definitions:
Most of these pins can be moved to any digital or analog pin.
@@ -36,4 +36,4 @@ void LCD_invertDisplay(bool invert);
//This sends the magical commands to the PCD8544
void LCD_setup(void);
#endif // GFX_NOKIA
#endif // GFX_NOKIA_H
+2 -2
View File
@@ -53,11 +53,10 @@ static void __attribute__((noreturn)) gui_thread(void *arg) {
uint32_t last_wheel_time = 0;
while (1) {
bool want_repaint = false;
uint32_t value = 0;
xTaskNotifyWait(0, ULONG_MAX, &value, pdMS_TO_TICKS(10));
bool want_repaint = false;
if (value & 0b10000) {
// TICK
want_repaint |= Liquid_handleTick(liquid);
@@ -72,6 +71,7 @@ static void __attribute__((noreturn)) gui_thread(void *arg) {
if (value & 0b11) {
uint32_t time = xTaskGetTickCount();
uint32_t increment = 1;
// wheel delta changes with speed
if (last_wheel_time != 0) {
uint32_t ela = time - last_wheel_time;
if (ela < pdMS_TO_TICKS(20)) {
+26 -4
View File
@@ -9,6 +9,8 @@
static const char *TAG = "Liquid";
extern struct Scene *NewScene_Root(void);
static struct SceneEvent Liquid_initTopScene(struct Liquid *container);
static bool processReturnValue(struct Liquid *container, struct SceneEvent result);
struct RunningScene {
struct Scene *scene;
@@ -27,7 +29,6 @@ static struct SceneEvent Default_onChildReturn(struct Scene *scene, uint32_t tag
static void addChild(struct Liquid *container, struct Scene *child) {
struct RunningScene *elm = calloc(1, sizeof(struct RunningScene));
if (!child->onChildReturn) child->onChildReturn = Default_onChildReturn;
assert(child->paint != NULL);
elm->scene = child;
SLIST_INSERT_HEAD(&container->stack, elm, next);
}
@@ -36,7 +37,12 @@ struct Liquid *Liquid_start(void) {
struct Liquid *container = calloc(1, sizeof(struct Liquid));
addChild(container, NewScene_Root());
Liquid_paint(container);
struct SceneEvent result = Liquid_initTopScene(container);
if (processReturnValue(container, result)) {
Liquid_paint(container);
}
return container;
}
@@ -69,7 +75,7 @@ static struct SceneEvent handleSceneEvent(struct Liquid *container, struct Scene
newScene = event.open.scene;
newScene->tag = event.open.tag;
addChild(container, newScene);
return SceneEvent_Repaint();
return Liquid_initTopScene(container);
case SceneEventKind_RequestRepaint:
case SceneEventKind_None:
@@ -79,6 +85,9 @@ static struct SceneEvent handleSceneEvent(struct Liquid *container, struct Scene
}
}
/**
* returns true if repaint is requested
*/
static bool processReturnValue(struct Liquid *container, struct SceneEvent result) {
bool repaint = false;
while (result.kind != SceneEventKind_None) {
@@ -122,10 +131,23 @@ bool Liquid_handleTick(struct Liquid *container) {
}
}
static struct SceneEvent Liquid_initTopScene(struct Liquid *container) {
struct RunningScene *topmost = SLIST_FIRST(&container->stack);
assert(topmost != NULL);
assert(topmost->scene != NULL);
if (topmost->scene->init) {
return topmost->scene->init(topmost->scene);
} else {
return SceneEvent_Repaint();
}
}
void Liquid_paint(struct Liquid *container) {
struct RunningScene *topmost = SLIST_FIRST(&container->stack);
assert(topmost != NULL);
topmost->scene->paint(topmost->scene);
if (topmost->scene->paint) {
topmost->scene->paint(topmost->scene);
}
LCD_updateDisplay();
}
+15 -2
View File
@@ -40,6 +40,14 @@ typedef struct SceneEvent (*Scene_onChildReturn_t)(struct Scene *scene, uint32_t
*/
typedef struct SceneEvent (*Scene_onTick_t)(struct Scene *scene);
/**
* Scene::init fp type
*
* @param scene - self
* @return follow-up scene event, can be SceneEvent_None() if not used.
*/
typedef struct SceneEvent (*Scene_init_t)(struct Scene *scene);
/**
* Scene::paint fp type
*
@@ -65,6 +73,12 @@ struct Scene {
*/
uint32_t tag;
/**
* Init function, called once after the scene has been opened.
* Nullable field.
*/
Scene_init_t init;
/**
* Handle input event.
* Can return a follow-up scene event, e.g. repaint request.
@@ -90,8 +104,7 @@ struct Scene {
/**
* Draw the scene to the LCD buffer.
* DO NOT write to the display yet, it will be done by the GUI engine.
*
* MANDATORY FIELD
* Nullable field.
*/
Scene_paint_t paint;
+36
View File
@@ -0,0 +1,36 @@
#include <malloc.h>
#include <stdio.h>
#include <graphics/bitmaps.h>
#include "scenes.h"
#include "liquid.h"
#include "analog.h"
#include "graphics/nokia.h"
#include "graphics/drawing.h"
struct BootScene {
struct Scene base;
uint32_t cnt;
};
static struct SceneEvent Boot_onTick(struct BootScene *self) {
self->cnt += 10; // ms
if (self->cnt == 2000) {
return SceneEvent_Close(0, NULL);
}
return SceneEvent_None();
}
static void Boot_paint(struct BootScene *self)
{
LCD_clearDisplay(0);
LCD_setBitmap(Bitmap_Get("boot_logo")->bytes);
}
struct Scene *NewScene_Boot(void) {
struct BootScene *scene = calloc(1, sizeof(struct BootScene));
scene->base.paint = (Scene_paint_t) Boot_paint;
scene->base.onTick = (Scene_onTick_t) Boot_onTick;
return (struct Scene *) scene;
}
+92
View File
@@ -0,0 +1,92 @@
#include <malloc.h>
#include <stdio.h>
#include <graphics/bitmaps.h>
#include "scenes.h"
#include "liquid.h"
#include "analog.h"
#include "graphics/nokia.h"
#include "graphics/drawing.h"
/**
* The struct is allocated bigger than 'Scene' to accommodate private fields.
* Since the base struct is located at the beginning, it can be cast and passed around as Scene.
*/
struct DemoScene {
struct Scene base;
int32_t pos;
uint32_t timer_phase;
uint32_t timer_prescale;
};
static struct SceneEvent Demo_onInput(struct DemoScene *self, struct InputEvent event) {
switch (event.kind) {
case InputEventKind_Wheel:
self->pos += event.wheel.delta;
break;
case InputEventKind_Button:
if (event.button.state) {
return SceneEvent_OpenChild(NewScene_Car(), 0);
}
break;
}
return SceneEvent_Repaint();
}
static struct SceneEvent Demo_onTick(struct DemoScene *self) {
// top text anim
self->timer_prescale += 1;
if (self->timer_prescale == 100) {
self->timer_prescale = 0;
self->timer_phase += 1;
self->timer_phase = self->timer_phase & 0b11; // 0..3
return SceneEvent_Repaint();
}
return SceneEvent_None();
}
static void Demo_paint(struct DemoScene *self)
{
LCD_clearDisplay(0);
const char *header = "???";
switch (self->timer_phase) {
case 0:
header = "Drink water";
break;
case 1:
header = " Drink water";
break;
case 2:
header = " Drink water";
break;
case 3:
header = " Drink water";
break;
}
LCD_setStrEx(header, 0, 3, 1, 3);
LCD_setRect(0, 12, 83, 31, 1, 1);
char buf[10];
// sprintf(buf, "%3d", priv->pos);
// LCD_setStr(buf, 2, 15, 0);
sprintf(buf, "🌡%.0f°C", analog_read());
LCD_setStrEx(buf, 2, 14, 0, 2);
LCD_setStr("×↑↓←→⏰⌛☸⏎🌡°🔙▶◀", 2, 33, 1);
}
struct Scene *NewScene_Demo(void) {
struct DemoScene *scene = calloc(1, sizeof(struct DemoScene));
scene->base.onInput = (Scene_onInput_t) Demo_onInput;
scene->base.paint = (Scene_paint_t) Demo_paint;
scene->base.onTick = (Scene_onTick_t) Demo_onTick;
return (struct Scene *) scene;
}
+17 -63
View File
@@ -1,5 +1,6 @@
#include <malloc.h>
#include <stdio.h>
#include <graphics/bitmaps.h>
#include "scenes.h"
#include "liquid.h"
@@ -13,78 +14,31 @@
*/
struct RootScene {
struct Scene base;
int32_t pos;
uint32_t timer_phase;
uint32_t timer_prescale;
};
static struct SceneEvent Root_onInput(struct RootScene *self, struct InputEvent event) {
switch (event.kind) {
case InputEventKind_Wheel:
self->pos += event.wheel.delta;
break;
case InputEventKind_Button:
if (event.button.state) {
return SceneEvent_OpenChild(NewScene_Car(), 0);
}
break;
}
return SceneEvent_Repaint();
}
static struct SceneEvent Root_onTick(struct RootScene *self) {
self->timer_prescale += 1;
if (self->timer_prescale == 100) {
self->timer_prescale = 0;
self->timer_phase += 1;
self->timer_phase = self->timer_phase & 0b11; // 0..3
return SceneEvent_Repaint();
}
return SceneEvent_None();
}
static void Root_paint(struct RootScene *self)
static struct SceneEvent Root_init(struct RootScene *self)
{
LCD_clearDisplay(0);
const char *header = "???";
switch (self->timer_phase) {
case 0:
header = "Drink water";
break;
case 1:
header = " Drink water";
break;
case 2:
header = " Drink water";
break;
case 3:
header = " Drink water";
break;
return SceneEvent_OpenChild(NewScene_Boot(), 1);
}
struct SceneEvent Root_onChildReturn(
struct RootScene *self,
uint32_t tag,
int32_t status,
void *data)
{
if (tag == 1) {
return SceneEvent_OpenChild(NewScene_Demo(), 0);
}
LCD_setStrEx(header, 0, 3, 1, 3);
LCD_setRect(0, 12, 83, 31, 1, 1);
char buf[10];
// sprintf(buf, "%3d", priv->pos);
// LCD_setStr(buf, 2, 15, 0);
sprintf(buf, "🌡%.0f°C", analog_read());
LCD_setStrEx(buf, 2, 14, 0, 2);
LCD_setStr("×↑↓←→⏰⌛☸⏎🌡°🔙▶◀", 2, 33, 1);
LCD_updateDisplay();
// this should be unreachable
return SceneEvent_None();
}
struct Scene *NewScene_Root(void) {
struct RootScene *scene = calloc(1, sizeof(struct RootScene));
scene->base.onInput = (Scene_onInput_t) Root_onInput;
scene->base.paint = (Scene_paint_t) Root_paint;
scene->base.onTick = (Scene_onTick_t) Root_onTick;
scene->base.init = (Scene_init_t) Root_init;
scene->base.onChildReturn = (Scene_onChildReturn_t) Root_onChildReturn;
return (struct Scene *) scene;
}
+2
View File
@@ -8,6 +8,8 @@
#define REFLOWER_SCENES_H
struct Scene *NewScene_Root(void);
struct Scene *NewScene_Boot(void);
struct Scene *NewScene_Demo(void);
struct Scene *NewScene_Car(void);
#endif //REFLOWER_SCENES_H