diff --git a/light.py b/light.py new file mode 100644 index 0000000..816f772 --- /dev/null +++ b/light.py @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: MIT +# +# Little christmas candle +# +# Author: Nils Freydank +# Year: 2022 +# Copyright: MIT + +import machine +import time + +pin_led1 = machine.Pin(22) +pin_led2 = machine.Pin(10) +pin_led3 = machine.Pin(8) + + +def _get_new_duty( + factor: int, signed_step_width: int, limit_lower: int, limit_upper: int +) -> Tuple[int, int]: + duty_fac: int = factor + + duty_fac += signed_step_width + if duty_fac > limit_upper: + duty_fac = limit_upper + signed_step_width = (-1) * signed_step_width + elif duty_fac < limit_lower: + duty_fac = limit_lower + signed_step_width = (-1) * signed_step_width + + return duty_fac, signed_step_width + + +def flicker() -> None: + pwm_led1 = machine.PWM(pin_led1) + pwm_led2 = machine.PWM(pin_led2) + pwm_led3 = machine.PWM(pin_led3) + + pwm_led1.freq(1_000) + pwm_led2.freq(1_000) + pwm_led3.freq(1_000) + + duty_fac_led1: int = 0 + signed_step_width_led1: int = 1 + duty_fac_led2: int = 0 + signed_step_width_led2: int = 1 + duty_fac_led3: int = 0 + signed_step_width_led3: int = 1 + + delay_s: float = 0.01 + + while True: + duty_fac_led1, signed_step_width_led1 = _get_new_duty( + duty_fac_led1, signed_step_width_led1, 240, 250 + ) + pwm_led1.duty_u16(duty_fac_led1**2) + + duty_fac_led2, signed_step_width_led2 = _get_new_duty( + duty_fac_led2, signed_step_width_led2, 0, 200 + ) + pwm_led2.duty_u16(duty_fac_led2**2) + + duty_fac_led3, signed_step_width_led3 = _get_new_duty( + duty_fac_led3, signed_step_width_led3, 30, 100 + ) + pwm_led2.duty_u16(duty_fac_led3**2) + + time.sleep(delay_s) + + +# vim:fileencoding=utf-8:ts=4:syntax=python:expandtab diff --git a/main.py b/main.py new file mode 100644 index 0000000..f424dce --- /dev/null +++ b/main.py @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: MIT +# +# Little christmas candle +# +# Author: Nils Freydank +# Year: 2022 +# Copyright: MIT + +import _thread + +from light import flicker +from music import play_music + +if __name__ == "__main__": + _thread.start_new_thread(flicker, ()) + play_music() + +# vim:fileencoding=utf-8:ts=4:syntax=python:expandtab diff --git a/music.py b/music.py new file mode 100644 index 0000000..01965a4 --- /dev/null +++ b/music.py @@ -0,0 +1,129 @@ +# SPDX-License-Identifier: MIT +# +# Little christmas candle +# +# Author: Nils Freydank +# Year: 2022 +# Copyright: MIT + +import json +import machine +import time + +pin_piezo = machine.Pin(12) + + +def _note_to_freq(note_name: str) -> int: + """ + Translate note names from strings into frequency values. + """ + freq: int = 0 + + # frequencies, see e.g. + # https://en.wikipedia.org/wiki/Piano_key_frequencies + note_names: dict = { + "c4": 262, + "cis4": 277, + "d4": 294, + "e4": 330, + "f4": 349, + "fis4": 370, + "g4": 392, + "a4": 440, + "b4": 466, + "h4": 494, + "c5": 523, + "cis5": 554, + "d5": 587, + "e5": 659, + "f5": 698, + "fis5": 740, + "g5": 784, + "a5": 880, + "b5": 923, + "h5": 988, + "c6": 1047, + } + + try: + freq = note_names[note_name] + except KeyError: + freq = -1 + print(f"exc in _note_to_freq() for {note_name}.") + + return freq + + +def _len_to_second(len_name: str, len_scaling: float = 0.6) -> float: + time: float = 0.0 + + len_names = { + "_t": 0.125 * len_scaling, # thirty-second note or pause + "_s": 0.25 * len_scaling, # sixteenth note or pause + "_e": 0.5 * len_scaling, # eight note or pause + "_q": 1.0 * len_scaling, # quarter note or pause + "_h": 2.0 * len_scaling, # half note or pause + "_f": 4.0 * len_scaling, # whole note or pause + } + try: + for elem in len_name.split("+"): + time += len_names[elem.replace(" ", "")] + except KeyError: + time = 0.0 + print(f"exc in _len_to_second() for {elem}.") + print("Do you have a typo there, e.g. 'a' + 'b' instead of 'a + b'?") + + return time + + +def play_note(note_name: str, duration: str, loud: bool = False) -> int: + if loud: + duty_cycle: int = 16_000 + else: + duty_cycle: int = 400 + real_pwm_freq: int = 0 + + pwm_piezo = machine.PWM(pin_piezo) + pwm_piezo.freq(_note_to_freq(note_name)) + real_pwm_freq = pwm_piezo.freq() + + pwm_piezo.duty_u16(duty_cycle) + + time.sleep(_len_to_second(duration)) + pwm_piezo.duty_u16(0) + time.sleep(0.05) + + return real_pwm_freq + + +def pause(duration: str) -> None: + time.sleep(_len_to_second(duration)) + time.sleep(0.05) + + +def play_music(file_name: str = "o-tannenbaum.json") -> None: + data: dict = {} + + try: + with open(file_name, "r") as f: + data = json.load(f) + + try: + if data["metadata"]["format"] == "json-0.1.0": + for notes in data["music"]: + for note in notes.keys(): + if note == "0": + pause(notes[note]) + else: + play_note(note, notes[note]) + elif data["metadata"]["format"] == "json-0.2.0": + pass + + except: + print(f"play_oh_tannnebaum() failed to parse {data}.") + + except FileNotFoundError: + print("Could not load the JSON file containing the song's details.") + + +# vim:fileencoding=utf-8:ts=4:syntax=python:expandtab