Created
April 25, 2020 21:32
-
-
Save okyeron/e0cb1aa6973b722a791a466af47af153 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### Baudot TTY/TDD Message Transmitter | |
### The 5-bit mode is defined in ANSI TIA/EIA-825 (2000) | |
### "A Frequency Shift Keyed Modem for use on the Public Switched Telephone Network" | |
import audiocore | |
from audiocore import RawSample | |
from audioio import AudioOut | |
from audiomixer import Mixer | |
import adafruit_trellism4 | |
import board | |
import array | |
import time | |
import math | |
trellis = adafruit_trellism4.TrellisM4Express() | |
led_on = [] | |
for x in range(trellis.pixels.width): | |
led_on.append([]) | |
for y in range(trellis.pixels.height): | |
led_on[x].append(False) | |
trellis.pixels.fill((0, 0, 0)) | |
current_press = set() | |
# constants for sine wave generation | |
SIN_LENGTH = 100 # more is less choppy | |
SIN_AMPLITUDE = 2**12 # 0 (min) to 32768 (max) 8192 is nice | |
SIN_OFFSET = 32767.5 # for 16bit range, (2**16 - 1) / 2 | |
DELTA_PI = 2 * math.pi / SIN_LENGTH # happy little constant | |
sine_wave = [int(SIN_OFFSET + SIN_AMPLITUDE * math.sin(DELTA_PI * i)) for i in range(SIN_LENGTH)] | |
tones = ( | |
RawSample(array.array("H", sine_wave), sample_rate = 1800 * SIN_LENGTH), # Bit 0 | |
RawSample(array.array("H", sine_wave), sample_rate = 1400 * SIN_LENGTH), # Bit 1 | |
) | |
bit_0 = tones[0] | |
bit_1 = tones[1] | |
carrier = tones[1] | |
char_pause = 0.1 # pause time between chars, set to 0 for fastest rate possible | |
# dac = audiopwmio.PWMAudioOut(board.A2) # the CLUE edge connector marked "#0" to STEMMA speaker | |
# The CLUE's on-board speaker works OK, not great, just crank amplitude to full before trying. | |
#dac = audiopwmio.PWMAudioOut(board.SPEAKER) | |
# main audio object | |
dac = AudioOut(left_channel=board.A0, right_channel=board.A1) | |
# mixer to allow pylyphonic playback | |
mixer = Mixer( | |
voice_count=8, | |
sample_rate=8000, | |
channel_count=2, | |
bits_per_sample=16, | |
samples_signed=True, | |
) | |
dac.play(mixer) | |
LTRS = ("\b", "E", "\n", "A", " ", "S", "I", "U", "\r", "D", "R", "J", | |
"N", "F", "C", "K", "T", "Z", "L", "W", "H", "Y", "P", "Q", "O", | |
"B", "G", "FIGS", "M", "X", "V", "LTRS") | |
FIGS = ("\b", "3", "\n", "-", " ", "-", "8", "7", "\r", "$", "4", "'", | |
",", "!", ":", "(", "5", '"', ")", "2", "=", "6", "0", "1", "9", | |
"?", "+", "FIGS", ".", "/", ";", "LTRS") | |
char_count = 0 | |
current_mode = LTRS | |
# The 5-bit Baudot text telephone (TTY/TDD) mode is a Frequency Shift Keyed modem | |
# for use on the Public Switched Telephone network. | |
# | |
# Definitions: | |
# Carrier tone is a 1400Hz tone. | |
# Binary 0 is an 1800Hz tone. | |
# Binary 1 is a 1400Hz tone. | |
# Bit duration is 20ms. | |
# Two modes exist: Letters, aka LTRS, for alphabet characters | |
# and Figures aka FIGS for numbers and symbols. These modes are switched by | |
# sending the appropriate 5-bit LTRS or FIGS character. | |
# | |
# Character transmission sequence: | |
# Carrier tone transmits for 150ms before each character. | |
# Start bit is a binary 0 (sounded for one bit duration of 20ms). | |
# 5-bit character code can be a combination of binary 0s and binary 1s. | |
# Stop bit is a binary 1 with a minimum duration of 1-1/2 bits (30ms) | |
# | |
# | |
def baudot_bit(pitch=bit_1, duration=0.022): # spec says 20ms, but adjusted as needed | |
dac.play(pitch, loop=True) | |
time.sleep(duration) | |
#dac.stop() | |
def baudot_carrier(duration = 0.15): # Carrier is transmitted 150 ms before the first character is transmitted | |
baudot_bit(carrier, 0.15) | |
dac.stop() | |
def baudot_start(): | |
baudot_bit(bit_0) | |
def baudot_stop(): | |
baudot_bit(bit_1, 0.04) # minimum duration is 30ms | |
dac.stop() | |
def send_character(value): | |
baudot_carrier() # send carrier tone | |
baudot_start() # send start bit tone | |
for i in range(5): # send each bit of the character | |
bit = (value >> i) & 0x01 # bit shift and bit mask to get value of each bit | |
baudot_bit(tones[bit]) # send each bit, either 0 or 1, of a character | |
baudot_stop() # send stop bit | |
baudot_carrier() # not to spec, but works better to extend carrier | |
def send_message(text): | |
global char_count, current_mode | |
for char in text: | |
if char not in LTRS and char not in FIGS: # just skip unknown characters | |
print("Unknown character:", char) | |
continue | |
if char not in current_mode: # switch mode | |
if current_mode == LTRS: | |
print("Switching mode to FIGS") | |
current_mode = FIGS | |
send_character(current_mode.index("FIGS")) | |
elif current_mode == FIGS: | |
print("Switching mode to LTRS") | |
current_mode = LTRS | |
send_character(current_mode.index("LTRS")) | |
# Send char mode at beginning of message and every 72 characters | |
if char_count >= 72 or char_count == 0: | |
print ("Resending mode") | |
if current_mode == LTRS: | |
send_character(current_mode.index("LTRS")) | |
elif current_mode == FIGS: | |
send_character(current_mode.index("FIGS")) | |
# reset counter | |
char_count = 0 | |
print(char) | |
send_character(current_mode.index(char)) | |
time.sleep(char_pause) | |
# increment counter | |
char_count += 1 | |
while True: | |
#send_message("\nADAFRUIT 1234567890 -$!+='()/:;?,. ") | |
#time.sleep(2) | |
#send_message("\nWELCOME TO JOHN PARK'S WORKSHOP!") | |
#time.sleep(3) | |
#send_message("\nWOULD YOU LIKE TO PLAY A GAME?") | |
#time.sleep(5) | |
pressed = set(trellis.pressed_keys) | |
for press in pressed - current_press: | |
x, y = press | |
if press: | |
# print("Pressed:", press) | |
pixel = (press[1] * 8) + press[0] | |
pixel_index = pixel * 256 // 32 | |
trellis.pixels[x, y] = (128,128,128) | |
pixel_code = LTRS[pixel] | |
print (pixel_code) | |
send_character(current_mode.index(pixel_code)) | |
for release in current_press - pressed: | |
if release: | |
# print("Released:", release) | |
for x in range(trellis.pixels.width): | |
for y in range(trellis.pixels.height): | |
pixel_index = ((y * 8) + x) * 256 // 32 | |
trellis.pixels[x, y] = (0,0,0) | |
time.sleep(0.06) | |
current_press = pressed | |
# here's an example of sending a character | |
#send_character(current_mode.index("A")) | |
#time.sleep(char_pause) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment