Datastorage
The data storage is though to use both blockchain and a database based on OpenBravo. OpenBravo will be the web service for the website to communicate with and blockchain will be the trust-machine.
Ethereum data storage
Once we have initialized our private node (explained in the echo server configuration) we have developed some smart contracts that allow us to store data on the blockchain via authorised entities. To do so we've decided to develop one contract per sensor to ease the understanding of non-tech stakeholders. This contracts can be found in: GitHub - openvino/data-storage-solidity .
To test the contracts you can follow the instructions given in the README file or check the javadoc written in the code. Once we have this contracts implemented we had to prepare a script to lead the deployment of the given contracts in our private network which is the following one:
import json
import web3
from web3 import Web3, HTTPProvider
from solc import compile_source
def compile_source_file(file_path):
with open(file_path, 'r') as f:
source = f.read()
return compile_source(source)
print("\n 1) Connecting to the costaflores-node1 (http://10.112.48.25:8545) ...")
w3 = Web3(HTTPProvider("http://10.112.48.25:8545"))
print("\n 2) Preparing contracts (IoT, Datastorage)...")
compiled_sol = compile_source_file('contracts.sol')
IoT = w3.eth.contract(abi=compiled_sol['<stdin>:IoT']['abi'], bytecode=compiled_sol['<stdin>:IoT']['bin']);
Datastorage = w3.eth.contract(abi=compiled_sol['<stdin>:Datastorage']['abi'], bytecode=compiled_sol['<stdin>:Datastorage']['bin']);
print("\n 3) Deploying contracts (IoT, Datastorage)...")
tx_hash = Datastorage.constructor(w3.eth.coinbase).transact({'from': w3.eth.coinbase, 'gas': 672197})
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
print("\n - Contract Datastorage.sol was deployed at address: " + w3.toChecksumAddress(tx_receipt.contractAddress) + "on transaction " + w3.toHex(tx_hash))
datastorage = w3.eth.contract(address = w3.toChecksumAddress(tx_receipt.contractAddress), abi = compiled_sol['<stdin>:Datastorage']['abi'], )
tx_hash = IoT.constructor(tx_receipt.contractAddress, w3.toChecksumAddress("0xBD918Bf5d6A6DD2b6A7091f114d4382b8368Cc96")).transact({'from': w3.eth.coinbase, 'gas': 672197})
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
print("\n - Contract IoT.sol was deployed at address " + w3.toChecksumAddress(tx_receipt.contractAddress) + "on transaction " + w3.toHex(tx_hash))
print("\n 4) Initializing contracts (IoT, Datastorage)...")
tx_hash = datastorage.functions.setLogic(w3.toChecksumAddress(tx_receipt.contractAddress)).transact({'from': w3.eth.coinbase, 'gas': 672197})
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
tx_hash = datastorage.functions.transferOwnership(w3.toChecksumAddress("0xd8c897d27681bea0444474d9564f986d0ec864f1")).transact({'from': w3.eth.coinbase, 'gas': 672197})
tx_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
We ran this script for the deployment and got the following result:
1) Connecting to the costaflores-node1 (http://10.112.48.25:8545) ...
2) Preparing contracts (IoT, Datastorage)...
3) Deploying contracts (IoT, Datastorage)...
- Contract Datastorage.sol was deployed at address: 0xe87eFa27A14ABD7f8b3f281a0EeeD692562886b8 on transaction 0x366d013aac5a8cb9ce945b36916c1d8027b54ab3f281190c1605923adc115bed
- Contract IoT.sol was deployed at address 0x866d841A567d1D486CbD7ab0CDeD04479500BF25 on transaction 0x1ebda6eec19dba44cb2de053c97ce2c3a2ff4ac3766203a12905a121b6509ae0
4) Initializing contracts (IoT, Datastorage)...
We can check with the result that the contracts were deployed correctly and that the blockchain storage structure is prepared to receive the data.
Now it's just a matter of connecting the blockchain to the raspberry pi to receive the input and to OpenBravo to emit the output. To begin with the input we have the following information being collected in the weatherstion and the vinduinos (data structure):
Source | Type | Name | Extra description | Show on Dailylog Site |
---|---|---|---|---|
Weather station | String | timestamp | PK | No |
Vinduino | String | id_vinduino | PK | No |
Vinduino | int | sensor2 |
| Yes |
Vinduino | int | sensor1 |
| Yes |
Vinduino | int | sensor05 |
| Yes |
Vinduino | int | sensor005 |
| Yes |
Vinduino | float | battery |
| No |
Vinduino | float | temperature_local |
| No |
Vinduino | float | humidity | Not connected | No |
Weather station | float | rain |
| Yes |
Weather station | float | wind speed |
| No |
Weather station | float | wind gust |
| No |
Weather station | float | wind direction |
| No |
Weather station | float | pressure | Not working | No |
Weather station | float | temperature_global |
| Yes |
Weather station | float | humidity |
| Yes |
We have developed the following script to run and gather all the information from the sensors and redirect it to the blockchain (remember that previously we've set up the Weather Station to write its data to a file in: *******):
import sys
import json
import serial
import time
import web3
from web3 import Web3, HTTPProvider
from time import localtime, strftime
from datetime import datetime
print("\nSetting up enviroment for the Vinduinos")
ser = serial.Serial('/dev/ttyACM0', 9600, timeout=900)
print("Connecting to the node... (http://10.112.48.25:8545)")
print("\n ================================================== \n")
w3 = Web3(HTTPProvider("http://10.112.48.25:8545"))
with open('contracts.json') as json_file:
compiled_sol = json.load(json_file)
IoT = w3.eth.contract(address = w3.toChecksumAddress("0x866d841A567d1D486CbD7ab0CDeD04479500BF25") , abi = compiled_sol['contracts']['contracts.sol:IoT']['abi'], );
with open('keystore/UTC--2018-03-28T17-48-45.929617976Z--ff317bb0c816c449d6b5bb5085d71561fcb94a37') as keyfile:
encrypted_key = keyfile.read()
private_key = w3.eth.account.decrypt(encrypted_key,'*********')
while True:
print("\n1) Reading from serial port");
line = ser.readline();
if len(line)==0:
print("1.1) Error: Time out")
sys.exit()
line=line.decode("utf-8")
splitted_line = line.split(",")
start_char = splitted_line[0]
write_key = splitted_line[1]
sensor2 = int(float(splitted_line[2]) * 100)
sensor1 = int(float(splitted_line[3]) * 100)
sensor05 = int(float(splitted_line[4]) * 100)
sensor005 = int(float(splitted_line[5]) * 100)
battery = int(float(splitted_line[6]) * 100)
temperature = int(float(splitted_line[7]) * 100)
humidity = int(float(splitted_line[8]) * 100)
timestamp = strftime("%d/%m/%y %H:%M:%S", localtime())
data = [sensor2, sensor1, sensor05, sensor005, battery, temperature, humidity]
print("2) Line was read and preprocessed {" + str(write_key) + ", " + str(sensor2) + ", " + str(sensor1) + ", "
+ str(sensor05) + ", " + str(sensor005) + ", " + str(battery) + ", "
+ str(temperature) + ", " + str(humidity) + "} at " + timestamp + ".")
if(write_key == "********"):
write_key = "pe";
if(write_key == "********"):
write_key = "cs"
if(write_key == "********"):
write_key = "me"
if(write_key == "********"):
write_key = "mo"
print("3) Reading from weatherstation");
file = open("/home/pi/sensor-forwarder-python/weatherstation.txt", "r")
line = file.readline()
splitted_line = line.split(",")
wind_speed = int(float(splitted_line[0]) * 100)
wind_gust = int(float(splitted_line[1]) * 100)
wind_direction = int(float(splitted_line[2]) * 100)
pressure = int(float(splitted_line[3]) * 100)
rain = int(float(splitted_line[4]) * 100)
temperature = int(float(splitted_line[5]) * 100)
humidity = int(float(splitted_line[6]) * 100)
data.extend([wind_speed, wind_gust, wind_direction, pressure, rain, temperature, humidity])
print("4) Line was read and preprocessed: {" + str(wind_speed) + ", " + str(wind_gust) + ", " + str(wind_direction) + ", " + str(pressure) + ", " + str(rain) + ", " + str(temperature) + ", " + str(humidity) + "}")
print("5) Redirecting data from vinduino and weather station " + (timestamp + "$" + str(write_key)) + " to blockchain.")
reference = (timestamp + "$" + str(write_key)).encode('utf-8')
nonce = w3.eth.getTransactionCount(w3.toChecksumAddress('0xff317bb0c816c449d6b5bb5085d71561fcb94a37'))
tx = IoT.functions.post(reference, data).buildTransaction({'chainId': 42, 'gas': 7034534, 'gasPrice': w3.toWei('1','gwei'), 'nonce': nonce,})
signed_tx = w3.eth.account.signTransaction(tx, private_key = private_key)
tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction)
w3.eth.waitForTransactionReceipt(tx_hash)
print("6) Transaction " + w3.toHex(tx_hash) + " sent to remote node at http://10.112.48.25:8545");
Once we have implemented this script we need to run it in the background by executing the following command in the raspberry pi.
With this command the script will start writing the gathered data to the blockchain and will write into sensors-1.log the logs of its state so that we can check what's happening.
If python3 is not installed in the raspberry run the following commands:
Now we have our sensors connected to blockchain. We can check that it is running and the information being collected by using:
Now we have to collect the event generated in the blockchain once data is written to redirect it to OpenBravo. To do so we have implemented another python script that's going to run on ECHO:
forwarder-openbravo.py
Once it is implemented again we just have to executing by using the following command:
Check that everything is working properly
From raspberry:
From ECHO:
From browser:
Ethereum Network Status: http://10.112.48.25:3000/
OpenBravo: http://10.112.48.22:80/
First observe raspberry pi console until a data arrives. When a data arrives, it will read the data from both the vinduino and the weatherstation and send the transaction waiting for the step (6 - confirmation of the tx).
At this moment we can see in Ethereum Network Status how a new bar appears in the transaction graph. (appears when a block is mined)
Once it appears we can check in the ECHO console that the data inserted by the raspberry was read and redirected to OpenBravo.
Finally we look at the record and check that it has been inserted correctly in OpenBravo.