Latests posts
Navbar and "remaining-full-height" container for SPAs
In the last years I wrote several smaller single-page applications (SPAs), mainly for demos and experiments. The typical layout I want for these SPAs is:
- navbar at the top with fixed height
- content-container below the navbar with 100% of the remaining height.
This is what I cam currently using (demo):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Awesome SPA</title>
<style>
/* CSS RESET */
html {
box-sizing: border-box;
font-size: 16px;
}
*, *:before, *:after {
box-sizing: inherit;
}
body, h1, h2, h3, h4, h5, h6, p, ol, ul {
margin: 0;
padding: 0;
font-weight: normal;
}
/* END OF CSS RESET */
/* set explicit height on html => child-elements can use %-based heights*/
html {
height: 100%;
}
/* "body" is the top-level flex container */
body {
height: 100%;
display: flex;
flex-direction: column;
}
/* titlebar is flex "child". For complex cases, make this another container */
.titlebar {
background-color: rgb(37, 37, 37);
color: white;
width: 100%;
height: 4rem;
line-height: 4rem; /* vertically centers text */
padding-left: 4rem;
font-size: 2rem;
font-family: Arial, Helvetica, sans-serif;
}
/* main-container is flex child and at the same time another flex-container
for vertical+horizontal centering */
.main-container {
flex: 1; /* grows main-container to rest of height */
display: flex;
background-color: rgb(40, 40, 80);
align-items: center;
justify-content: center;
}
.main-container > div {
font: 2rem Arial;
width: 100%;
max-width: 600px;
height: 4rem;
line-height: 4rem;
margin: 20px;
background-color:#f1f1f1;
text-align: center;
}
</style>
</head>
<body>
<div class='titlebar'><b>Titlebar</b> ...with subtitle</div>
<div class="main-container">
<div>centered</div>
</div>
</body>
</html>
Matlab: Serial Communication with Pfeiffer MaxiGauge
Connect the MaxGauge to the PC using a simple (3-wire) cross-over serial cable.
The communication protocol (viewed from the PC) is roughly:
--> Mnemonic <CR>
<-- <ACK> <CR> <LF>
--> <ENQ>
<-- Data <CR> <LF>
The mnemonics are described in the user manual (page 85ff). CR (carriage return), ACK (acknowledge), LF (line-feed), ENQ (enquire) are standard ASCII control chararacters.
A simple script for first interactions (almost no error checks!):
maxigauge = serial('COM1');
fopen(maxigauge);
% ASCII control characters
ENQ = int8(5);
LF = int8(10);
CR = int8(13);
ACK = int8(6);
NAK = int8(21);
% Send command: I want to know the part number.
message = [int8('PNR'), CR];
fwrite(maxigauge, message)
% Device the "command received" answer from the device
answer = fscanf(maxigauge);
if all(answer == [ACK CR LF])
% disp('Command received by MaxiGauge')
else
error('Command not received correctly by MaxiGauge')
end
% Send the ENQ signal
fwrite(maxigauge, ENQ)
% Receive Answer
answer = fscanf(maxigauge);
disp(answer(1:end-2))
% End communication and cleanup
fclose(maxigauge);
delete(maxigauge)
clear maxigauge
Here is somewhat equivalent code as a Matlab class. I don’t do much OOP, so take it with a grain of salt.
classdef PfeifferMaxiGauge < handleAllHidden
properties (Hidden, SetAccess = immutable)
ENQ @ int8 scalar = 5; % Undocumented @ syntax: value is of type doulbe, a scalar, with initinal value = 0
LF @ int8 scalar = 10;
CR @ int8 scalar = 13;
ACK @ int8 scalar = 6;
NAK @ int8 scalar = 21;
com_port @ char vector;
end
properties (SetAccess = immutable)
serial_handle @ serial scalar;
end
methods (Hidden) % Low-level methods that the user should not (but can) call
function obj = PfeifferMaxiGauge(com_port) % constructor
obj.com_port = com_port;
obj.serial_handle = serial(obj.com_port);
fopen(obj.serial_handle);
end
function delete(obj) % destructor
fclose(obj.serial_handle);
end
function write(obj, str)
% Adds CR to str.
% Low-level function. Use send_command(), if appropriate.
message = [int8(str), obj.CR];
fwrite(obj.serial_handle, message)
end
function output = read(obj)
% Removes CR LF from output
output = fscanf(obj.serial_handle);
if output(end-1) ~= obj.CR || output(end) ~= obj.LF
error('MaxiGauge Read Operation: Unexpected data format - no CR LF at end!')
end
output = output(1:end-2);
end
end
methods % High-level methods that the user should use.
function send_command(obj, str)
obj.write(str)
ack_msg = obj.read();
if length(ack_msg) > 1 || ack_msg ~= obj.ACK;
error(['Command "' str '" not acknowledged by device'])
end
end
function output = query(obj, str)
if nargin > 1
obj.send_command(str)
end
fwrite(obj.serial_handle, obj.ENQ);
output = obj.read();
end
function pnr = get_pnr(obj)
pnr = obj.query('PNR');
end
end
end
This class derives from handleAllHidden, which is a trick to hide the standard methods when deriving from the handle class. I only implemented one helper method in my class (get_pnr), the rest will come later, as I need it.
Accessing Windows Registry from Ubuntu
I needed to find the hostname of a PC, but I had its harddrive connected
to my Ubuntu box as an external drive. Solution: Browse the registry
file of the Windows PC using chntpw
sudo apt-get install chntpw
cd /media/myusername/WINDOWS/Windows/System32/config
chntpw -e SYSTEM
This loads the SYSTEM
hive (the file containing everything that's
related to HKeyLocalMachine\System). It can take a few seconds to load.
You can use "standard" cmd/bash commands to navigate the registry (use "?" for help). Find the number of the "current control set":
cd Select dir
(Look at "Current" value, usually 1) Go back and navigate to the key that contains the hostname (computer name):
cd ..
cd ControlSet001
cd Control
cd ComputerName
cd ComputerName
cat ComputerName
The cat
command prints out the string of the variable ComputerName
,
which is the host name of the computer.
Use "q" to quit.
Running Startup Code on Mathematica
I wanted to have quicker access to the physics constants that I use most
often.
Therefore, I edited the init.m
file of the Kernel. There are several
init.m
files - for the Kernel and for the FrontEnd, and in each case
for the User, for the system, ...
I chose the init.m
of the User&Kernel, because it's in a folder that
I back up, and because it's reloaded when I restart the Kernel. In my
case, the file path was
"C:\Users\myusername\AppData\Roaming\Mathematica\FrontEnd\init.m"
. You
can find the User directory by running $UserBaseDirectory
in
Mathematica.
I added the following lines to this file:
(** User Mathematica initialization file **)
loadPhysicsShortcuts := (
me = Quantity[1, "ElectronMass"];
mp = Quantity[1, "ProtonMass"];
u = Quantity[1, "AtomicMassUnit"];
e = Quantity[1, "ElementaryCharge"];
kB = Quantity [1, "BoltzmannConstant"];
T = Quantity[1, "Teslas"];
Hz = Quantity[1, "Hertz" ];
mHz = Quantity[1, "Millihertz"];
MHz = Quantity[1, "Megahertz"];
m = Quantity[1, "Meters"];
eV = Quantity[1, "Electronvolts"];
c = Quantity[1, "SpeedOfLight"];
mm = Quantity[1, "Millimeters"];
V = Quantity[1, "Volts"];
s = Quantity[1, "Seconds"];
)
I use setDelayed
( :=
) to define the variable (or rather, the
expression!) loadPhysicsShortcuts
. Since it's defined through
setDelayed
, the definitions in it are only exececuted when I execute
loadPhysicsShortcuts
. Thereby my definitions don't pollute my
workspace unless I need them.
I guess professionals would package their definitions, but I am not at that level yet. Also, I like how easy it is to call these definitions when I need them.
Keyboard Shortcuts in Matlab 2016a (Windows 7, German)
I extracted the following list from Matlab by clicking the preferences wheel INSIDE the keyboard shortcuts preferences. I replaced German keyboard descriptions with English (Strg -> Ctrl).
It's possible these shortcuts are somehow tied to Locale, i.e. they might be different on a US keyboard. Also, note that these are not all keyboard shortcuts: There are keyboard shortcuts that cannot be customized, see bottom of this page. Most important: Ctrl+0 takes you to the command window (the "console"), and Ctrl+Shift+0 takes you to the editor window.
MATLAB Desktop
Alt+Backspace Undo
Alt+Shift+Backspace Redo
Ctrl+A Select All
Ctrl+C Copy
Ctrl+F Find...
Ctrl+F5 Pause
Ctrl+F6 Next Window
Ctrl+H Find...
Ctrl+Insert Copy
Ctrl+N New Script
Ctrl+O Open...
Ctrl+P Print...
Ctrl+Page down Next Tab
Ctrl+Page up Previous Tab
Ctrl+Q Exit MATLAB
Ctrl+S Save
Ctrl+S Save Workspace...
Ctrl+Shift+D Dock
Ctrl+Shift+F Find Files...
Ctrl+Shift+F6 Previous Window
Ctrl+Shift+M Maximize/Restore Docked Window
Ctrl+Shift+Tab Previous Tool
Ctrl+Shift+U Undock
Ctrl+Shift+Z Redo
Ctrl+Tab Next Tool
Ctrl+V Paste
Ctrl+W Close
Ctrl+X Cut
Ctrl+Y Redo
Ctrl+Z Undo
Entf Delete
F1 Product Help
F10 Step
F11 Step In
F5 Run or Continue Execution
Shift+Entf Cut
Shift+F1 Function Browser
Shift+F11 Step Out
Shift+F5 Exit Debug Mode
Shift+Insert Paste
Command Window
Alt+Backspace Undo
Alt+Shift+Backspace Redo
Backspace Delete Previous Character
Ctrl+\ Clear Selection
Ctrl+A Select All
Ctrl+B Cursor Backward
Ctrl+C Copy
Ctrl+D Open Selection
Ctrl+E Cursor End Line
Ctrl+End Cursor End Document
Ctrl+F Find...
Ctrl+F1 Function Hints
Ctrl+H Find...
Ctrl+Insert Copy
Ctrl+K Kill Line
Ctrl+Left Cursor Previous Word
Ctrl+N New Script
Ctrl+P Print...
Ctrl+Pos 1 Cursor Begin Document
Ctrl+Return Follow Hyperlink
Ctrl+Right Cursor Next Word
Ctrl+S Save Workspace...
Ctrl+Shift+End Selection End Document
Ctrl+Shift+F Find Files...
Ctrl+Shift+Left Selection Previous Word
Ctrl+Shift+Pos 1 Selection Begin Document
Ctrl+Shift+R Start Incremental Search Backwards
Ctrl+Shift+Right Selection Next Word
Ctrl+Shift+S Start Incremental Search Forwards
Ctrl+Shift+Z Redo
Ctrl+U Clear Text at Prompt
Ctrl+V Paste
Ctrl+W Close
Ctrl+X Cut
Ctrl+Y Redo
Ctrl+Z Undo
Down Next History Command
End Cursor End Line
Entf Delete
ESC Clear Text at Prompt
F1 Help on Selection
F9 Evaluate Selection
Left Cursor Backward
Page down Page Down
Page up Page Up
Pos 1 Cursor Begin Line
Return Break Line
Right Cursor Forward
Shift+Backspace Delete Previous Character
Shift+Down Selection Down
Shift+End Selection End Line
Shift+Entf Cut
Shift+F1 Function Browser
Shift+Insert Paste
Shift+Left Selection Backward
Shift+Page down Selection Page Down
Shift+Page up Selection Page Up
Shift+Pos 1 Selection Begin Line
Shift+Return Break Line Without Code Execution
Shift+Right Selection Forward
Shift+Up Selection Up
Tab Tab
Up Previous History Command
MATLAB Editor
Alt+Backspace Undo
Alt+Down Go To Next Underline or Highlight
Alt+Return Autofix Message
Alt+Shift+Backspace Redo
Alt+Up Go To Previous Underline or Highlight
Backspace Delete Previous Character
Ctrl+. Fold Code
Ctrl+[ Decrease Indent
Ctrl+] Increase Indent
Ctrl+= Fold All
Ctrl+A Select All
Ctrl+Backspace Remove Previous Word
Ctrl+C Copy
Ctrl+D Open Selection
Ctrl+Down Next Section
Ctrl+Down Scroll Down
Ctrl+End Cursor End Document
Ctrl+Entf Remove Next Word
Ctrl+F Find...
Ctrl+F1 Function Hints
Ctrl+F2 Set/Clear Bookmark
Ctrl+F3 Find Selection
Ctrl+G Go To...
Ctrl+H Find...
Ctrl+I Smart Indent
Ctrl+Insert Copy
Ctrl+J Wrap Comments
Ctrl+Left Cursor Previous Word
Ctrl+M Open Message or Expand Details
Ctrl+N New Script
Ctrl+NumPad - Decrement Value Near Cursor and Evaluate Section
Ctrl+NumPad * Multiply Value Near Cursor and Evaluate Section
Ctrl+NumPad / Divide Value Near Cursor and Evaluate Section
Ctrl+NumPad + Increment Value Near Cursor and Evaluate Section
Ctrl+O Open...
Ctrl+P Print...
Ctrl+Pos 1 Cursor Begin Document
Ctrl+Q Exit MATLAB
Ctrl+R Comment
Ctrl+Return Evaluate Current Section
Ctrl+Right Cursor Next Word
Ctrl+S Save
Ctrl+Shift+. Expand Folded Code
Ctrl+Shift+= Expand All
Ctrl+Shift+End Selection End Document
Ctrl+Shift+F Find Files...
Ctrl+Shift+F3 Find Previous Selection
Ctrl+Shift+H Highlight all occurrences of selected variable
Ctrl+Shift+Left Selection Previous Word
Ctrl+Shift+NumPad - Decrease Increment Amount
Ctrl+Shift+NumPad * Increase Multiplication Factor
Ctrl+Shift+NumPad / Decrease Multiplication Factor
Ctrl+Shift+NumPad + Increase Increment Amount
Ctrl+Shift+Pos 1 Selection Begin Document
Ctrl+Shift+R Start Incremental Search Backwards
Ctrl+Shift+Return Evaluate Current Section and Advance
Ctrl+Shift+Right Selection Next Word
Ctrl+Shift+S Start Incremental Search Forwards
Ctrl+Shift+Z Redo
Ctrl+T Uncomment
Ctrl+Up Previous Section
Ctrl+Up Scroll Up
Ctrl+V Paste
Ctrl+W Close
Ctrl+X Cut
Ctrl+Y Redo
Ctrl+Z Undo
Down Cursor Down
End Cursor End Line
Entf Delete
ESC Clear Selection
F1 Help on Selection
F12 Set/Clear Breakpoint
F2 Next Bookmark
F3 Find Next
F5 Run or Continue Execution
F6 Switch Active File in Split Screen
F9 Evaluate Selection
Left Cursor Backward
Page down Page Down
Page up Page Up
Pos 1 Cursor Begin Line
Return Break Line
Right Cursor Forward
Shift+Backspace Delete Previous Character
Shift+Down Selection Down
Shift+End Selection End Line
Shift+Entf Cut
Shift+F1 Function Browser
Shift+F2 Previous Bookmark
Shift+F3 Find Previous
Shift+Insert Paste
Shift+Left Selection Backward
Shift+Page down Selection Page Down
Shift+Page up Selection Page Up
Shift+Pos 1 Selection Begin Line
Shift+Return Break Line Without Indenting
Shift+Right Selection Forward
Shift+Tab Shift Tab
Shift+Up Selection Up
Tab Tab
Up Cursor Up
Coder Editor
Ctrl+F Find...
Ctrl+H Find...
Ctrl+Shift+F Find Files...
F3 Find Next
Shift+F3 Find Previous
Command History
Alt+Backspace Undo
Ctrl+A Select All
Ctrl+C Copy
Ctrl+F Find...
Ctrl+H Find...
Ctrl+Insert Copy
Ctrl+P Print...
Ctrl+Shift+A Match Anywhere
Ctrl+Shift+B Match Beginning
Ctrl+Shift+C Match Case
Ctrl+Shift+L Filter Matches
Ctrl+X Cut
Ctrl+Z Undo
Entf Delete
F9 Evaluate Selection
Return Evaluate Selection
Shift+Entf Cut
Current Folder
Backspace Up One Level
Ctrl+A Select All
Ctrl+C Copy
Ctrl+D Open Selection
Ctrl+F Find Files...
Ctrl+Insert Copy
Ctrl+Shift+End Selection End Document
Ctrl+Shift+F Find Files...
Ctrl+Shift+Pos 1 Selection Begin Document
Ctrl+V Paste
Ctrl+X Cut
End Cursor End Document
Entf Delete
F1 Help on Selection
F2 Rename Selection
F5 Refresh
F9 Evaluate Selection
Left Collapse or Select
NumPad - Collapse
NumPad + Expand
Pos 1 Cursor Begin Document
Return Extract Archive
Return Open Selection
Right Expand or Select
Shift+End Selection End Document
Shift+Insert Paste
Shift+Pos 1 Selection Begin Document
Deployment Tool
Ctrl+C Copy
Ctrl+D Open Selection
Ctrl+Insert Copy
Ctrl+Shift+End Selection End Document
Ctrl+Shift+Pos 1 Selection Begin Document
Ctrl+V Paste
Ctrl+X Cut
End Cursor End Document
Entf Delete
F2 Rename Selection
F5 Refresh
Pos 1 Cursor Begin Document
Return Open Selection
Shift+End Selection End Document
Shift+Insert Paste
Shift+Pos 1 Selection Begin Document
Find Dialog Box
Ctrl+A Select All
Ctrl+C Copy
Ctrl+Insert Copy
Ctrl+V Paste
Ctrl+X Cut
F3 Find Next
Shift+Entf Cut
Shift+F3 Find Previous
Shift+Insert Paste
Help and HTML Viewers
Alt+Left Back
Alt+Pos 1 Home
Alt+Right Forward
Ctrl+= Zoom In
Ctrl+C Copy
Ctrl+D Open Selection
Ctrl+F Find...
Ctrl+H Find...
Ctrl+Insert Copy
Ctrl+Minus Zoom Out
Ctrl+N New Browser Window
Ctrl+NumPad - Zoom Out
Ctrl+NumPad + Zoom In
Ctrl+P Print...
Ctrl+Plus Zoom In
Ctrl+Shift+= Zoom In
Ctrl+T New Browser Tab
F1 Help on Selection
F5 Refresh
F9 Evaluate Selection
Help Browser
Ctrl+= Collapse All
Ctrl+Shift+= Expand All
Ctrl+Shift+T Reopen Last Closed Help Browser
ESC Clear Search
MATLAB Comparison Tool
Ctrl+Down Go To Next Change
Ctrl+N Start New Comparison
Ctrl+Up Go To Previous Change
Coder App
Ctrl+Shift+Tab Go to the previous workflow step
Ctrl+Tab Go to the next workflow step
MATLAB Function Block Editor
Ctrl+B Simulation Build
Ctrl+D Update Diagram
Ctrl+F Find...
Ctrl+G Go To...
Ctrl+H Find...
Ctrl+N New Simulink Model
Ctrl+O Open...
Ctrl+P Print...
Ctrl+Q Exit MATLAB
Ctrl+S Save
Ctrl+Shift+A Update Ports
Ctrl+W Close
F10 Step
F11 Step In
F12 Set/Clear Breakpoint
F5 Run or Continue Execution
F5 Start Simulation
F9 Evaluate Selection
Shift+F11 Step Out
Shift+F5 Exit Debug Mode
Shift+F5 Stop Simulation
Workspace Browser
Ctrl+P Print...
Ctrl+S Save Workspace...
Ctrl+V Paste to Workspace...
Shift+Insert Paste to Workspace...
Workspace Browser Table
Ctrl+A Select All
Ctrl+C Copy
Ctrl+D Open Selection
Ctrl+Insert Copy
Entf Delete
F5 Refresh
Variable Editor
Ctrl+A Select All
Ctrl+D Open Selection
Ctrl+P Print...
Ctrl+S Save Workspace...
Variable Editor Table
Alt+Backspace Undo
Alt+Shift+Backspace Redo
Ctrl+A Select All
Ctrl+C Copy
Ctrl+Insert Copy
Ctrl+Minus Delete Selected Rows/Columns
Ctrl+Plus Insert Rows/Columns
Ctrl+Shift+= Insert Rows/Columns
Ctrl+Shift+Leertaste Select All
Ctrl+Shift+V Paste Excel Data
Ctrl+Shift+Z Redo
Ctrl+V Paste
Ctrl+X Cut
Ctrl+Y Redo
Ctrl+Z Undo
End Stop Editing Spreadsheet
Entf Delete
Shift+Entf Cut
Shift+Insert Paste
Shift+Entf Cut
Shift+Insert Paste
Minimal Working Example in LaTeX
A Minimal Working Example (MWE) for a LaTeX document:
\documentclass{article}
\begin{document}
Hello World.
\end{document}
I use this template as the basis for my questions on tex.stackexchange.com.
Small rant: Ironically, the most prevalent sites about LaTeX MWEs either don't provide MWEs, or they are so bloated that they only get around to it in Chapter 4...To be fair, the sites I linked to do contain lot's of helpful tricks on how to keep MWEs minimal when the circumstances are more complex (questions about the bibliography, about figures, ...).
Matlab: Asynchronous Program Flow
Matlab scripts and functions are designed to run top-to-bottom, in a synchronous fashion. However, sometimes you need to run a piece of code "a little later", or in the background.
For such occasions, Yair Altman posted a trick on Undocumented Matlab. He uses a timer to call another function asynchronously. Simplifying his example, and using an anonymous function for the callback, this can be written as
timer_cb = @(obj, e, str) disp(str);
disp('first')
t = timer('StartDelay',0.1, 'TimerFcn',{timer_cb, 'second?'});
start(t)
disp('third?')
which outputs
first
third?
second?
Birthday cake
One of my first memories is my grandfather's 60th birthday. My parents prepared a birthday cake for him, but instead of putting 60 candles on it, they baked a 60 Watt lightbulb into the cake, with lightswitch and everything. As a kid I was fascinated with everything that involved light, and I was heavily impressed by this cake.
So naturally, for my father's 60th birthday I wanted to make a cake with 60 LEDs on it. I bought a 5m RGB LED strip, with 30 LEDs per meter, and each LED addressable individually. The LED type was WS2812B. I bought it from China and payed 40€ for the 5m strip (now they seem to sell for 25€ for 5m, including shipping from Germany!). I also bought cheap Arduino nano clones from China for less than 3€/piece to drive it. I wanted to use battery power for the thing, so I bought a LiPo battery (10€), a USB LiPo charger (2€ ?), a 3.7V -> 5V upconverter (2€ again?), and then realized that I am essentially building a USB power-bank. So I got a cheap (7€) USB power bank from Amazon.
There were a few design iterations on how to get the LEDs on the cake. In the end, I settled for an envelope that goes around the cake. This was easiest to make, and can be "installed" on most cakes within seconds.
Unfortunately, I don't have any pictures of the build process. I bought a 100cm x 50cm sheet of aluminum (I don't recall the thickness) from the local hardware store and used tinner's snips to cut a 100cm x 20cm strip. The cut was suprisingly clean and only needed light sanding to be smooth. I bent the whole thing into a loop, overlapped the ends by a few cm and epoxied them together. I used JB auto-weld for the epoxy, which I had laying around, but most epoxies should do. I attached the LED strips to the outside using double-sided tape. My LED strip was of the IP67 waterproof variety, so it came in some clear plastic-like tubing. I tested a few glues, but most did not stick to the plastic well. Double sided tape worked best.
I wired up the Arduino and hid it in a small plastic project box. The library support for driving WS2812B LED strips is amazing, and I had to only slightly modify one of the examples to get the effect I wanted: A slowly rotating "color wheel" with the occasional (and only *slightly* cheesy) sparkle.
#include "FastLED.h"
FASTLED_USING_NAMESPACE
#define DATA_PIN 13
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS 58
CRGB leds[NUM_LEDS];
#define FRAMES_PER_SECOND 120
void setup() {
delay(1000);
FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
// FastLED.setBrightness(BRIGHTNESS);
}
uint8_t gHue = 0; // rotating "base color" used by many of the patterns
void loop()
{
// Call the current pattern function once, updating the 'leds' array
double_rainbow();
addGlitter(2);
FastLED.show();
FastLED.delay(1000/FRAMES_PER_SECOND);
// do some periodic updates
EVERY_N_MILLISECONDS( 100 ) { gHue++; } // slowly cycle the "base color" through the rainbow
}
void double_rainbow()
{
// FastLED's built-in rainbow generator, repurposed for DOUBLE RAINBOW ALL THE WAY
CHSV hsv;
float fl_hue = (float) gHue;
hsv.hue = (uint8_t) fl_hue;
hsv.val = 150;
hsv.sat = 255;
for( int i = 0; i < NUM_LEDS/2; i++) {
leds[i] = hsv;
leds[i+(NUM_LEDS/2)] = hsv;
fl_hue += 8.8;
hsv.hue = (uint8_t) fl_hue;
}
}
void addGlitter( fract8 chanceOfGlitter)
{
if( random8() < chanceOfGlitter) {
leds[ random16(NUM_LEDS) ] += CRGB::White;
}
}
Unfortunately, I don't have any pictures of the LED envelope on the actual cake. It looked nice! The only thing I would do differently is to make the cable between the envelope and the controller box a bit longer, so that you can put the controller box under the table, instead of right next to the envelope.