Using PyFirmata
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
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.
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
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
Set the USB port in the following line to match the output from
Edit pyfirmata_pwm.py to match the device name
Then, connect the pin D3 of the Arduino to a LED and a 270Ω resistor on the breadboard.
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:
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.