Advanced Distributed Systems module at HSLU
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

/*
* 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 */