Skip to content

Commit 44362b4

Browse files
committed
Merge branch 'action' of github.com:ladyada/Adafruit_Learning_System_Guides into action
2 parents 99580fa + 9555207 commit 44362b4

File tree

38 files changed

+18686
-47
lines changed

38 files changed

+18686
-47
lines changed

BLE_Buzzy_Box/code.py

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import time
2+
import board
3+
import busio
4+
import neopixel
5+
import adafruit_drv2605
6+
import adafruit_ble
7+
from adafruit_ble.advertising.standard import SolicitServicesAdvertisement
8+
from adafruit_ble.services.standard import CurrentTimeService
9+
from adafruit_ble_apple_notification_center import AppleNotificationCenterService
10+
from digitalio import DigitalInOut, Direction
11+
12+
# setup for onboard NeoPixel
13+
pixel_pin = board.NEOPIXEL
14+
num_pixels = 1
15+
16+
pixel = neopixel.NeoPixel(pixel_pin, num_pixels, brightness=0.3, auto_write=False)
17+
18+
# setup for haptic motor driver
19+
i2c = busio.I2C(board.SCL, board.SDA)
20+
drv = adafruit_drv2605.DRV2605(i2c)
21+
22+
# onboard blue LED
23+
blue_led = DigitalInOut(board.BLUE_LED)
24+
blue_led.direction = Direction.OUTPUT
25+
26+
# setup for BLE
27+
ble = adafruit_ble.BLERadio()
28+
if ble.connected:
29+
for c in ble.connections:
30+
c.disconnect()
31+
32+
advertisement = SolicitServicesAdvertisement()
33+
34+
# adds ANCS and current time services for BLE to advertise
35+
advertisement.solicited_services.append(AppleNotificationCenterService)
36+
advertisement.solicited_services.append(CurrentTimeService)
37+
38+
# state machines
39+
current_notification = None # tracks the current notification from ANCS
40+
current_notifications = {} # array to hold all current notifications from ANCS
41+
cleared = False # state to track if notifications have been cleared from ANCS
42+
notification_service = None # holds the array of active notifications from ANCS
43+
all_ids = [] # array to hold all of the ids from ANCS
44+
hour = 0 # used to track when it is on the hour for the mindfulness reminder
45+
mindful = False # state used to track if it is time for mindfulness
46+
vibration = 16 # vibration effect being used for the haptic motor
47+
blue = (0, 0, 255) # color blue for the NeoPixel
48+
purple = (255, 0, 255) # color purple for the NeoPixel
49+
red = (255, 0, 0) # color red for the NeoPixel
50+
clear = (0, 0, 0) # allows for NeoPixel to be turned 'off'
51+
52+
# function for blinking NeoPixel
53+
# blinks: # of blinks
54+
# speed: how fast/slow blinks
55+
# color1: first color
56+
# color2: second color
57+
def blink_pixel(blinks, speed, color1, color2):
58+
for _ in range(0, blinks):
59+
pixel.fill(color1)
60+
pixel.show()
61+
time.sleep(speed)
62+
pixel.fill(color2)
63+
pixel.show()
64+
time.sleep(speed)
65+
66+
# function for haptic motor vibration
67+
# num_zzz: # of times vibrates
68+
# effect: type of vibration
69+
# delay: time between vibrations
70+
def vibe(num_zzz, effect, delay):
71+
drv.sequence[0] = adafruit_drv2605.Effect(effect)
72+
for _ in range(0, num_zzz):
73+
drv.play() # play the effect
74+
time.sleep(delay) # for 0.5 seconds
75+
drv.stop()
76+
77+
# start BLE
78+
ble.start_advertising(advertisement)
79+
80+
while True:
81+
82+
blue_led.value = False
83+
print("Waiting for connection")
84+
85+
# NeoPixel is red when not connected to BLE
86+
while not ble.connected:
87+
blue_led.value = False
88+
pixel.fill(red)
89+
pixel.show()
90+
91+
print("Connected")
92+
93+
while ble.connected:
94+
blue_led.value = True # blue LED is on when connected
95+
all_ids.clear()
96+
for connection in ble.connections:
97+
if not connection.paired:
98+
# pairs to phone
99+
connection.pair()
100+
print("paired")
101+
# allows connection to CurrentTimeService
102+
cts = connection[CurrentTimeService]
103+
notification_service = connection[AppleNotificationCenterService]
104+
# grabs notifications from ANCS
105+
current_notifications = notification_service.active_notifications
106+
107+
for notif_id in current_notifications:
108+
# adds notifications into array
109+
notification = current_notifications[notif_id]
110+
all_ids.append(notif_id)
111+
112+
if current_notification and current_notification.removed:
113+
# Stop showing the latest and show that there are no new notifications.
114+
current_notification = None
115+
pixel.fill(clear)
116+
pixel.show()
117+
118+
if not current_notification and not all_ids and not cleared:
119+
# updates cleared state for notification
120+
cleared = True
121+
# turns off NeoPixel when notifications are clear
122+
pixel.fill(clear)
123+
pixel.show()
124+
125+
elif all_ids:
126+
cleared = False
127+
if current_notification and current_notification.id in all_ids:
128+
index = all_ids.index(current_notification.id)
129+
else:
130+
index = len(all_ids) - 1
131+
notif_id = all_ids[index]
132+
# if there is a notification:
133+
if not current_notification or current_notification.id != notif_id:
134+
current_notification = current_notifications[notif_id]
135+
# parses notification info into a string
136+
category = str(notification).split(" ", 1)[0]
137+
# haptic motor vibrates
138+
vibe(2, vibration, 0.5)
139+
# all info for notification is printed to REPL
140+
print('-'*36)
141+
print("Msg #%d - Category %s" % (notification.id, category))
142+
print("From app:", notification.app_id)
143+
if notification.title:
144+
print("Title:", notification.title)
145+
if notification.subtitle:
146+
print("Subtitle:", notification.subtitle)
147+
if notification.message:
148+
print("Message:", notification.message)
149+
# NeoPixel blinks and then stays on until cleared
150+
blink_pixel(2, 0.5, purple, clear)
151+
pixel.fill(purple)
152+
pixel.show()
153+
# if it's on the hour:
154+
if cts.current_time[4] == hour and not mindful:
155+
print(cts.current_time[4])
156+
print("mindful time")
157+
# haptic motor vibrates
158+
vibe(5, vibration, 1)
159+
# NeoPixel blinks and then stays on
160+
blink_pixel(5, 1, blue, clear)
161+
mindful = True
162+
pixel.fill(blue)
163+
pixel.show()
164+
print("hour = ", hour)
165+
# if it's no longer on the hour:
166+
if cts.current_time[4] == (hour + 1) and mindful:
167+
# NeoPixel turns off
168+
mindful = False
169+
pixel.fill(clear)
170+
pixel.show()
171+
print("mindful time over")
172+
173+
# if BLE becomes disconnected then blue LED turns off
174+
# and BLE begins advertising again to reconnect
175+
print("Disconnected")
176+
blue_led.value = False
177+
print()
178+
ble.start_advertising(advertisement)
179+
notification_service = None

Baudot_TTY/baudot_tty.py

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
### Baudot TTY Message Transmitter
2+
3+
### The 5-bit mode is defined in ANSI TIA/EIA-825 (2000)
4+
### "A Frequency Shift Keyed Modem for use on the Public Switched Telephone Network"
5+
6+
import time
7+
import math
8+
import array
9+
import board
10+
from audiocore import RawSample
11+
import audiopwmio
12+
13+
# constants for sine wave generation
14+
SIN_LENGTH = 100 # more is less choppy
15+
SIN_AMPLITUDE = 2 ** 12 # 0 (min) to 32768 (max) 8192 is nice
16+
SIN_OFFSET = 32767.5 # for 16bit range, (2**16 - 1) / 2
17+
DELTA_PI = 2 * math.pi / SIN_LENGTH # happy little constant
18+
19+
sine_wave = [
20+
int(SIN_OFFSET + SIN_AMPLITUDE * math.sin(DELTA_PI * i)) for i in range(SIN_LENGTH)
21+
]
22+
tones = (
23+
RawSample(array.array("H", sine_wave), sample_rate=1800 * SIN_LENGTH), # Bit 0
24+
RawSample(array.array("H", sine_wave), sample_rate=1400 * SIN_LENGTH), # Bit 1
25+
)
26+
27+
bit_0 = tones[0]
28+
bit_1 = tones[1]
29+
carrier = tones[1]
30+
31+
32+
char_pause = 0.1 # pause time between chars, set to 0 for fastest rate possible
33+
34+
dac = audiopwmio.PWMAudioOut(
35+
board.A2
36+
) # the CLUE edge connector marked "#0" to STEMMA speaker
37+
# The CLUE's on-board speaker works OK, not great, just crank amplitude to full before trying.
38+
# dac = audiopwmio.PWMAudioOut(board.SPEAKER)
39+
40+
41+
LTRS = (
42+
"\b",
43+
"E",
44+
"\n",
45+
"A",
46+
" ",
47+
"S",
48+
"I",
49+
"U",
50+
"\r",
51+
"D",
52+
"R",
53+
"J",
54+
"N",
55+
"F",
56+
"C",
57+
"K",
58+
"T",
59+
"Z",
60+
"L",
61+
"W",
62+
"H",
63+
"Y",
64+
"P",
65+
"Q",
66+
"O",
67+
"B",
68+
"G",
69+
"FIGS",
70+
"M",
71+
"X",
72+
"V",
73+
"LTRS",
74+
)
75+
76+
FIGS = (
77+
"\b",
78+
"3",
79+
"\n",
80+
"-",
81+
" ",
82+
"-",
83+
"8",
84+
"7",
85+
"\r",
86+
"$",
87+
"4",
88+
"'",
89+
",",
90+
"!",
91+
":",
92+
"(",
93+
"5",
94+
'"',
95+
")",
96+
"2",
97+
"=",
98+
"6",
99+
"0",
100+
"1",
101+
"9",
102+
"?",
103+
"+",
104+
"FIGS",
105+
".",
106+
"/",
107+
";",
108+
"LTRS",
109+
)
110+
111+
char_count = 0
112+
current_mode = LTRS
113+
114+
# The 5-bit Baudot text telephone (TTY) mode is a Frequency Shift Keyed modem
115+
# for use on the Public Switched Telephone network.
116+
#
117+
# Definitions:
118+
# Carrier tone is a 1400Hz tone.
119+
# Binary 0 is an 1800Hz tone.
120+
# Binary 1 is a 1400Hz tone.
121+
# Bit duration is 20ms.
122+
123+
# Two modes exist: Letters, aka LTRS, for alphabet characters
124+
# and Figures aka FIGS for numbers and symbols. These modes are switched by
125+
# sending the appropriate 5-bit LTRS or FIGS character.
126+
#
127+
# Character transmission sequence:
128+
# Carrier tone transmits for 150ms before each character.
129+
# Start bit is a binary 0 (sounded for one bit duration of 20ms).
130+
# 5-bit character code can be a combination of binary 0s and binary 1s.
131+
# Stop bit is a binary 1 with a minimum duration of 1-1/2 bits (30ms)
132+
#
133+
#
134+
135+
136+
def baudot_bit(pitch=bit_1, duration=0.022): # spec says 20ms, but adjusted as needed
137+
dac.play(pitch, loop=True)
138+
time.sleep(duration)
139+
# dac.stop()
140+
141+
142+
def baudot_carrier(duration=0.15): # Carrier tone is transmitted for 150 ms before the
143+
# first character is transmitted
144+
baudot_bit(carrier, duration)
145+
dac.stop()
146+
147+
148+
def baudot_start():
149+
baudot_bit(bit_0)
150+
151+
152+
def baudot_stop():
153+
baudot_bit(bit_1, 0.04) # minimum duration is 30ms
154+
dac.stop()
155+
156+
157+
def send_character(value):
158+
baudot_carrier() # send carrier tone
159+
baudot_start() # send start bit tone
160+
for i in range(5): # send each bit of the character
161+
bit = (value >> i) & 0x01 # bit shift and bit mask to get value of each bit
162+
baudot_bit(tones[bit]) # send each bit, either 0 or 1, of a character
163+
baudot_stop() # send stop bit
164+
baudot_carrier() # not to spec, but works better to extend carrier
165+
166+
167+
def send_message(text):
168+
global char_count, current_mode # pylint: disable=global-statement
169+
for char in text:
170+
if char not in LTRS and char not in FIGS: # just skip unknown characters
171+
print("Unknown character:", char)
172+
continue
173+
174+
if char not in current_mode: # switch mode
175+
if current_mode == LTRS:
176+
print("Switching mode to FIGS")
177+
current_mode = FIGS
178+
send_character(current_mode.index("FIGS"))
179+
elif current_mode == FIGS:
180+
print("Switching mode to LTRS")
181+
current_mode = LTRS
182+
send_character(current_mode.index("LTRS"))
183+
# Send char mode at beginning of message and every 72 characters
184+
if char_count >= 72 or char_count == 0:
185+
print("Resending mode")
186+
if current_mode == LTRS:
187+
send_character(current_mode.index("LTRS"))
188+
elif current_mode == FIGS:
189+
send_character(current_mode.index("FIGS"))
190+
# reset counter
191+
char_count = 0
192+
print(char)
193+
send_character(current_mode.index(char))
194+
time.sleep(char_pause)
195+
# increment counter
196+
char_count += 1
197+
198+
199+
while True:
200+
send_message("\nADAFRUIT 1234567890 -$!+='()/:;?,. ")
201+
time.sleep(2)
202+
send_message("\nWELCOME TO JOHN PARK'S WORKSHOP!")
203+
time.sleep(3)
204+
send_message("\nWOULD YOU LIKE TO PLAY A GAME?")
205+
time.sleep(5)
206+
207+
# here's an example of sending a character
208+
# send_character(current_mode.index("A"))
209+
# time.sleep(char_pause)

0 commit comments

Comments
 (0)