Skip to content

Commit 31e14a2

Browse files
authored
Merge branch 'master' into aqi-node-guide
2 parents bc37f95 + e49e384 commit 31e14a2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1485
-10
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{\rtf1\ansi\ansicpg1252\cocoartf2511
2+
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
3+
{\colortbl;\red255\green255\blue255;}
4+
{\*\expandedcolortbl;;}
5+
\margl1440\margr1440\vieww10800\viewh8400\viewkind0
6+
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0
7+
8+
\f0\fs24 \cf0 }
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// 'Firewalker' LED sneakers sketch for Adafruit NeoPixels by Phillip Burgess
2+
3+
#include <Adafruit_NeoPixel.h>
4+
5+
const uint8_t gamma1[] PROGMEM = { // Gamma correction table for LED brightness
6+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
7+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
8+
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
9+
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
10+
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
11+
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
12+
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
13+
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
14+
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
15+
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
16+
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
17+
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
18+
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
19+
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
20+
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
21+
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };
22+
23+
// LEDs go around the full perimeter of the shoe sole, but the step animation
24+
// is mirrored on both the inside and outside faces, while the strip doesn't
25+
// necessarily start and end at the heel or toe. These constants help configure
26+
// the strip and shoe sizes, and the positions of the front- and rear-most LEDs.
27+
// Becky's shoes: 39 LEDs total, 20 LEDs long, LED #5 at back.
28+
// Phil's shoes: 43 LEDs total, 22 LEDs long, LED #6 at back.
29+
#define N_LEDS 39 // TOTAL number of LEDs in strip
30+
#define SHOE_LEN_LEDS 20 // Number of LEDs down ONE SIDE of shoe
31+
#define SHOE_LED_BACK 5 // Index of REAR-MOST LED on shoe
32+
#define STEP_PIN A2 // Analog input for footstep
33+
#define LED_PIN A0 // NeoPixel strip is connected here
34+
#define MAXSTEPS 3 // Process (up to) this many concurrent steps
35+
36+
Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
37+
38+
// The readings from the sensors are usually around 250-350 when not being pressed,
39+
// then dip below 100 when the heel is standing on it (for Phil's shoes; Becky's
40+
// don't dip quite as low because she's smaller).
41+
#define STEP_TRIGGER 150 // Reading must be below this to trigger step
42+
#define STEP_HYSTERESIS 200 // After trigger, must return to this level
43+
44+
int
45+
stepMag[MAXSTEPS], // Magnitude of steps
46+
stepX[MAXSTEPS], // Position of 'step wave' along strip
47+
mag[SHOE_LEN_LEDS], // Brightness buffer (one side of shoe)
48+
stepFiltered, // Current filtered pressure reading
49+
stepCount, // Number of 'frames' current step has lasted
50+
stepMin; // Minimum reading during current step
51+
uint8_t
52+
stepNum = 0, // Current step number in stepMag/stepX tables
53+
dup[SHOE_LEN_LEDS]; // Inside/outside copy indexes
54+
boolean
55+
stepping = false; // If set, step was triggered, waiting to release
56+
57+
58+
void setup() {
59+
pinMode(9, INPUT_PULLUP); // Set internal pullup resistor for sensor pin
60+
// As previously mentioned, the step animation is mirrored on the inside and
61+
// outside faces of the shoe. To avoid a bunch of math and offsets later, the
62+
// 'dup' array indicates where each pixel on the outside face of the shoe should
63+
// be copied on the inside. (255 = don't copy, as on front- or rear-most LEDs).
64+
// Later, the colors for the outside face of the shoe are calculated and then get
65+
// copied to the appropriate positions on the inside face.
66+
memset(dup, 255, sizeof(dup));
67+
int8_t a, b;
68+
for(a=1 , b=SHOE_LED_BACK-1 ; b>=0 ;) dup[a++] = b--;
69+
for(a=SHOE_LEN_LEDS-2, b=SHOE_LED_BACK+SHOE_LEN_LEDS; b<N_LEDS;) dup[a--] = b++;
70+
71+
// Clear step magnitude and position buffers
72+
memset(stepMag, 0, sizeof(stepMag));
73+
memset(stepX , 0, sizeof(stepX));
74+
strip.begin();
75+
stepFiltered = analogRead(STEP_PIN); // Initial input
76+
}
77+
78+
void loop() {
79+
uint8_t i, j;
80+
81+
// Read analog input, with a little noise filtering
82+
//stepFiltered = ((stepFiltered * 3) + analogRead(STEP_PIN)) >> 2;
83+
stepFiltered = (((stepFiltered * 3) - 100) + analogRead(STEP_PIN)) >> 2;
84+
85+
// The strip doesn't simply display the current pressure reading. Instead,
86+
// there's a bit of an animated flourish from heel to toe. This takes time,
87+
// and during quick foot-tapping there could be multiple step animations
88+
// 'in flight,' so a short list is kept.
89+
if(stepping) { // If a step was previously triggered...
90+
if(stepFiltered >= STEP_HYSTERESIS) { // Has step let up?
91+
stepping = false; // Yep! Stop monitoring.
92+
// Add new step to the step list (may be multiple in flight)
93+
stepMag[stepNum] = (STEP_HYSTERESIS - stepMin) * 6; // Step intensity
94+
stepX[stepNum] = -80; // Position starts behind heel, moves forward
95+
if(++stepNum >= MAXSTEPS) stepNum = 0; // If many, overwrite oldest
96+
} else if(stepFiltered < stepMin) stepMin = stepFiltered; // Track min val
97+
} else if(stepFiltered < STEP_TRIGGER) { // No step yet; watch for trigger
98+
stepping = true; // Got one!
99+
stepMin = stepFiltered; // Note initial value
100+
}
101+
102+
// Render a 'brightness map' for all steps in flight. It's like
103+
// a grayscale image; there's no color yet, just intensities.
104+
int mx1, px1, px2, m;
105+
memset(mag, 0, sizeof(mag)); // Clear magnitude buffer
106+
for(i=0; i<MAXSTEPS; i++) { // For each step...
107+
if(stepMag[i] <= 0) continue; // Skip if inactive
108+
for(j=0; j<SHOE_LEN_LEDS; j++) { // For each LED...
109+
// Each step has sort of a 'wave' that's part of the animation,
110+
// moving from heel to toe. The wave position has sub-pixel
111+
// resolution (4X), and is up to 80 units (20 pixels) long.
112+
mx1 = (j << 2) - stepX[i]; // Position of LED along wave
113+
if((mx1 <= 0) || (mx1 >= 80)) continue; // Out of range
114+
if(mx1 > 64) { // Rising edge of wave; ramp up fast (4 px)
115+
m = ((long)stepMag[i] * (long)(80 - mx1)) >> 4;
116+
} else { // Falling edge of wave; fade slow (16 px)
117+
m = ((long)stepMag[i] * (long)mx1) >> 6;
118+
}
119+
mag[j] += m; // Add magnitude to buffered sum
120+
}
121+
stepX[i]++; // Update position of step wave
122+
if(stepX[i] >= (80 + (SHOE_LEN_LEDS << 2)))
123+
stepMag[i] = 0; // Off end; disable step wave
124+
else
125+
stepMag[i] = ((long)stepMag[i] * 127L) >> 7; // Fade
126+
}
127+
128+
// For a little visual interest, some 'sparkle' is added.
129+
// The cumulative step magnitude is added to one pixel at random.
130+
long sum = 0;
131+
for(i=0; i<MAXSTEPS; i++) sum += stepMag[i];
132+
if(sum > 0) {
133+
i = random(SHOE_LEN_LEDS);
134+
mag[i] += sum / 4;
135+
}
136+
137+
// Now the grayscale magnitude buffer is remapped to color for the LEDs.
138+
// The code below uses a blackbody palette, which fades from white to yellow
139+
// to red to black. The goal here was specifically a "walking on fire"
140+
// aesthetic, so the usual ostentatious rainbow of hues seen in most LED
141+
// projects is purposefully skipped in favor of a more plain effect.
142+
uint8_t r, g, b;
143+
int level;
144+
for(i=0; i<SHOE_LEN_LEDS; i++) { // For each LED on one side...
145+
level = mag[i]; // Pixel magnitude (brightness)
146+
if(level < 255) { // 0-254 = black to red-1
147+
r = pgm_read_byte(&gamma1[level]);
148+
g = b = 0;
149+
} else if(level < 510) { // 255-509 = red to yellow-1
150+
r = 255;
151+
g = pgm_read_byte(&gamma1[level - 255]);
152+
b = 0;
153+
} else if(level < 765) { // 510-764 = yellow to white-1
154+
r = g = 255;
155+
b = pgm_read_byte(&gamma1[level - 510]);
156+
} else { // 765+ = white
157+
r = g = b = 255;
158+
}
159+
// Set R/G/B color along outside of shoe
160+
strip.setPixelColor(i+SHOE_LED_BACK, r, g, b);
161+
// Pixels along inside are funny...
162+
j = dup[i];
163+
if(j < 255) strip.setPixelColor(j, r, g, b);
164+
}
165+
166+
strip.show();
167+
delayMicroseconds(1500);
168+
}

IoT_Party_Parrot/code.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import time
2+
import board
3+
import displayio
4+
from adafruit_matrixportal.matrixportal import MatrixPortal
5+
from adafruit_matrixportal.matrix import Matrix
6+
import adafruit_imageload
7+
8+
# Get wifi details and more from a secrets.py file
9+
try:
10+
from secrets import secrets
11+
except ImportError:
12+
print("WiFi secrets are kept in secrets.py, please add them there!")
13+
raise
14+
15+
print("Party Parrot Twitter Matrix")
16+
17+
# import your bearer token
18+
bear = secrets['bearer_token']
19+
20+
# query URL for tweets. looking for hashtag partyparrot sent to a specific username
21+
DATA_SOURCE = ('https://api.twitter.com/2/tweets/search/recent?query=#partyparrot to:blitzcitydiy')
22+
# json data path to get most recent tweet's ID number
23+
DATA_LOCATION = ["meta", "newest_id"]
24+
25+
# create MatrixPortal object to grab data/connect to internet
26+
matrixportal = MatrixPortal(
27+
url=DATA_SOURCE,
28+
json_path=DATA_LOCATION,
29+
status_neopixel=board.NEOPIXEL
30+
)
31+
32+
# create matrix display
33+
matrix = Matrix(width=32, height=32)
34+
display = matrix.display
35+
36+
group = displayio.Group(max_size=20)
37+
38+
# load in party parrot bitmap
39+
parrot_bit, parrot_pal = adafruit_imageload.load("/partyParrotsTweet.bmp",
40+
bitmap=displayio.Bitmap,
41+
palette=displayio.Palette)
42+
43+
parrot_grid = displayio.TileGrid(parrot_bit, pixel_shader=parrot_pal,
44+
width=1, height=1,
45+
tile_height=32, tile_width=32,
46+
default_tile=10,
47+
x=0, y=0)
48+
49+
group.append(parrot_grid)
50+
51+
display.show(group)
52+
53+
# add bearer token as a header to request
54+
matrixportal.set_headers({'Authorization': 'Bearer ' + bear})
55+
56+
last_value = 0 # checks last tweet's ID
57+
check = 0 # time.monotonic() holder
58+
parrot = False # state to track if an animation is currently running
59+
party = 0 # time.monotonic() holder
60+
p = 0 # index for tilegrid
61+
party_count = 0 # count for animation cycles
62+
63+
while True:
64+
# every 30 seconds...
65+
if (check + 30) < time.monotonic():
66+
# store most recent tweet's ID number in value
67+
value = matrixportal.fetch()
68+
print("Response is", value)
69+
# reset time count
70+
check = time.monotonic()
71+
# compare last tweet ID and current tweet ID
72+
if last_value != value:
73+
print("new party!")
74+
# if it's new, then it's a party!
75+
last_value = value
76+
parrot = True
77+
else:
78+
# if it's not new, then the wait continues
79+
print("no new party... :(")
80+
# when a new tweet comes in...
81+
if parrot:
82+
# every 0.1 seconds...
83+
if (party + 0.1) < time.monotonic():
84+
# the party parrot animation cycles
85+
parrot_grid[0] = p
86+
# p is the tilegrid index location
87+
p += 1
88+
party = time.monotonic()
89+
# if an animation cycle ends
90+
if p > 9:
91+
# index is reset
92+
p = 0
93+
# animation cycle count is updated
94+
party_count += 1
95+
print("party parrot", party_count)
96+
# after 16 animations cycles...
97+
if party_count > 15:
98+
# reset states
99+
parrot = False
100+
party_count = 0
101+
p = 0
102+
# clear the matrix so that it's blank
103+
parrot_grid[0] = 10
104+
print("the party is over")
11.1 KB
Binary file not shown.

IoT_Party_Parrot/secrets.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# This file is where you keep secret settings, passwords, and tokens!
2+
# If you put them in the code you risk committing that info or sharing it
3+
4+
secrets = {
5+
'ssid' : 'insert your network name here',
6+
'password' : 'insert your network password here',
7+
'timezone' : "America/New_York", # http://worldtimeapi.org/timezones
8+
'twitter_api_key' : 'insert your twitter api key here',
9+
'twitter_secret_key' : 'insert your twitter secret key here',
10+
'bearer_token' : 'insert your bearer token here'
11+
}

Mask_Efficacy/process_run.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import time
2+
import imageio
3+
from skimage.color import rgb2gray
4+
import matplotlib.pyplot as plt
5+
import numpy as np
6+
7+
THRESH = 0.3
8+
9+
RUN = int(input('Enter run number: '))
10+
11+
vid = imageio.get_reader('run_{:03d}.mp4'.format(RUN), 'ffmpeg')
12+
13+
#----------------
14+
# MAIN PROCESSING
15+
#----------------
16+
frame_data = []
17+
start = time.monotonic()
18+
# go through video frame by frame
19+
print("Processing", end='')
20+
for frame in vid:
21+
print('.', end='', flush=True)
22+
frame_bin = rgb2gray(frame) > THRESH
23+
frame_count = np.count_nonzero(frame_bin == True)
24+
frame_percent = 100 * frame_count / (1920*1080)
25+
frame_data.append((frame_count, frame_percent))
26+
# overall stats
27+
avg_count = sum([x[0] for x in frame_data]) / len(frame_data)
28+
avg_percent = 100 * avg_count / (1920*1080)
29+
30+
end = time.monotonic()
31+
print("\nProcessing done in {} secs.".format(end - start))
32+
print("Average Count = {}".format(avg_count))
33+
print("Average Percent = {}".format(avg_percent))
34+
35+
#-------------
36+
# SAVE TO FILE
37+
#-------------
38+
print("Saving data to file...")
39+
with open('run_{:03d}.csv'.format(RUN), 'w') as fp:
40+
for frame, data in enumerate(frame_data):
41+
fp.write('{},{},{}\n'.format(frame, data[0], data[1]))
42+
43+
#---------
44+
# PLOTTING
45+
#---------
46+
print("Generating plots...")
47+
fig, ax = plt.subplots(1, figsize = (10,5))
48+
ax.set_title("RUN {:03d}\nTHRESH = {}, AVG_CNT = {:4.2}, AVG_PER = {:.3}".format(RUN, THRESH,avg_count, avg_percent))
49+
ax.set_xlabel("FRAME")
50+
ax.set_ylabel("COUNT")
51+
ax.plot([x[0] for x in frame_data])
52+
fig.savefig('run_{:03d}_plot.png'.format(RUN))
53+
54+
print("DONE.")

Mask_Efficacy/take_video.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import time
2+
import math
3+
import os
4+
import RPi.GPIO as GPIO
5+
import simpleaudio as sa
6+
import picamera
7+
8+
camera = picamera.PiCamera()
9+
camera.resolution = (1920, 1080)
10+
VIDEO_LENGTH = 10
11+
12+
BUTTON = 4
13+
GPIO.setmode(GPIO.BCM)
14+
GPIO.setup(BUTTON, GPIO.IN, pull_up_down=GPIO.PUD_UP)
15+
16+
SIN_LENGTH = 500
17+
SIN_AMPLITUDE = 127
18+
SIN_OFFSET = 128
19+
DELTA_PI = 2 * math.pi / SIN_LENGTH
20+
sine_wave = bytes([
21+
int(SIN_OFFSET + SIN_AMPLITUDE * math.sin(DELTA_PI * i)) for i in range(SIN_LENGTH)
22+
])
23+
24+
def play_tone(length):
25+
play_back = sa.play_buffer(sine_wave*length, 2, 2, 44100)
26+
play_back.wait_done()
27+
28+
run_number = int(input("Enter run number:"))
29+
30+
print("Press button when ready.")
31+
while GPIO.input(BUTTON):
32+
pass
33+
34+
play_tone(100)
35+
camera.start_recording("run_{:03d}.h264".format(run_number))
36+
camera.wait_recording(VIDEO_LENGTH)
37+
camera.stop_recording()
38+
play_tone(100)
39+
40+
err = os.system("MP4Box -add run_{0:03d}.h264 run_{0:03d}.mp4".format(run_number))

0 commit comments

Comments
 (0)