# 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

# ===================== 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 20 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():
            while not self.sample(): pass
            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:
            m = '.' if tHigh() < 75 else '-'
            ch += m
            dt = tLow()
            if dt > 75:
                word += morse_codes[ch]
                ch=''
            if dt > 190: 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
    
    
    def test1():
        pin_mic = Pin(33, Pin.IN, Pin.PULL_UP)
        adc_mic = ADC(pin_mic, atten=ADC.ATTN_0DB)				# 0..950 mV
        MorseReader(adc_mic).printTiming()


    # print received morse words by microphone @ 20 words per minute
    def test2():
        pin_mic = Pin(33, Pin.IN, Pin.PULL_UP)
        adc_mic = ADC(pin_mic, atten=ADC.ATTN_0DB)				# 0..950 mV
        m_reader = MorseReader(adc_mic)
        while True:
            print(m_reader.getWord(), end=' ')
        
        
    test2()
    
# ======================= end =============================
