Skip to content

Code — morse

# Copyright 2024 Allen Synthesis
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from europi import *
from europi_script import EuroPiScript
BLANK_LETTER = "."
morse_alphabet = {
BLANK_LETTER: "",
"A": ".-",
"B": "-...",
"C": "-.-.",
"D": "-..",
"E": ".",
"F": "..-.",
"G": "--.",
"H": "....",
"I": "..",
"J": ".---",
"K": "-.-",
"L": ".-..",
"M": "--",
"N": "-.",
"O": "---",
"P": ".--.",
"Q": "--.-",
"R": ".-.",
"S": "...",
"T": "-",
"U": "..-",
"V": "...-",
"W": ".--",
"X": "-..-",
"Y": "-.--",
"Z": "--..",
"0": "-----",
"1": ".----",
"2": "..---",
"3": "...--",
"4": "....-",
"5": ".....",
"6": "-....",
"7": "--...",
"8": "---..",
"9": "----.",
}
valid_letters = [
BLANK_LETTER,
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
]
class Morse(EuroPiScript):
dot_high_out = cv1
dot_high_latched_out = cv2
dot_low_out = cv4
dot_low_latched_out = cv5
end_of_letter_out = cv3
end_of_word_out = cv6
def __init__(self):
super().__init__()
default_letters = [BLANK_LETTER] * 16
saved_cfg = self.load_state_json()
self.letters = saved_cfg.get("letters", default_letters)
# the index of the letter the user is hovering over
self.hover_letter = 0
self.ui_dirty = True
self.gate_recvd = False
self.sequence_counter = 0
@b1.handler
def on_b1_press():
# advance the letter 1 place down
idx = valid_letters.index(self.letters[self.hover_letter])
idx = (idx - 1) % len(valid_letters)
self.letters[self.hover_letter] = valid_letters[idx]
self.ui_dirty = True
@b2.handler
def on_b2_press():
# advance the letter 1 place up
idx = valid_letters.index(self.letters[self.hover_letter])
idx = (idx + 1) % len(valid_letters)
self.letters[self.hover_letter] = valid_letters[idx]
self.ui_dirty = True
@din.handler
def on_din_rise():
self.gate_recvd = True
@din.handler_falling
def on_din_fall():
# turn off the unlatched CVs
self.dot_low_out.off()
self.dot_high_out.off()
self.end_of_letter_out.off()
self.end_of_word_out.off()
def save_state(self):
cfg = {
"letters": self.letters
}
self.save_state_json(cfg)
def expand_sequence(self):
"""
Expand the more representation to a boolean sequence
We treat "." as True and "-" as False
"""
seq = []
for ch in self.letters:
if ch is not None:
for bit in morse_alphabet[ch]:
if bit == ".":
seq.append(True)
else:
seq.append(False)
return seq
def get_letter_ends(self):
seq = []
n = 0
for ch in self.letters:
if ch is not None:
n += len(morse_alphabet[ch])
seq.append(n)
return seq
def draw(self):
top = OLED_HEIGHT // 2 - CHAR_HEIGHT // 2
left = OLED_WIDTH // 2 - (CHAR_WIDTH * len(self.letters)) // 2
oled.fill(0)
w = ''
for ch in self.letters:
if ch is None:
w = w + "."
else:
w = w + ch
oled.text(
w,
left,
top,
1
)
oled.line(
left + self.hover_letter * CHAR_WIDTH,
top + CHAR_HEIGHT + 1,
left + (self.hover_letter + 1) * CHAR_WIDTH,
top + CHAR_HEIGHT + 1,
1
)
oled.show()
def main(self):
while True:
letter = int(k2.percent() * len(self.letters))
if letter == len(self.letters):
# .percent() can return 1, so avoid out-of-bounds issues
letter -= 1
if letter != self.hover_letter:
self.hover_letter = letter
self.ui_dirty = True
if self.ui_dirty:
self.ui_dirty = False
self.draw()
sequence = self.expand_sequence()
end_of_letters = self.get_letter_ends()
if len(sequence) == 0:
turn_off_all_cvs()
continue
# kick out if there's no gate to process
if not self.gate_recvd:
continue
self.gate_recvd = False
# if we've modified the sequence, check for overflow
if self.sequence_counter >= len(sequence):
self.sequence_counter = 0
# play the sequence
if sequence[self.sequence_counter]:
self.dot_high_out.on()
self.dot_high_latched_out.on()
self.dot_low_out.off()
self.dot_low_latched_out.off()
else:
self.dot_high_out.off()
self.dot_high_latched_out.off()
self.dot_low_out.on()
self.dot_low_latched_out.on()
# increment
self.sequence_counter += 1
# check for end of letter
if self.sequence_counter in end_of_letters:
self.end_of_letter_out.on()
# check for end of word
if self.sequence_counter == len(sequence):
self.end_of_word_out.on()
if __name__ == "__main__":
Morse().main()