grovepi/Software/Scratch/GrovePiScratch.py
2025-03-21 16:04:17 +01:00

400 lines
15 KiB
Python
Executable file

#!/usr/bin/python
###############################################################################################################
# This library is for using the GrovePi with Scratch
# http://www.dexterindustries.com/GrovePi/
# History
# ------------------------------------------------
# Author Date Comments
# Karan 29 June 15 Initial Authoring
# John 22 Feb 16 Adding GrovePi Barometer
# Nicole Nov 16 Added Folder support for take_picture
# Nicole Nov 16 Added eSpeak Support
# Nicole 18 Nov 16 Adding PivotPi support
# SimonW 22 Mar 18 Bug fix in error handling line 383
'''
## License
The MIT License (MIT)
GrovePi for the Raspberry Pi: an open source platform for connecting Grove Sensors to the Raspberry Pi.
Copyright (C) 2016 Dexter Industries
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
'''
'''
#
# Based on the BrickPi Scratch Library written by Jaikrishna
#
# The Python program acts as the Bridge between Scratch & GrovePi and must be running for the Scratch program to run.
##############################################################################################################
'''
import scratch,sys,threading,math
import grovepi
import time
import os # to handle folder paths
try:
sys.path.insert(0, '/home/pi/Dexter/PivotPi/Software/Scratch/')
import PivotPiScratch
pivotpi_available=True
except:
pivotpi_available=False
# Folders where pictures get saved
defaultCameraFolder="/home/pi/Desktop/"
cameraFolder = defaultCameraFolder
# Pi user ID Number - used for setting permissions
pi_user=1000
pi_group=1000
# used is GrovePi is actually connected - allows for testing without a GrovePi
en_grovepi=1
# print debugging statements
en_debug=1
try:
s = scratch.Scratch()
if s.connected:
print "GrovePi Scratch: Connected to Scratch successfully"
#else:
#sys.exit(0)
except scratch.ScratchError:
print "GrovePi Scratch: Scratch is either not opened or remote sensor connections aren't enabled"
#sys.exit(0)
class myThread (threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
while running:
time.sleep(.2) # sleep for 200 ms
thread1 = myThread(1, "Thread-1", 1) #Setup and start the thread
thread1.setDaemon(True)
analog_sensors=['analogRead','rotary','sound','light','moisture']
digitalInp=['button']
digitalOp=['led','relay']
pwm=['LEDPower','buzzer','analogWrite']
def match_sensors(msg,lst):
for i,e in enumerate(lst):
if msg[:len(e)].lower()==e.lower():
return i
return -1
try:
s.broadcast('READY')
except NameError:
print "GrovePi Scratch: Unable to Broadcast"
while True:
try:
m = s.receive()
while m==None or m[0] == 'sensor-update':
m = s.receive()
originalmsg = m[1]
# change input to all lowercase.
# Users can enter any which way they want
msg = originalmsg.lower()
if en_debug:
print "Rx:",originalmsg
if msg == 'SETUP'.lower() :
print "Setting up sensors done"
elif msg == 'START'.lower() :
running = True
if thread1.is_alive() == False:
thread1.start()
print "Service Started"
# ANALOG SENSORS
elif match_sensors(msg,analog_sensors) >=0:
if en_grovepi:
s_no=match_sensors(msg,analog_sensors)
sens=analog_sensors[s_no]
port=int(msg[len(sens):])
a_read=grovepi.analogRead(port)
s.sensorupdate({sens:a_read})
if en_debug:
print msg
print sens +'op:'+ str(a_read)
elif msg[:8].lower()=="setInput".lower():
if en_grovepi:
port=int(msg[8:])
grovepi.pinMode(port,"INPUT")
if en_debug:
print msg
elif msg[:9].lower()=="setOutput".lower():
if en_grovepi:
port=int(msg[9:])
grovepi.pinMode(port,"OUTPUT")
if en_debug:
print msg
elif msg[:11].lower()=="digitalRead".lower():
if en_grovepi:
port=int(msg[11:])
grovepi.pinMode(port,"INPUT")
d_read=grovepi.digitalRead(port)
s.sensorupdate({'digitalRead':d_read})
if en_debug:
print msg
print "Digital Reading: " + str(d_read)
elif match_sensors(msg,digitalInp) >=0:
if en_grovepi:
s_no=match_sensors(msg,digitalInp)
sens=digitalInp[s_no]
port=int(msg[len(sens):])
sens += str(port)
grovepi.pinMode(port,"INPUT")
d_read=grovepi.digitalRead(port)
s.sensorupdate({sens:d_read})
if en_debug:
print msg,
print sens +' output:'+ str(d_read)
elif msg[:16].lower()=="digitalWriteHigh".lower():
if en_grovepi:
port=int(msg[16:])
grovepi.pinMode(port,"OUTPUT")
grovepi.digitalWrite(port,1)
if en_debug:
print msg
elif msg[:15].lower()=="digitalWriteLow".lower():
if en_grovepi:
port=int(msg[15:])
grovepi.pinMode(port,"OUTPUT")
grovepi.digitalWrite(port,0)
if en_debug:
print msg
elif match_sensors(msg,pwm) >=0:
if en_grovepi:
s_no=match_sensors(msg,pwm)
sens=pwm[s_no]
l=len(sens)
port=int(msg[l:l+1])
power=int(msg[l+1:])
grovepi.pinMode(port,"OUTPUT")
grovepi.analogWrite(port,power)
if en_debug:
print msg
elif match_sensors(msg,digitalOp) >=0:
if en_grovepi:
s_no=match_sensors(msg,digitalOp)
sens=digitalOp[s_no]
l=len(sens)
port=int(msg[l:l+1])
state=msg[l+1:]
grovepi.pinMode(port,"OUTPUT")
if state=='on':
grovepi.digitalWrite(port,1)
else:
grovepi.digitalWrite(port,0)
if en_debug:
print msg
elif msg[:4].lower()=="temp".lower():
if en_grovepi:
port=int(msg[4:])
[temp,humidity] = grovepi.dht(port,0)
s.sensorupdate({'temp':temp})
if en_debug:
print msg
print "temp: ",temp
elif msg[:8].lower()=="humidity".lower():
if en_grovepi:
port=int(msg[8:])
[temp,humidity] = grovepi.dht(port,0)
s.sensorupdate({'humidity':humidity})
if en_debug:
print msg
print "humidity:",humidity
elif msg[:8].lower()=="distance".lower():
if en_grovepi:
port=int(msg[8:])
dist=grovepi.ultrasonicRead(port)
s.sensorupdate({'distance':dist})
if en_debug:
print msg
print "distance=",dist
elif msg[:3].lower()=="lcd".lower():
if en_grovepi:
if en_debug:
print msg[:3], msg[3:6], originalmsg[6:]
sys.path.insert(0, '/home/pi/Dexter/GrovePi/Software/Python/grove_rgb_lcd')
import grove_rgb_lcd
if msg[3:6].lower() == "col".lower(): #lower() added just for consistency. Not really needed
rgb = []
for i in range(0,6,2):
rgb.append(int(msg[6:][i:i+2],16)) # convert from one hex string to three ints
if en_debug:
print "colours are:",rgb[0],rgb[1],rgb[2]
grove_rgb_lcd.setRGB(rgb[0],rgb[1],rgb[2])
elif msg[3:6].lower() == "txt".lower():
txt = originalmsg[6:]
grove_rgb_lcd.setText_norefresh(txt)
else:
pass
if en_debug:
print msg
# elif msg[:10].lower()=="setOutput".lower():
# if en_grovepi:
# port=int(msg[10:])
# a_read=grovepi.analogRead(port)
# s.sensorupdate({'analogRead':a_read})
# if en_debug:
# print msg
# print "Analog Reading: " + str(a_read)
elif msg.lower()=="READ_IR".lower() or msg.lower()=="IR".lower():
print "READ_IR!"
if en_ir_sensor==0:
import lirc
sockid = lirc.init("keyes", blocking = False)
en_ir_sensor=1
try:
read_ir= lirc.nextcode() # press 1
if len(read_ir) !=0:
print read_ir[0]
except:
if en_debug:
e = sys.exc_info()[1]
print "Error reading IR sensor: " + str(read_ir)
if en_debug:
print "IR Recv Reading: " + str(read_ir)
if en_gpg:
if len(read_ir) !=0:
s.sensorupdate({'read_ir':read_ir[0]})
else:
s.sensorupdate({'read_ir':""})
# CREATE FOLDER TO SAVE PHOTOS IN
elif msg[:6].lower()=="FOLDER".lower():
print "Camera folder"
try:
cameraFolder=defaultCameraFolder+str(msg[6:])
if not os.path.exists(cameraFolder):
os.makedirs(cameraFolder)
os.chown(cameraFolder,pi_user,pi_group)
s.sensorupdate({"folder":"created"})
else:
s.sensorupdate({"folder":"set"})
except:
print "error with folder name"
elif msg.lower()=="TAKE_PICTURE".lower():
print "TAKE_PICTURE!"
try:
from subprocess import call
import datetime
newimage = "{}/img_{}.jpg".format(cameraFolder,str(datetime.datetime.now()).replace(" ","_",10).replace(":","_",10))
photo_cmd="raspistill -o {} -w 640 -h 480 -t 1".format(newimage)
print photo_cmd
call ([photo_cmd], shell=True)
os.chown(newimage,pi_user,pi_group)
print "Picture Taken"
except:
if en_debug:
e = sys.exc_info()[1]
print "Error taking picture",e
s.sensorupdate({'camera':"Error"})
s.sensorupdate({'camera':"Picture Taken"})
# Barometer code, pressure
elif msg[:9].lower()=="pressure".lower():
if en_grovepi:
# We import here to prevent errors thrown. If the import fails, you just get an error message instead of the communicator crashing.
# If user is using multiple sensors and using their own image which does not have the pythonpath set correctly then
# they'll just not get the output for 1 sensor, and the others will still keep working
from grove_i2c_barometic_sensor_BMP180 import BMP085 # Barometric pressure sensor.
bmp = BMP085(0x77, 1) #Initialize the pressure sensor (barometer)
press = bmp.readPressure()/100.0
s.sensorupdate({'pressure':press})
if en_debug:
print "Pressure: " + str(press)
if en_debug: # If Debug is enabled, print the value of msg.
print msg
elif (msg[:5].lower()=="SPEAK".lower() or msg[:3].lower()=="SAY".lower() ):
try:
if en_grovepi:
from subprocess import call
cmd_beg = "espeak-ng -ven+f1 "
in_text = msg[len("SPEAK"):]
cmd_end = " 2>/dev/null"
call([cmd_beg+"\""+in_text+"\""+cmd_end], shell=True)
if en_debug:
print(msg)
except:
print("Issue with espeak")
# PIVOTPI
elif pivotpi_available==True and PivotPiScratch.isPivotPiMsg(msg):
pivotsensors = PivotPiScratch.handlePivotPi(msg)
# print "Back from PivotPi",pivotsensors
s.sensorupdate(pivotsensors)
else:
if en_debug:
print "Ignoring: ",msg
except KeyboardInterrupt:
running= False
print "GrovePi Scratch: Disconnected from Scratch"
break
except (scratch.ScratchConnectionError,NameError) as e: #bug fix simonw 22Mar18
while True:
#thread1.join(0)
print "GrovePi Scratch: Scratch connection error, Retrying"
# print e
time.sleep(5)
try:
s = scratch.Scratch()
s.broadcast('READY')
print "GrovePi Scratch: Connected to Scratch successfully"
break;
except scratch.ScratchError:
print "GrovePi Scratch: Scratch is either not opened or remote sensor connections aren't enabled\n..............................\n"
except:
e = sys.exc_info()[0]
print "GrovePi Scratch: Error %s" % e