-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add the emulator files (needs refactor to support multiple platforms)
- Loading branch information
Showing
7 changed files
with
424 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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""" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) |
Oops, something went wrong.