Merge branch 'master' into hoop-earrings-show-instead-of-write

This commit is contained in:
Kattni 2019-04-05 12:32:33 -04:00 committed by GitHub
commit dd9e457ff7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1495 changed files with 3677174 additions and 2990 deletions

3
.gitignore vendored
View file

@ -1,2 +1,5 @@
*~
Hue_Controller/secrets.h
.idea
CircuitPython_Logger/secrets\.py

433
.pylintrc Normal file
View file

@ -0,0 +1,433 @@
[MASTER]
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
# Add files or directories matching the regex patterns to the blacklist. The
# regex matches against base names, not paths.
ignore-patterns=
# Python code to execute, usually for sys.path manipulation such as
# pygtk.require().
#init-hook=
# Use multiple processes to speed up Pylint.
# jobs=1
jobs=2
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
# Pickle collected data for later comparisons.
persistent=yes
# Specify a configuration file.
#rcfile=
# Allow loading of arbitrary C extensions. Extensions are imported into the
# active Python interpreter and may run arbitrary code.
unsafe-load-any-extension=no
[MESSAGES CONTROL]
# Only show warnings with the listed confidence levels. Leave empty to show
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
confidence=
# Disable the message, report, category or checker with the given id(s). You
# can either give multiple identifiers separated by comma (,) or put this
# option multiple times (only on the command line, not in the configuration
# file where it should appear only once).You can also use "--disable=all" to
# disable everything first and then reenable specific checks. For example, if
# you want to run only the similarities checker, you can use "--disable=all
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
# disable=import-error,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call
disable=too-many-instance-attributes,len-as-condition,too-few-public-methods,anomalous-backslash-in-string,no-else-return,simplifiable-if-statement,too-many-arguments,duplicate-code,no-name-in-module,no-member,print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,import-error,missing-docstring,invalid-name,bad-whitespace,consider-using-enumerate
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=
[REPORTS]
# Python expression which should return a note less than 10 (10 is the highest
# note). You have access to the variables errors warning, statement which
# respectively contain the number of errors / warnings messages and the total
# number of statements analyzed. This is used by the global evaluation report
# (RP0004).
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
# Template used to display messages. This is a python new-style format string
# used to format the message information. See doc for all details
msg-template='{path} {line}: {msg} ({symbol})'
# Set the output format. Available formats are text, parseable, colorized, json
# and msvs (visual studio).You can also give a reporter class, eg
# mypackage.mymodule.MyReporterClass.
output-format=text
# Tells whether to display a full report or only the messages
reports=no
# Activate the evaluation score.
score=yes
[REFACTORING]
# Maximum number of nested blocks for function / method body
max-nested-blocks=5
[LOGGING]
# Logging modules to check that the string format arguments are in logging
# function parameter format
logging-modules=logging
[SPELLING]
# Spelling dictionary name. Available dictionaries: none. To make it working
# install python-enchant package.
spelling-dict=
# List of comma separated words that should not be checked.
spelling-ignore-words=
# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
# Tells whether to store unknown words to indicated private dictionary in
# --spelling-private-dict-file option instead of raising a message.
spelling-store-unknown-words=no
[MISCELLANEOUS]
# List of note tags to take in consideration, separated by a comma.
# notes=FIXME,XXX,TODO
notes=FIXME,XXX
[TYPECHECK]
# List of decorators that produce context managers, such as
# contextlib.contextmanager. Add to this list to register other decorators that
# produce valid context managers.
contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
ignore-mixin-members=yes
# This flag controls whether pylint should warn about no-member and similar
# checks whenever an opaque object is returned when inferring. The inference
# can return multiple potential results while evaluating a Python object, but
# some branches might not be evaluated, which results in partial inference. In
# that case, it might be useful to still emit no-member and other checks for
# the rest of the inferred objects.
ignore-on-opaque-inference=yes
# List of class names for which member attributes should not be checked (useful
# for classes with dynamically set attributes). This supports the use of
# qualified names.
ignored-classes=optparse.Values,thread._local,_thread._local
# List of module names for which member attributes should not be checked
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis. It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
missing-member-hint=yes
# The minimum edit distance a name should have in order to be considered a
# similar match for a missing member name.
missing-member-hint-distance=1
# The total number of similar names that should be taken in consideration when
# showing a hint for a missing member.
missing-member-max-choices=1
[VARIABLES]
# List of additional names supposed to be defined in builtins. Remember that
# you should avoid to define new builtins when possible.
additional-builtins=
# Tells whether unused global variables should be treated as a violation.
allow-global-unused-variables=yes
# List of strings which can identify a callback function by name. A callback
# name must start or end with one of those strings.
callbacks=cb_,_cb
# A regular expression matching the name of dummy variables (i.e. expectedly
# not used).
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Argument names that match this expression will be ignored. Default to name
# with leading underscore
ignored-argument-names=_.*|^ignored_|^unused_
# Tells whether we should check for unused import in __init__ files.
init-import=no
# List of qualified module names which can have objects that can redefine
# builtins.
redefining-builtins-modules=six.moves,future.builtins
[FORMAT]
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
# expected-line-ending-format=
expected-line-ending-format=LF
# Regexp for a line that is allowed to be longer than the limit.
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
# tab).
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=100
# Maximum number of lines in a module
max-module-lines=1000
# List of optional constructs for which whitespace checking is disabled. `dict-
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
# `empty-line` allows space-only lines.
no-space-check=trailing-comma,dict-separator
# Allow the body of a class to be on the same line as the declaration if body
# contains single statement.
single-line-class-stmt=no
# Allow the body of an if to be on the same line as the test if there is no
# else.
single-line-if-stmt=no
[SIMILARITIES]
# Ignore comments when computing similarities.
ignore-comments=yes
# Ignore docstrings when computing similarities.
ignore-docstrings=yes
# Ignore imports when computing similarities.
ignore-imports=no
# Minimum lines number of a similarity.
min-similarity-lines=4
[BASIC]
# Naming hint for argument names
argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct argument names
argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for attribute names
attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct attribute names
attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Bad variable names which should always be refused, separated by a comma
bad-names=foo,bar,baz,toto,tutu,tata
# Naming hint for class attribute names
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Regular expression matching correct class attribute names
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
# Naming hint for class names
# class-name-hint=[A-Z_][a-zA-Z0-9]+$
class-name-hint=[A-Z_][a-zA-Z0-9_]+$
# Regular expression matching correct class names
# class-rgx=[A-Z_][a-zA-Z0-9]+$
class-rgx=[A-Z_][a-zA-Z0-9_]+$
# Naming hint for constant names
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Regular expression matching correct constant names
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
# Minimum line length for functions/classes that require docstrings, shorter
# ones are exempt.
docstring-min-length=-1
# Naming hint for function names
function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct function names
function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Good variable names which should always be accepted, separated by a comma
# good-names=i,j,k,ex,Run,_
good-names=r,g,b,w,i,j,k,n,x,y,z,ex,ok,Run,_
# Include a hint for the correct naming format with invalid-name
include-naming-hint=no
# Naming hint for inline iteration names
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
# Regular expression matching correct inline iteration names
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
# Naming hint for method names
method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct method names
method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Naming hint for module names
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Regular expression matching correct module names
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
# Colon-delimited sets of names that determine each other's naming style when
# the name regexes allow several styles.
name-group=
# Regular expression which should only match function or class names that do
# not require a docstring.
no-docstring-rgx=^_
# List of decorators that produce properties, such as abc.abstractproperty. Add
# to this list to register other decorators that produce valid properties.
property-classes=abc.abstractproperty
# Naming hint for variable names
variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
# Regular expression matching correct variable names
variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$
[IMPORTS]
# Allow wildcard imports from modules that define __all__.
allow-wildcard-with-all=no
# Analyse import fallback blocks. This can be used to support both Python 2 and
# 3 compatible code, which means that the block might have code that exists
# only in one or another interpreter, leading to false positives when analysed.
analyse-fallback-blocks=no
# Deprecated modules which should not be used, separated by a comma
deprecated-modules=optparse,tkinter.tix
# Create a graph of external dependencies in the given file (report RP0402 must
# not be disabled)
ext-import-graph=
# Create a graph of every (i.e. internal and external) dependencies in the
# given file (report RP0402 must not be disabled)
import-graph=
# Create a graph of internal dependencies in the given file (report RP0402 must
# not be disabled)
int-import-graph=
# Force import order to recognize a module as part of the standard
# compatibility libraries.
known-standard-library=
# Force import order to recognize a module as part of a third party library.
known-third-party=enchant
[CLASSES]
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,__new__,setUp
# List of member names, which should be excluded from the protected access
# warning.
exclude-protected=_asdict,_fields,_replace,_source,_make
# List of valid names for the first argument in a class method.
valid-classmethod-first-arg=cls
# List of valid names for the first argument in a metaclass class method.
valid-metaclass-classmethod-first-arg=mcs
[DESIGN]
# Maximum number of arguments for function / method
max-args=5
# Maximum number of attributes for a class (see R0902).
# max-attributes=7
max-attributes=11
# Maximum number of boolean expressions in a if statement
max-bool-expr=5
# Maximum number of branch for function / method body
max-branches=12
# Maximum number of locals for function / method body
max-locals=15
# Maximum number of parents for a class (see R0901).
max-parents=7
# Maximum number of public methods for a class (see R0904).
max-public-methods=20
# Maximum number of return / yield for function / method body
max-returns=6
# Maximum number of statements in function / method body
max-statements=50
# Minimum number of public methods for a class (see R0903).
min-public-methods=1
[EXCEPTIONS]
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception

35
.travis.yml Normal file
View file

@ -0,0 +1,35 @@
dist: trusty
sudo: false
language: python
python:
- "3.6"
cache:
cache:
pip: true
directories:
- ~/arduino_ide
- ~/.arduino15/packages/
- ~/Arduino
env:
global:
- ARDUINO_IDE_VERSION="1.8.7"
- PRETTYNAME="Adafruit Learning System Guides"
- PLATFORM_CHECK_ONLY_ON_FILE=true
before_install:
- source <(curl -SLs https://raw.githubusercontent.com/adafruit/travis-ci-arduino/master/install.sh)
install:
- ./arduino_libinstall
- pip install --force-reinstall pylint==1.9.2
script:
- ./pylint_check
- build_main_platforms
- build_aux_platforms
- build_cplay_platforms
- build_m4_platforms
- build_io_platforms

View file

@ -0,0 +1,73 @@
/*******************************************************************
Bionic Eye sketch for Adafruit Trinket.
by Bill Earl
for Adafruit Industries
Required library is the Adafruit_SoftServo library
available at https://github.com/adafruit/Adafruit_SoftServo
The standard Arduino IDE servo library will not work with 8 bit
AVR microcontrollers like Trinket and Gemma due to differences
in available timer hardware and programming. We simply refresh
by piggy-backing on the timer0 millis() counter
Trinket: Bat+ Gnd Pin #0 Pin #1
Connection: Servo+ Servo- Tilt Rotate
(Red) (Brown) Servo Servo
(Orange)(Orange)
*******************************************************************/
#include <Adafruit_SoftServo.h> // SoftwareServo (works on non PWM pins)
#define TILTSERVOPIN 0 // Servo control line (orange) on Trinket Pin #0
#define ROTATESERVOPIN 1 // Servo control line (orange) on Trinket Pin #1
Adafruit_SoftServo TiltServo, RotateServo; //create TWO servo objects
void setup()
{
// Set up the interrupt that will refresh the servo for us automagically
OCR0A = 0xAF; // any number is OK
TIMSK |= _BV(OCIE0A); // Turn on the compare interrupt (below!)
TiltServo.attach(TILTSERVOPIN); // Attach the servo to pin 0 on Trinket
RotateServo.attach(ROTATESERVOPIN); // Attach the servo to pin 1 on Trinket
delay(15); // Wait 15ms for the servo to reach the position
}
void loop()
{
delay(100);
TiltServo.detach(); // release the servo
RotateServo.detach(); // release the servo
if(random(100) > 80) // on average, move once every 500ms
{
TiltServo.attach(TILTSERVOPIN); // Attach the servo to pin 0 on Trinket
TiltServo.write(random(120, 180)); // Tell servo to go to position
}
if(random(100) > 90) // on average, move once every 500ms
{
RotateServo.attach(ROTATESERVOPIN); // Attach the servo to pin 1 on Trinket
RotateServo.write(random(0, 180)); // Tell servo to go to position
}
}
// We'll take advantage of the built in millis() timer that goes off
// to keep track of time, and refresh the servo every 20 milliseconds
// The SIGNAL(TIMER0_COMPA_vect) function is the interrupt that will be
// Called by the microcontroller every 2 milliseconds
volatile uint8_t counter = 0;
SIGNAL(TIMER0_COMPA_vect)
{
// this gets called every 2 milliseconds
counter += 2;
// every 20 milliseconds, refresh the servos!
if (counter >= 20)
{
counter = 0;
TiltServo.refresh();
RotateServo.refresh();
}
}

View file

@ -0,0 +1,50 @@
# Bionic Eye sketch for Adafruit Trinket.
#
# written by Bill Earl for Arduino
# ported to CircuitPython by Mikey Sklar
# for Adafruit Industries
#
# Required library is the Adafruit_SoftServo library
# available at https://github.com/adafruit/Adafruit_SoftServo
# The standard Arduino IDE servo library will not work with 8 bit
# AVR microcontrollers like Trinket and Gemma due to differences
# in available timer hardware and programming. We simply refresh
# by piggy-backing on the timer0 millis() counter
#
# Trinket: Bat+ Gnd Pin #0 Pin #2
# Connection: Servo+ Servo- Tilt Rotate
# (Red) (Black) Servo Servo
# (Orange)(Orange)
import time
import random
import board
import pulseio
from adafruit_motor import servo
# we are intentionally avoiding Trinket Pin #1 (board.A0)
# as it does not have PWM capability
tilt_servo_pin = board.A2 # servo control line (orange) Trinket Pin #0
rotate_servo_pin = board.A1 # servo control line (orange) Trinket Pin #2
# servo object setup for the M0 boards:
tilt_pwm = pulseio.PWMOut(tilt_servo_pin, duty_cycle=2 ** 15, frequency=50)
rotate_pwm = pulseio.PWMOut(rotate_servo_pin, duty_cycle=2 ** 15, frequency=50)
tilt_servo = servo.Servo(tilt_pwm)
rotate_servo = servo.Servo(rotate_pwm)
# servo timing and angle range
tilt_min = 120 # lower limit to tilt rotation range
max_rotate = 180 # rotation range limited to half circle
while True:
# servo tilt - on average move every 500ms
if random.randint(0,100) > 80:
tilt_servo.angle = random.randint(tilt_min, max_rotate)
time.sleep(.25)
# servo rotate - on average move every 500ms
if random.randint(0,100) > 90:
rotate_servo.angle = random.randint(0, max_rotate)
time.sleep(.25)

View file

@ -0,0 +1,4 @@
# 3D_Printed_Bionic_Eye
Code to accompany this Adafruit tutorial:
https://learn.adafruit.com/3d-printed-bionic-eye

View file

@ -0,0 +1,128 @@
// Fiery demon horns (rawr!) for Adafruit Trinket/Gemma.
// Adafruit invests time and resources providing this open source code,
// please support Adafruit and open-source hardware by purchasing
// products from Adafruit!
#include <Adafruit_NeoPixel.h>
#include <avr/power.h>
#define N_HORNS 1
#define N_LEDS 30 // Per horn
#define PIN 0
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(N_HORNS * N_LEDS, PIN);
// /\ -> Fire-like effect is the sum of multiple triangle
// ____/ \____ waves in motion, with a 'warm' color map applied.
#define N_WAVES 6 // Number of simultaneous waves (per horn)
// Coordinate space for waves is 16x the pixel spacing,
// allowing fixed-point math to be used instead of floats.
struct {
int16_t lower; // Lower bound of wave
int16_t upper; // Upper bound of wave
int16_t mid; // Midpoint (peak) ((lower+upper)/2)
uint8_t vlower; // Velocity of lower bound
uint8_t vupper; // Velocity of upper bound
uint16_t intensity; // Brightness at peak
} wave[N_HORNS][N_WAVES];
long fade; // Decreases brightness as wave moves
// Gamma correction improves appearance of midrange colors
uint8_t gamma[] PROGMEM = {
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, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
static void random_wave(uint8_t h,uint8_t w) { // Randomize one wave struct
wave[h][w].upper = -1; // Always start just below head of strip
wave[h][w].lower = -16 * (3 + random(4)); // Lower end starts ~3-7 pixels back
wave[h][w].mid = (wave[h][w].lower + wave[h][w].upper) / 2;
wave[h][w].vlower = 3 + random(4); // Lower end moves at ~1/8 to 1/4 pixel/frame
wave[h][w].vupper = wave[h][w].vlower + random(4); // Upper end moves a bit faster, spreading wave
wave[h][w].intensity = 300 + random(600);
}
void setup() {
uint8_t h, w;
randomSeed(analogRead(1));
pixels.begin();
for(h=0; h<N_HORNS; h++) {
for(w=0; w<N_WAVES; w++) random_wave(h, w);
}
fade = 234 + N_LEDS / 2;
if(fade > 255) fade = 255;
// A ~100 Hz timer interrupt on Timer/Counter1 makes everything run
// at regular intervals, regardless of current amount of motion.
#if F_CPU == 16000000L
clock_prescale_set(clock_div_1);
TCCR1 = _BV(PWM1A) | _BV(CS13) | _BV(CS11) | _BV(CS10); // 1:1024 prescale
OCR1C = F_CPU / 1024 / 100 - 1;
#else
TCCR1 = _BV(PWM1A) | _BV(CS13) | _BV(CS11); // 1:512 prescale
OCR1C = F_CPU / 512 / 100 - 1;
#endif
GTCCR = 0; // No PWM out
TIMSK |= _BV(TOIE1); // Enable overflow interrupt
}
void loop() { } // Not used -- everything's in interrupt below
ISR(TIMER1_OVF_vect) {
uint8_t h, w, i, r, g, b;
int16_t x;
uint16_t sum;
for(h=0; h<N_HORNS; h++) { // For each horn...
for(x=7, i=0; i<N_LEDS; i++, x+=16) { // For each LED along horn...
for(sum=w=0; w<N_WAVES; w++) { // For each wave of horn...
if((x < wave[h][w].lower) || (x > wave[h][w].upper)) continue; // Out of range
if(x <= wave[h][w].mid) { // Lower half of wave (ramping up to peak brightness)
sum += wave[h][w].intensity * (x - wave[h][w].lower) / (wave[h][w].mid - wave[h][w].lower);
} else { // Upper half of wave (ramping down from peak)
sum += wave[h][w].intensity * (wave[h][w].upper - x) / (wave[h][w].upper - wave[h][w].mid);
}
}
// Now the magnitude (sum) is remapped to color for the LEDs.
// A blackbody palette is used - fades white-yellow-red-black.
if(sum < 255) { // 0-254 = black to red-1
r = pgm_read_byte(&gamma[sum]);
g = b = 0;
} else if(sum < 510) { // 255-509 = red to yellow-1
r = 255;
g = pgm_read_byte(&gamma[sum - 255]);
b = 0;
} else if(sum < 765) { // 510-764 = yellow to white-1
r = g = 255;
b = pgm_read_byte(&gamma[sum - 510]);
} else { // 765+ = white
r = g = b = 255;
}
pixels.setPixelColor(h * N_LEDS + i, r, g, b);
}
for(w=0; w<N_WAVES; w++) { // Update wave positions for each horn
wave[h][w].lower += wave[h][w].vlower; // Advance lower position
if(wave[h][w].lower >= (N_LEDS * 16)) { // Off end of strip?
random_wave(h, w); // Yes, 'reboot' wave
} else { // No, adjust other values...
wave[h][w].upper += wave[h][w].vupper;
wave[h][w].mid = (wave[h][w].lower + wave[h][w].upper) / 2;
wave[h][w].intensity = (wave[h][w].intensity * fade) / 256; // Dimmer
}
}
}
pixels.show();
}

View file

@ -0,0 +1,147 @@
# Fiery demon horns (rawr!) for Adafruit Trinket/Gemma.
# Adafruit invests time and resources providing this open source code,
# please support Adafruit and open-source hardware by purchasing
# products from Adafruit!
import board
import neopixel
from analogio import AnalogIn
# pylint: disable=global-statement
try:
import urandom as random
except ImportError:
import random
# /\ -> Fire-like effect is the sum_total of multiple triangle
# ____/ \____ waves in motion, with a 'warm' color map applied.
n_horns = 1 # number of horns
led_pin = board.D0 # which pin your pixels are connected to
n_leds = 30 # number of LEDs per horn
frames_per_second = 50 # animation frames per second
brightness = 0 # current wave height
fade = 0 # Decreases brightness as wave moves
pixels = neopixel.NeoPixel(led_pin, n_leds, brightness=1, auto_write=False)
offset = 0
# Coordinate space for waves is 16x the pixel spacing,
# allowing fixed-point math to be used instead of floats.
lower = 0 # lower bound of wave
upper = 1 # upper bound of wave
mid = 2 # midpoint (peak) ((lower+upper)/2)
vlower = 3 # velocity of lower bound
vupper = 4 # velocity of upper bound
intensity = 5 # brightness at peak
y = 0
brightness = 0
count = 0
# initialize 3D list
wave = [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6
# Number of simultaneous waves (per horn)
n_waves = len(wave)
# Gamma-correction table
gamma = [
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, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110,
112, 114, 115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133,
135, 137, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158,
160, 162, 164, 167, 169, 171, 173, 175, 177, 180, 182, 184, 186,
189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213, 215, 218,
220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252,
255
]
def random_wave(he, wi):
wave[he][wi][upper] = -1 # Always start below head of strip
wave[he][wi][lower] = -16 * (3 + random.randint(0,4)) # Lower end starts ~3-7 pixels back
wave[he][wi][mid] = (wave[he][wi][lower]+ wave[he][wi][upper]) / 2
wave[he][wi][vlower] = 3 + random.randint(0,4) # Lower end moves at ~1/8 to 1/pixels
wave[he][wi][vupper] = wave[he][wi][vlower]+ random.randint(0,4) # Upper end moves a bit faster
wave[he][wi][intensity] = 300 + random.randint(0,600)
def setup():
global fade
# Random number generator is seeded from an unused 'floating'
# analog input - this helps ensure the random color choices
# aren't always the same order.
pin = AnalogIn(board.A0)
random.seed(pin.value)
pin.deinit()
for he in range(n_horns):
for wi in range(n_waves):
random_wave(he, wi)
fade = 233 + n_leds / 2
if fade > 233:
fade = 233
setup()
while True:
h = w = i = r = g = b = 0
x = 0
for h in range(n_horns): # For each horn...
x = 7
sum_total = 0
for i in range(n_leds): # For each LED along horn...
x += 16
for w in range(n_waves): # For each wave of horn...
if (x < wave[h][w][lower]) or (x > wave[h][w][upper]):
continue # Out of range
if x <= wave[h][w][mid]: # Lower half of wave (ramping up peak brightness)
sum_top = wave[h][w][intensity] * (x - wave[h][w][lower])
sum_bottom = (wave[h][w][mid] - wave[h][w][lower])
sum_total += sum_top / sum_bottom
else: # Upper half of wave (ramping down from peak)
sum_top = wave[h][w][intensity] * (wave[h][w][upper] - x)
sum_bottom = (wave[h][w][upper] - wave[h][w][mid])
sum_total += sum_top / sum_bottom
sum_total = int(sum_total) # convert from decimal to whole number
# Now the magnitude (sum_total) is remapped to color for the LEDs.
# A blackbody palette is used - fades white-yellow-red-black.
if sum_total < 255: # 0-254 = black to red-1
r = gamma[sum_total]
g = b = 0
elif sum_total < 510: # 255-509 = red to yellow-1
r = 255
g = gamma[sum_total - 255]
b = 0
elif sum_total < 765: # 510-764 = yellow to white-1
r = g = 255
b = gamma[sum_total - 510]
else: # 765+ = white
r = g = b = 255
pixels[i] = (r, g, b)
for w in range(n_waves): # Update wave positions for each horn
wave[h][w][lower] += wave[h][w][vlower] # Advance lower position
if wave[h][w][lower] >= (n_leds * 16): # Off end of strip?
random_wave(h, w) # Yes, 'reboot' wave
else: # No, adjust other values...
wave[h][w][upper] += wave[h][w][vupper]
wave[h][w][mid] = (wave[h][w][lower] + wave[h][w][upper]) / 2
wave[h][w][intensity] = (wave[h][w][intensity] * fade) / 256 # Dimmer
pixels.show()

View file

@ -0,0 +1,157 @@
// Adafruit Trinket+NeoPixel animation for Daft Punk-inspired helmet.
// Contains some ATtiny85-specific stuff; won't run as-is on Uno, etc.
// Operates in HSV (hue, saturation, value) colorspace rather than RGB.
// Animation is an interference pattern between two waves; one controls
// saturation, the other controls value (brightness). The wavelength,
// direction, speed and type (square vs triangle wave) for each is randomly
// selected every few seconds. Hue is always linear, but other parameters
// are similarly randomized.
#include <Adafruit_NeoPixel.h>
#include <avr/power.h>
// GLOBAL STUFF --------------------------------------------------------------
#define N_LEDS 29
#define PIN 0
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(N_LEDS, PIN);
volatile uint16_t count = 1; // Countdown to next animation change
extern const uint8_t gamma[]; // Big table at end of this code
volatile struct {
uint8_t type, // 0 = square wave, 1 = triangle wave
value[2]; // 0 = start-of-frame value, 1 = pixel-to-pixel value
int8_t inc[2]; // 0 = frame-to-frame increment, 1 = pixel-to-pixel inc
} wave[3]; // 0 = Hue, 1 = Saturation, 2 = Value (brightness)
#define WAVE_H 0 // Array indices for wave[]
#define WAVE_S 1
#define WAVE_V 2
#define FRAME 0 // Array indices for value[] and inc[]
#define PIXEL 1
// INITIALIZATION ------------------------------------------------------------
void setup() {
pixels.begin();
randomSeed(analogRead(0)); // Seed random() from a floating pin (D2)
// Timer/Counter 1 is used to generate a steady ~50 Hz frame rate.
#if F_CPU == 16000000L
clock_prescale_set(clock_div_1);
TCCR1 = _BV(PWM1A) | _BV(CS13) | _BV(CS12); // 1:2048 prescale
OCR1C = F_CPU / 2048 / 50 - 1;
#else
TCCR1 = _BV(PWM1A) | _BV(CS13) | _BV(CS11) | _BV(CS10); // 1:1024
OCR1C = F_CPU / 1024 / 50 - 1;
#endif
GTCCR = 0; // No PWM out
TIMSK |= _BV(TOIE1); // Enable overflow interrupt
}
void loop() { } // Not used here -- everything's in interrupt below
// 50 HZ LOOP ----------------------------------------------------------------
ISR(TIMER1_OVF_vect) {
uint8_t w, i, n, s, v, r, g, b;
uint16_t v1, s1;
if(!(--count)) { // Time for new animation?
count = 250 + random(250); // New effect will run for 5-10 sec.
for(w=0; w<3; w++) { // Three waves (H,S,V)...
wave[w].type = random(2); // Assign random type (square/triangle)
for(i=0; i<2; i++) { // For frame and pixel increments...
while(!(wave[w].inc[i] = random(15) - 7)); // Set non-zero random
// wave value is never initialized; it's allowed to carry over
}
wave[w].value[PIXEL] = wave[w].value[FRAME];
}
wave[WAVE_S].inc[PIXEL] *= 16; // Make saturation & value
wave[WAVE_V].inc[PIXEL] *= 16; // blinkier along strip
} else { // Continue current animation; update waves
for(w=0; w<3; w++) {
wave[w].value[FRAME] += wave[w].inc[FRAME]; // OK if this wraps!
wave[w].value[PIXEL] = wave[w].value[FRAME];
}
}
// Render current animation frame. COGNITIVE HAZARD: fixed point math.
for(i=0; i<N_LEDS; i++) { // For each LED along strip...
// Coarse (8-bit) HSV-to-RGB conversion, hue first:
n = (wave[WAVE_H].value[PIXEL] % 43) * 6; // Angle within sextant; 0-255
switch(wave[WAVE_H].value[PIXEL] / 43) { // Sextant number; 0-5
case 0 : r = 255 ; g = n ; b = 0 ; break; // R to Y
case 1 : r = 254 - n; g = 255 ; b = 0 ; break; // Y to G
case 2 : r = 0 ; g = 255 ; b = n ; break; // G to C
case 3 : r = 0 ; g = 254 - n; b = 255 ; break; // C to B
case 4 : r = n ; g = 0 ; b = 255 ; break; // B to M
default: r = 255 ; g = 0 ; b = 254 - n; break; // M to R
}
// Saturation = 1-256 to allow >>8 instead of /255
s = wave[WAVE_S].value[PIXEL];
if(wave[WAVE_S].type) { // Triangle wave?
if(s & 0x80) { // Downslope
s = (s & 0x7F) << 1;
s1 = 256 - s;
} else { // Upslope
s <<= 1;
s1 = 1 + s;
s = 255 - s;
}
} else { // Square wave
if(s & 0x80) { // 100% saturation
s1 = 256;
s = 0;
} else { // 0% saturation (white)
s1 = 1;
s = 255;
}
}
// Value (brightness) = 1-256 for similar reasons
v = wave[WAVE_V].value[PIXEL];
v1 = (wave[WAVE_V].type) ? // Triangle wave?
((v & 0x80) ? 64 - ((v & 0x7F) << 1) : // Downslope
1 + ( v << 1) ) : // Upslope
((v & 0x80) ? 256 : 1); // Square wave; on/off
pixels.setPixelColor(i,
pgm_read_byte(&gamma[((((r * s1) >> 8) + s) * v1) >> 8]),
pgm_read_byte(&gamma[((((g * s1) >> 8) + s) * v1) >> 8]),
pgm_read_byte(&gamma[((((b * s1) >> 8) + s) * v1) >> 8]));
// Update wave values along length of strip (values may wrap, is OK!)
for(w=0; w<3; w++) wave[w].value[PIXEL] += wave[w].inc[PIXEL];
}
pixels.show();
}
// Gamma correction improves appearance of midrange colors.
// This table is positioned down here because it's a big annoying
// distraction. The 'extern' near the top lets us reference it earlier.
const uint8_t gamma[] PROGMEM = {
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, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };

View file

@ -0,0 +1,189 @@
# Adafruit Trinket+NeoPixel animation for Daft Punk-inspired helmet.
# Contains some ATtiny85-specific stuff; won't run as-is on Uno, etc.
# Operates in HSV (hue, saturation, value) colorspace rather than RGB.
# Animation is an interference pattern between two waves; one controls
# saturation, the other controls value (brightness). The wavelength,
# direction, speed and type (square vs triangle wave) for each is randomly
# selected every few seconds. Hue is always linear, but other parameters
# are similarly randomized.
import random
import board
import neopixel
from analogio import AnalogIn
n_leds = 29 # number of LEDs per horn
led_pin = board.D0 # which pin your pixels are connected to
# initialize neopixel strip
pixels = neopixel.NeoPixel(led_pin, n_leds, brightness=1, auto_write=False)
count = 1 # countdown to next animation change
# Gamma-correction table
gamma = [
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, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110,
112, 114, 115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133,
135, 137, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158,
160, 162, 164, 167, 169, 171, 173, 175, 177, 180, 182, 184, 186,
189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213, 215, 218,
220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252,
255
]
# initialize 3D list
wave = [0] * 5, [0] * 5, [0] * 5
wave_type = 0 # 0 = square wave, 1 = triangle wave
value_frame = 1 # start-of-frame value
value_pixel = 2 # pixel-to-pixel value
inc_frame = 3 # frame-to-frame increment
inc_pixel = 4 # pixel-to-pixel inc
wave_h = 0 # hue
wave_s = 1 # saturation
wave_v = 2 # brightness
# Random number generator is seeded from an unused 'floating'
# analog input - this helps ensure the random color choices
# aren't always the same order.
pin = AnalogIn(board.A0)
random.seed(pin.value)
pin.deinit()
# generate a non-zero random number for frame and pixel increments
def nz_random():
random_number = 0
while random_number <= 0:
random_number = random.randint(0,15) - 7
return random_number
while True:
w = i = n = s = v = r = g = b = v1 = s1 = 0
if count <= 0: # time for new animation
count = 250 + random.randint(0,250) # effect run for 5-10 sec.
for w in range(3): # three waves (H,S,V)
wave[w][wave_type] = random.randint(0,2)# square vs triangle
wave[w][inc_frame] = nz_random() # frame increment
wave[w][inc_pixel] = nz_random() # pixel increment
wave[w][value_pixel] = wave[w][value_frame]
wave[wave_s][inc_pixel] *= 16 # make saturation & value
wave[wave_v][inc_pixel] *= 16 # blinkier along strip
else: # continue animation
count -= 1
for w in range(3):
wave[w][value_frame] += wave[w][inc_frame]
wave[w][value_pixel] = wave[w][value_frame]
# Render current animation frame. COGNITIVE HAZARD: fixed point math.
for i in range(n_leds): # for each LED along strip...
# Coarse (8-bit) HSV-to-RGB conversion, hue first:
n = (wave[wave_h][value_pixel] % 43) * 6 # angle within sextant
sextant = wave[wave_h][value_pixel] / 43 # sextant number 0-5
# R to Y
if sextant == 0:
r = 255
g = n
b = 0
# Y to G
elif sextant == 1:
r = 254 - n
g = 255
b = 0
# G to C
elif sextant == 2:
r = 0
g = 255
b = n
# C to B
elif sextant == 3:
r = 0
g = 254 - n
b = 255
# B to M
elif sextant == 4:
r = n
g = 0
b = 255
# M to R
else:
r = 255
g = 0
b = 254 - n
# Saturation = 1-256 to allow >>8 instead of /255
s = wave[wave_s][value_pixel]
if wave[wave_s][wave_type]: # triangle wave?
if s & 0x80: # downslope
s = (s & 0x7F) << 1
s1 = 256 - s
else: # upslope
s = s<<1
s1 = 1 + s
s = 255 - s
else:
if s & 0x80: # square wave
s1 = 256 # 100% saturation
s = 0
else: # 0% saturation
s1 = 1
s = 255
# Value (brightness) = 1-256 for similar reasons
v = wave[wave_v][value_pixel]
# value (brightness) = 1-256 for similar reasons
if wave[wave_v][wave_type]: # triangle wave?
if v & 0x80: # downslope
v1 = 64 - ((v & 0x7F) << 1)
else: # upslope
v1 = 1 + (v << 1)
else:
if v & 0x80: # square wave; on/off
v1 = 256
else:
v1 = 1
# gamma rgb values
gr = ((((r * s1) >> 8) + s) * v1) >> 8
gg = ((((g * s1) >> 8) + s) * v1) >> 8
gb = ((((b * s1) >> 8) + s) * v1) >> 8
# gamma rgb indices range check
if -256 < gr < 256:
r = gamma[gr]
if -256 < gg < 256:
g = gamma[gg]
if -256 < gb < 256:
b = gamma[gb]
pixels[i] = (r, g, b)
# update wave values along length of strip (values may wrap, is OK!)
for w in range(3):
wave[w][value_pixel] += wave[w][inc_pixel]
pixels.show()

View file

@ -0,0 +1,4 @@
# 3D Printed Daft Punk Helmet
Code to accompany this tutorial:
https://learn.adafruit.com/3d-printed-daft-punk-helmet

View file

@ -1,49 +1,50 @@
# 3D_Printed_Guardian_Sword
# https://learn.adafruit.com/breath-of-the-wild-guardian-sword-led-3d-printed
import board
import neopixel
import time
pin = board.D4 # DIGITAL IO pin for NeoPixel OUTPUT from GEMMA
pixel_count = 93 # number of neopixels
delayval = .01 # 10 ms delay
import board
import neopixel
APIXELS = 14 # number of first orange pixels
BPIXELS = 84 # number of blue pixels
CPIXELS = 93 # second orange pixels
pin = board.D4 # DIGITAL IO pin for NeoPixel OUTPUT from GEMMA
pixel_count = 93 # number of neopixels
delayval = .01 # 10 ms delay
APIXELS = 14 # number of first orange pixels
BPIXELS = 84 # number of blue pixels
CPIXELS = 93 # second orange pixels
# initialize neopixels
pixels = neopixel.NeoPixel(pin, pixel_count, brightness=1, auto_write=False)
while True:
# For the first 14 pixels, make them orange,
# For the first 14 pixels, make them orange,
# starting from pixel number 0.
for i in range( 0, APIXELS ):
# Set Pixels to Orange Color
pixels[i] = ( 255, 50, 0 )
for i in range(0, APIXELS):
# Set Pixels to Orange Color
pixels[i] = (255, 50, 0)
# This sends the updated pixel color to the hardware.
pixels.write()
# Delay for a period of time (in milliseconds).
time.sleep(delayval)
pixels.write()
# Delay for a period of time (in milliseconds).
time.sleep(delayval)
# Fill up 84 pixels with blue,
# Fill up 84 pixels with blue,
# starting with pixel number 14.
for i in range ( 14, BPIXELS ):
# Set Pixels to Orange Color
pixels[i] = ( 0, 250, 200 )
for i in range(APIXELS, BPIXELS):
# Set Pixels to Orange Color
pixels[i] = (0, 250, 200)
# This sends the updated pixel color to the hardware.
pixels.write()
# Delay for a period of time (in milliseconds).
time.sleep(delayval)
pixels.write()
# Delay for a period of time (in milliseconds).
time.sleep(delayval)
# Fill up 9 pixels with orange,
# Fill up 9 pixels with orange,
# starting from pixel number 84.
for i in range ( 84, CPIXELS ):
# Set Pixels to Orange Color
pixels[i] = ( 250, 50, 0 )
for i in range(BPIXELS, CPIXELS):
# Set Pixels to Orange Color
pixels[i] = (250, 50, 0)
# This sends the updated pixel color to the hardware.
pixels.write()
# Delay for a period of time (in milliseconds).
time.sleep(delayval)
pixels.write()
# Delay for a period of time (in milliseconds).
time.sleep(delayval)

View file

@ -0,0 +1,90 @@
// Trinket/Gemma + LED matrix backpack jewelry. Plays animated
// sequence on LED matrix. Press reset button to display again,
// or add optional momentary button between pin #1 and +V.
// THERE IS NO ANIMATION DATA IN THIS SOURCE FILE, you should
// rarely need to change anything here. EDIT anim.h INSTEAD.
#define BRIGHTNESS 12 // 0=min, 15=max
#define I2C_ADDR 0x70 // Edit if backpack A0/A1 jumpers set
#include <TinyWireM.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include "bmo.h" // Animation data is located here
static const uint8_t PROGMEM reorder[] = { // Column-reordering table
0x00,0x40,0x20,0x60,0x10,0x50,0x30,0x70,0x08,0x48,0x28,0x68,0x18,0x58,0x38,0x78,
0x04,0x44,0x24,0x64,0x14,0x54,0x34,0x74,0x0c,0x4c,0x2c,0x6c,0x1c,0x5c,0x3c,0x7c,
0x02,0x42,0x22,0x62,0x12,0x52,0x32,0x72,0x0a,0x4a,0x2a,0x6a,0x1a,0x5a,0x3a,0x7a,
0x06,0x46,0x26,0x66,0x16,0x56,0x36,0x76,0x0e,0x4e,0x2e,0x6e,0x1e,0x5e,0x3e,0x7e,
0x01,0x41,0x21,0x61,0x11,0x51,0x31,0x71,0x09,0x49,0x29,0x69,0x19,0x59,0x39,0x79,
0x05,0x45,0x25,0x65,0x15,0x55,0x35,0x75,0x0d,0x4d,0x2d,0x6d,0x1d,0x5d,0x3d,0x7d,
0x03,0x43,0x23,0x63,0x13,0x53,0x33,0x73,0x0b,0x4b,0x2b,0x6b,0x1b,0x5b,0x3b,0x7b,
0x07,0x47,0x27,0x67,0x17,0x57,0x37,0x77,0x0f,0x4f,0x2f,0x6f,0x1f,0x5f,0x3f,0x7f,
0x80,0xc0,0xa0,0xe0,0x90,0xd0,0xb0,0xf0,0x88,0xc8,0xa8,0xe8,0x98,0xd8,0xb8,0xf8,
0x84,0xc4,0xa4,0xe4,0x94,0xd4,0xb4,0xf4,0x8c,0xcc,0xac,0xec,0x9c,0xdc,0xbc,0xfc,
0x82,0xc2,0xa2,0xe2,0x92,0xd2,0xb2,0xf2,0x8a,0xca,0xaa,0xea,0x9a,0xda,0xba,0xfa,
0x86,0xc6,0xa6,0xe6,0x96,0xd6,0xb6,0xf6,0x8e,0xce,0xae,0xee,0x9e,0xde,0xbe,0xfe,
0x81,0xc1,0xa1,0xe1,0x91,0xd1,0xb1,0xf1,0x89,0xc9,0xa9,0xe9,0x99,0xd9,0xb9,0xf9,
0x85,0xc5,0xa5,0xe5,0x95,0xd5,0xb5,0xf5,0x8d,0xcd,0xad,0xed,0x9d,0xdd,0xbd,0xfd,
0x83,0xc3,0xa3,0xe3,0x93,0xd3,0xb3,0xf3,0x8b,0xcb,0xab,0xeb,0x9b,0xdb,0xbb,0xfb,
0x87,0xc7,0xa7,0xe7,0x97,0xd7,0xb7,0xf7,0x8f,0xcf,0xaf,0xef,0x9f,0xdf,0xbf,0xff };
void ledCmd(uint8_t x) { // Issue command to LED backback driver
TinyWireM.beginTransmission(I2C_ADDR);
TinyWireM.write(x);
TinyWireM.endTransmission();
}
void clear(void) { // Clear display buffer
TinyWireM.beginTransmission(I2C_ADDR);
for(uint8_t i=0; i<17; i++) TinyWireM.write(0);
TinyWireM.endTransmission();
}
void setup() {
power_timer1_disable(); // Disable unused peripherals
power_adc_disable(); // to save power
PCMSK |= _BV(PCINT1); // Set change mask for pin 1
TinyWireM.begin(); // I2C init
clear(); // Blank display
ledCmd(0x21); // Turn on oscillator
ledCmd(0xE0 | BRIGHTNESS); // Set brightness
ledCmd(0x81); // Display on, no blink
}
uint8_t rep = REPS;
void loop() {
for(int i=0; i<sizeof(anim); i) { // For each frame...
TinyWireM.beginTransmission(I2C_ADDR);
TinyWireM.write(0); // Start address
for(uint8_t j=0; j<8; j++) { // 8 rows...
TinyWireM.write(pgm_read_byte(&reorder[pgm_read_byte(&anim[i++])]));
TinyWireM.write(0);
}
TinyWireM.endTransmission();
delay(pgm_read_byte(&anim[i++]) * 10);
}
if(!--rep) { // If last cycle...
ledCmd(0x20); // LED matrix in standby mode
GIMSK = _BV(PCIE); // Enable pin change interrupt
power_all_disable(); // All peripherals off
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sei(); // Keep interrupts disabled
sleep_mode(); // Power down CPU (pin 1 will wake)
// Execution resumes here on wake.
GIMSK = 0; // Disable pin change interrupt
rep = REPS; // Reset animation counter
power_timer0_enable(); // Re-enable timer
power_usi_enable(); // Re-enable USI
TinyWireM.begin(); // Re-init I2C
clear(); // Blank display
ledCmd(0x21); // Re-enable matrix
}
}
ISR(PCINT0_vect) {} // Button tap

View file

@ -0,0 +1,117 @@
# Trinket/Gemma + LED matrix backpack jewelry. Plays animated
# sequence on LED matrix. Press reset button to display again.
import time
import adafruit_ht16k33.matrix
import board
import busio as io
import touchio
touch = touchio.TouchIn(board.D1)
i2c = io.I2C(board.SCL, board.SDA)
matrix = adafruit_ht16k33.matrix.Matrix8x8(i2c)
# pixels initializers
x_pix = y_pix = 8
x = y = 0
matrix.fill(0)
matrix.show()
# seconds to pause between frames
frame_delay = [.25, .25, .25, .25, .25, .25, .25, .25, .25, .25]
# counter for animation frames
frame_count = 0
# repeat entire animation multiple times
reps = 255
rep_count = reps
# animation bitmaps
animation = [
# frame 1
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 0, 1, 1, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 1], [1, 1, 0, 0, 0, 0, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]],
# frame 2
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 0, 1, 1, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 1, 1, 1, 1, 0, 1], [1, 0, 1, 1, 1, 1, 0, 1],
[1, 1, 0, 0, 0, 0, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]],
# frame 3
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 0, 1, 1, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 1], [1, 1, 0, 0, 0, 0, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]],
# frame 4
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 0, 1, 1, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 1, 1, 1, 1, 0, 1], [1, 0, 1, 1, 1, 1, 0, 1],
[1, 1, 0, 0, 0, 0, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]],
# frame 5
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 0, 1, 1, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1]],
# frame 6
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 1, 1, 0, 0, 1],
[1, 0, 0, 1, 1, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 0, 1, 1, 0, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]],
# frame 7
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 1, 1, 1, 1, 0, 1],
[0, 0, 0, 1, 1, 0, 0, 0], [1, 0, 1, 1, 1, 1, 0, 1],
[1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 0, 1, 1, 0, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]],
# frame 8
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 1, 1, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 0, 1, 1, 0, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]],
# frame 9
[[1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 1, 1, 1, 1, 0, 1],
[0, 0, 0, 1, 1, 0, 0, 0], [1, 0, 1, 1, 1, 1, 0, 1],
[1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 0, 1, 1, 0, 1, 1],
[1, 1, 1, 0, 0, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1]],
]
#
# run until we are out of animation frames
# use Gemma's built-in reset button or switch to restart
#
# populate matrix
while True:
if frame_count < len(animation) and rep_count >= 0:
for x in range(x_pix):
for y in range(y_pix):
matrix.pixel(x, y, animation[frame_count][x][y])
# next animation frame
frame_count += 1
# show animation
matrix.show()
# pause for effect
time.sleep(frame_delay[frame_count])
else:
matrix.fill(0)
matrix.show()
time.sleep(.1)
# track repitions
rep_count -= 1
# play it again
frame_count = 0
# A0/D1 pin has been touched
# reset animation
if touch.value:
frame_count = 0
rep_count = reps

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View file

@ -0,0 +1,4 @@
# 3D Printed LED-Animation BMO
Code to accompany this Adafruit tutorial:
https://learn.adafruit.com/3d-printed-led-animation-bmo/overview

View file

@ -0,0 +1,95 @@
// Animation data for Trinket/Gemma + LED matrix backpack jewelry.
#define REPS 255 // Number of times to repeat the animation loop (1-255)
const uint8_t PROGMEM anim[] = {
B11111111, // 1 frame
B10011001,
B10011001,
B11111111,
B10000001,
B11000011,
B11100111,
B11111111,
25, // 0.25 second delay
B11111111, // 2 frame
B10011001,
B10011001,
B11111111,
B10111101,
B10111101,
B11000011,
B11111111,
25, // 0.25 second delay
B11111111, // 3 frame
B10011001,
B10011001,
B11111111,
B10000001,
B11000011,
B11100111,
B11111111,
25, // 0.25 second delay
B11111111, // 4 frame
B10011001,
B10011001,
B11111111,
B10111101,
B10111101,
B11000011,
B11111111,
25, // 0.25 second delay
B11111111, // 5 frame
B10011001,
B10011001,
B11111111,
B11111111,
B11111111,
B10000001,
B11111111,
25, // 0.25 second delay
B11111111, // 6 frame
B10011001,
B10011001,
B11111111,
B11100111,
B11011011,
B11100111,
B11111111,
25, // 0.25 second delay
B11111111, // 7 frame
B10111101,
B00011000,
B10111101,
B11100111,
B11011011,
B11100111,
B11111111,
25, // 0.25 second delay
B11111111, // 8 frame
B11111111,
B00011000,
B11111111,
B11100111,
B11011011,
B11100111,
B11111111,
25, // 0.25 second delay
B11111111, // 9 frame
B10111101,
B00011000,
B10111101,
B11100111,
B11011011,
B11100111,
B11111111,
25, // 0.25 second delay
};

View file

View file

View file

@ -0,0 +1,128 @@
// Fiery demon horns (rawr!) for Adafruit Trinket/Gemma.
// Adafruit invests time and resources providing this open source code,
// please support Adafruit and open-source hardware by purchasing
// products from Adafruit!
#include <Adafruit_NeoPixel.h>
#include <avr/power.h>
#define N_HORNS 1
#define N_LEDS 30 // Per horn
#define PIN 0
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(N_HORNS * N_LEDS, PIN);
// /\ -> Fire-like effect is the sum of multiple triangle
// ____/ \____ waves in motion, with a 'warm' color map applied.
#define N_WAVES 6 // Number of simultaneous waves (per horn)
// Coordinate space for waves is 16x the pixel spacing,
// allowing fixed-point math to be used instead of floats.
struct {
int16_t lower; // Lower bound of wave
int16_t upper; // Upper bound of wave
int16_t mid; // Midpoint (peak) ((lower+upper)/2)
uint8_t vlower; // Velocity of lower bound
uint8_t vupper; // Velocity of upper bound
uint16_t intensity; // Brightness at peak
} wave[N_HORNS][N_WAVES];
long fade; // Decreases brightness as wave moves
// Gamma correction improves appearance of midrange colors
const uint8_t gamma[] PROGMEM = {
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, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
static void random_wave(uint8_t h,uint8_t w) { // Randomize one wave struct
wave[h][w].upper = -1; // Always start just below head of strip
wave[h][w].lower = -16 * (3 + random(4)); // Lower end starts ~3-7 pixels back
wave[h][w].mid = (wave[h][w].lower + wave[h][w].upper) / 2;
wave[h][w].vlower = 3 + random(4); // Lower end moves at ~1/8 to 1/4 pixel/frame
wave[h][w].vupper = wave[h][w].vlower + random(4); // Upper end moves a bit faster, spreading wave
wave[h][w].intensity = 300 + random(600);
}
void setup() {
uint8_t h, w;
randomSeed(analogRead(1));
pixels.begin();
for(h=0; h<N_HORNS; h++) {
for(w=0; w<N_WAVES; w++) random_wave(h, w);
}
fade = 233 + N_LEDS / 2;
if(fade > 255) fade = 255;
// A ~100 Hz timer interrupt on Timer/Counter1 makes everything run
// at regular intervals, regardless of current amount of motion.
#if F_CPU == 16000000L
clock_prescale_set(clock_div_1);
TCCR1 = _BV(PWM1A) | _BV(CS13) | _BV(CS11) | _BV(CS10); // 1:1024 prescale
OCR1C = F_CPU / 1024 / 100 - 1;
#else
TCCR1 = _BV(PWM1A) | _BV(CS13) | _BV(CS11); // 1:512 prescale
OCR1C = F_CPU / 512 / 100 - 1;
#endif
GTCCR = 0; // No PWM out
TIMSK |= _BV(TOIE1); // Enable overflow interrupt
}
void loop() { } // Not used -- everything's in interrupt below
ISR(TIMER1_OVF_vect) {
uint8_t h, w, i, r, g, b;
int16_t x;
uint16_t sum;
for(h=0; h<N_HORNS; h++) { // For each horn...
for(x=7, i=0; i<N_LEDS; i++, x+=16) { // For each LED along horn...
for(sum=w=0; w<N_WAVES; w++) { // For each wave of horn...
if((x < wave[h][w].lower) || (x > wave[h][w].upper)) continue; // Out of range
if(x <= wave[h][w].mid) { // Lower half of wave (ramping up to peak brightness)
sum += wave[h][w].intensity * (x - wave[h][w].lower) / (wave[h][w].mid - wave[h][w].lower);
} else { // Upper half of wave (ramping down from peak)
sum += wave[h][w].intensity * (wave[h][w].upper - x) / (wave[h][w].upper - wave[h][w].mid);
}
}
// Now the magnitude (sum) is remapped to color for the LEDs.
// A blackbody palette is used - fades white-yellow-red-black.
if(sum < 255) { // 0-254 = black to red-1
r = pgm_read_byte(&gamma[sum]);
g = b = 0;
} else if(sum < 510) { // 255-509 = red to yellow-1
r = 255;
g = pgm_read_byte(&gamma[sum - 255]);
b = 0;
} else if(sum < 765) { // 510-764 = yellow to white-1
r = g = 255;
b = pgm_read_byte(&gamma[sum - 510]);
} else { // 765+ = white
r = g = b = 255;
}
pixels.setPixelColor(h * N_LEDS + i, r, g, b);
}
for(w=0; w<N_WAVES; w++) { // Update wave positions for each horn
wave[h][w].lower += wave[h][w].vlower; // Advance lower position
if(wave[h][w].lower >= (N_LEDS * 16)) { // Off end of strip?
random_wave(h, w); // Yes, 'reboot' wave
} else { // No, adjust other values...
wave[h][w].upper += wave[h][w].vupper;
wave[h][w].mid = (wave[h][w].lower + wave[h][w].upper) / 2;
wave[h][w].intensity = (wave[h][w].intensity * fade) / 256; // Dimmer
}
}
}
pixels.show();
}

View file

@ -0,0 +1,147 @@
# Fiery demon horns (rawr!) for Adafruit Trinket/Gemma.
# Adafruit invests time and resources providing this open source code,
# please support Adafruit and open-source hardware by purchasing
# products from Adafruit!
import board
import neopixel
from analogio import AnalogIn
# pylint: disable=global-statement
try:
import urandom as random
except ImportError:
import random
# /\ -> Fire-like effect is the sum_total of multiple triangle
# ____/ \____ waves in motion, with a 'warm' color map applied.
n_horns = 1 # number of horns
led_pin = board.D0 # which pin your pixels are connected to
n_leds = 30 # number of LEDs per horn
frames_per_second = 50 # animation frames per second
brightness = 0 # current wave height
fade = 0 # Decreases brightness as wave moves
pixels = neopixel.NeoPixel(led_pin, n_leds, brightness=1, auto_write=False)
offset = 0
# Coordinate space for waves is 16x the pixel spacing,
# allowing fixed-point math to be used instead of floats.
lower = 0 # lower bound of wave
upper = 1 # upper bound of wave
mid = 2 # midpoint (peak) ((lower+upper)/2)
vlower = 3 # velocity of lower bound
vupper = 4 # velocity of upper bound
intensity = 5 # brightness at peak
y = 0
brightness = 0
count = 0
# initialize 3D list
wave = [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6, [[0] * 6] * 6
# Number of simultaneous waves (per horn)
n_waves = len(wave)
# Gamma-correction table
gamma = [
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, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99, 101, 102, 104, 105, 107, 109, 110,
112, 114, 115, 117, 119, 120, 122, 124, 126, 127, 129, 131, 133,
135, 137, 138, 140, 142, 144, 146, 148, 150, 152, 154, 156, 158,
160, 162, 164, 167, 169, 171, 173, 175, 177, 180, 182, 184, 186,
189, 191, 193, 196, 198, 200, 203, 205, 208, 210, 213, 215, 218,
220, 223, 225, 228, 231, 233, 236, 239, 241, 244, 247, 249, 252,
255
]
def random_wave(he, wi):
wave[he][wi][upper] = -1 # Always start below head of strip
wave[he][wi][lower] = -16 * (3 + random.randint(0,4)) # Lower end starts ~3-7 pixels back
wave[he][wi][mid] = (wave[he][wi][lower]+ wave[he][wi][upper]) / 2
wave[he][wi][vlower] = 3 + random.randint(0,4) # Lower end moves at ~1/8 to 1/pixels
wave[he][wi][vupper] = wave[he][wi][vlower]+ random.randint(0,4) # Upper end moves a bit faster
wave[he][wi][intensity] = 300 + random.randint(0,600)
def setup():
global fade
# Random number generator is seeded from an unused 'floating'
# analog input - this helps ensure the random color choices
# aren't always the same order.
pin = AnalogIn(board.A0)
random.seed(pin.value)
pin.deinit()
for he in range(n_horns):
for wi in range(n_waves):
random_wave(he, wi)
fade = 233 + n_leds / 2
if fade > 233:
fade = 233
setup()
while True:
h = w = i = r = g = b = 0
x = 0
for h in range(n_horns): # For each horn...
x = 7
sum_total = 0
for i in range(n_leds): # For each LED along horn...
x += 16
for w in range(n_waves): # For each wave of horn...
if (x < wave[h][w][lower]) or (x > wave[h][w][upper]):
continue # Out of range
if x <= wave[h][w][mid]: # Lower half of wave (ramping up peak brightness)
sum_top = wave[h][w][intensity] * (x - wave[h][w][lower])
sum_bottom = (wave[h][w][mid] - wave[h][w][lower])
sum_total += sum_top / sum_bottom
else: # Upper half of wave (ramping down from peak)
sum_top = wave[h][w][intensity] * (wave[h][w][upper] - x)
sum_bottom = (wave[h][w][upper] - wave[h][w][mid])
sum_total += sum_top / sum_bottom
sum_total = int(sum_total) # convert from decimal to whole number
# Now the magnitude (sum_total) is remapped to color for the LEDs.
# A blackbody palette is used - fades white-yellow-red-black.
if sum_total < 255: # 0-254 = black to red-1
r = gamma[sum_total]
g = b = 0
elif sum_total < 510: # 255-509 = red to yellow-1
r = 255
g = gamma[sum_total - 255]
b = 0
elif sum_total < 765: # 510-764 = yellow to white-1
r = g = 255
b = gamma[sum_total - 510]
else: # 765+ = white
r = g = b = 255
pixels[i] = (r, g, b)
for w in range(n_waves): # Update wave positions for each horn
wave[h][w][lower] += wave[h][w][vlower] # Advance lower position
if wave[h][w][lower] >= (n_leds * 16): # Off end of strip?
random_wave(h, w) # Yes, 'reboot' wave
else: # No, adjust other values...
wave[h][w][upper] += wave[h][w][vupper]
wave[h][w][mid] = (wave[h][w][lower] + wave[h][w][upper]) / 2
wave[h][w][intensity] = (wave[h][w][intensity] * fade) / 256 # Dimmer
pixels.show()

View file

@ -0,0 +1,4 @@
# 3D Printed LED Fire Horns
Code to accompany this tutorial:
https://learn.adafruit.com/3d-printed-led-fire-horns/fire-code

View file

View file

@ -27,42 +27,46 @@
# Modified fromhere code by Greg Shakar
# Ported to Circuit Python by Mikey Sklar
import time
import board
import neopixel
import time
from analogio import AnalogIn
import array
n_pixels = 16 # Number of pixels you are using
mic_pin = AnalogIn(board.A1) # Microphone is attached to this analog pin
led_pin = board.D1 # NeoPixel LED strand is connected to this pin
sample_window = .1 # Sample window for average level
peak_hang = 24 # Time of pause before peak dot falls
peak_fall = 4 # Rate of falling peak dot
input_floor = 10 # Lower range of analogRead input
input_ceiling = 300 # Max range of analogRead input, the lower the value the more sensitive (1023 = max)
n_pixels = 16 # Number of pixels you are using
mic_pin = AnalogIn(board.A1) # Microphone is attached to this analog pin
led_pin = board.D1 # NeoPixel LED strand is connected to this pin
sample_window = .1 # Sample window for average level
peak_hang = 24 # Time of pause before peak dot falls
peak_fall = 4 # Rate of falling peak dot
input_floor = 10 # Lower range of analogRead input
# Max range of analogRead input, the lower the value the more sensitive
# (1023 = max)
input_ceiling = 300
peak = 16 # Peak level of column; used for falling dots
peak = 16 # Peak level of column; used for falling dots
sample = 0
dotcount = 0 # Frame counter for peak dot
dothangcount = 0 # Frame counter for holding peak dot
dotcount = 0 # Frame counter for peak dot
dothangcount = 0 # Frame counter for holding peak dot
strip = neopixel.NeoPixel(led_pin, n_pixels, brightness=1, auto_write=False)
def wheel(pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
if (pos < 0) or (pos > 255):
if pos < 0 or pos > 255:
return (0, 0, 0)
if (pos < 85):
return (int(pos * 3), int(255 - (pos*3)), 0)
elif (pos < 170):
if pos < 85:
return (int(pos * 3), int(255 - (pos * 3)), 0)
elif pos < 170:
pos -= 85
return (int(255 - pos*3), 0, int(pos*3))
return (int(255 - pos * 3), 0, int(pos * 3))
else:
pos -= 170
return (0, int(pos*3), int(255 - pos*3))
return (0, int(pos * 3), int(255 - pos * 3))
def remapRange(value, leftMin, leftMax, rightMin, rightMax):
# this remaps a value fromhere original (left) range to new (right) range
@ -76,91 +80,89 @@ def remapRange(value, leftMin, leftMax, rightMin, rightMax):
# Convert the 0-1 range into a value in the right range.
return int(rightMin + (valueScaled * rightSpan))
def fscale(originalmin, originalmax, newbegin, newend, inputvalue, curve):
originalrange = 0
newrange = 0
zerorefcurval = 0
normalizedcurval = 0
rangedvalue = 0
invflag = 0
# condition curve parameter
# limit range
if (curve > 10):
if curve > 10:
curve = 10
if (curve < -10):
if curve < -10:
curve = -10
# - invert and scale -
# this seems more intuitive
# - invert and scale -
# this seems more intuitive
# postive numbers give more weight to high end on output
curve = (curve * -.1)
curve = pow(10, curve) # convert linear scale into lograthimic exponent for other pow function
curve = (curve * -.1)
# convert linear scale into lograthimic exponent for other pow function
curve = pow(10, curve)
# Check for out of range inputValues
if (inputvalue < originalmin):
if inputvalue < originalmin:
inputvalue = originalmin
if (inputvalue > originalmax):
if inputvalue > originalmax:
inputvalue = originalmax
# Zero Refference the values
originalrange = originalmax - originalmin
if (newend > newbegin):
if newend > newbegin:
newrange = newend - newbegin
else:
newrange = newbegin - newend
invflag = 1
zerorefcurval = inputvalue - originalmin
normalizedcurval = zerorefcurval / originalrange # normalize to 0 - 1 float
# normalize to 0 - 1 float
normalizedcurval = zerorefcurval / originalrange
# Check for originalMin > originalMax
# -the math for all other cases
# Check for originalMin > originalMax
# -the math for all other cases
# i.e. negative numbers seems to work out fine
if (originalmin > originalmax ):
return(0)
if originalmin > originalmax:
return 0
if (invflag == 0):
rangedvalue = (pow(normalizedcurval, curve) * newrange) + newbegin
else: # invert the ranges
rangedvalue = newbegin - (pow(normalizedcurval, curve) * newrange);
if invflag == 0:
rangedvalue = (pow(normalizedcurval, curve) * newrange) + newbegin
else: # invert the ranges
rangedvalue = newbegin - (pow(normalizedcurval, curve) * newrange)
return rangedvalue
return(rangedvalue)
def drawLine(fromhere, to):
fromheretemp = 0
if (fromhere > to):
if fromhere > to:
fromheretemp = fromhere
fromhere = to
to = fromheretemp
for i in range(fromhere, to):
strip[i] = (0,0,0)
for index in range(fromhere, to):
strip[index] = (0, 0, 0)
while True:
time_start = time.monotonic() # current time used for sample window
peaktopeak = 0 # peak-to-peak level
time_start = time.monotonic() # current time used for sample window
peaktopeak = 0 # peak-to-peak level
signalmax = 0
signalmin = 1023
signalmin = 1023
c = 0
y = 0
# collect data for length of sample window (in seconds)
while ( ( time.monotonic() - time_start ) < sample_window):
while (time.monotonic() - time_start) < sample_window:
sample = mic_pin.value / 64 # convert to arduino 10-bit [1024] fromhere 16-bit [65536]
# convert to arduino 10-bit [1024] fromhere 16-bit [65536]
sample = mic_pin.value / 64
if (sample < 1024): # toss out spurious readings
if sample < 1024: # toss out spurious readings
if (sample > signalmax):
signalmax = sample # save just the max levels
elif (sample < signalmin):
signalmin = sample # save just the min levels
if sample > signalmax:
signalmax = sample # save just the max levels
elif sample < signalmin:
signalmin = sample # save just the min levels
peaktopeak = signalmax - signalmin # max - min = peak-peak amplitude
@ -171,11 +173,11 @@ while True:
# Scale the input logarithmically instead of linearly
c = fscale(input_floor, input_ceiling, (n_pixels - 1), 0, peaktopeak, 2)
if (c < peak):
peak = c # keep dot on top
dothangcount = 0 # make the dot hang before falling
if c < peak:
peak = c # keep dot on top
dothangcount = 0 # make the dot hang before falling
if (c <= n_pixels): # fill partial column with off pixels
if c <= n_pixels: # fill partial column with off pixels
drawLine(n_pixels, n_pixels - int(c))
# Set the peak dot to match the rainbow gradient
@ -184,8 +186,9 @@ while True:
strip.write()
# Frame based peak dot animation
if(dothangcount > peak_hang): # Peak pause length
if(++dotcount >= peak_fall): # Fall rate
if dothangcount > peak_hang: # Peak pause length
dotcount += 1
if dotcount >= peak_fall: # Fall rate
peak += 1
dotcount = 0
else:

View file

View file

@ -1,55 +1,58 @@
import time
import board
import neopixel
import time
try:
import urandom as random # for v1.0 API support
import urandom as random # for v1.0 API support
except ImportError:
import random
import random
numpix = 24 # Number of NeoPixels
numpix = 24 # Number of NeoPixels
pixpin = board.D0 # Pin where NeoPixels are connected
strip = neopixel.NeoPixel(pixpin, numpix, brightness=0.3)
strip = neopixel.NeoPixel(pixpin, numpix, brightness=0.3)
mode = 0 # Current animation effect
offset = 0 # Position of spinner animation
color = [255, 0, 0] # RGB color - red
mode = 0 # Current animation effect
offset = 0 # Position of spinner animation
color = [255, 0, 0] # RGB color - red
prevtime = time.monotonic() # Time of last animation mode switch
while True: # Loop forever...
if mode == 0: # Random sparkles - lights just one LED at a time
i = random.randint(0, numpix - 1) # Choose random pixel
strip[i] = color # Set it to current color
strip.write() # Refresh LED states
# Set same pixel to "off" color now but DON'T refresh...
# it stays on for now...bot this and the next random
# pixel will be refreshed on the next pass.
strip[i] = [0,0,0]
time.sleep(0.008) # 8 millisecond delay
elif mode == 1: # Spinny wheels
# A little trick here: pixels are processed in groups of 8
# (with 2 of 8 on at a time), NeoPixel rings are 24 pixels
# (8*3) and 16 pixels (8*2), so we can issue the same data
# to both rings and it appears correct and contiguous
# (also, the pixel order is different between the two ring
# types, so we get the reversed motion on #2 for free).
for i in range(numpix): # For each LED...
if ((offset + i) & 7) < 2: # 2 pixels out of 8...
strip[i] = color # are set to current color
else:
strip[i] = [0,0,0] # other pixels are off
strip.write() # Refresh LED states
time.sleep(0.04) # 40 millisecond delay
offset += 1 # Shift animation by 1 pixel on next frame
if offset >= 8: offset = 0
# Additional animation modes could be added here!
if mode == 0: # Random sparkles - lights just one LED at a time
i = random.randint(0, numpix - 1) # Choose random pixel
strip[i] = color # Set it to current color
strip.write() # Refresh LED states
# Set same pixel to "off" color now but DON'T refresh...
# it stays on for now...bot this and the next random
# pixel will be refreshed on the next pass.
strip[i] = [0, 0, 0]
time.sleep(0.008) # 8 millisecond delay
elif mode == 1: # Spinny wheels
# A little trick here: pixels are processed in groups of 8
# (with 2 of 8 on at a time), NeoPixel rings are 24 pixels
# (8*3) and 16 pixels (8*2), so we can issue the same data
# to both rings and it appears correct and contiguous
# (also, the pixel order is different between the two ring
# types, so we get the reversed motion on #2 for free).
for i in range(numpix): # For each LED...
if ((offset + i) & 7) < 2: # 2 pixels out of 8...
strip[i] = color # are set to current color
else:
strip[i] = [0, 0, 0] # other pixels are off
strip.write() # Refresh LED states
time.sleep(0.04) # 40 millisecond delay
offset += 1 # Shift animation by 1 pixel on next frame
if offset >= 8:
offset = 0
# Additional animation modes could be added here!
t = time.monotonic() # Current time in seconds
if (t - prevtime) >= 8: # Every 8 seconds...
mode += 1 # Advance to next mode
if mode > 1: # End of modes?
mode = 0 # Start over from beginning
# Rotate color R->G->B
color = [ color[2], color[0], color[1] ]
strip.fill([0,0,0]) # Turn off all pixels
prevtime = t # Record time of last mode change
t = time.monotonic() # Current time in seconds
if (t - prevtime) >= 8: # Every 8 seconds...
mode += 1 # Advance to next mode
if mode > 1: # End of modes?
mode = 0 # Start over from beginning
# Rotate color R->G->B
color = [color[2], color[0], color[1]]
strip.fill([0, 0, 0]) # Turn off all pixels
prevtime = t # Record time of last mode change

View file

@ -1,56 +1,62 @@
#
# 3D_Printed_NeoPixel_Ring_Hair_Dress.py
#
# this was ported to CircuitPython from the 'Gemma Hoop Animator'
# this was ported to CircuitPython from the 'Gemma Hoop Animator'
#
# https://github.com/HerrRausB/GemmaHoopAnimator
#
# unless you # don't like the preset animations or find a
# unless you # don't like the preset animations or find a
# major bug, you don't need tochange anything here
#
import time
import board
import neopixel
import time
try:
import urandom as random # for v1.0 API support
import urandom as random # for v1.0 API support
except ImportError:
import random
import random
from analogio import AnalogIn
# available actions
ACT_NOP = 0x00 # all leds off, do nothing
ACT_SIMPLE_RING = 0x01 # all leds on
ACT_CYCLING_RING_ACLK = 0x02 # anti clockwise cycling colors
ACT_CYCLING_RING_CLKW = 0x04 # clockwise cycling colors
ACT_WHEEL_ACLK = 0x08 # anti clockwise spinning wheel
ACT_WHEEL_CLKW = 0x10 # clockwise spinning wheel
ACT_SPARKLING_RING = 0x20 # sparkling effect
ACT_NOP = 0x00 # all leds off, do nothing
ACT_SIMPLE_RING = 0x01 # all leds on
ACT_CYCLING_RING_ACLK = 0x02 # anti clockwise cycling colors
ACT_CYCLING_RING_CLKW = 0x04 # clockwise cycling colors
ACT_WHEEL_ACLK = 0x08 # anti clockwise spinning wheel
ACT_WHEEL_CLKW = 0x10 # clockwise spinning wheel
ACT_SPARKLING_RING = 0x20 # sparkling effect
numpix = 16 # total number of NeoPixels
pixel_output = board.D0 # pin where NeoPixels are connected
analog_input = board.A0 # needed to seed the random generator
strip = neopixel.NeoPixel(pixel_output, numpix, brightness=.3, auto_write=False)
numpix = 16 # total number of NeoPixels
pixel_output = board.D0 # pin where NeoPixels are connected
analog_input = board.A0 # needed to seed the random generator
strip = neopixel.NeoPixel(pixel_output, numpix,
brightness=.3, auto_write=False)
# available color generation methods
COL_RANDOM = 0x40 # colors will be generated randomly
COL_SPECTRUM = 0x80 # colors will be set as cyclic spectral wipe
COL_RANDOM = 0x40 # colors will be generated randomly
COL_SPECTRUM = 0x80 # colors will be set as cyclic spectral wipe
# specifiyng the action list
action_duration = 0 # the action's overall duration in milliseconds (be careful not
# to use values > 2^16-1 - roughly one minute :-)
# the action's overall duration in milliseconds (be careful not
action_duration = 0
# to use values > 2^16-1 - roughly one minute :-)
action_and_color_gen = 1 # the color generation method
action_and_color_gen = 1 # the color generation method
action_step_duration = 2 # the duration of each action step rsp. the delay of the main
# loop in milliseconds - thus, controls the action speed (be
# careful not to use values > 2^16-1 - roughly one minute :-)
# the duration of each action step rsp. the delay of the main
action_step_duration = 2
# loop in milliseconds - thus, controls the action speed (be
# careful not to use values > 2^16-1 - roughly one minute :-)
color_granularity = 3 # controls the increment of the R, G, and B portions of the
# rsp. color. 1 means the increment is 0,1,2,3,..., 10 means
# the increment is 0,10,20,... don't use values > 255, and note
# that even values > 127 wouldn't make much sense...
color_granularity = 3 # controls the increment of the R, G, and B
# portions of the rsp. color. 1 means the increment is 0,1,2,3,...,
# 10 means the increment is 0,10,20,... don't use values > 255, and
# note that even values > 127 wouldn't make much sense...
color_interval = 4 # controls the speed of color changing independently from action
# controls the speed of color changing independently from action
color_interval = 4
# general global variables
color = 0
@ -65,7 +71,7 @@ curr_action = 0
curr_color_gen = COL_RANDOM
idx = 0
offset = 0
number_of_actions = 31
number_of_actions = 31
curr_action_idx = 0
curr_color_granularity = 1
spectrum_part = 0
@ -74,111 +80,139 @@ spectrum_part = 0
# this array variable must be called theactionlist !!!
#
# valid actions are:
# ACT_NOP simply do nothing and switch everything off
# ACT_SIMPLE_RING all leds on
# ACT_CYCLING_RING_ACLK anti clockwise cycling colors
# ACT_CYCLING_RING_CLKW clockwise cycling colors acording
# ACT_WHEEL_ACLK anti clockwise spinning wheel
# ACT_WHEEL_CLKW clockwise spinning wheel
# ACT_SPARKLING_RING sparkling effect
#
# ACT_NOP simply do nothing and switch everything off
# ACT_SIMPLE_RING all leds on
# ACT_CYCLING_RING_ACLK anti clockwise cycling colors
# ACT_CYCLING_RING_CLKW clockwise cycling colors acording
# ACT_WHEEL_ACLK anti clockwise spinning wheel
# ACT_WHEEL_CLKW clockwise spinning wheel
# ACT_SPARKLING_RING sparkling effect
#
# valid color options are:
# COL_RANDOM colors will be selected randomly, which might
# be not very sufficient due to well known
# limitations of the random generation algorithm
# COL_SPECTRUM colors will be set as cyclic spectral wipe
# R -> G -> B -> R -> G -> B -> R -> ...
# COL_RANDOM colors will be selected randomly, which might
# be not very sufficient due to well known
# limitations of the random generation algorithm
# COL_SPECTRUM colors will be set as cyclic spectral wipe
# R -> G -> B -> R -> G -> B -> R -> ...
# action action name & action step color color change
# duration color generation method duration granularity interval
# action action name & action step color color change
# duration color generation method duration granularity interval
theactionlist = [
[ 5, ACT_SPARKLING_RING | COL_RANDOM, 0.01, 25, 1 ],
[ 2, ACT_CYCLING_RING_CLKW | COL_RANDOM, 0.02, 1, 0.005 ],
[ 5, ACT_SPARKLING_RING | COL_RANDOM, 0.01, 25, 1 ],
[ 2, ACT_CYCLING_RING_ACLK | COL_RANDOM, 0.02, 1, 0.005 ],
[ 5, ACT_SPARKLING_RING | COL_RANDOM, 0.01, 25, 1 ],
[ 2.5, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 0.25, 20, 0.020 ],
[ 1, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 0.50, 1, 0.020 ],
[ .750, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 0.075, 1, 0.020 ],
[ .500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 0.100, 1, 0.020 ],
[ .500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 0.125, 1, 0.020 ],
[ .500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 0.150, 1, 0.050 ],
[ .500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 0.175, 1, 0.100 ],
[ .500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 0.200, 1, 0.200 ],
[ .750, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 0.225, 1, 0.250 ],
[ 1, ACT_CYCLING_RING_CLKW | COL_SPECTRUM, 0.250, 1, 0.350 ],
[ 30, ACT_SIMPLE_RING | COL_SPECTRUM, 0.050, 1, 0.010 ],
[ 2.5, ACT_WHEEL_ACLK | COL_SPECTRUM, 0.010, 1, 0.010 ],
[ 2.5, ACT_WHEEL_ACLK | COL_SPECTRUM, 0.015, 1, 0.020 ],
[ 2, ACT_WHEEL_ACLK | COL_SPECTRUM, 0.025, 1, 0.030 ],
[ 1, ACT_WHEEL_ACLK | COL_SPECTRUM, 0.050, 1, 0.040 ],
[ 1, ACT_WHEEL_ACLK | COL_SPECTRUM, 0.075, 1, 0.040 ],
[ 1, ACT_WHEEL_ACLK | COL_SPECTRUM, 0.100, 1, 0.050 ],
[ .500, ACT_WHEEL_ACLK | COL_SPECTRUM, 0.125, 1, 0.060 ],
[ .500, ACT_WHEEL_CLKW | COL_SPECTRUM, 0.125, 5, 0.050 ],
[ 1, ACT_WHEEL_CLKW | COL_SPECTRUM, 0.100, 10, 0.040 ],
[ 1.5, ACT_WHEEL_CLKW | COL_SPECTRUM, 0.075, 15, 0.030 ],
[ 2, ACT_WHEEL_CLKW | COL_SPECTRUM, 0.050, 20, 0.020 ],
[ 2.5, ACT_WHEEL_CLKW | COL_SPECTRUM, 0.025, 25, 0.010 ],
[ 3, ACT_WHEEL_CLKW | COL_SPECTRUM, 0.010, 30, 0.005 ],
[ 5, ACT_SPARKLING_RING | COL_RANDOM, 0.010, 25, 1 ],
[ 5, ACT_NOP, 0, 0, 0 ]
[5, ACT_SPARKLING_RING | COL_RANDOM, 0.01, 25, 1],
[2, ACT_CYCLING_RING_CLKW | COL_RANDOM,
0.02, 1, 0.005],
[5, ACT_SPARKLING_RING | COL_RANDOM, 0.01, 25, 1],
[2, ACT_CYCLING_RING_ACLK | COL_RANDOM,
0.02, 1, 0.005],
[5, ACT_SPARKLING_RING | COL_RANDOM, 0.01, 25, 1],
[2.5, ACT_CYCLING_RING_CLKW | COL_SPECTRUM,
0.25, 20, 0.020],
[1, ACT_CYCLING_RING_CLKW | COL_SPECTRUM,
0.50, 1, 0.020],
[.750, ACT_CYCLING_RING_CLKW | COL_SPECTRUM,
0.075, 1, 0.020],
[.500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM,
0.100, 1, 0.020],
[.500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM,
0.125, 1, 0.020],
[.500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM,
0.150, 1, 0.050],
[.500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM,
0.175, 1, 0.100],
[.500, ACT_CYCLING_RING_CLKW | COL_SPECTRUM,
0.200, 1, 0.200],
[.750, ACT_CYCLING_RING_CLKW | COL_SPECTRUM,
0.225, 1, 0.250],
[1, ACT_CYCLING_RING_CLKW | COL_SPECTRUM,
0.250, 1, 0.350],
[30, ACT_SIMPLE_RING | COL_SPECTRUM,
0.050, 1, 0.010],
[2.5, ACT_WHEEL_ACLK | COL_SPECTRUM,
0.010, 1, 0.010],
[2.5, ACT_WHEEL_ACLK | COL_SPECTRUM,
0.015, 1, 0.020],
[2, ACT_WHEEL_ACLK | COL_SPECTRUM,
0.025, 1, 0.030],
[1, ACT_WHEEL_ACLK | COL_SPECTRUM,
0.050, 1, 0.040],
[1, ACT_WHEEL_ACLK | COL_SPECTRUM,
0.075, 1, 0.040],
[1, ACT_WHEEL_ACLK | COL_SPECTRUM,
0.100, 1, 0.050],
[.500, ACT_WHEEL_ACLK | COL_SPECTRUM,
0.125, 1, 0.060],
[.500, ACT_WHEEL_CLKW | COL_SPECTRUM,
0.125, 5, 0.050],
[1, ACT_WHEEL_CLKW | COL_SPECTRUM,
0.100, 10, 0.040],
[1.5, ACT_WHEEL_CLKW | COL_SPECTRUM,
0.075, 15, 0.030],
[2, ACT_WHEEL_CLKW | COL_SPECTRUM,
0.050, 20, 0.020],
[2.5, ACT_WHEEL_CLKW | COL_SPECTRUM,
0.025, 25, 0.010],
[3, ACT_WHEEL_CLKW | COL_SPECTRUM,
0.010, 30, 0.005],
[5, ACT_SPARKLING_RING | COL_RANDOM, 0.010, 25, 1],
[5, ACT_NOP, 0, 0, 0]
]
# pylint: disable=global-statement
def nextspectrumcolor():
global spectrum_part, color_idx, curr_color_granularity, color
# spectral wipe from green to red
if (spectrum_part == 2):
if spectrum_part == 2:
color = (color_idx, 0, 255-color_idx)
color_idx += curr_color_granularity
if (color_idx > 255):
if color_idx > 255:
spectrum_part = 0
color_idx = 0
# spectral wipe from blue to green
elif (spectrum_part == 1):
elif spectrum_part == 1:
color = (0, 255 - color_idx, color_idx)
color_idx += curr_color_granularity
if (color_idx > 255):
if color_idx > 255:
spectrum_part = 2
color_idx = 0
# spectral wipe from red to blue
elif (spectrum_part == 0 ):
elif spectrum_part == 0:
color = (255 - color_idx, color_idx, 0)
color_idx += curr_color_granularity
if (color_idx > 255):
if color_idx > 255:
spectrum_part = 1
color_idx = 0
def nextrandomcolor():
global color
# granularity = 1 --> [0 .. 255] * 1 --> 0,1,2,3 ... 255
# granularity = 10 --> [0 .. 25] * 10 --> 0,10,20,30 ... 250
# granularity = 100 --> [0 .. 2] * 100 --> 0,100, 200 (boaring...)
random_red = random.randint(0, int (256 / curr_color_granularity))
random_red = random.randint(0, int(256 / curr_color_granularity))
random_red *= curr_color_granularity
random_green = random.randint(0, int (256 / curr_color_granularity))
random_green = random.randint(0, int(256 / curr_color_granularity))
random_green *= curr_color_granularity
random_blue = random.randint(0, int (256 / curr_color_granularity))
random_blue = random.randint(0, int(256 / curr_color_granularity))
random_blue *= curr_color_granularity
color = (random_red, random_green, random_blue)
def nextcolor():
# save some RAM for more animation actions
if (curr_color_gen & COL_RANDOM):
nextrandomcolor()
else:
if curr_color_gen & COL_RANDOM:
nextrandomcolor()
else:
nextspectrumcolor()
def setup():
def setup():
# fingers corssed, the seeding makes sense to really get random colors...
apin = AnalogIn(analog_input)
random.seed(apin.value)
@ -187,53 +221,58 @@ def setup():
# let's go!
nextcolor()
strip.write()
setup()
while True: # Loop forever...
# do we need to load the next action?
if ( (time.monotonic() - action_timer) > curr_action_duration ):
curr_action_duration = theactionlist[curr_action_idx][action_duration]
curr_action = theactionlist[curr_action_idx][action_and_color_gen] & 0x3F
curr_action_step_duration = theactionlist[curr_action_idx][action_step_duration]
curr_color_gen = theactionlist[curr_action_idx][action_and_color_gen] & 0xC0
curr_color_granularity = theactionlist[curr_action_idx][color_granularity]
curr_color_interval = theactionlist[curr_action_idx][color_interval]
if (time.monotonic() - action_timer) > curr_action_duration:
current_action = theactionlist[curr_action_idx]
curr_action_duration = current_action[action_duration]
curr_action = current_action[action_and_color_gen] & 0x3F
curr_action_step_duration = current_action[action_step_duration]
curr_color_gen = current_action[action_and_color_gen] & 0xC0
curr_color_granularity = current_action[color_granularity]
curr_color_interval = current_action[color_interval]
curr_action_idx += 1
# take care to rotate the action list!
curr_action_idx %= number_of_actions
action_timer = time.monotonic()
action_timer = time.monotonic()
# do we need to change to the next color?
if ((time.monotonic() - color_timer) > curr_color_interval):
if (time.monotonic() - color_timer) > curr_color_interval:
nextcolor()
color_timer = time.monotonic()
color_timer = time.monotonic()
# do we need to step up the current action?
if ((time.monotonic() - action_step_timer) > curr_action_step_duration):
if (time.monotonic() - action_step_timer) > curr_action_step_duration:
if (curr_action):
if curr_action:
if (curr_action == ACT_NOP):
is_act_cycling = (ACT_CYCLING_RING_ACLK or ACT_CYCLING_RING_CLKW)
if curr_action == ACT_NOP:
# rather trivial even tho this will be repeated as long as the
# NOP continues - i could have prevented it from repeating
# unnecessarily, but that would mean more code and less
# space for more actions within the animation
for i in range(0, numpix):
strip[i] = (0,0,0)
strip[i] = (0, 0, 0)
elif (curr_action == ACT_SIMPLE_RING):
elif curr_action == ACT_SIMPLE_RING:
# even more trivial - just set the new color, if there is one
for i in range(0, numpix):
strip[i] = color
elif ( curr_action == (ACT_CYCLING_RING_ACLK or ACT_CYCLING_RING_CLKW)):
elif curr_action == is_act_cycling:
# spin the ring clockwise or anti clockwise
if (curr_action == ACT_CYCLING_RING_ACLK):
if curr_action == ACT_CYCLING_RING_ACLK:
idx += 1
else:
else:
idx -= 1
# prevent overflows or underflows
@ -242,27 +281,27 @@ while True: # Loop forever...
# set the new color, if there is one
strip[idx] = color
elif (curr_action == ACT_WHEEL_ACLK or ACT_WHEEL_CLKW):
elif curr_action == ACT_WHEEL_ACLK or ACT_WHEEL_CLKW:
# switch on / off the appropriate pixels according to
# the current offset
for idx in range(0, numpix):
if ( ((offset + idx) & 7 ) < 2 ):
if ((offset + idx) & 7) < 2:
strip[idx] = color
else:
strip[idx] = (0,0,0)
strip[idx] = (0, 0, 0)
# advance the offset and thus, spin the wheel
if (curr_action == ACT_WHEEL_CLKW):
if curr_action == ACT_WHEEL_CLKW:
offset += 1
else:
else:
offset -= 1
# prevent overflows or underflows
offset %= numpix
elif (curr_action == ACT_SPARKLING_RING):
elif curr_action == ACT_SPARKLING_RING:
# switch current pixel off
strip[idx] = (0,0,0)
strip[idx] = (0, 0, 0)
# pick a new pixel
idx = random.randint(0, numpix)
# set new pixel to the current color

View file

View file

@ -1,8 +1,9 @@
from digitalio import DigitalInOut, Direction
import board
import neopixel
import time
import board
import neopixel
from digitalio import DigitalInOut, Direction
pixpin = board.D1
numpix = 8
@ -11,32 +12,37 @@ led.direction = Direction.OUTPUT
strip = neopixel.NeoPixel(pixpin, numpix, brightness=1, auto_write=True)
def wheel(pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
if (pos < 0) or (pos > 255):
return (0, 0, 0)
if (pos < 85):
if pos < 85:
return (int(pos * 3), int(255 - (pos*3)), 0)
elif (pos < 170):
elif pos < 170:
pos -= 85
return (int(255 - pos*3), 0, int(pos*3))
return (int(255 - pos * 3), 0, int(pos * 3))
else:
pos -= 170
return (0, int(pos*3), int(255 - pos*3))
return (0, int(pos * 3), int(255 - pos * 3))
def rainbow_cycle(wait):
for j in range(255*5):
for j in range(255 * 5):
for i in range(len(strip)):
idx = int ((i * 256 / len(strip)) + j)
idx = int((i * 256 / len(strip)) + j)
strip[i] = wheel(idx & 255)
time.sleep(wait)
def rainbow(wait):
for j in range(255):
for i in range(len(strip)):
idx = int (i+j)
idx = int(i + j)
strip[i] = wheel(idx & 255)
time.sleep(wait)
while True:
rainbow_cycle(0.05)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,189 @@
# Talking A, B, Cs Soundboards: Animal ABCs and "E is for Electronics" ABCs
import time
import board
import audioio
import adafruit_fancyled.adafruit_fancyled as fancy
import adafruit_trellism4
# Custom colors for keys
RED = 0xFF0000
MAROON = 0xFF0044
ORANGE = 0xFF6600
YELLOW = 0xFFFF00
BROWN = 0x8B4513
GREEN = 0x008000
AQUA = 0x33ff33
TEAL = 0x66ffff
BLUE = 0x0000FF
NAVY = 0x24248f
PURPLE = 0x660066
PINK = 0xFF66B3
WHITE = 0xFFFFFF
EXTRA = 0x888888
# Select the folder for the ABC files, only define one,
# the other line should have a # to comment it out
#SAMPLE_FOLDER = "/animals/"
SAMPLE_FOLDER = "/electronics/"
# This soundboard can select up to *32* sound clips! each one has a filename
# which will be inside the SAMPLE_FOLDER above, and a *color* in a tuple ()
SAMPLES = [("A.wav", RED),
("B.wav", MAROON),
("C.wav", ORANGE),
("D.wav", YELLOW),
("E.wav", BROWN),
("F.wav", GREEN),
("G.wav", AQUA),
("H.wav", TEAL),
("I.wav", BLUE),
("J.wav", NAVY),
("K.wav", PURPLE),
("L.wav", PINK),
("M.wav", RED),
("N.wav", MAROON),
("O.wav", ORANGE),
("P.wav", YELLOW),
("Q.wav", BROWN),
("R.wav", GREEN),
("S.wav", AQUA),
("T.wav", TEAL),
("U.wav", BLUE),
("V.wav", NAVY),
("W.wav", PURPLE),
("X.wav", PINK),
("Y.wav", RED),
("Z.wav", MAROON),
("01.wav", EXTRA), # Keys beyond the 26 alphabetic keys
("02.wav", EXTRA),
("03.wav", EXTRA),
("04.wav", EXTRA),
("05.wav", EXTRA),
("06.wav", EXTRA)]
# For the intro, pick any number of colors to make a fancy gradient!
INTRO_SWIRL = [RED, GREEN, BLUE]
# The color for the pressed key
SELECTED_COLOR = 0x333300
PLAY_SAMPLES_ON_START = False # Will not play all the sounds on start
# Our keypad + NeoPixel driver
trellis = adafruit_trellism4.TrellisM4Express(rotation=0)
# Play the welcome wav (if its there)
with audioio.AudioOut(board.A1, right_channel=board.A0) as audio:
try:
f = open(SAMPLE_FOLDER+SAMPLES[27][0], "rb") # Use 02.wav as welcome
wave = audioio.WaveFile(f)
audio.play(wave)
swirl = 0 # we'll swirl through the colors in the gradient
while audio.playing:
for i in range(32):
palette_index = ((swirl+i) % 32) / 32
color = fancy.palette_lookup(INTRO_SWIRL, palette_index)
# display it!
trellis.pixels[(i%8, i//8)] = color.pack()
swirl += 1
time.sleep(0.005)
f.close()
# just hold a moment
time.sleep(0.5)
except OSError:
# no biggie, they could have deleted it
pass
# Parse the first file to figure out what format it's in
channel_count = None
bits_per_sample = None
sample_rate = None
with open(SAMPLE_FOLDER+SAMPLES[0][0], "rb") as f:
wav = audioio.WaveFile(f)
print("%d channels, %d bits per sample, %d Hz sample rate " %
(wav.channel_count, wav.bits_per_sample, wav.sample_rate))
# Audio playback object - we'll go with either mono or stereo depending on
# what we see in the first file
if wav.channel_count == 1:
audio = audioio.AudioOut(board.A1)
elif wav.channel_count == 2:
audio = audioio.AudioOut(board.A1, right_channel=board.A0)
else:
raise RuntimeError("Must be mono or stereo waves!")
# Turn on, maybe play all of the buttons
for i, v in enumerate(SAMPLES):
filename = SAMPLE_FOLDER+v[0]
try:
with open(filename, "rb") as f:
wav = audioio.WaveFile(f)
print(filename,
"%d channels, %d bits per sample, %d Hz sample rate " %
(wav.channel_count, wav.bits_per_sample, wav.sample_rate))
if wav.channel_count != channel_count:
pass
if wav.bits_per_sample != bits_per_sample:
pass
if wav.sample_rate != sample_rate:
pass
trellis.pixels[(i%8, i//8)] = v[1]
if PLAY_SAMPLES_ON_START:
audio.play(wav)
while audio.playing:
pass
except OSError:
# File not found! skip to next
pass
def stop_playing_sample(playback_details):
print("playing: ", playback_details)
audio.stop()
trellis.pixels[playback_details['neopixel_location']] = playback_details['neopixel_color']
playback_details['file'].close()
playback_details['voice'] = None
current_press = set()
currently_playing = {'voice' : None}
last_samplenum = None
while True:
pressed = set(trellis.pressed_keys)
# if pressed:
# print("Pressed:", pressed)
just_pressed = pressed - current_press
just_released = current_press - pressed
# if just_pressed:
# print("Just pressed", just_pressed)
for down in just_pressed:
sample_num = down[1]*8 + down[0]
print(sample_num)
try:
filename = SAMPLE_FOLDER+SAMPLES[sample_num][0]
f = open(filename, "rb")
wav = audioio.WaveFile(f)
# is something else playing? interrupt it!
if currently_playing['voice'] != None:
print("Interrupt")
stop_playing_sample(currently_playing)
trellis.pixels[down] = SELECTED_COLOR
audio.play(wav)
# voice, neopixel tuple, color, and sample, file handle
currently_playing = {
'voice': 0,
'neopixel_location': down,
'neopixel_color': SAMPLES[sample_num][1],
'sample_num': sample_num,
'file': f}
except OSError:
pass # File not found! skip to next
# check if any samples are done
if not audio.playing and currently_playing['voice'] != None:
stop_playing_sample(currently_playing)
time.sleep(0.01) # a little delay here helps avoid debounce annoyances
current_press = pressed

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more