From d5357b2021551545b6fd8291837a6a3f4357f4df Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Mon, 23 Dec 2019 19:56:33 +0100 Subject: [PATCH] Show IP address on startup To accomplish this, the following changes were made: - Added an image-scrolling animation - Implemented a bitmap font engine to generate an image from text - On startup, an image-scrolling animation is set up with an image generated from the local IP address --- include/Animation/AllAnimations.h | 1 + include/Animation/AnimationController.h | 4 +- include/Animation/ImageScrollerAnimation.h | 31 ++++++++++++ include/Bitmap.h | 33 ++++++++++++ include/Font.h | 12 +++++ include/font16_data.h | 12 +++++ src/Animation/AnimationController.cpp | 11 ++++ src/Animation/ImageScrollerAnimation.cpp | 58 ++++++++++++++++++++++ src/Bitmap.cpp | 9 ++++ src/Font.cpp | 46 +++++++++++++++++ src/font16_data.c | 10 ++++ src/main.cpp | 10 +++- 12 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 include/Animation/ImageScrollerAnimation.h create mode 100644 include/Bitmap.h create mode 100644 include/Font.h create mode 100644 include/font16_data.h create mode 100644 src/Animation/ImageScrollerAnimation.cpp create mode 100644 src/Bitmap.cpp create mode 100644 src/Font.cpp create mode 100644 src/font16_data.c diff --git a/include/Animation/AllAnimations.h b/include/Animation/AllAnimations.h index 28416c8..3a2b3ca 100644 --- a/include/Animation/AllAnimations.h +++ b/include/Animation/AllAnimations.h @@ -5,3 +5,4 @@ #include "FireAnimation.h" #include "SnowfallAnimation.h" #include "FadeToColorAnimation.h" +#include "ImageScrollerAnimation.h" diff --git a/include/Animation/AnimationController.h b/include/Animation/AnimationController.h index edf58f5..aa2338f 100644 --- a/include/Animation/AnimationController.h +++ b/include/Animation/AnimationController.h @@ -15,6 +15,7 @@ class AnimationController FIRE_HOT = 0, FIRE_COLD = 1, SNOWFALL = 2, + FONT_TEST = 3, NUM_DEFAULT_ANIMATIONS }; @@ -22,7 +23,8 @@ class AnimationController static const constexpr std::array AnimationNames{ "Hot Fire", "Cold Fire", - "Snowfall" + "Snowfall", + "Font Test" }; AnimationController(Fader *fader); diff --git a/include/Animation/ImageScrollerAnimation.h b/include/Animation/ImageScrollerAnimation.h new file mode 100644 index 0000000..fde7c79 --- /dev/null +++ b/include/Animation/ImageScrollerAnimation.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "Animation.h" + +#include "Bitmap.h" + +class ImageScrollerAnimation : public Animation +{ + public: + ImageScrollerAnimation(Fader *fader, Bitmap *image, uint32_t interval = 3); + + void loop(uint64_t frame) override; + + void stop(void) override + { + m_stopping = true; + } + + void reset(void) override; + + private: + Bitmap m_image; + + int32_t m_startIdx; + uint32_t m_interval; + + bool m_stopping; +}; diff --git a/include/Bitmap.h b/include/Bitmap.h new file mode 100644 index 0000000..dabcec0 --- /dev/null +++ b/include/Bitmap.h @@ -0,0 +1,33 @@ +#pragma once + +#include "Fader.h" + +class Bitmap +{ +public: + std::vector m_pixdata; + + uint32_t width; + uint32_t height; + + Bitmap(uint32_t w, uint32_t h) + : m_pixdata(w * h, Fader::Color{0, 0, 0, 0}), + width(w), + height(h) + { + } + + Bitmap() : Bitmap(0, 0) {} + + Fader::Color &pixel(uint32_t x, uint32_t y) + { + return m_pixdata[idx(x, y)]; + } + + uint32_t idx(uint32_t x, uint32_t y) + { + return x * height + y; + } + + void resize(uint32_t w, uint32_t h); +}; diff --git a/include/Font.h b/include/Font.h new file mode 100644 index 0000000..b57b3a1 --- /dev/null +++ b/include/Font.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +class Font +{ + public: + static void textToBitmap(const char *text, + Bitmap *bmp, + const Fader::Color &color_on = Fader::Color{0, 0, 0, 32}, + const Fader::Color &color_off = Fader::Color{0, 0, 0, 0}); +}; diff --git a/include/font16_data.h b/include/font16_data.h new file mode 100644 index 0000000..7467fd6 --- /dev/null +++ b/include/font16_data.h @@ -0,0 +1,12 @@ +#ifndef FONT16_DATA_H +#define FONT16_DATA_H + +#include + +extern const uint16_t font_data[]; +extern const uint16_t font_pos[]; +extern const uint8_t font_width[]; +extern const uint8_t font_lut[]; +extern const uint8_t font_height; + +#endif // FONT16_DATA_H diff --git a/src/Animation/AnimationController.cpp b/src/Animation/AnimationController.cpp index 125b1a1..19844a9 100644 --- a/src/Animation/AnimationController.cpp +++ b/src/Animation/AnimationController.cpp @@ -2,6 +2,8 @@ #include "Animation/AllAnimations.h" +#include "Font.h" + const constexpr std::array AnimationController::AnimationNames; @@ -47,6 +49,15 @@ void AnimationController::changeAnimation(AnimationController::DefaultAnimation changeAnimation(std::unique_ptr(new SnowfallAnimation(m_fader)), transition); break; + case FONT_TEST: + { + Bitmap bmp(16, 16); + Font::textToBitmap("Hello World!", &bmp, Fader::Color{0, 0, 32, 32}); + + changeAnimation(std::unique_ptr(new ImageScrollerAnimation(m_fader, &bmp)), transition); + } + break; + default: return; // unknown id, do nothing } diff --git a/src/Animation/ImageScrollerAnimation.cpp b/src/Animation/ImageScrollerAnimation.cpp new file mode 100644 index 0000000..72009c4 --- /dev/null +++ b/src/Animation/ImageScrollerAnimation.cpp @@ -0,0 +1,58 @@ +#include "Animation/ImageScrollerAnimation.h" + +#include + +ImageScrollerAnimation::ImageScrollerAnimation(Fader *fader, Bitmap *image, uint32_t interval) + : Animation(fader), + m_image(*image), + m_interval(interval) +{ + reset(); +} + +void ImageScrollerAnimation::loop(uint64_t frame) +{ + int32_t nled = m_fader->modules_per_strip(); + int32_t nstrip = m_fader->strips(); + + if((frame % m_interval) != 0) { + return; + } + + // blit the image + m_fader->set_color(Fader::Color{0, 0, 0, 0}); + + for(int32_t strip = 0; strip < nstrip; strip++) { + for(int32_t led = 0; led < nled; led++) { + int32_t im_x = m_startIdx - strip; + int32_t im_y = m_fader->modules_per_strip() - led - 1; + + if(im_x < 0 || im_x >= m_image.width || im_y < 0 || im_y >= m_image.height) { + // the current pixel is not inside the shifted image + continue; + } + + int32_t s = (nstrip - strip - 1 + 5) % nstrip; + m_fader->set_color(s, led, m_image.pixel(im_x, im_y)); + } + } + + // move the image around the lamp + m_startIdx++; + + if(m_startIdx >= static_cast(nstrip + m_image.width)) { + if(m_stopping) { + m_running = false; + } else { + reset(); + } + } +} + +void ImageScrollerAnimation::reset(void) +{ + m_stopping = false; + m_running = true; + + m_startIdx = 0; +} diff --git a/src/Bitmap.cpp b/src/Bitmap.cpp new file mode 100644 index 0000000..e160790 --- /dev/null +++ b/src/Bitmap.cpp @@ -0,0 +1,9 @@ +#include "Bitmap.h" + +void Bitmap::resize(uint32_t w, uint32_t h) +{ + width = w; + height = h; + + m_pixdata.resize(w*h, Fader::Color{0, 0, 0, 0}); +} diff --git a/src/Font.cpp b/src/Font.cpp new file mode 100644 index 0000000..b1110db --- /dev/null +++ b/src/Font.cpp @@ -0,0 +1,46 @@ +#include "Font.h" + +#include "font16_data.h" + +void Font::textToBitmap(const char *text, Bitmap *bmp, const Fader::Color &color_on, const Fader::Color &color_off) +{ + uint32_t bitmapWidth = 0; + + const char *cp = text; + while(*cp) { + uint8_t idx = font_lut[static_cast(*cp)]; + + bitmapWidth += font_width[idx]; + + cp++; + } + + bmp->resize(bitmapWidth, font_height); + + uint32_t bmp_x = 0; + cp = text; + while(*cp) { + uint8_t idx = font_lut[static_cast(*cp)]; + + uint16_t char_start = font_pos[idx]; + uint16_t char_end = char_start + font_width[idx]; + + for(uint16_t char_pos = char_start; char_pos < char_end; char_pos++) { + uint16_t char_data = font_data[char_pos]; + + for(uint8_t y = 0; y < font_height; y++) { + uint32_t bmp_y = y; + + if(char_data & (1 << y)) { + bmp->pixel(bmp_x, bmp_y) = color_on; + } else { + bmp->pixel(bmp_x, bmp_y) = color_off; + } + } + + bmp_x++; + } + + cp++; + } +} diff --git a/src/font16_data.c b/src/font16_data.c new file mode 100644 index 0000000..ac0251f --- /dev/null +++ b/src/font16_data.c @@ -0,0 +1,10 @@ + +#include "font16_data.h" + +const uint16_t font_data[643] = { 0x0, 0x0, 0x0, 0x0, 0x0, 0x7f0, 0x808, 0x1004, 0x10c4, 0x10c4, 0x808, 0x7f0, 0x0, 0x0, 0x0, 0x1008, 0x1004, 0x1ffc, 0x1000, 0x1000, 0x0, 0x0, 0x0, 0x1018, 0x180c, 0x1404, 0x1204, 0x1104, 0x1088, 0x1078, 0x0, 0x0, 0x808, 0x1004, 0x1084, 0x1084, 0x1084, 0x1948, 0xf78, 0x0, 0x0, 0x300, 0x2c0, 0x260, 0x218, 0x20c, 0x1ffc, 0x200, 0x0, 0x0, 0x8fc, 0x1044, 0x1044, 0x1044, 0x1044, 0x884, 0x700, 0x0, 0x0, 0x7e0, 0x898, 0x104c, 0x1044, 0x1044, 0x18c4, 0xf88, 0x0, 0x0, 0x4, 0x1004, 0xc04, 0x304, 0xc4, 0x3c, 0xc, 0x0, 0x0, 0xf78, 0x194c, 0x1084, 0x1084, 0x1084, 0x194c, 0xf78, 0x0, 0x0, 0x8f8, 0x118c, 0x1104, 0x1104, 0x1904, 0xc88, 0x3f0, 0x0, 0x0, 0xe00, 0x1340, 0x1120, 0x1120, 0x1120, 0x920, 0x1fc0, 0x0, 0x0, 0x1ffc, 0x840, 0x1020, 0x1020, 0x1020, 0x840, 0x780, 0x0, 0x0, 0x780, 0x840, 0x1020, 0x1020, 0x1020, 0x840, 0x0, 0x0, 0x0, 0x780, 0x840, 0x1020, 0x1020, 0x1020, 0x840, 0x1ffc, 0x0, 0x0, 0x780, 0xa40, 0x1220, 0x1220, 0x1220, 0x1240, 0xb80, 0x0, 0x0, 0x0, 0x20, 0x20, 0x1ff8, 0x24, 0x24, 0x24, 0x0, 0x0, 0x780, 0x4840, 0x9020, 0x9020, 0x9020, 0xc840, 0x7fe0, 0x0, 0x0, 0x1ffc, 0x40, 0x20, 0x20, 0x20, 0x60, 0x1fc0, 0x0, 0x0, 0x1000, 0x1020, 0x1020, 0x1fec, 0x1000, 0x1000, 0x1000, 0x0, 0x0, 0x8000, 0x8020, 0x8020, 0x7fec, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1ffc, 0x100, 0x180, 0x240, 0x440, 0x820, 0x1000, 0x0, 0x4, 0x4, 0x4, 0xffc, 0x1000, 0x1000, 0x1000, 0x0, 0x0, 0x0, 0x1fe0, 0x20, 0x20, 0x1fe0, 0x20, 0x20, 0x1fc0, 0x0, 0x0, 0x1fe0, 0x40, 0x20, 0x20, 0x20, 0x60, 0x1fc0, 0x0, 0x0, 0x780, 0x840, 0x1020, 0x1020, 0x1020, 0x840, 0x780, 0x0, 0x0, 0xffe0, 0x840, 0x1020, 0x1020, 0x1020, 0x840, 0x780, 0x0, 0x0, 0x780, 0x840, 0x1020, 0x1020, 0x1020, 0x840, 0xffe0, 0x0, 0x0, 0x0, 0x0, 0x1fe0, 0x40, 0x20, 0x20, 0x20, 0x40, 0x0, 0x9c0, 0x1120, 0x1120, 0x1120, 0x1220, 0x1220, 0xe40, 0x0, 0x0, 0x20, 0x20, 0xff8, 0x1020, 0x1020, 0x1020, 0x0, 0x0, 0x0, 0xfe0, 0x1800, 0x1000, 0x1000, 0x1000, 0x800, 0x1fe0, 0x0, 0x0, 0x20, 0x1c0, 0xe00, 0x1000, 0xe00, 0x1c0, 0x20, 0x0, 0x60, 0x780, 0x1800, 0x600, 0x180, 0x600, 0x1800, 0x780, 0x60, 0x0, 0x1020, 0x1860, 0x480, 0x300, 0x480, 0x1860, 0x1020, 0x0, 0x0, 0x20, 0x81c0, 0x8600, 0x7800, 0xe00, 0x1c0, 0x20, 0x0, 0x0, 0x1820, 0x1420, 0x1220, 0x1220, 0x1120, 0x10a0, 0x1060, 0x0, 0x0, 0x1800, 0x780, 0x278, 0x204, 0x278, 0x780, 0x1800, 0x0, 0x0, 0x1ffc, 0x1084, 0x1084, 0x1084, 0x1084, 0x19cc, 0xf78, 0x0, 0x0, 0x3e0, 0xc18, 0x1004, 0x1004, 0x1004, 0x1004, 0x808, 0x0, 0x0, 0x1ffc, 0x1004, 0x1004, 0x1004, 0x180c, 0xc18, 0x3e0, 0x0, 0x0, 0x1ffc, 0x1084, 0x1084, 0x1084, 0x1084, 0x1084, 0x1084, 0x0, 0x0, 0x1ffc, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x0, 0x0, 0x3e0, 0xc18, 0x1004, 0x1004, 0x1084, 0x1084, 0xf88, 0x0, 0x0, 0x1ffc, 0x80, 0x80, 0x80, 0x80, 0x80, 0x1ffc, 0x0, 0x0, 0x0, 0x1004, 0x1004, 0x1ffc, 0x1004, 0x1004, 0x0, 0x0, 0x0, 0x800, 0x1000, 0x1004, 0x1004, 0x1804, 0xffc, 0x0, 0x0, 0x0, 0x1ffc, 0x80, 0x40, 0x1a0, 0x210, 0xc08, 0x1004, 0x0, 0x0, 0x1ffc, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x1000, 0x0, 0x0, 0x1ffc, 0x1c, 0xe0, 0x100, 0xe0, 0x1c, 0x1ffc, 0x0, 0x0, 0x1ffc, 0xc, 0x70, 0x1c0, 0x600, 0x1800, 0x1ffc, 0x0, 0x0, 0x7f0, 0x808, 0x1004, 0x1004, 0x1004, 0x808, 0x7f0, 0x0, 0x0, 0x1ffc, 0x104, 0x104, 0x104, 0x104, 0x188, 0xf8, 0x0, 0x0, 0x7f0, 0x808, 0x1004, 0x1004, 0x3004, 0x7808, 0x7f0, 0x0, 0x0, 0x1ffc, 0x84, 0x84, 0x84, 0x84, 0x14c, 0xe78, 0x1000, 0x0, 0x878, 0x1848, 0x1084, 0x1084, 0x1084, 0x190c, 0xf08, 0x0, 0x0, 0x4, 0x4, 0x4, 0x1ffc, 0x4, 0x4, 0x4, 0x0, 0x0, 0xffc, 0x1800, 0x1000, 0x1000, 0x1000, 0x1800, 0xffc, 0x0, 0x0, 0xc, 0xf0, 0xf00, 0x1000, 0xf00, 0xf0, 0xc, 0x0, 0x1c, 0x7e0, 0x1800, 0x7c0, 0x20, 0x7c0, 0x1800, 0x7e0, 0x1c, 0x0, 0x1004, 0xc18, 0x360, 0x80, 0x360, 0xc18, 0x1004, 0x0, 0x4, 0xc, 0x30, 0x60, 0x1f80, 0x40, 0x30, 0xc, 0x4, 0x0, 0x1804, 0x1404, 0x1304, 0x1084, 0x1064, 0x1014, 0x100c, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1800, 0x1800, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1860, 0x1860, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x4, 0x1b84, 0x84, 0x44, 0x38, 0x0, 0x0, 0x0, 0x0, 0x0, 0x19fc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100, 0x100, 0x100, 0xfe0, 0x100, 0x100, 0x100, 0x0, 0x0, 0x0, 0x0, 0x100, 0x100, 0x100, 0x100, 0x0, 0x0, 0x0, 0x48, 0x50, 0x30, 0xfc, 0x30, 0x50, 0x48, 0x0, 0x0, 0x2000, 0x1800, 0x600, 0x180, 0x60, 0x18, 0x4, 0x0, 0x0, 0x240, 0x240, 0x240, 0x240, 0x240, 0x240, 0x240, 0x240 }; +const uint16_t font_pos[64] = { 0, 4, 13, 22, 31, 40, 49, 58, 67, 76, 85, 94, 103, 112, 121, 130, 139, 148, 157, 166, 175, 184, 193, 202, 211, 220, 229, 238, 247, 256, 265, 274, 283, 292, 301, 310, 319, 328, 337, 346, 355, 364, 373, 382, 391, 400, 409, 418, 427, 436, 445, 454, 463, 472, 481, 490, 499, 508, 517, 526, 535, 544, 553, 562 }; +const uint8_t font_width[64] = { 4, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 }; + +const uint8_t font_lut[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 69, 67, 0, 68, 63, 70, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 64, 0, 0, 71, 0, 66, 0, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 0, 0, 0, 0, 0, 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +const uint8_t font_height = 16; diff --git a/src/main.cpp b/src/main.cpp index 059fd16..bf80e52 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,7 @@ #include "Fader.h" #include "UDPProto.h" #include "UpdateServer.h" +#include "Font.h" #include "Config.h" #include "Animation/AllAnimations.h" @@ -118,7 +119,12 @@ static void ledFSM(void) animController.loop(); if(animController.isIdle()) { - animController.changeAnimation(std::unique_ptr(new FireAnimation(&ledFader, false)), false); + Bitmap bmp(16, 16); + String infoStr = "IP: " + WiFi.localIP().toString(); // + " - IPv6: " + WiFi.localIPv6().toString(); + Font::textToBitmap(infoStr.c_str(), &bmp, Fader::Color{0, 0, 0, 32}); + + animController.changeAnimation(std::unique_ptr(new ImageScrollerAnimation(&ledFader, &bmp, 5)), false); + //animController.changeAnimation(std::unique_ptr(new FireAnimation(&ledFader, false)), false); ledState = ANIMATION; } break; @@ -262,6 +268,8 @@ void wifi_setup(void) { Serial.println("Trying to connect..."); + WiFi.setHostname("mlmini"); + for(size_t tries = 0; tries < 10; tries++) { if(wiFiMulti.run() == WL_CONNECTED) {