Skip to content

Commit

Permalink
add the emulator files (needs refactor to support multiple platforms)
Browse files Browse the repository at this point in the history
  • Loading branch information
VzxPLnHqr committed May 6, 2024
1 parent 7da32df commit 1e05e4a
Show file tree
Hide file tree
Showing 7 changed files with 424 additions and 70 deletions.
158 changes: 158 additions & 0 deletions src/seedsigner/emulator/desktopDisplay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
######################################################################
# Seedsigner desktop display driver and button emulator
#
# by: @EnteroPositivo (Twitter, Gmail, GitHub)



import time

from seedsigner.emulator.virtualGPIO import GPIO
from seedsigner.hardware.buttons import HardwareButtons

from tkinter import *
import tkinter as tk


from PIL import ImageTk

import threading
import os

EMULATOR_VERSION = '0.5'


class desktopDisplay(threading.Thread):
"""class for desktop display."""
root=0
def __init__(self):
self.width = 240
self.height = 240

# Multithreading
threading.Thread.__init__(self)
self.start()

def callback(self):
self.root.quit()
self.root.destroy()
# terminate the main thread forcefully
pid = os.getpid()
os.kill(pid,9)

def run(self):
"""run thread"""
self.root = tk.Tk()

from seedsigner.controller import Controller
controller = Controller.get_instance()
title= "SeedSigner Emulator v"+EMULATOR_VERSION+ " / "+controller.VERSION;

print("*****************************************************");
print(title);
print("https://github.com/enteropositivo/seedsigner-emulator");
print("*****************************************************");

self.root.title(title)

self.root.protocol("WM_DELETE_WINDOW", self.callback)
self.root.geometry("480x260+240+240")
self.root.configure(bg='orange')
self.root.iconphoto(False, tk.PhotoImage(file='seedsigner/resources/icons/emulator_icon.png'))
# ....


self.label=Label(self.root)
self.label.pack()

self.joystick=Frame(self.root)
self.joystick.pack()
self.joystick.place(x=20, y=85)
self.joystick.configure(bg='orange')

pixel = tk.PhotoImage(width=1, height=1)


self.btnL = Button(self.joystick, image=pixel, width=20, height=20, command = HardwareButtons.KEY_LEFT_PIN, bg='white')
self.btnL.grid(row=1, column=0)
self.bindButtonClick(self.btnL)

self.btnR = Button(self.joystick, image=pixel, width=20, height=20, command = HardwareButtons.KEY_RIGHT_PIN, bg='white')
self.btnR.grid(row=1, column=2)
self.bindButtonClick(self.btnR)

self.btnC = Button(self.joystick, image=pixel, width=20, height=20, command = HardwareButtons.KEY_PRESS_PIN)
self.btnC.grid(row=1, column=1)
self.bindButtonClick(self.btnC)

self.btnU = Button(self.joystick, image=pixel, width=20, height=20, command = HardwareButtons.KEY_UP_PIN, bg='white')
self.btnU.grid(row=0, column=1)
self.bindButtonClick(self.btnU)

self.btnD = Button(self.joystick, image=pixel, width=20, height=20, command = HardwareButtons.KEY_DOWN_PIN, bg='white')
self.btnD.grid(row=2, column=1)
self.bindButtonClick(self.btnD)

self.btn1 = Button(self.root, image=pixel, width=40, height=20, command = HardwareButtons.KEY1_PIN, bg='white')
self.btn1.place(x=400, y=60)
self.bindButtonClick(self.btn1)

self.btn2 = Button(self.root, image=pixel, width=40, height=20, command = HardwareButtons.KEY2_PIN, bg='white')
self.btn2.place(x=400, y=116)
self.bindButtonClick(self.btn2)

self.btn3 = Button(self.root, image=pixel, width=40, height=20, command = HardwareButtons.KEY3_PIN, bg='white')
self.btn3.place(x=400, y=172)
self.bindButtonClick(self.btn3)


def key_handler(event):

if(event.keysym=="Up"): GPIO.set_input(HardwareButtons.KEY_UP_PIN, GPIO.HIGH)
if(event.keysym=="Down"): GPIO.set_input(HardwareButtons.KEY_DOWN_PIN, GPIO.HIGH)
if(event.keysym=="Left"): GPIO.set_input(HardwareButtons.KEY_LEFT_PIN, GPIO.HIGH)
if(event.keysym=="Right"): GPIO.set_input(HardwareButtons.KEY_RIGHT_PIN, GPIO.HIGH)

if(event.keysym in ("1", "KP_1") ): GPIO.set_input(HardwareButtons.KEY1_PIN, GPIO.HIGH)
if(event.keysym in ("2", "KP_2") ): GPIO.set_input(HardwareButtons.KEY2_PIN, GPIO.HIGH)
if(event.keysym in ("3", "KP_3") ): GPIO.set_input(HardwareButtons.KEY3_PIN, GPIO.HIGH)

if(event.keysym=="Return"): GPIO.set_input(HardwareButtons.KEY_PRESS_PIN, GPIO.HIGH)

self.root.bind("<Key>", key_handler)

self.root.resizable(width = True, height = True)
self.root.mainloop()


def bindButtonClick(self, objBtn):
objBtn.bind("<Button>", self.buttonDown)
objBtn.bind("<ButtonRelease>", self.buttonUp)

def buttonDown(self, objBtn):
gpioID = (objBtn.widget.config('command')[-1])
GPIO.set_input(gpioID, GPIO.HIGH)

def buttonUp(self, objBtn):
gpioID = (objBtn.widget.config('command')[-1])
GPIO.set_input(gpioID, GPIO.LOW)

def setGPIO(self, pin):
GPIO.fire_raise_event(pin)

def ShowImage(self,Image2,Xstart,Ystart):
while(self.root==0): time.sleep(0.1)
imwidth, imheight = Image2.size
if imwidth != self.width or imheight != self.height:
raise ValueError('Image must be same dimensions as display \
({0}x{1}).' .format(self.width, self.height))

self.tkimage= ImageTk.PhotoImage(Image2, master=self.root)
self.label.configure(image=self.tkimage)
self.label.image=self.tkimage
self.label.place(x=125, y=10)


def clear(self):
"""Clear contents of image buffer"""

151 changes: 151 additions & 0 deletions src/seedsigner/emulator/virtualGPIO.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
######################################################################
# virtualGPIO allows program with the same GPIO code as allways
# to test your code on a desktop enviroment
#
# by: @EnteroPositivo (Twitter, Gmail, GitHub)
#
# Code adapted from: https://roderickvella.wordpress.com/2016/06/28/raspberry-pi-gpio-emulator/


import time


dictionaryPins = {}
raisedPin=""

class GPIO:

#constants
LOW = 0
HIGH = 1
OUT = 2
IN = 3
PUD_OFF = 4
PUD_DOWN = 5
PUD_UP = 6
BCM = 7
BOARD = 101

SLEEP_TIME_S=0.1
SLEEP_TIME_L=1.5

#GPIO LIBRARY Functions
def setmode(mode):
time.sleep(GPIO.SLEEP_TIME_L)

def setwarnings(flag):
pass

def setup(channel, state, initial=-1,pull_up_down=-1):
global dictionaryPins

#check if channel is already setup
if str(channel) in dictionaryPins:
raise Exception('GPIO is already setup')

if(state == GPIO.OUT):
#GPIO is set as output, default OUT 0
objTemp = PIN("OUT")
if(initial == GPIO.HIGH):
objTemp.Out = "1"

dictionaryPins[str(channel)] =objTemp
#drawGPIOOut(channel)

elif(state == GPIO.IN):
#set input
objTemp = PIN("IN")
if(pull_up_down == -1):
objTemp.pull_up_down = "PUD_DOWN" #by default pud_down
objTemp.In = "0"
elif(pull_up_down == GPIO.PUD_DOWN):
objTemp.pull_up_down = "PUD_DOWN"
objTemp.In = "0"

elif(pull_up_down == GPIO.PUD_UP):
objTemp.pull_up_down = "PUD_UP"
objTemp.In = "1"

#drawBindUpdateButtonIn(str(channel),objTemp.In)
dictionaryPins[str(channel)] =objTemp


def output(channel, outmode):
global dictionaryPins
channel = str(channel)

if channel not in dictionaryPins:
#if channel is not setup
raise Exception('GPIO must be setup before used')
else:
objPin = dictionaryPins[channel]
if(objPin.SetMode == "IN"):
#if channel is setup as IN and used as an OUTPUT
raise Exception('GPIO must be setup as OUT')


if(outmode != GPIO.LOW and outmode != GPIO.HIGH):
raise Exception('Output must be set to HIGH/LOW')

objPin = dictionaryPins[channel]
if(outmode == GPIO.LOW):
objPin.Out = "0"
elif(outmode == GPIO.HIGH):
objPin.Out = "1"


def input(channel):
global dictionaryPins, raisedPin
global raisedPin

#print(channel)
channel = str(channel)

if channel not in dictionaryPins:
#if channel is not setup
raise Exception('GPIO must be setup before used')
else:
objPin = dictionaryPins[channel]
if(objPin.SetMode == "OUT"):
#if channel is setup as OUTPUT and used as an INPUT
raise Exception('GPIO must be setup as IN')

objPin = dictionaryPins[channel]

if(channel==raisedPin):
raisedPin=""
return GPIO.LOW
else:
return GPIO.HIGH



def cleanup():
pass

def add_event_detect(channel,edge,callback):
GPIO.risecallback=callback

def set_input(gpioID, state):
global raisedPin

#print( "Emulator GPIO:", gpioID, " = ", str(state))
if(state==GPIO.HIGH):
raisedPin=str(gpioID)
GPIO.risecallback(gpioID)
else:
raisedPin=""





class PIN():
SetMode = "None" #IN/OUT/NONE
Out = "0"
pull_up_down = "PUD_OFF" #PUD_UP/PUD_DOWN/PUD_OFF
In = "1"

def __init__(self, SetMode):
self.SetMode = SetMode
self.Out = "0"
73 changes: 73 additions & 0 deletions src/seedsigner/emulator/webcamvideostream.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@

from threading import Thread
import time
import cv2


class WebcamVideoStream:
def __init__(self, resolution=(320, 240), framerate=32, format="bgr", **kwargs):
# initialize the camera
self.camera = cv2.VideoCapture(0)
self.set_resolution(resolution)

# initialize the frame and the variable used to indicate
# if the thread should be stopped
self.frame = None
self.should_stop = False
self.is_stopped = True

def start(self):
# start the thread to read frames from the video stream
t = Thread(target=self.update, args=())
t.daemon = True
t.start()
self.is_stopped = False
return self

def hasCamera(self):
return self.camera.isOpened()

def update(self):

if self.hasCamera():

# keep looping infinitely until the thread is stopped
while(not self.should_stop):
# grab the frame from the stream and clear the stream in
# preparation for the next frame
ret, stream = self.camera.read()
stream = cv2.resize(stream, (240,240))
stream = cv2.cvtColor(stream,cv2.COLOR_BGR2RGB)
time.sleep(0.05)
self.frame = stream

self.is_stopped = True
self.should_stop = False
return

else:
self.is_stopped = True
self.should_stop = False
return



def read(self):
return self.frame

def single_frame():
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
return frame

def stop(self):
# indicate that the thread should be stopped
self.should_stop = True

# Block in this thread until stopped
while not self.is_stopped:
pass

def set_resolution(self, resolution):
self.camera.set(3, resolution[0])
self.camera.set(4, resolution[1])
Loading

0 comments on commit 1e05e4a

Please sign in to comment.