PyFirmata by Tino de Bruijn is an amazing tool for combining the powers of the Raspberry Pi (full operating system, python, …) and the Arduino (high time precision, …). This will allow us to control the Arduino Mega from the Raspberry Pi. This is important, because of two reasons:
The Raspberry Pi has the input data from the ethereum node script.
If we want to use 16 PWM pins, the Arduino Mega only has 14 (or 15), but we could use one (or 2) of the RPI PWM pins.
Breaking it down, these are the questions to resolve:
Can we read a slider position, from the RPi through the Arduino slave, using python?
Can we move the slider?
Can we do this with two sliders?
Can we read data from a file, and move a slider?
Can we move multiple sliders?
Can we read multiple data points and move multiple sliders?
Can we detect touch sensitivity? (is this useful?)
Overview of vinophonics.py
The vinophonics.py program follows this sequence:
check the blockchain switch position:
if the switch is set to LOCAL, do nothing
If the switch is in the middle position, execute the rest of the code every 180 seconds
If the switch is set to REMOTE, execute the rest of the code continuously
import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BCM) GPIO.setwarnings(False) GPIO.setup(22, GPIO.IN) GPIO.setup(23, GPIO.IN) while True: if GPIO.input(22) == 1: LOCAL - DO NOTHING elif GPIO.input(23) == 1: MIDDLE - WAIT 3 MINUTES ... OR AT LEAST WAIT UNTIL THE SMART CONTRACT UPDATES else: REMOTE - CONTINUE WITH THE REST OF THE CODE time.sleep(1)
Create a “for” loop to execute sequentially for each of the sliders (1-16)
FOR LOOPread ALL the variables from the smart_contract
import json import web3 import time from web3 import Web3, HTTPProvider print("\n1) Connecting to the costaflores-node2 (http://10.112.48.25:8547) ...") w3 = Web3(HTTPProvider("http://10.112.48.25:8547")) print("2) Preparing contracts (Datastorage)...") with open('contracts.json') as json_file: compiled_sol = json.load(json_file) contract = w3.eth.contract(address = w3.toChecksumAddress("0x864d6819946dF8a763454627342bb3A7a2692805"), abi = compiled_sol['contracts']['contracts.sol:Datastorage']['abi'], ) print("\n ================================================== \n") def handle_event(event): receipt = w3.eth.waitForTransactionReceipt(event['transactionHash']) result = contract.events.DataInserted().processReceipt(receipt)
# => Sensor specific data. sensor2 = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 0).call()/100 sensor1 = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 1).call()/100 sensor05 = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 2).call()/100 sensor005 = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 3).call()/100 battery = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 4).call()/100 temperature = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 5).call()/100 # => Weather station data. (global data) wind_speed = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 7).call()/100 wind_gust = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 8).call()/100 wind_direction = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 9).call()/100 pressure = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 10).call()/100 rain = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 11).call()/100 temperature = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 12).call()/100 humidity = contract.functions.data(result[0]['args']['_identifier'].decode('utf-8').encode('utf-8'), 13).call()/100 # Add communication with Vinophonics here, to do: # data = ... TREAT DATA HERE AS STRING # file = open("/var/tmp/data.txt", data) def log_loop(event_filter, poll_interval): while True: for event in event_filter.get_new_entries(): handle_event(event) time.sleep(poll_interval) block_filter = w3.eth.filter({'fromBlock':'latest'}) log_loop(block_filter, 2)
map the variable values from the smart_contract in the range 0-1000
The idea is to convert different sensor data scales to 0-1000 range.
Sensor variableSliderMinMaxMeaningsensor210255soil moisture at 2m depthsensor120255soil moisture at 1m depthsensor0530255soil moisture at 50cm depthsensor00540255soil moisture at 5cm depthbattery524.5battery voltage for the vinduino (this will be replaced with the sunlight sensor data)temperature6035temperature reading from the vinduinowind_speed7030wind speed in knotswind_gust8050wind gusts in knotswind_direction90360wind direction in degreespressure108001000milibars of pressure (also needs to be calibrated)rain110100rainfall in milimeters (month)temperature12035temperature at the platformhumidity131090relative humidity (percentage)sensor2140255soil moisture at 2m depth (REPEAT OF SLIDER1)sensor1150255soil moisture at 1m depth(REPEAT OF SLIDER2)sensor5160255soil moisture at 50cm depth (REPEAT OF SLIDER3)
In Python, this is done like this:
def map(x, in_min, in_max, out_min, out_max):
For slider(1)
read slider(1) position
slider1 = slider_one.read()
Decide whether to move the slider up or down
Turn on the slider on for x amount of time
Turn off the slider
Read the slider position. If the position is still not correct, repeat this sequence until we reach the target.
if (slider1 - targetValue) > threshold and (slider1 > targetValue): slider_one_up.write(0) slider_one_down.write(1) slider_one_enable.write(speed) elif (targetValue - slider1) > threshold and (slider1 < targetValue): slider_one_up.write(1) slider_one_down.write(0) slider_one_enable.write(speed) else: slider_one_enable.write(0)
In other words: If the slider position is greater than the target, AND the slider position, minus the target, is greater than the threshold, then point slider south, and enable the motor for x amount of time, then turn off the motor.
Continue for slider(2-16)
At the end of the 16 sliders, repeat from step 1 (reading the blockchain switch)
Installing PyFirmata
sudo apt-get -y install arduino python-serial mercurial git clone https://github.com/tino/pyFirmata cd pyFirmata sudo python setup.py install
Using the Arduino IDE, upload the default PyFirmata firmware on the arduino (file → Examples → Firmata → Standard Firmata)
Test using PyFirmata "Hello World"
Download the script from GitHub:
wget https://github.com/JoBergs/RaspiContent/raw/master/pyfirmata_pwm.py
#!/usr/bin/python #encoding:utf-8 #Tutorial: http://www.knight-of-pi.org/raspi-ardi-big-love-pyfirmata-introduction/ #Licence: http://creativecommons.org/licenses/by-nc-sa/3.0/ # Author: Johannes Bergs import time import pyfirmata board = pyfirmata.Arduino('/dev/ttyACM0') # arduino setup iter8 = pyfirmata.util.Iterator(board) iter8.start() LED = board.get_pin('d:3:p') STEPS = 10000.0 if __name__ == '__main__': # increase PWM output in STEPS increments for i in range(int(STEPS)): print i LED.write(i / STEPS) # hardware-PWM accepts values 0.0 ... 1.0 time.sleep(0.001)
Set the USB port in the following line to match the output from
dmesg | grep tty
pi@vinophonics:~/pyFirmata $ dmesg | grep tty [ 0.000000] Kernel command line: coherent_pool=1M 8250.nr_uarts=0 cma=64M cma=256M smsc95xx.macaddr=DC:A6:32:1A:AB:AE vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000 dwc_otg.lpm_enable=0 console=ttyS0,115200 console=tty1 root=PARTUUID=4224e0ab-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait [ 0.000714] console [tty1] enabled [ 0.855408] fe201000.serial: ttyAMA0 at MMIO 0xfe201000 (irq = 33, base_baud = 0) is a PL011 rev2 [ 1911.239197] cdc_acm 1-1.3:1.0: ttyACM0: USB ACM device
Edit pyfirmata_pwm.py to match the device name
board = pyfirmata.Arduino('/dev/ttyACM0')
Then, connect the pin D3 of the Arduino to a LED and a 270Ω resistor on the breadboard, like this
Now, start the script by typing:
python pyfirmata_pwm.py
The glow of the LED will increase slowly until the full capacity is reached:
Can we read a slider position (from the RPi through the Arduino slave)
Here is a good summary of reading and writing to pins through PyFirmata.
Working from the previous example, we can test reading slider positions like this:
import time import pyfirmata board = pyfirmata.ArduinoMega('/dev/ttyACM0') # arduino setup # import only system from os from os import system, name # define our clear function def clear(): # for windows if name == 'nt': _ = system('cls') # for mac and linux(here, os.name is 'posix') else: _ = system('clear') it = pyfirmata.util.Iterator(board) it.start() slider_one = board.get_pin('a:0:i') slider_two = board.get_pin('a:1:i') slider_three = board.get_pin('a:2:i') slider_four = board.get_pin('a:3:i') slider_five = board.get_pin('a:4:i') slider_six = board.get_pin('a:5:i') slider_seven = board.get_pin('a:6:i') slider_eight = board.get_pin('a:7:i') slider_nine = board.get_pin('a:8:i') slider_ten = board.get_pin('a:9:i') slider_eleven = board.get_pin('a:10:i') slider_twelve = board.get_pin('a:11:i') slider_thirteen = board.get_pin('a:12:i') slider_fourteen = board.get_pin('a:13:i') slider_fifteen = board.get_pin('a:14:i') slider_sixteen = board.get_pin('a:15:i') while True: slider1 = slider_one.read() slider2 = slider_two.read() slider3 = slider_three.read() slider4 = slider_four.read() slider5 = slider_five.read() slider6 = slider_six.read() slider7 = slider_seven.read() slider8 = slider_eight.read() slider9 = slider_nine.read() slider10 = slider_ten.read() slider11 = slider_eleven.read() slider12 = slider_twelve.read() slider13 = slider_thirteen.read() slider14 = slider_fourteen.read() slider15 = slider_fifteen.read() slider16 = slider_sixteen.read() print "Slider 1: ", slider1 print "Slider 2: ", slider2 print "Slider 3: ", slider3 print "Slider 4: ", slider4 print "Slider 5: ", slider5 print "Slider 6: ", slider6 print "Slider 7: ", slider7 print "Slider 8: ", slider8 print "Slider 9: ", slider9 print "Slider 10: ", slider10 print "Slider 11: ", slider11 print "Slider 12: ", slider12 print "Slider 13: ", slider13 print "Slider 14: ", slider14 print "Slider 15: ", slider15 print "Slider 16: ", slider16 time.sleep(1) clear() board.exit()
Can we move the slider?
Here is a code example moving two sliders, to an input "target value". This is written for python3.
What is noticeable about this example, is the velocity and threshold for "overshooting" the target. If the speed is too fast (i.e. without using PWM) and the target threshold is too small, the slider can overshoot the target position, resulting in an infinite back-and-forth, searching the target. This could be corrected by throttling down the speed the closer the target becomes, or by using min() and max() as in the arduino sketch example SlideToValue. But this code gets us pretty close.
#!/usr/bin/python3 import time import pyfirmata board = pyfirmata.ArduinoMega('/dev/ttyACM0') # arduino setup # import only system from os from os import system, name # define our clear function def clear(): # for windows if name == 'nt': _ = system('cls') # for mac and linux(here, os.name is 'posix') else: _ = system('clear') it = pyfirmata.util.Iterator(board) it.start() speed = 1 threshold = 0.05 # declare pin assignments for reading the potentiometer position slider_one = board.get_pin('a:0:i') slider_two = board.get_pin('a:1:i') slider_three = board.get_pin('a:2:i') slider_four = board.get_pin('a:3:i') slider_five = board.get_pin('a:4:i') slider_six = board.get_pin('a:5:i') slider_seven = board.get_pin('a:6:i') slider_eight = board.get_pin('a:7:i') slider_nine = board.get_pin('a:8:i') slider_ten = board.get_pin('a:9:i') slider_eleven = board.get_pin('a:10:i') slider_twelve = board.get_pin('a:11:i') slider_thirteen = board.get_pin('a:12:i') slider_fourteen = board.get_pin('a:13:i') slider_fifteen = board.get_pin('a:14:i') slider_sixteen = board.get_pin('a:15:i') # declar pin assignments to control the H-Bridge direction slider_one_enable = board.get_pin('d:2:p') slider_one_up = board.get_pin('d:22:o') slider_one_down = board.get_pin('d:24:o') slider_two_enable = board.get_pin('d:3:p') slider_two_up = board.get_pin('d:26:o') slider_two_down = board.get_pin('d:28:o') targetValue = float(input("Please enter a target value 0-9: ")) targetValue = targetValue/10 while True: print("Desired position: ", targetValue) slider1 = slider_one.read() slider2 = slider_two.read() slider3 = slider_three.read() slider4 = slider_four.read() slider5 = slider_five.read() slider6 = slider_six.read() slider7 = slider_seven.read() slider8 = slider_eight.read() slider9 = slider_nine.read() slider10 = slider_ten.read() slider11 = slider_eleven.read() slider12 = slider_twelve.read() slider13 = slider_thirteen.read() slider14 = slider_fourteen.read() slider15 = slider_fifteen.read() slider16 = slider_sixteen.read() print("Slider 1: ", slider1) print("Slider 2: ", slider2) print("Slider 3: ", slider3) print("Slider 4: ", slider4) print("Slider 5: ", slider5) print("Slider 6: ", slider6) print("Slider 7: ", slider7) print("Slider 8: ", slider8) print("Slider 9: ", slider9) print("Slider 10: ", slider10) print("Slider 11: ", slider11) print("Slider 12: ", slider12) print("Slider 13: ", slider13) print("Slider 14: ", slider14) print("Slider 15: ", slider15) print("Slider 16: ", slider16) if (slider1 - targetValue) > threshold and (slider1 > targetValue): slider_one_up.write(0) slider_one_down.write(1) slider_one_enable.write(speed) elif (targetValue - slider1) > threshold and (slider1 < targetValue): slider_one_up.write(1) slider_one_down.write(0) slider_one_enable.write(speed) else: slider_one_enable.write(0) if (slider2 - targetValue) > threshold and (slider2 > targetValue): slider_two_up.write(0) slider_two_down.write(1) slider_two_enable.write(speed) elif (targetValue - slider2) > threshold and (slider2 < targetValue): slider_two_up.write(1) slider_two_down.write(0) slider_two_enable.write(speed) else: slider_two_enable.write(0) clear() board.exit()