first commit

This commit is contained in:
pandacraft 2025-03-21 16:04:17 +01:00
commit a5a0434432
1126 changed files with 439481 additions and 0 deletions

View file

@ -0,0 +1,400 @@
#!/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

View file

@ -0,0 +1,5 @@
#! /bin/bash
id=$(ps aux | grep "python GrovePiScratch.py" | grep -v grep | awk '{print $2}')
kill -15 $id 2> /dev/null
python /home/pi/Desktop/GrovePi/Software/Scratch/GrovePiScratch.py

View file

@ -0,0 +1,11 @@
[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
Exec=/home/pi/Desktop/GrovePi/Software/Scratch/GrovePi_Scratch_Scripts/GrovePi_Scratch_Start.sh
Icon=/usr/share/scratch/Media/Costumes/Animals/cat1-b.gif
Terminal=false
Name=GrovePi_Scratch_Start
Comment= Programming system and content development tool
Categories=Application;Education;Development;
MimeType=application/x-scratch-project

View file

@ -0,0 +1,2 @@
#! /bin/bash
lxterminal --command "sudo /home/pi/Desktop/GrovePi/Software/Scratch/GrovePi_Scratch_Scripts/GrovePiScratch_debug.sh"

View file

@ -0,0 +1,38 @@
NOTE: THIS IS FOR DEVELOPERS ONLY. Normal user need not do this.
See more at http://www.dexterindustries.com/grovepi/
1. Make both **GrovePiScratch_debug.sh** and **GrovePi_Scratch_Start.sh** executable:
> sudo chmod +x GrovePiScratch_debug.sh
> sudo chmod +x GrovePi_Scratch_Start.sh
2. Copy **GrovePi_Scratch_Start.desktop** to **/usr/share/applications/**
3. Make a desktop Shortcut from **Desktop->Education->GrovePi_Scratch_Start**
3. Double click on **GrovePi_Scratch_Start** to start running the script
## 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.

View file

@ -0,0 +1,28 @@
These are instructions for installing GrovePi Scratch on any Raspberry Pi image not from Dexter Industries.
To skip this step, use the Dexter Industries Raspberry Pi image, found here: http://www.dexterindustries.com/BrickPi/getting-started/pi-prep/
The following is a short list of changes needed to setup GrovePi for Scratch.
You need the ScratchPy library to be installed. Install it by the following procedure.
Run the command:
sudo wget https://bitbucket.org/pypa/setuptools/raw/0.7.4/ez_setup.py -O - | sudo python
Clone scratchpy:
cd Desktop/GrovePi/Software/Scratch
git clone https://github.com/pilliq/scratchpy.git
Navigate to extracted directory and run:
sudo make install
Start Scratch
Select "Sensing"
Right Click Enable Remote sensor connections
You will see "Remote Sensor Connections Enabled"
back to terminal
cd Desktop/GrovePi/Software/Scratch
sudo python GrovePiScratch.py
Now open up an example:
Might see "Remote sensor connections enabled" again. That's fine, hit ok.
And now go!

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,46 @@
Scratch examples for GrovePi
============================
These are simple examples meant to illustrate how you can access GrovePi features through Scratch.
## 1. Simple LED: led_on_port_7 :
Plug any GrovePi LED onto Port 7 and run this Scratch script.
Click on the LED (on the screen) to control the physical LED
Click on Dex to turn the LED on for 2 seeconds and then it will turn off automatically
Click on the letter B on your keyboard to generate a blinking LED
Both the physical LED and the Scratch LED should react together
## 2. two_tone_buzzer :
Plug the buzzer in port 5
Move the ball around. When it hits the leg a low buzz will be emitted. When it hits the head, a low buzz will be heard
## 3. two_buttons_in_parallel :
Put a button in port 2 and another one in port 3.
Press the first button (in port 2). Dex will wave
Press the second button (in port 3). Dex will rotate.
## 4. LCD screen :
Put LCD screen in any of the I2C ports
Clicking on any of the six coloured circles will change the background colour of the LCD screen (and change the colour of the sunglasses)
Clicking on any of the four text buttons in the corners will display that text (and have Dex say the selected text)
Clicking on Dex will have Dex ask you your name, and then display it on the LCD screen
## 5. Rotary sensor:
Plug the rotary sensor into port A0
Click on the Green flag and use the rotary sensor to move Dex left to right

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,12 @@
[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
Exec=xhost +
Icon=/usr/share/scratch/Media/Costumes/Animals/cat1-a.gif
Exec=sudo bash /home/pi/Dexter/GrovePi/Software/Scratch/launch_scratch2.sh
Terminal=false
Name=GrovePi+ Scratch2
Comment= Programming system and content development tool
Categories=Application;Education;Development;
MimeType=application/x-scratch-project

View file

@ -0,0 +1,12 @@
[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
Exec=xhost +
Icon=/usr/share/scratch/Media/Costumes/Animals/cat1-b.gif
Exec=sudo bash /home/pi/Dexter/GrovePi/Software/Scratch/start_local_scratch.sh
Terminal=false
Name=GrovePi+ Scratch 1.4
Comment= Programming system and content development tool
Categories=Application;Education;Development;
MimeType=application/x-scratch-project

View file

@ -0,0 +1,6 @@
sudo pkill -f GrovePiScratch.py
sudo pkill -f wstosgh.py
python /home/pi/Dexter/GrovePi/Software/Scratch/GrovePiScratch.py &
python /home/pi/Dexter/GrovePi/Software/Scratch/wstosgh.py &
/usr/bin/scratch2

BIN
Software/Scratch/new.sb Normal file

Binary file not shown.

View file

@ -0,0 +1,36 @@
# Program the GrovePi in Scratch
You can program the GrovePi in Scratch. This repo contains support programs and example programs to help you program the [GrovePi](http://www.dexterindustries.com/GrovePi) in Scratch.
## Getting Started Examples
Please see our Examples folder for examples on how to get started with programming the GrovePi in Scratch!
Here are a list of example commands.
![Scratch Functions](scratch_functions.png "Overview of functions in Scratch.")
## See Also
## License
The MIT License (MIT)
GrovePi for the Raspberry Pi: an open source platform for connecting Grove Sensors to the Raspberry Pi.
Copyright (C) 2017 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.

View file

@ -0,0 +1,5 @@
[
{ "name":"Pi GPIO", "type":"extension", "file":"piGPIOExtension.js", "md5":"pigpio.png", "url":"file:///usr/lib/scratch2/scratch_extensions/gpio.html", "tags":["hardware"] },
{ "name":"Pi SenseHAT", "type":"extension", "file":"piSenseHATExtension.js", "md5":"pisensehat.png", "url":"file:///usr/lib/scratch2/scratch_extensions/sensehat.html", "tags":["hardware"] },
{ "name":"GrovePi", "type":"extension", "file":"piGrovePiExtension.js", "md5":"grovepi.png", "url":"file:///usr/lib/scratch2/scratch_extensions/grovepi.html", "tags":["hardware"] }
]

View file

@ -0,0 +1,118 @@
<!doctype html>
<html lang="en-GB">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>Worksheet - Physical Computing With Scratch | Raspberry Pi Learning Resources</title>
<meta name="description" content="The worksheet for the Physical Computing With Scratch Learning Resource for Raspberry Pi" />
<link rel="icon" type="image/png" href="/wp-content/themes/mind-control/images/favicon.png" />
<link rel="publisher" href="https://plus.google.com/+RaspberryPi" />
<link rel="stylesheet" href="/wp-content/themes/mind-control/css/prism.css" />
<link rel="stylesheet" href="./style.css?1482225765" />
<link rel="stylesheet" href="./robotoslab.css?family=Roboto+Slab:100,300,400,700">
<link rel="stylesheet" href="./robotothin.css?family=Roboto:100italic,100,300italic,300,400italic,400,500italic,500,700italic,700,900italic,900">
<script>
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-46270871-1']);
_gaq.push(['_gat._forceSSL']);
_gaq.push(['_trackPageview']);
(function () {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : '//www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script>
<script src="/wp-content/themes/mind-control/js/prism.js"></script>
</head>
<body class="learning worksheet learn worksheet ">
<div class="container">
<header class="header">
<a href="/resources/" class="heading" title="Back to raspberrypi.org">Raspberry Pi Learning Resources</a>
</header>
<div class="main">
<article class="content">
<h1>Physical computing with Scratch</h1>
<h2>GPIO pins</h2>
<p>One powerful feature of the Raspberry Pi is the row of GPIO pins along the top edge of the board. GPIO stands for General-Purpose Input/Output. These pins are a physical interface between the Raspberry Pi and the outside world. At the simplest level, you can think of them as switches that you can turn on or off (input) or that the Pi can turn on or off (output).</p>
<p>The GPIO pins allow the Raspberry Pi to control and monitor the outside world by being connected to electronic circuits. The Pi is able to control LEDs, turning them on or off, run motors, and many other things. It's also able to detect whether a switch has been pressed, the temperature, and light. We refer to this as physical computing.</p>
<p>There are 40 pins on the Raspberry Pi (26 pins on early models), and they provide various different functions.</p>
<p>If you have a RasPiO pin label, it can help to identify what each pin is used for. Make sure your pin label is placed with the keyring hole facing the USB ports, pointed outwards.</p>
<p><img src="./images/raspio-ports.jpg" alt="" /></p>
<p>If you don't have a pin label, then this guide can help you to identify the pin numbers:</p>
<p><img src="./images/GPIO.png" alt="" /></p>
<p>You'll see pins labelled as 3V3, 5V, GND and GP2, GP3, etc:</p>
<table>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td>3V3</td>
<td>3.3 volts</td>
<td>Anything connected to these pins will always get 3.3V of power</td>
</tr>
<tr>
<td>5V</td>
<td>5 volts</td>
<td>Anything connected to these pins will always get 5V of power</td>
</tr>
<tr>
<td>GND</td>
<td>ground</td>
<td>Zero volts, used to complete a circuit</td>
</tr>
<tr>
<td>GP2</td>
<td>GPIO pin 2</td>
<td>These pins are for general-purpose use and can be configured as input or output pins</td>
</tr>
<tr>
<td>ID_SC/ID_SD/DNC</td>
<td>Special purpose pins</td>
</tr>
</tbody>
</table>
<p><strong>WARNING</strong>: If you follow the instructions, then playing about with the GPIO pins is safe and fun. Randomly plugging wires and power sources into your Pi, however, may destroy it, especially if using the 5V pins. Bad things can also happen if you try to connect things to your Pi that use a lot of power; LEDs are fine, motors are not. If you're worried about this, then you might want to consider using an add-on board such as the <a href="https://shop.pimoroni.com/products/explorer-hat">Explorer HAT</a> until you're confident enough to use the GPIO directly.</p>
<h2>Lighting an LED</h2>
<p>You can test whether your GPIO pins and LEDs are working by building the circuit below. You can use any resistor over about 50Ω.</p>
<ol>
<li>
<p>The LED is connected directly to the <strong>GND</strong> pin and the <strong>3V3</strong> pin via the 330 Ohm resistor, and should light up.</p>
</li>
<li>Be sure to connect your LED the correct way round; the longer leg should be connected to the 3V3 pin:</li>
</ol>
<p><img src="./images/led-3v3.png" alt="Test Circuit" /></p>
<h2>Using a switchable pin</h2>
<ol>
<li>
<p>To control the LED, you'll need to adapt your circuit to use a switchable pin.</p>
</li>
<li>In the diagram below <strong>pin 17</strong> has been used, but you can use any numbered pin you wish.</li>
</ol>
<p><img src="./images/led-gpio17.png" alt="Test Circuit" /></p>
</article>
<div style="clear:both;"></div>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -0,0 +1,170 @@
// Modified piGPIOExtension.js for GrovePi
// Ver 2.25Feb18
new (function() {
var ext = this;
var fs = require('fs');
var websocket;
var sensorSplit;
var sensorDict = {};
var variableDict = {};
function doConnect()
{
websocket = new WebSocket('ws://localhost:8000/')
websocket.onopen = function(evt) { onOpen(evt) }
websocket.onclose = function(evt) { onClose(evt) }
websocket.onmessage = function(evt) { onMessage(evt) }
websocket.onerror = function(evt) { onError(evt) }
console.log('websocket connected from piGrovePiExtension')
}
function onOpen(evt) {
console.log('websocket opened')
}
function onClose(evt) {
console.log('websocket closed')
}
function onMessage(evt) {
var data = evt.data
console.log('msg from sgh:' + data)
sensorSplit = data.split(":");
sensorDict[sensorSplit[0]] = sensorSplit[1];
// console.log('sensorDict=' + JSON.stringify(sensorDict))
}
function onError(evt) {
var error = evt.data
console.log('websocket error', error);
websocket.close();
}
function sendMessage(message) {
websocket.send(message);
console.log('msg to sgh:' + message)
}
function doDisconnect() {
websocket.close();
}
doConnect();
// Cleanup function when the extension is unloaded
ext._shutdown = function ()
{
for (pin = 2; pin < 28; pin++)
{
if (fs.existsSync("/sys/class/gpio/gpio" + pin))
fs.writeFileSync("/sys/class/gpio/unexport", pin, "utf8");
}
};
// Status reporting code
// Use this to report missing hardware, plugin or unsupported browser
ext._getStatus = function ()
{
return {status: 2, msg: 'Ready'};
};
ext.set_gpio = function (pin, val)
{
if (pin === '' || pin < 0 || pin > 27) return;
var dir = 0, lev;
if (val == 'output high') lev = 1;
else if (val == 'output low') lev = 0;
else dir = 1;
// check the pin is exported
if (!fs.existsSync("/sys/class/gpio/gpio" + pin))
fs.writeFileSync("/sys/class/gpio/export", pin, "utf8");
// the ownership of direction takes time to establish, so try this until it succeeds
while (true)
{
try {
fs.writeFileSync("/sys/class/gpio/gpio" + pin + "/direction", dir == 0 ? "out" : "in", "utf8");
break;
}
catch (error) {
continue;
}
}
// set the output value
if (dir == 0)
sendMessage('pin ' + pin + ' = ' + (lev == 1 ? "1" : "0"));
fs.writeFileSync("/sys/class/gpio/gpio" + pin + "/value", lev == 1 ? "1" : "0", "utf8");
};
ext.get_gpio = function (pin)
{
if (pin === '' || pin < 0 || pin > 27) return;
// check the pin is exported
if (!fs.existsSync("/sys/class/gpio/gpio" + pin))
fs.writeFileSync("/sys/class/gpio/export", pin);
// read the pin value
var data = fs.readFileSync ("/sys/class/gpio/gpio" + pin + "/value", 'utf8');
if (data.slice(0,1) == "1") return true;
else return false;
};
//my code
ext.send_broadcast1 = function (bmsg1 ,bmsg2)
{
sendMessage('broadcast "' + bmsg1 + bmsg2 + '"');
};
ext.send_broadcast2 = function (bmsg1 ,bmsg2,bmsg3)
{
sendMessage('broadcast "' + bmsg1 + bmsg2 + bmsg3 + '"');
};
ext.send_broadcast0 = function (bmsg1)
{
sendMessage('broadcast "' + bmsg1 + '"');
};
ext.get_sensorMsgs = function (sensorName)
{
console.log(sensorName.toLowerCase() + ':' + sensorDict[sensorName.toLowerCase()])
//fs.writeFileSync("/home/pi/GrovePiSensors.json", JSON.stringify(sensorDict), "utf8");
return sensorDict[sensorName.toLowerCase()];
};
// Block and block menu descriptions
var descriptor = {
blocks: [
[' ', 'broadcast %m.broadcastType %s', 'send_broadcast1', 'digitalRead','0'],
[' ', 'broadcast %m.broadcastType2 %m.ports %s', 'send_broadcast2', 'analogWrite','0','0'],
[' ', 'broadcast %m.broadcastType0', 'send_broadcast0', 'take_picture'],
['r', '%m.sensorvals value', 'get_sensorMsgs', 'digitalread'],
],
menus: {
sensorvals: ['digitalread','analogread','moisture','light','sound','rotary','distance','temp','humidty','folder','camera'],
broadcastType: ['digitalRead','analogRead','digitalWriteHigh','moisture','light','sound','rotary','distance','button','relay','temp','humidty','folder','take_picture','speak','lcdcol','lcdtxt'],
broadcastType2: ['analogWrite','buzzer','led'],
broadcastType0: ['take_picture'],
ports: ['0','1','2','3','4','5','6','7']
}
};
// Register the extension
ScratchExtensions.register('GrovePi', descriptor, ext);
})();

View file

@ -0,0 +1,9 @@
sudo cp extensions.json /usr/lib/scratch2/scratch_extensions/extensions.json
sudo cp piGrovePiExtension.js /usr/lib/scratch2/scratch_extensions/piGrovePiExtension.js
sudo cp grovepi.html /usr/lib/scratch2/scratch_extensions/grovepi.html
sudo cp -u grovepi.png /usr/lib/scratch2/medialibrarythumbnails/grovepi.png
pip install scratchpy

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View file

@ -0,0 +1,11 @@
echo "ensuring only one instance of GoPiGo3 Scratch Communicator"
sudo pkill -f GrovePiScratch.py
sudo python /home/pi/Dexter/GrovePi/Software/Scratch/GrovePiScratch.py &
echo "starting Scratch"
scratch /home/pi/Dexter/lib/Dexter/Scratch_GUI/new.sb
echo "killing background process"
sudo pkill -f GrovePiScratch.py
echo "background process killed"

View file

@ -0,0 +1,347 @@
# Author: Johan Hanssen Seferidis
# License: MIT
import re
import sys
import struct
from base64 import b64encode
from hashlib import sha1
import logging
if sys.version_info[0] < 3:
from SocketServer import ThreadingMixIn, TCPServer, StreamRequestHandler
else:
from socketserver import ThreadingMixIn, TCPServer, StreamRequestHandler
logger = logging.getLogger(__name__)
logging.basicConfig()
'''
+-+-+-+-+-------+-+-------------+-------------------------------+
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len | Extended payload length |
|I|S|S|S| (4) |A| (7) | (16/64) |
|N|V|V|V| |S| | (if payload len==126/127) |
| |1|2|3| |K| | |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
| Extended payload length continued, if payload len == 127 |
+ - - - - - - - - - - - - - - - +-------------------------------+
| Payload Data continued ... |
+---------------------------------------------------------------+
'''
FIN = 0x80
OPCODE = 0x0f
MASKED = 0x80
PAYLOAD_LEN = 0x7f
PAYLOAD_LEN_EXT16 = 0x7e
PAYLOAD_LEN_EXT64 = 0x7f
OPCODE_CONTINUATION = 0x0
OPCODE_TEXT = 0x1
OPCODE_BINARY = 0x2
OPCODE_CLOSE_CONN = 0x8
OPCODE_PING = 0x9
OPCODE_PONG = 0xA
# -------------------------------- API ---------------------------------
class API():
def run_forever(self):
try:
logger.info("Listening on port %d for clients.." % self.port)
self.serve_forever()
except KeyboardInterrupt:
self.server_close()
logger.info("Server terminated.")
except Exception as e:
logger.error(str(e), exc_info=True)
exit(1)
def new_client(self, client, server):
pass
def client_left(self, client, server):
pass
def message_received(self, client, server, message):
pass
def set_fn_new_client(self, fn):
self.new_client = fn
def set_fn_client_left(self, fn):
self.client_left = fn
def set_fn_message_received(self, fn):
self.message_received = fn
def send_message(self, client, msg):
self._unicast_(client, msg)
def send_message_to_all(self, msg):
self._multicast_(msg)
# ------------------------- Implementation -----------------------------
class WebsocketServer(ThreadingMixIn, TCPServer, API):
"""
A websocket server waiting for clients to connect.
Args:
port(int): Port to bind to
host(str): Hostname or IP to listen for connections. By default 127.0.0.1
is being used. To accept connections from any client, you should use
0.0.0.0.
loglevel: Logging level from logging module to use for logging. By default
warnings and errors are being logged.
Properties:
clients(list): A list of connected clients. A client is a dictionary
like below.
{
'id' : id,
'handler' : handler,
'address' : (addr, port)
}
"""
allow_reuse_address = True
daemon_threads = True # comment to keep threads alive until finished
clients = []
id_counter = 0
def __init__(self, port, host='127.0.0.1', loglevel=logging.WARNING):
logger.setLevel(loglevel)
self.port = port
TCPServer.__init__(self, (host, port), WebSocketHandler)
def _message_received_(self, handler, msg):
self.message_received(self.handler_to_client(handler), self, msg)
def _ping_received_(self, handler, msg):
handler.send_pong(msg)
def _pong_received_(self, handler, msg):
pass
def _new_client_(self, handler):
self.id_counter += 1
client = {
'id': self.id_counter,
'handler': handler,
'address': handler.client_address
}
self.clients.append(client)
self.new_client(client, self)
def _client_left_(self, handler):
client = self.handler_to_client(handler)
self.client_left(client, self)
if client in self.clients:
self.clients.remove(client)
def _unicast_(self, to_client, msg):
to_client['handler'].send_message(msg)
def _multicast_(self, msg):
for client in self.clients:
self._unicast_(client, msg)
def handler_to_client(self, handler):
for client in self.clients:
if client['handler'] == handler:
return client
class WebSocketHandler(StreamRequestHandler):
def __init__(self, socket, addr, server):
self.server = server
StreamRequestHandler.__init__(self, socket, addr, server)
def setup(self):
StreamRequestHandler.setup(self)
self.keep_alive = True
self.handshake_done = False
self.valid_client = False
def handle(self):
while self.keep_alive:
if not self.handshake_done:
self.handshake()
elif self.valid_client:
self.read_next_message()
def read_bytes(self, num):
# python3 gives ordinal of byte directly
bytes = self.rfile.read(num)
if sys.version_info[0] < 3:
return map(ord, bytes)
else:
return bytes
def read_next_message(self):
try:
b1, b2 = self.read_bytes(2)
except ValueError as e:
b1, b2 = 0, 0
fin = b1 & FIN
opcode = b1 & OPCODE
masked = b2 & MASKED
payload_length = b2 & PAYLOAD_LEN
if not b1:
logger.info("Client closed connection.")
self.keep_alive = 0
return
if opcode == OPCODE_CLOSE_CONN:
logger.info("Client asked to close connection.")
self.keep_alive = 0
return
if not masked:
logger.warn("Client must always be masked.")
self.keep_alive = 0
return
if opcode == OPCODE_CONTINUATION:
logger.warn("Continuation frames are not supported.")
return
elif opcode == OPCODE_BINARY:
logger.warn("Binary frames are not supported.")
return
elif opcode == OPCODE_TEXT:
opcode_handler = self.server._message_received_
elif opcode == OPCODE_PING:
opcode_handler = self.server._ping_received_
elif opcode == OPCODE_PONG:
opcode_handler = self.server._pong_received_
else:
logger.warn("Unknown opcode %#x." + opcode)
self.keep_alive = 0
return
if payload_length == 126:
payload_length = struct.unpack(">H", self.rfile.read(2))[0]
elif payload_length == 127:
payload_length = struct.unpack(">Q", self.rfile.read(8))[0]
masks = self.read_bytes(4)
decoded = ""
for char in self.read_bytes(payload_length):
char ^= masks[len(decoded) % 4]
decoded += chr(char)
opcode_handler(self, decoded)
def send_message(self, message):
self.send_text(message)
def send_pong(self, message):
self.send_text(message, OPCODE_PONG)
def send_text(self, message, opcode=OPCODE_TEXT):
"""
Important: Fragmented(=continuation) messages are not supported since
their usage cases are limited - when we don't know the payload length.
"""
# Validate message
if isinstance(message, bytes):
message = try_decode_UTF8(message) # this is slower but ensures we have UTF-8
if not message:
logger.warning("Can\'t send message, message is not valid UTF-8")
return False
elif isinstance(message, str) or isinstance(message, unicode):
pass
else:
logger.warning('Can\'t send message, message has to be a string or bytes. Given type is %s' % type(message))
return False
header = bytearray()
payload = encode_to_UTF8(message)
payload_length = len(payload)
# Normal payload
if payload_length <= 125:
header.append(FIN | opcode)
header.append(payload_length)
# Extended payload
elif payload_length >= 126 and payload_length <= 65535:
header.append(FIN | opcode)
header.append(PAYLOAD_LEN_EXT16)
header.extend(struct.pack(">H", payload_length))
# Huge extended payload
elif payload_length < 18446744073709551616:
header.append(FIN | opcode)
header.append(PAYLOAD_LEN_EXT64)
header.extend(struct.pack(">Q", payload_length))
else:
raise Exception("Message is too big. Consider breaking it into chunks.")
return
self.request.send(header + payload)
def handshake(self):
message = self.request.recv(1024).decode().strip()
upgrade = re.search('\nupgrade[\s]*:[\s]*websocket', message.lower())
if not upgrade:
self.keep_alive = False
return
key = re.search('\n[sS]ec-[wW]eb[sS]ocket-[kK]ey[\s]*:[\s]*(.*)\r\n', message)
if key:
key = key.group(1)
else:
logger.warning("Client tried to connect but was missing a key")
self.keep_alive = False
return
response = self.make_handshake_response(key)
self.handshake_done = self.request.send(response.encode())
self.valid_client = True
self.server._new_client_(self)
def make_handshake_response(self, key):
return \
'HTTP/1.1 101 Switching Protocols\r\n'\
'Upgrade: websocket\r\n' \
'Connection: Upgrade\r\n' \
'Sec-WebSocket-Accept: %s\r\n' \
'\r\n' % self.calculate_response_key(key)
def calculate_response_key(self, key):
GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
hash = sha1(key.encode() + GUID.encode())
response_key = b64encode(hash.digest()).strip()
return response_key.decode('ASCII')
def finish(self):
self.server._client_left_(self)
def encode_to_UTF8(data):
try:
return data.encode('UTF-8')
except UnicodeEncodeError as e:
logger.error("Could not encode data to UTF-8 -- %s" % e)
return False
except Exception as e:
raise(e)
return False
def try_decode_UTF8(data):
try:
return data.decode('utf-8')
except UnicodeDecodeError:
return False
except Exception as e:
raise(e)

123
Software/Scratch/wstosgh.py Executable file
View file

@ -0,0 +1,123 @@
import array
import itertools
import socket
import struct
import sys
import threading
import time
from websocket_server import WebsocketServer
def rcv_from_sgh():
global s,c,server
dataPrevious = ""
while True:
print "listening for data from sgh"
# time.sleep(1)
data = dataPrevious + c.recv(8192)
print "data length" , len(data)
if data != "":
print "Data received from sgh", data
#print ("datalen: %s", len(data))
if len(data) > 0: # Connection still valid so process the data received
dataIn = data
datawithCAPS = data
# dataOut = ""
dataList = [] # used to hold series of broadcasts or sensor updates
dataPrefix = "" # data to be re-added onto front of incoming data
while len(dataIn) > 0: # loop thru data
if len(dataIn) < 4: # If whole length not received then break out of loop
# print "<4 chrs received"
dataPrevious = dataIn # store data and tag it onto next data read
break
sizeInfo = dataIn[0:4]
size = struct.unpack(">L", sizeInfo)[0] # get size of Scratch msg
# print "size:", size
if size > 0:
# print dataIn[4:size + 4]
dataMsg = dataIn[4:size + 4].lower() # turn msg into lower case
if len(dataMsg) < size: # if msg recieved is too small
# print "half msg found"
# print size, len(dataMsg)
dataPrevious = dataIn # store data and tag it onto next data read
break
#print "msg:",dataMsg
dataList.append(dataMsg)
dataIn = dataIn[size + 4:] # cut data down that's been processed
# print "previous:", dataPrevious
print "datalist:",dataList
for msg in dataList:
#print "msg:",msg[0:13]
if msg[0:13] == 'sensor-update':
msgsplit = msg[14:].replace('"','').split(' ')
print "split",msgsplit
#for loop in range(int(len(msgsplit) / 2)):
# server.send_message_to_all(msgsplit[loop * 2] + ':' + msgsplit[(loop * 2) + 1])
server.send_message_to_all(msgsplit[0] + ':' + msgsplit[1])
else:
time.sleep(0.1)
# Called for every client connecting (after handshake)
def new_client(client, server):
print("New client connected and was given id %d" % client['id'])
#server.send_message_to_all("Hey all, a new client has joined us")
# Called for every client disconnecting
def client_left(client, server):
print("Client(%d) disconnected" % client['id'])
# Called when a client sends a message
def message_received(client, server, message):
if len(message) > 200:
message = message[:200]+'..'
print("Client(%d) said: %s" % (client['id'], message))
dataOut = message
n = len(dataOut)
b = (chr((n >> 24) & 0xFF)) + (chr((n >> 16) & 0xFF)) + (chr((n >> 8) & 0xFF)) + (
chr(n & 0xFF))
c.send(b + dataOut)
print "Data sent to sgh", dataOut
# For Scratch 3 handle long as int
if sys.version > '3':
long = int
s = socket.socket() #Create a socket object
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
#host = socket.gethostname() #Get the local machine name
port = 42001 # Reserve a port for your service
s.bind(('127.0.0.1',port)) #Bind to the port
s.listen(5) #Wait for the client connection
print "wstosgh listening to scratchGPIO_handler"
c,addr = s.accept() #Establish a connection with the client
print "Got connection from ScratchGPIOHandler", addr
PORT=8000
server = WebsocketServer(PORT)
server.set_fn_new_client(new_client)
server.set_fn_client_left(client_left)
server.set_fn_message_received(message_received)
d = threading.Thread(name='rcv_from_sgh', target=rcv_from_sgh)
d.setDaemon(True)
d.start()
server.run_forever()