1. System IR remote information

eInstruction CPS-IR

8-button infrared remotes with unique serial numbers.

ir remote

1.1. Framing

scope measurements, in microseconds:

  • start: 976

  • pause: 524

  • pulse: 160

  • zero: 472

  • one: 952

Circuit has a 455 kHz oscillator. 2.20 us period.

  • LED ON time is 3 clocks.

  • LEF OFF time is 9 clocks.

  • Period tick is 12 clocks / 26.37 us / 37.917 kHz.

  • start burst is 38 pulses. 1002.2 us or ON-ON of 995.6 us.

  • short burst is 7 LED pulses. 184us period, but ON-ON width is 178 us.

  • zero gap is 476 us from start of last to start of next LED pulse. 18 ticks, so gap is 17 ticks.

  • one gap is 950 us from start of last to start of next LED pulse. 36 ticks, gap is 35 ticks.

1.2. Sample decodes

1.2.1. Serial 10679c

prefix

0001 0000 0110 0111 1001 1100 0x10679c

A

0110 0011 0x10679c63

B

0111 0010 0x10679c72

C

1010 1111 0x10679caf

D

1110 1011 0x10679ceb

E

1011 1110 0x10679cbe

F

1111 1010 0x10679cfa

G

1001 1100 0x10679c9c

H

1101 1000 0x10679cd8

1.2.2. Serial 1051b5

prefix

0001 0000 0101 0001 1011 0101 0x1051b5

A

1000 0011 0x1051b583

B

1001 0010 0x1051b592

C

0100 1111 0x1051b54f

D

0000 1011 0x1051b50b

E

0101 1110 0x1051b55e

F

0001 1010 0x1051b51a

G

0111 1100 0x1051b57c

H

0011 1000 0x1051b538

1.3. Encoding scheme

Bits are in time-order. Leftmost bit is the first received from the packet.

iiii iiii iiii iiii iiii iiii xxxx kkkk (32 bits)

octets

id[2], id[1], id[0], b

b octet

xxxx kkkk
upper 4-bits of last byte have unknown meaning.

keymap 0x0f & key
  • 0x03 → A

  • 0x02 → B

  • 0x0f → C

  • 0x0b → D

  • 0x0e → E

  • 0x0a → F or *

  • 0x0c → G or <

  • 0x08 → H or >

2. Morse code library

Place this file onto your Maker Pi board using Thonny
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
import board
import re
import simpleio
import time

PIEZO_PIN = board.GP22

MORSE_PITCH = 700
dot = 0.050   # = 60 / (50 * WPM)
dash = 3*dot
gap = dot
lspace = 3*dot
wspace = 7*dot

MORSE_CHARS = {
    "A":(dot, dash),
    "B":(dash, dot, dot, dot),
    "C":(dash, dot, dash, dot),
    "D":(dash, dot, dot),
    "E":(dot,),
    "F":(dot, dot, dash, dot),
    "G":(dash, dash, dot),
    "H":(dot, dot, dot, dot),
    "I":(dot, dot),
    "J":(dot, dash, dash, dash),
    "K":(dash, dot, dash),
    "L":(dot, dash, dot, dot),
    "M":(dash, dash),
    "N":(dash, dot),
    "O":(dash, dash, dash),
    "P":(dot, dash, dash, dot),
    "Q":(dash, dash, dot, dash),
    "R":(dot, dash, dot),
    "S":(dot, dot, dot),
    "T":(dash,),
    "U":(dot, dot, dash),
    "V":(dot, dot, dot, dash),
    "W":(dot, dash, dash),
    "X":(dash, dot, dot, dash),
    "Y":(dash, dot, dash, dash),
    "Z":(dash, dash, dot, dot),
    
    "0":(dash, dash, dash, dash, dash),
    "1":(dot, dash, dash, dash, dash),
    "2":(dot, dot, dash, dash, dash),
    "3":(dot, dot, dot, dash, dash),
    "4":(dot, dot, dot, dot, dash),
    "5":(dot, dot, dot, dot, dot),
    "6":(dash, dot, dot, dot, dot),
    "7":(dash, dash, dot, dot, dot),
    "8":(dash, dash, dash, dot, dot),
    "9":(dash, dash, dash, dash, dot),
    
    # https://en.wikipedia.org/wiki/Prosigns_for_Morse_code
    "?":(dot, dot, dash, dash, dot, dot), # ? - SAY AGAIN or indicate question/request
    "AR":(dot, dash, dot, dash, dot),  # AR - OUT - end of message
    "AS":(dot, dash, dot, dot, dot),   # AS - WAIT - I must pause for a few minutes
    "VE":(dot, dot, dot, dash, dot),   # VE - VERIFIED - message is verified
    "BT":(dash, dot, dot, dot, dash),  # BT - BREAK - start new section
    "KA":(dash, dot, dash, dot, dash), # KA - ATTENTION - new message
    "SK":(dot, dot, dot, dash, dot, dash), # SK - OUT - end of contact
}


def morse_char(c):
    if c == " ":
        time.sleep(wspace - lspace)
    else:
        try:
            for t in MORSE_CHARS[c]:
                simpleio.tone(PIEZO_PIN, MORSE_PITCH, duration=t)
                time.sleep(dot)
        except KeyError:
            # unknown character
            simpleio.tone(PIEZO_PIN, MORSE_PITCH/3, duration=dot)


RE_PROSIGN_START = re.compile("<")
RE_PROSIGN_END = re.compile(">")
def morse(string):
    first, *rest = RE_PROSIGN_START.split(string, 1)
    # rest is a list of length 0 or 1
    
    for c in first.upper():
        morse_char(c)
        time.sleep(lspace)

    # send a prosign
    if rest:
        prosign, *rest = RE_PROSIGN_END.split(rest[0], 1)
        # rest is a list of length 0 or 1
        morse_char(prosign.upper())
        time.sleep(lspace)
    
    # send what's after the prosign
    if rest:
        # recursion!  seems an appropriate solution to handle an
        # arbitrary number of embedded prosigns
        morse(rest[0])
        

2.1. Example usage

1
2
3
4
5
# this library must be ON your Maker Pi board
# either in the root directory or under lib/ folder
import morse

morse.morse("CQ CQ CQ DE AD0CQ")

3. Infrared remote code

Place this file onto your Maker Pi board using Thonny
Example code to start
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

# Circuit Playground Express Demo Code
# Adjust the pulseio 'board.PIN' if using something else
import pulseio
import board
import adafruit_irremote
import digitalio


import time
from time import sleep, monotonic
import board
import pwmio
import digitalio

# This "morse.py" needs to be downloaded to your board
import morse


# button setup
button0 = digitalio.DigitalInOut(board.GP20)
button1 = digitalio.DigitalInOut(board.GP21)
button0.direction = button1.direction = digitalio.Direction.INPUT

pulsein = pulseio.PulseIn(board.GP26, maxlen=120, idle_state=True)
decoder = adafruit_irremote.GenericDecode()

def match_pulse(p, expected, tolerance=50):
    for idx, e in enumerate(expected):
        err = p - e
        #print("match:", idx, p, e, err)
        if abs(err) <= tolerance:
            return (idx, err)
    return (None, None)


def decode_bit(pulses):
    sync = 1000
    start = 476
    burst = 150
    mark = 950
    space = 476
    
    header = [sync, start, burst]
    data = [space, mark]
    gap = [burst]
    
    piter = iter(pulses)
    for h in header:
        p = next(piter)
        value, err = match_pulse(p, [h])
        #print("header:", value)
    
    while True:
        try:
            p = next(piter)
            bit, err = match_pulse(p, data)
            #print("bit:", bit)
            
            p = next(piter)
            burst, err = match_pulse(p, gap)
            #print("burst:", burst)
            yield bit
        except StopIteration:
            break
    
def decode(p, n_pulses=67):
    if len(p) != n_pulses:
        #print(f"Invalid packet: expected {n_pulses}, got {len(p)} pulses")
        return None
    
    bits = []
    for bit in decode_bit(p):
        if bit is not None:
            bits.append(bit)
        else:
            return None

    return bits


def bits2bytes(bits, n=4):
    values = [0] * n
    for idx, bit in enumerate(bits):
        byte, i = divmod(idx, 8)
        values[byte] += bit << (7-i)
    return values
        
            
    

REMOTE_KEY = {
    0x03: "A",
    0x02: "B",
    0x0f: "C",
    0x0b: "D",
    0x0e: "E",
    0x0a: "F",
    0x0c: "G",
    0x08: "H",
}
    


def remote_morse():
    print("****************************")
    print("*** IR remote morse code ***")
    print("****************************")
    
    last_key = ""
    while (button0.value == 1) or (button1.value == 1):
        pulses = decoder.read_pulses(pulsein, blocking=False, pulse_window=0.1)

        if pulses is None:
            continue
        
        bits = decode(pulses)
        if not bits:
            print("bad packet:", bits)
            continue
        
        #print(len(bits), bits)
        values = bits2bytes(bits)
        #print(values)
        print([hex(v) for v in values])
        
        key = REMOTE_KEY[values[3] & 0x0f]

        morse.morse_char(key)       
        last_key = key
        
        #print("----------------------------")







while True:
    remote_morse()
    time.sleep(1)