You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
308 lines
12 KiB
308 lines
12 KiB
/*
|
|
* Copyright (c) 2019, Erich Styger
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
*****************************************
|
|
* Idea and image files from Adafruit:
|
|
* https://learn.adafruit.com/animated-flying-toaster-oled-jewelry
|
|
* Animated pendant for Adafruit Pro Trinket and SSD1306 OLED display,
|
|
* inspired by the After Dark "Flying Toasters" screensaver.
|
|
*/
|
|
|
|
#include "platform.h"
|
|
#if PL_CONFIG_USE_TOASTER
|
|
#include "toaster.h"
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include "McuUtility.h"
|
|
#include "McuArmTools.h"
|
|
#include "lv.h"
|
|
#include "McuSystemView.h"
|
|
#include "McuGDisplaySSD1306.h"
|
|
|
|
static const uint8_t
|
|
toastermask0[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
|
|
0x00, 0x20, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00,
|
|
0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0xF8, 0x00,
|
|
0x00, 0x1F, 0x80, 0x00, 0x00, 0x7C, 0x3F, 0x80,
|
|
0x01, 0xF1, 0xF8, 0xF0, 0x07, 0xC7, 0xC7, 0x00,
|
|
0x0F, 0x1F, 0x08, 0x2B, 0x1E, 0x7C, 0x11, 0x00,
|
|
0x04, 0xF0, 0x20, 0x56, 0x61, 0xE4, 0x22, 0x00,
|
|
0x78, 0x48, 0x40, 0xA8, 0x4E, 0x10, 0x44, 0x00,
|
|
0x53, 0x90, 0x81, 0xF8, 0x5C, 0xA0, 0x90, 0x58,
|
|
0x7C, 0xA1, 0x07, 0x90, 0x74, 0xA1, 0x21, 0x38,
|
|
0x7F, 0xB5, 0x0E, 0x30, 0x77, 0xB0, 0x90, 0x78,
|
|
0x7F, 0xB4, 0x60, 0xF0, 0x77, 0xBB, 0x03, 0xE0,
|
|
0x7F, 0xBC, 0x0F, 0xC0, 0x77, 0xBF, 0xFF, 0x00,
|
|
0x7F, 0xBF, 0xFC, 0x00, 0x3F, 0xBF, 0xF0, 0x00,
|
|
0x1F, 0xBF, 0xC0, 0x00, 0x07, 0xBE, 0x00, 0x00,
|
|
0x01, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
|
toastermask1[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0xF8, 0x00,
|
|
0x00, 0x1F, 0x80, 0x00, 0x00, 0x7C, 0x3F, 0x80,
|
|
0x01, 0xF1, 0xF8, 0x00, 0x07, 0xC7, 0xC1, 0xE0,
|
|
0x0F, 0x1F, 0x06, 0x10, 0x1E, 0x7C, 0x00, 0x00,
|
|
0x04, 0xF0, 0x07, 0xF0, 0x61, 0xE4, 0x18, 0x08,
|
|
0x78, 0x48, 0x20, 0x80, 0x4E, 0x10, 0x40, 0x2B,
|
|
0x53, 0x90, 0x81, 0x00, 0x5C, 0xA0, 0x80, 0x58,
|
|
0x7C, 0xA1, 0x02, 0x00, 0x74, 0xA1, 0x28, 0xB8,
|
|
0x7F, 0xB5, 0x0F, 0xF0, 0x77, 0xB0, 0x90, 0x78,
|
|
0x7F, 0xB4, 0x60, 0xF0, 0x77, 0xBB, 0x03, 0xE0,
|
|
0x7F, 0xBC, 0x0F, 0xC0, 0x77, 0xBF, 0xFF, 0x00,
|
|
0x7F, 0xBF, 0xFC, 0x00, 0x3F, 0xBF, 0xF0, 0x00,
|
|
0x1F, 0xBF, 0xC0, 0x00, 0x07, 0xBE, 0x00, 0x00,
|
|
0x01, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
|
toastermask2[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00,
|
|
0x00, 0x1F, 0x80, 0x00, 0x00, 0x7C, 0x3F, 0x80,
|
|
0x01, 0xF1, 0xF8, 0x00, 0x07, 0xC7, 0xC1, 0xE0,
|
|
0x0F, 0x1F, 0x06, 0x10, 0x1E, 0x7C, 0x00, 0x00,
|
|
0x04, 0xF0, 0x00, 0x00, 0x61, 0xE4, 0x00, 0x08,
|
|
0x78, 0x48, 0x00, 0x10, 0x4E, 0x10, 0x00, 0x18,
|
|
0x53, 0x90, 0x60, 0x70, 0x5C, 0xA0, 0x9F, 0xC8,
|
|
0x7C, 0xA1, 0x04, 0x92, 0x74, 0xA1, 0x09, 0x24,
|
|
0x7F, 0xB5, 0x02, 0x48, 0x77, 0xB0, 0x80, 0x10,
|
|
0x7F, 0xB4, 0x41, 0x00, 0x77, 0xBB, 0x20, 0x40,
|
|
0x7F, 0xBC, 0x10, 0x00, 0x77, 0xBF, 0xF8, 0x00,
|
|
0x7F, 0xBF, 0xFC, 0x00, 0x3F, 0xBF, 0xF0, 0x00,
|
|
0x1F, 0xBF, 0xC0, 0x00, 0x07, 0xBE, 0x00, 0x00,
|
|
0x01, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
|
toastmask[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00,
|
|
0x00, 0x0C, 0x30, 0x00, 0x00, 0x30, 0x0C, 0x00,
|
|
0x00, 0xC2, 0x23, 0x00, 0x07, 0x00, 0x80, 0xC0,
|
|
0x08, 0x25, 0x50, 0x30, 0x10, 0x0B, 0xA8, 0x08,
|
|
0x21, 0x37, 0xF5, 0x04, 0x30, 0x4A, 0xE8, 0x0C,
|
|
0x3C, 0x15, 0x52, 0x34, 0x2B, 0x00, 0x80, 0xEC,
|
|
0x35, 0xC2, 0x23, 0x54, 0x1A, 0xB0, 0x0E, 0xAC,
|
|
0x0D, 0x5C, 0x15, 0x58, 0x03, 0xAB, 0xEA, 0xE0,
|
|
0x00, 0xD5, 0x55, 0x80, 0x00, 0x3A, 0xAE, 0x00,
|
|
0x00, 0x0D, 0x58, 0x00, 0x00, 0x03, 0xE0, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
|
toaster0[] = {
|
|
0x00, 0x30, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00,
|
|
0x01, 0xD8, 0x00, 0x00, 0x03, 0x74, 0xF0, 0x00,
|
|
0x07, 0xEF, 0xFE, 0x00, 0x07, 0xBC, 0x07, 0x80,
|
|
0x0F, 0xE0, 0x7F, 0xE0, 0x0F, 0x83, 0xC0, 0x70,
|
|
0x1E, 0x0E, 0x07, 0x08, 0x18, 0x38, 0x38, 0xFF,
|
|
0x30, 0xE0, 0xF7, 0xD4, 0x61, 0x83, 0xEE, 0xFF,
|
|
0x7B, 0x0F, 0xDF, 0xA8, 0x9E, 0x1B, 0xDD, 0xFE,
|
|
0x87, 0xB7, 0xBF, 0x50, 0xB1, 0xEF, 0xBB, 0xF8,
|
|
0xAC, 0x6F, 0x7E, 0x00, 0xA3, 0x5F, 0x6F, 0xA0,
|
|
0x83, 0x5E, 0xF8, 0x68, 0x8B, 0x5E, 0xDE, 0xC0,
|
|
0x80, 0x4A, 0xF1, 0xC8, 0x88, 0x4F, 0x6F, 0x80,
|
|
0x80, 0x4B, 0x9F, 0x08, 0x88, 0x44, 0xFC, 0x10,
|
|
0x80, 0x43, 0xF0, 0x20, 0x88, 0x40, 0x00, 0xC0,
|
|
0x80, 0x40, 0x03, 0x00, 0x40, 0x40, 0x0C, 0x00,
|
|
0x20, 0x40, 0x30, 0x00, 0x18, 0x41, 0xC0, 0x00,
|
|
0x06, 0x4E, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00 },
|
|
toaster1[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xF0, 0x00,
|
|
0x00, 0xF7, 0xFE, 0x00, 0x01, 0xBC, 0x07, 0x80,
|
|
0x03, 0xE0, 0x7F, 0xE0, 0x07, 0x83, 0xC0, 0x70,
|
|
0x0E, 0x0E, 0x07, 0xF8, 0x18, 0x38, 0x3E, 0x18,
|
|
0x30, 0xE0, 0xF9, 0xE8, 0x61, 0x83, 0xFF, 0xF8,
|
|
0x7B, 0x0F, 0xF8, 0x08, 0x9E, 0x1B, 0xE7, 0xF0,
|
|
0x87, 0xB7, 0xDF, 0x7F, 0xB1, 0xEF, 0xBF, 0xD4,
|
|
0xAC, 0x6F, 0x7E, 0xFF, 0xA3, 0x5F, 0x7F, 0xA6,
|
|
0x83, 0x5E, 0xFD, 0xF8, 0x8B, 0x5E, 0xD7, 0x40,
|
|
0x80, 0x4A, 0xF0, 0x08, 0x88, 0x4F, 0x6F, 0x80,
|
|
0x80, 0x4B, 0x9F, 0x08, 0x88, 0x44, 0xFC, 0x10,
|
|
0x80, 0x43, 0xF0, 0x20, 0x88, 0x40, 0x00, 0xC0,
|
|
0x80, 0x40, 0x03, 0x00, 0x40, 0x40, 0x0C, 0x00,
|
|
0x20, 0x40, 0x30, 0x00, 0x18, 0x41, 0xC0, 0x00,
|
|
0x06, 0x4E, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00 },
|
|
toaster2[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00,
|
|
0x00, 0x07, 0xFE, 0x00, 0x00, 0x3C, 0x07, 0x80,
|
|
0x00, 0xE0, 0x7F, 0xE0, 0x03, 0x83, 0xC0, 0x70,
|
|
0x0E, 0x0E, 0x07, 0xF8, 0x18, 0x38, 0x3E, 0x18,
|
|
0x30, 0xE0, 0xF9, 0xE8, 0x61, 0x83, 0xFF, 0xF8,
|
|
0x7B, 0x0F, 0xFF, 0xF8, 0x9E, 0x1B, 0xFF, 0xF0,
|
|
0x87, 0xB7, 0xFF, 0xE8, 0xB1, 0xEF, 0xFF, 0xE0,
|
|
0xAC, 0x6F, 0x9F, 0x88, 0xA3, 0x5F, 0x60, 0x36,
|
|
0x83, 0x5E, 0xFB, 0x6D, 0x8B, 0x5E, 0xF6, 0xDB,
|
|
0x80, 0x4A, 0xFD, 0xB6, 0x88, 0x4F, 0x7F, 0xEE,
|
|
0x80, 0x4B, 0xBE, 0xFC, 0x88, 0x44, 0xDF, 0xBC,
|
|
0x80, 0x43, 0xEF, 0xF8, 0x88, 0x40, 0x07, 0xF0,
|
|
0x80, 0x40, 0x03, 0xE0, 0x40, 0x40, 0x0C, 0xC0,
|
|
0x20, 0x40, 0x30, 0x00, 0x18, 0x41, 0xC0, 0x00,
|
|
0x06, 0x4E, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00 },
|
|
toast[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x03, 0xC0, 0x00, 0x00, 0x0F, 0xF0, 0x00,
|
|
0x00, 0x3D, 0xDC, 0x00, 0x00, 0xFF, 0x7F, 0x00,
|
|
0x07, 0xDA, 0xAF, 0xC0, 0x0F, 0xF4, 0x57, 0xF0,
|
|
0x1E, 0xC8, 0x0A, 0xF8, 0x0F, 0xB5, 0x17, 0xF0,
|
|
0x03, 0xEA, 0xAD, 0xC8, 0x14, 0xFF, 0x7F, 0x10,
|
|
0x0A, 0x3D, 0xDC, 0xA8, 0x05, 0x4F, 0xF1, 0x50,
|
|
0x02, 0xA3, 0xEA, 0xA0, 0x00, 0x54, 0x15, 0x00,
|
|
0x00, 0x2A, 0xAA, 0x00, 0x00, 0x05, 0x50, 0x00,
|
|
0x00, 0x02, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
static const uint8_t* const mask[] = {
|
|
toastermask0, toastermask1, toastermask2, toastermask1, toastmask };
|
|
static const uint8_t* const img[] = {
|
|
toaster0 , toaster1 , toaster2 , toaster1 , toast };
|
|
|
|
#define N_FLYERS 3 // Number of flying things
|
|
|
|
struct Flyer { // Array of flying things
|
|
int16_t x, y; // Top-left position * 16 (for subpixel pos updates)
|
|
int8_t depth; // Stacking order is also speed, 12-24 subpixels/frame
|
|
uint8_t frame; // Animation frame; Toasters cycle 0-3, Toast=255
|
|
} flyer[N_FLYERS];
|
|
|
|
// Flyer depth comparison function for qsort()
|
|
static int compare(const void *a, const void *b) {
|
|
return ((struct Flyer *)a)->depth - ((struct Flyer *)b)->depth;
|
|
}
|
|
|
|
static void drawBitmap(McuGDisplaySSD1306_PixelDim x, McuGDisplaySSD1306_PixelDim y, const uint8_t *bitmap, McuGDisplaySSD1306_PixelDim w, McuGDisplaySSD1306_PixelDim h, McuGDisplaySSD1306_PixelColor color) {
|
|
TIMAGE image;
|
|
|
|
image.name = "";
|
|
image.width = w;
|
|
image.height = h;
|
|
image.size = w*h;
|
|
image.pixmap = bitmap;
|
|
McuGDisplaySSD1306_DrawMonoBitmapMask(x, y, &image, color);
|
|
}
|
|
|
|
void TOASTER_DisplayToasters(void) {
|
|
uint8_t i, f;
|
|
int16_t x, y;
|
|
bool resort = false; // By default, don't re-sort depths
|
|
|
|
McuGDisplaySSD1306_UpdateFull();
|
|
McuGDisplaySSD1306_Clear(); /* screen black */
|
|
|
|
for(i=0; i<N_FLYERS; i++) { // For each flyer...
|
|
// First draw each item...
|
|
f = (flyer[i].frame == 255) ? 4 : (flyer[i].frame++ & 3); // Frame #
|
|
x = flyer[i].x / 16;
|
|
y = flyer[i].y / 16;
|
|
drawBitmap(x, y, mask[f], 32, 32, McuGDisplaySSD1306_COLOR_BLACK);
|
|
drawBitmap(x, y, img[ f], 32, 32, McuGDisplaySSD1306_COLOR_WHITE);
|
|
|
|
// Then update position, checking if item moved off screen...
|
|
flyer[i].x -= flyer[i].depth * 2; // Update position based on depth,
|
|
flyer[i].y += flyer[i].depth; // for a sort of pseudo-parallax effect.
|
|
if((flyer[i].y >= (64*16)) || (flyer[i].x <= (-32*16))) { // Off screen?
|
|
if(McuUtility_random(0, 7-1) < 5) { // Pick random edge; 0-4 = top
|
|
flyer[i].x = McuUtility_random(0, 160-1) * 16;
|
|
flyer[i].y = -32 * 16;
|
|
} else { // 5-6 = right
|
|
flyer[i].x = 128 * 16;
|
|
flyer[i].y = McuUtility_random(0, 64-1) * 16;
|
|
}
|
|
flyer[i].frame = McuUtility_random(0, 3-1) ? McuUtility_random(0, 4-1) : 255; // 66% toaster, else toast
|
|
flyer[i].depth = 10 + McuUtility_random(0, 16-1);
|
|
resort = true;
|
|
}
|
|
}
|
|
// If any items were 'rebooted' to new position, re-sort all depths
|
|
if(resort) {
|
|
qsort(flyer, N_FLYERS, sizeof(struct Flyer), compare);
|
|
}
|
|
}
|
|
|
|
//#define REFR_TIME_MS (150) /* with I2C bit banging, it requires about 130 ms for a OLED refresh */
|
|
#define REFR_TIME_MS (500) /* with I2C bit banging, it requires about 130 ms for a OLED refresh */
|
|
static lv_task_t *refr_task;
|
|
static lv_obj_t *win;
|
|
static bool screenSaverIsRunning = false;
|
|
static bool stopScreenSaver = false;
|
|
|
|
bool TOASTER_IsRunning(void) {
|
|
return screenSaverIsRunning;
|
|
}
|
|
|
|
void TOASTER_StopScreenSaver(void) {
|
|
stopScreenSaver = true;
|
|
}
|
|
|
|
static void refresh_task(struct _lv_task_t *param) {
|
|
(void)param; /* not used */
|
|
if (stopScreenSaver) {
|
|
lv_obj_del(win);
|
|
win = NULL;
|
|
lv_task_del(refr_task);
|
|
refr_task = NULL;
|
|
screenSaverIsRunning = false;
|
|
} else {
|
|
screenSaverIsRunning = true;
|
|
#if 1
|
|
TOASTER_DisplayToasters();
|
|
#elif 1
|
|
static uint32_t cntr = 0;
|
|
McuGDisplaySSD1306_PixelColor color;
|
|
|
|
cntr++;
|
|
if (cntr<20) {
|
|
color = McuGDisplaySSD1306_COLOR_BLACK;
|
|
} else if (cntr<40) {
|
|
color = McuGDisplaySSD1306_COLOR_WHITE;
|
|
} else {
|
|
color = McuGDisplaySSD1306_COLOR_BLACK;
|
|
cntr = 0;
|
|
}
|
|
McuGDisplaySSD1306_DrawLine(
|
|
McuUtility_random(0, LV_HOR_RES-1), McuUtility_random(0, LV_VER_RES-1),
|
|
McuUtility_random(0, LV_HOR_RES-1), McuUtility_random(0, LV_VER_RES-1),
|
|
color);
|
|
McuGDisplaySSD1306_UpdateFull();
|
|
#else
|
|
line_points[0].x = McuUtility_random(0, 64);
|
|
line_points[0].y = McuUtility_random(0, 64);
|
|
line_points[1].x = McuUtility_random(0, 64);
|
|
line_points[1].y = McuUtility_random(0, 64);
|
|
lv_line_set_points(line1, line_points, 2); /*Set the points*/
|
|
lv_obj_align(line1, NULL, LV_ALIGN_IN_TOP_MID, 0, 20);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void TOASTER_Show(void) {
|
|
stopScreenSaver = false;
|
|
screenSaverIsRunning = false;
|
|
refr_task = lv_task_create(refresh_task, REFR_TIME_MS, LV_TASK_PRIO_LOW, NULL);
|
|
|
|
win = lv_obj_create(lv_scr_act(), NULL);
|
|
lv_obj_set_style(win, &lv_style_transp);
|
|
lv_obj_set_size(win, LV_HOR_RES, LV_VER_RES);
|
|
}
|
|
|
|
void TOASTER_Init(void) {
|
|
McuUtility_randomSetSeed(McuArmTools_GetCycleCounter());
|
|
for(uint8_t i=0; i<N_FLYERS; i++) { // Randomize initial flyer states
|
|
flyer[i].x = (-32 + McuUtility_random(0, 160-1)) * 16;
|
|
flyer[i].y = (-32 + McuUtility_random(0, 96-1)) * 16;
|
|
flyer[i].frame = McuUtility_random(0, 3-1) ? McuUtility_random(0, 4-1) : 255; // 66% toaster, else toast
|
|
flyer[i].depth = 10 + McuUtility_random(0, 16-1); // Speed / stacking order
|
|
}
|
|
qsort(flyer, N_FLYERS, sizeof(struct Flyer), compare); // Sort depths
|
|
}
|
|
|
|
void TOASTER_Deinit(void) {}
|
|
|
|
#endif /* PL_CONFIG_USE_TOASTER */
|
|
|