#! /usr/bin/python # Commands: # C[R,G,B] - clear to colour (or off if no RGB provided) # R[rot] - rotate by rot (0,90,180,270) # P[x,y,R,G,B]+ - set individual pixel(s) to a colour # T[R,G,B[,R,G,B][,S]:]Message - scroll a message (nb: if message contains ':' it must be prefixed with ':') # if message is a single char, uses show_letter instead # F[H|V] - flip horizontal|vertical # X[0|1] - high frequency reporting (accel/gyro/orientation/compass) off|on # Y[0|1] - low frequency reporting (temperature/humidity/pressure) off|on # D[0|1] - Set light level low|high # # Outputs: # Xaccel.x,y,z,gyro.x,y,z,orientation.roll,pitch,yaw,compass # Ytemperature,humidity,pressure # K[U|L|R|D|E][0|1|2] - joystick event: direction,state import io import os import sys import glob import time import errno import ctypes import select import struct import inspect import threading from sense_hat import SenseHat try: StandardError # Python 2 except NameError: StandardError = Exception # Python 3 EVENT_FORMAT = 'llHHI' EVENT_SIZE = struct.calcsize(EVENT_FORMAT) EVENT_NAMES = {103:'U',105:'L',106:'R',108:'D',28:'E'} def get_stick(): for evdev in glob.glob('/sys/class/input/event*'): try: with io.open(os.path.join(evdev, 'device', 'name'), 'r') as f: if f.read().strip() == 'Raspberry Pi Sense HAT Joystick': return os.path.join('/dev', 'input', os.path.basename(evdev)) except IOError as e: sys.exit(1) sys.exit(1) stick_file = io.open(get_stick(),'rb') SH = SenseHat() SH.set_rotation(0) SH.clear() files = [sys.stdin,stick_file] last_hf_time = time.time() last_lf_time = time.time() hf_interval = 0.09 # Approx 10/s lf_interval = 1 hf_enabled = False lf_enabled = False scroll = None class ScrollThread(threading.Thread): def __init__(self,fcol,bcol,speed,message): threading.Thread.__init__(self) self.fcol = fcol self.bcol = bcol self.message = message self.speed = speed def run(self): global SH old_rotation = SH.rotation try: SH.show_message(self.message,text_colour=self.fcol,back_colour=self.bcol,scroll_speed=self.speed) except: try: SH.set_rotation(old_rotation,False) SH.clear(self.bcol); except: pass def interrupt(self): if not self.isAlive(): raise threading.ThreadError() for thread_id, thread_object in threading._active.items(): if thread_object == self: r = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,ctypes.py_object(StandardError)) if r == 1: pass else: if r > 1: ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0) raise SystemError() return def process_command(data): global hf_enabled, lf_enabled,scroll if data[0] == "X": if data[1] == '0': hf_enabled = False else: hf_enabled = True elif data[0] == "Y": if data[1] == '0': lf_enabled = False else: lf_enabled = True elif data[0] == "D": if data[1] == '0': SH.low_light = True else: SH.low_light = False else: if threading.activeCount() == 2: scroll.interrupt() while scroll.isAlive(): time.sleep(0.01) try: scroll.interrupt() except: pass if data[0] == "R": SH.set_rotation(float(data[1:])) elif data[0] == "C": data = data[1:].strip() if len(data) > 0: s = data.split(",") col = (int(s[0]),int(s[1]),int(s[2])) else: col = (0,0,0) SH.clear(col) elif data[0] == "P": data = data[1:].strip() s = data.split(',') for p in range(0,len(s),5): SH.set_pixel(int(s[p]),int(s[p+1]),int(s[p+2]),int(s[p+3]),int(s[p+4])) elif data[0] == "T": data = data[1:] tcol = (255,255,255) bcol = (0,0,0) speed = 0.1 s = data.split(':',1) if len(s) == 2: data = s[1][0:-1] if len(s[0]) > 0: c = s[0].split(",") if len(c) == 1: speed = float(c[0]) elif len(c) == 3: tcol = (int(c[0]),int(c[1]),int(c[2])) if len(c) == 4: tcol = (int(c[0]),int(c[1]),int(c[2])) speed = float(c[3]) elif len(c) == 6: tcol = (int(c[0]),int(c[1]),int(c[2])) bcol = (int(c[3]),int(c[4]),int(c[5])) elif len(c) == 7: tcol = (int(c[0]),int(c[1]),int(c[2])) bcol = (int(c[3]),int(c[4]),int(c[5])) speed = float(c[6]) if len(data) > 1: scroll = ScrollThread(tcol,bcol,speed,data); scroll.start() else: SH.show_letter(data,text_colour=tcol,back_colour=bcol) elif data[0] == "F": if data[1] == "H": SH.flip_h() elif data[1] == "V": SH.flip_v() def idle_work(): global last_hf_time, last_lf_time now = time.time() if hf_enabled and (now-last_hf_time > hf_interval): orientation = SH.get_orientation() # Calling get_compass interferes with get_orientation - so just reuse its value compass = orientation['yaw'] gyro = SH.get_gyroscope_raw() accel = SH.get_accelerometer_raw() print("X%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.4f,%0.0f"%(accel['x'],accel['y'],accel['z'],gyro['x'],gyro['y'],gyro['z'],orientation['roll'],orientation['pitch'],orientation['yaw'],compass)) last_hf_time = now if lf_enabled and (now-last_lf_time > lf_interval): temperature = SH.get_temperature(); humidity = SH.get_humidity(); pressure = SH.get_pressure(); print("Y%0.2f,%0.2f,%0.2f"%(temperature,humidity,pressure)) last_lf_time = now def process_joystick(): event = stick_file.read(EVENT_SIZE) (tv_sec, tv_usec, type, code, value) = struct.unpack(EVENT_FORMAT, event) if type == 0x01: print ("K%s%s"%(EVENT_NAMES[code],value)) def main_loop(): # while still waiting for input on at least one file try: while files: ready = select.select(files, [], [], 0.01)[0] if not ready: idle_work() else: for file in ready: if file == sys.stdin: line = file.readline() if not line: # EOF, remove file from input list sys.exit(0) elif line.rstrip(): # optional: skipping empty lines process_command(line) else: process_joystick() except: sys.exit(0) try: main_loop() except KeyboardInterrupt: pass