# This is a public domain (CC0 1.0 license) code by Wojciech M. Zabołotny ([email protected])
# demonstrating the two-key text input method described in
# https://www.reddit.com/r/embedded/comments/1csplu9/twokey_procedure_for_entering_text_in_an_embedded/
#
# The buttons are assigned to keys "Q" qnd "W" (you must click on the image of the system before)
# Selection of group is done with E2i1
# Selection of subgroup is done with E1 and E2
# Space is entered with E1i2*1
# Removal of the last character is done with E1i2*2
# Clearing the whole text is done with E1i2*3
# Sending the text is done with E1f2
#
# The events are defined as below:
#
# Event Wavevorfm
# E1 In1: __/‾‾\__
# In2: ________
#
# E2 In1: ________
# In2: __/‾‾\__
#
# E1i2 In1: ___/‾‾\___
# In2: _/‾‾‾‾‾‾\_
#
# E1i2*N In1: ___/‾‾\__[N pulses]_/‾‾\___
# In2: _/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\_
#
# E2i1 In1: _/‾‾‾‾‾‾\_
# In2: ___/‾‾\___
#
# E2i1*N In1: _/‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\_
# In2: ___/‾‾\__[N pulses]_/‾‾\___
#
# E1f2 In1: _/‾‾‾‾\_____
# In2: ____/‾‾‾‾\__
#
# E2f1 In1: ____/‾‾‾‾\__
# In2: _/‾‾‾‾\_____
print("Text inputs with buttons")
import machine as m
import collections
import asyncio
# Define a class implementing states for a state machine
class eventgen:
# Constants defining states
IDLE = 1
P1 = 2
P2 = 3
P12 = 4
P21 = 5
P120 = 6
P210 = 7
P212 = 8
P121 = 9
WAIT_IDLE = 10
# Constructor connecting pins and queue
def __init__(self,pin1,pin2,queue,sync):
# Functions for getting the pin state (possibly inverted)
self.p1 = lambda : 1-pin1.value()
self.p2 = lambda : 1-pin2.value()
self.sync = sync
self.queue = queue
self.reps = 0
self.st = self.IDLE
def send(self,msg):
self.queue.append(msg)
self.sync.set()
def pin_change(self,t): # The function called when the buttons are settled
while True:
p1 = self.p1()
p2 = self.p2()
#print(p1,p2,self.reps, self.st)
if self.st == self.IDLE:
self.reps = 0
if p1 == 0 and p2 == 0:
return
elif p1 == 1:
self.st = self.P1
elif p2 == 1:
self.st = self.P2
elif self.st == self.P1:
if p1 == 1 and p2 == 0:
return
elif p1 == 0:
self.send(("E1",1))
self.st = self.IDLE
elif p2 == 1:
self.st = self.P12
elif self.st == self.P2:
if p1 == 0 and p2 == 1:
return
elif p2 == 0:
self.send(("E2",1))
self.st = self.IDLE
elif p1 == 1:
self.st = self.P21
elif self.st == self.P12:
if p1 == 1 and p2 == 1:
return
elif p2 == 0:
self.reps = self.reps + 1
self.st = self.P120
elif p1 == 0:
self.st = self.P121
elif self.st == self.P121:
if p1 == 0 and p2 == 1:
return
if p2 == 0:
self.send(("E1f2",int(self.reps)))
self.st = self.IDLE
elif self.st == self.P120:
if p1 == 1 and p2 == 0:
return
elif p1 == 0:
self.send(("E2i1",int(self.reps)))
self.st = self.IDLE
elif p2 == 1:
self.st = self.P12
elif self.st == self.P21:
if p1 == 1 and p2 == 1:
return
elif p1 == 0:
self.reps = self.reps + 1
self.st = self.P210
elif p2 == 0:
self.st = self.P212
elif self.st == self.P212:
if p1 == 1 and p2 == 0:
return
if p1 == 0:
self.send(("E2f1",int(self.reps)))
self.st = self.IDLE
elif self.st == self.P210:
if p1 == 0 and p2 == 1:
return
elif p2 == 0:
self.send(("E1i2",int(self.reps)))
self.st = self.IDLE
elif p1 == 1:
self.st = self.P21
elif self.st == self.WAIT_IDLE:
if p1 == 0 and p2 == 0:
self.st = self.IDLE
else:
return
# Class responsible for text editing
class textentry:
def __init__(self,groups):
self.groups = groups
self.grnr = 0
self.gr = groups[0]
self.start = 0
self.end = len(self.gr)
self.split = 0
self.txt = ""
def disp(self):
self.split = (self.start + self.end) // 2
if self.start == self.split:
# The character is already selected, print it
self.txt += self.gr[self.start]
self.start = 0
self.end = len(self.gr)
self.split = (self.start + self.end) // 2
if self.split > self.start + 1:
sel = "["+self.gr[self.start]+" "+self.gr[self.split-1]+"]"
else:
sel = "["+self.gr[self.start]+"]"
if self.split < self.end - 1:
sel += "["+self.gr[self.split]+" "+self.gr[self.end-1]+"]"
else:
sel += "["+self.gr[self.split]+"]"
#print(start,split,end)
print("Text: \"" + self.txt + "\"")
print(sel)
def process(self,event):
if event[0] == "E1":
self.end = self.split
elif event[0] == "E2":
self.start = self.split
elif event[0] == "E1i2":
if event[1] == 1:
self.txt += " "
elif event[1] == 2:
self.txt = self.txt[:-1]
else:
self.txt = ""
elif event[0] == "E1f2":
print("Transmitted msg:"+self.txt)
self.txt = ""
self.start = 0
self.end = len(self.gr)
elif event[0] == "E2i1":
self.grnr = (self.grnr + 1) % len(self.groups)
self.gr = self.groups[self.grnr]
self.start = 0
self.end = len(self.gr)
else:
print("Unknown key")
q = collections.deque((),32)
settling_time = 10 # Set to 1000 to see how does it work
Pin1=m.Pin(32,m.Pin.IN, m.Pin.PULL_UP)
Pin2=m.Pin(33,m.Pin.IN, m.Pin.PULL_UP)
t1 = m.Timer(1)
# Create groups of characters
uc_letters=[]
for i in range(ord('A'),ord('Z')+1):
uc_letters.append(chr(i))
lc_letters=[]
for i in range(ord('a'),ord('z')+1):
lc_letters.append(chr(i))
digits=[]
for i in range(ord('0'),ord('9')+1):
digits.append(chr(i))
specs=list("\"\'{}[]!?@#$%^&*()-+=/\\,.<>")
groups=[uc_letters, lc_letters, digits, specs]
te = textentry(groups)
sync_ev = asyncio.ThreadSafeFlag()
ev = eventgen(Pin1,Pin2,q, sync_ev)
def p_cb(p):
t1.init(mode=m.Timer.ONE_SHOT, period=settling_time, callback=ev.pin_change)
Pin1.irq(trigger=m.Pin.IRQ_RISING | m.Pin.IRQ_FALLING, handler=p_cb)
Pin2.irq(trigger=m.Pin.IRQ_RISING | m.Pin.IRQ_FALLING, handler=p_cb)
async def main():
te.disp()
while True:
await sync_ev.wait()
sync_ev.clear()
while True:
try:
w = q.popleft()
te.process(w)
te.disp()
except Exception as e:
break
asyncio.run(main())