# Esp32 - Morse reader using kit FKS0001 microphone = SOUND
# Copytight by Anthony Vogelaar, Perth Scotland
# rev 0.10  2025-09-24  initial release

import	time
from	collections	import	deque

# Implemented:
# MorseReader(microphone)
#    MorseReader.sample()
#    MorseReader.getWord()		-->  s
#    MorseReader.printTiming()

# ===================== MorseWriter ======================

# dictionary with morse codes
morse_codes = {
    '.-'   : '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',
    '.----': '1',	'..---': '2',	'...--': '3',	'....-': '4',
    '.....': '5',	'-....': '6',	'--...': '7',	'---..': '8',
    '----.': '9',	'-----': '0'}


# morse reader for 10 words per minute
class MorseReader:
    
    def __init__(self, microphone):
        self.mic     = microphone
        self.que     = deque((), 14)
            

    # Highest Morse epeed = 30 words per minute
    # --> tick = 60000 / (30 * 50) = 40 mSec
    # sample rate = 40 mSec / 10 = 4 mSec, treshold 500_000
    def sample(self):
        for _ in range(14):				# 4 mSec  / 320 uSec
            self.que.append(self.mic.read_uv())
        return max(list(self.que)) >  500_000
    

    # blocking, return received word. implemented  for 20 words per minute
    def getWord(self):
        
        # return high time in sample periods @ 4 mSec
        def tHigh():
            n = 0
            while not self.sample():
                n += 1;
                if n > 1000: return				# time out, no morse received
            n = 0; armed = False
            while True:
                if self.sample():
                    n += 1; armed = False
                else:
                    if armed : return n
                    armed = True
                    
            
        # return low time in sample periods @ 4 mSec
        def tLow():
            while self.sample(): pass
            n = 0; armed = False
            while True:
                if not self.sample():
                    n += 1; armed = False
                    if n > 400: return n
                else:
                    if armed : return n
                    armed = True
            
            
        ch = ''; word = ''
        while True:
            if not (th := tHigh()): return
            m = '.' if th < 110 else '-'
            ch += m
            dt = tLow()
            if dt > 80:
                word += morse_codes[ch]
                ch=''
            if dt > 263: return word
        
        
    # test method, prints high and low times
    def printTiming(self):
        last = False
        n    = 0
        while True:
            s = self.sample()
            if s == last: n += 1
            else:
                if n: print(last, n)
                last = s; n = 0
  
# ======================= test ============================

if __name__ == '__main__':
    
    from	machine	import	Pin, ADC
    
    pin_mic = Pin(33, Pin.IN, Pin.PULL_UP)
    adc_mic = ADC(pin_mic, atten=ADC.ATTN_0DB)				# 0..950 mV
    buzzer	= Pin(32, Pin.OUT, value=0)

    def test():
        reader = MorseReader(adc_mic)
        for i in range(10):
            print(i, reader.sample())
        buzzer(1)
        for i in range(10):
            time.sleep_ms(200)
            print(i, reader.sample())
        buzzer(0)
        for i in range(5):
            print(i, reader.sample())
            time.sleep_ms(100)
        
            
            
    test()

# ======================= end =============================
