Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
The project is about a health monitor for a paraplegic person who has a spinal cord injury above T6. It works by monitoring some bodily signs and hopefully predicting accurately when to automatically call the caregiver to help.
Autonomic DysreflexiaThese paraplegic people suffer from Autonomic Dysreflexia (AD) when there is a stressor in their body that they cannot feel because their spinal cord is broken. The parasympathetic pathway which normally clears the stress is unavailable because it goes through the spinal column. The AD is a splitting headache with sweating and feeling very ill. The most common cause is that the urinary catheter got twisted or plugged with gunk from a bladder infection and the bladder is overfull. They have no sensation from the bladder or other parts innervated below the spinal cord lesion. Urine flows pretty steadily into the bladder, and in this case out into the collection bag.
The AD_Monitor works by weighing the urine collection bag every couple of minutes using a Load Cell and Hx711 digitizer to a Pi Zero w. It also reads up to 2 Moisture and temperature sensors to detect the profuse sweating. When the urine weight does not increase it means that perhaps the catheter is blocked or has fallen out. The carer can come and clear the problem and avoid the pain and stress of a visit to the Emergency. The Moisture monitors detect the sweating and show that an episode of AD is in progress.
The AD_Monitor sends its data to the cloud. The Android app picks up this data every 20 minutes and will raise an alarm for the carer. This remote alarm is important in this application since the carer may be at home miles away.
The ProjectThe project breaks down into several parts.
- Get the IP address of a headless Pi
- Get the new user to register the home wifi ssid and password by filling in a web form on his phone or computer
- Read and store the weight from a Load-cell and hx711 digitizer
- Read the temperature and humidity from a Low Energy Bluetooth Beacon
- Remotely update the software on the headless Pi
- Send an email from a web server
- Send yourself an email with the wifi IP address
- Get House wifi ssid and password (part1)
- Get House wifi ssid and password (part2)
- Get House wifi ssid and password (part3) the raspberry pi webserver!
- Update the code running on a headless Raspberry Pi automatically, checking every 24h (python)
- Update the code running on a headless Raspberry Pi automatically, checking every 24h (php)
- Read two ble beacons to get temperature and relative humidity
- read the Load-Cell Hx711
Send an email from a web server
PHPexample call from python...
import requests
url = "https://mydomain.com/webtest/UFTSendSimpleEmail.php?subject=Autonomic_Dysreflexia&to="+getEmail()+"&from=autonomicdysreflexia@gmail.com&body="+body
print(url)
try:
r = requests.get(url)
except:
print ("Exception",sys.exc_info()[0])
A webserver it is needed for the automatic updating of the programs in this project. I have found http://nearlyfreespeech.net/ very good and you can start for only $5. It offers PHP and MYSQL which are the tools used here. You do not need to have a domain name, you can use the default (http://<yourname>.nfshost.com/) domain name since nobody will ever need to type them in (except you)
<?php
require_once "swiftmailer/lib/swift_required.php";
error_reporting(E_ERROR);
// var_dump($_GET);
$from = urldecode($_GET['from']);
$body = urldecode($_GET['body']);
$to = urldecode($_GET['to']);
$subject = urldecode($_GET['subject']);
// echo "<br>to=".$to;
// echo "<br>from=".$from;
// echo "<br>body=".$body;
// echo "<br>subjuect".$subject;
$transport = Swift_MailTransport::newInstance();
$message = Swift_Message::newInstance();
$message->setTo(array( $to ));
$message->setSubject($subject);
$message->setBody($body);
$message->setFrom($from);
$mailer = Swift_Mailer::newInstance($transport);
$mailer->send($message);
?>
Send yourself an email with the wifi IP address
Pythonsave your email in hx711py/data/user.email
Headless Raspberry Pi computers don't have a display or a keyboard. Usually you use ssh to program them, which requires knowing the ip address (the syntax to start ssh on a mac is "ssh pi@192.168.86.78" where the "192... is the ip address). It can vary at each reboot. This is a program to run during the boot procedure /etc/rc.local to send yourself an email containing the ip address!
Note that you need to set up the pi initially using a keyboard and screen. Be sure to enable the SSH interface. When you are setting up other pi's to send to customers you can just install the software on an SD card and then use that to initialize the Pi and get things going.
import time
import os
import subprocess
import commands
import re
import sys
import requests
import netifaces as ni
import md5
import stat
#functions
def findQuoted(string):
needle = '"'
st = string.find(needle)
end = string.rfind(needle)
return string[st+1:end]
def findssid():
return findQuoted(subprocess.check_output('iwgetid'))
def getEmail():
file = open("/home/pi/hx711py/data/user.email", "r")
em = file.read()
return em.strip()
def sendEmail(body):
print (body)
url = "https://mydomain.com/webtest/UFTSendSimpleEmail.php?subject=Autonomic_Dysreflexia&to="+getEmail()+"&from=autonomicdysreflexia@gmail.com&body="+body
print(url)
try:
r = requests.get(url)
except:
print ("Exception",sys.exc_info()[0])
#main line starts here.....
ssid = findssid()
body = ""
if ssid == "hotspot":
#here is the stuff about finding the wifi ssid and password...
else:
#this is the case where we are connected to a house wifi
ni.ifaddresses('wlan0')
ip = ni.ifaddresses('wlan0')[ni.AF_INET][0]['addr']
body = "your apparatus is connected to the House WiFi "
sendEmail(body + findssid() +" at "+ ip +" all seems to be well!")
Get House wifi ssid and password (part1)
PHPThe approach we have taken is to send an email containing a link to a web page to the client. The web page asks for ssid and password. The web page NEEDS to be opened on a computer/phone etc connected to hotspot/12345678 (more on this further down). The web page submits its data to a http server on the raspberry pi, where it is stored in the correct place so that as the raspberry pi reboots it will link to the house wifi. The password and ssid do not go across the internet, they go only within the local subnet. Given the length and complexity of these instructions the user can make repeated attempts to get it right provided he reboots (on hotspot) before each attempt.
The theory of this part of the project is as follows.
The user activates his local hotspot on and Android or iPhone or tablet. He MUST use the ssid "hotspot" and the password "12345678" (no quotes!!).
The raspberry pi 'knows' about hotspot/12345678 in its list of available internet connections in /etc/wpa_supplicant/wpa_supplicant.conf. It is the only internet connection in this file when the box arrives at the client's house.
As the raspberry pi boots up it tries to connect to the internet using a connection in the /etc/wpa_supplicant/wpa_supplicant.conf. If 'hotspot' is working, it will connect.
When the raspberry pi connects to the internet during boot-up it sends an email to the user.
The user's email address is stored in /home/pi/hx711py/user.email. It is put there in the factory before sending it out.
During the boot-up process, if the wifi connection is 'hotspot' the email it sends will include a http link to a web page containing a form asking for the ssid and password.
When you press the "send" button in the form the url is constructed containing the ssid and password and sent to a webserver on the raspberry pi.
The webserver adds the new ssid and password to the /etc/wpa_supplicant/wpa_supplicant.conf making a new 'netrwork' entry. ( actually it copies over a file containing only the 'hotspot' network entry and adds the new network entry). This implies that it can 'remember' only one wifi connection at a time, you need to repeat the hotspot/12345678 gobbledygook when you move house or go on an extended stay.
When the raspberry pi is rebooted it will seek either hotspot or the new house wifi. The Hotspot has a priority of 2 which means that it will be preferred. So turn off the hotspot before rebooting! Also use the 'hotspot' to set the raspberry pi onto a new network, or just use the hotspot if you are staying at a hotel for a few days.
This PHP code is stored on http://www.<your domain>.com/webtest/sendToRaspi.php
the parameters ?ip=<ip.addr>&<youremail> are applied to the url in AD_Monitor.start.py in the bootup sequence.
<?
$ip= $_GET['ip'];
$email = $_GET['email'];
//echo "ip = ".$ip;
$action = "http://".$ip.":5000/cakes?";
//echo "<br> action = ".$action;
?>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="jquery.mobile-1.4.5.min.css">
<script src="jquery-1.11.3.min.js"></script>
<script src="jquery.mobile-1.4.5.min.js"></script>
</head>
<body>
<div data-role="page" data-dialog="true" id="pagetwo">
<div data-role="header">
<h1>Autonomic Dysreflexia WiFisetup</h1>
</div>
<div data-role="main" class="ui-content">
<p>The Autonomic Dysreflexia program on the Raspberry Pi needs the credentials of your wifi.<br>The SSID is something like PeterWifi and the Password is whatever it is, be careful about upper and lower case!<br> The Android App 'Autonomic Dysreflexia' shows the gradual filling of the urine collection bag and warns if it stops. Access to this app is controlled by your email <? echo $email; ?> address and the 'App Password' you create here.</p>
<form action = <? echo $action; ?> method = "get" target="_blank">
SSID: <input type="text"name = "ssid" size = "20" placeholder= "Like 'PeterBrownWifi'" style = "text-transform: none" >
Password: <input type = "text" name = "password" size = "20" placeholder="Maybe 'TopSecretPassKey'" style = "text-transform: none">
App Password: <input type = "text" name = "apppassword" size = "20" placeholder="'for use in Android app'" style = "text-transform: none">
<input type = "submit" value = "SEND" width = "10">
</form>
</div>
<div data-role="footer">
<h1>These data go directly to the RaspberryPi</h1>
</div>
</div>
</body>
</html>
Get House wifi ssid and password (part2)
Pythonimport time
import os
import subprocess
import commands
import re
import sys
import requests
import netifaces as ni
import md5
import stat
orig_stdout = sys.stdout
f = open('/home/pi/hx711py/rc.local.log', 'w')
sys.stdout = f
#functions
import subprocess
def findQuoted(string):
needle = '"'
st = string.find(needle)
end = string.rfind(needle)
return string[st+1:end]
def findssid():
return findQuoted(subprocess.check_output('iwgetid'))
def getEmail():
file = open("/home/pi/hx711py/data/user.email", "r")
em = file.read()
return em.strip()
def sendEmail(body):
print (body)
url = "https://mydomain.com/webtest/UFTSendSimpleEmail.php?subject=Autonomic_Dysreflexia&to="+getEmail()+"&from=autonomicdysreflexia@gmail.com&body="+body
url1 = "https://uroflowtracings.com/webtest/UFTSendSimpleEmail.php?subject=Autonomic_Dysreflexia&to="+"jbrohan@gmail.com"+"&from=autonomicdysreflexia@gmail.com&body="+body
print(url)
try:
r = requests.get(url)
r = requests.get(url1)
except:
print ("Exception",sys.exc_info()[0])
def file_age_in_seconds(pathname):
return int(time.time() - os.stat(pathname)[stat.ST_MTIME])
#main line starts here.....
ssid = findssid()
body = ""
if ssid == "hotspot":
ni.ifaddresses('wlan0')
ip = ni.ifaddresses('wlan0')[ni.AF_INET][0]['addr']
link = "https://mydomain.com/webtest/sendToRaspi.php?ip="+ip+"&email="+getEmail()
body = " Click on the link below so send the wifi name (ssid) and the password to the apparatus. The Autonomic Dysreflexia apparatus \
needs to use your house wifi to send the readings to the cloud, so that your carer can get alerted on her Android.\n "
#body = "poobah"
p = file_age_in_seconds("/etc/wpa_supplicant/wpa_supplicant.conf")
print p
if p < 1200:
print "file is less than 10 minutes old"
body = "your apparatus is still connected to the hotspot"
sendEmail(body + findssid() +" at "+ ip +" all seems to be well!\nYou can turn off the hotspot on your Phone now, and it will use the House wifi!" + link)
else:
sendEmail(body+link)
else:
ni.ifaddresses('wlan0')
ip = ni.ifaddresses('wlan0')[ni.AF_INET][0]['addr']
body = "your apparatus is connected to the House WiFi "
#body = "plonkoplinko"
sendEmail(body + findssid() +" at "+ ip +" all seems to be well!")
# move these commands from the rc.local to a puython program to give more flexibility and
# and uniformity to the "over the Air" updating system...
cmd1 = "sudo /home/pi/hx711py/python AD_Monitor.md5.py update"
cmd2 = "sudo python /home/pi/hx711py/AD_Monitor.web.py &"
cmd3 = "sudo python /home/pi/hx711py/AD_Monitor.hx711.py &"
cmd4 = "sudo python /home/pi/hx711py/AD_Monitor.moisture.py &"
os.system(cmd1)
time.sleep(0.5)
print (cmd1)
os.system(cmd2)
time.sleep(0.5)
print (cmd2)
os.system(cmd3)
time.sleep(0.5)
print (cmd3)
os.system(cmd4)
time.sleep(0.5)
print (cmd4)
print "AD_Monitor launched"
Get House wifi ssid and password (part3) the raspberry pi webserver!
PythonThe task of this AD_Monitor.web.py is to get the ssid and password parameters from the web form in "Get House wifi ssid and password (part1)" and write them onto /etc/wpa_supplicant/wpa_supplicant.conf in addition to the network entry for 'hotspot/12345678' which is found in /etc/wpa_supplicant/wpa_supplicant.conf.copy
The net result of this is that the raspi will boot onto the house wifi if the hotspot is not active.
The syntax of flask is unexpected, but it seems to achieve its purpose of linking a web form to the raspberry pi and processing the action correctly.
Make sure you are using the hotspot/12345678 connection when you run the form! (that's three times I remind you because I forgot several times during testing!!)
#webServe.py AD_Monitor.web.py
from shutil import copyfile
from flask import Flask,request
import os
import sys
orig_stdout = sys.stdout
f = open('/home/pi/hx711py/rc.local.log', 'w')
sys.stdout = f
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello complicated world!'
@app.route('/cakes',methods=["GET"])
def cakes():
try:
a = request.args.get('id')
ssid = request.args.get('ssid')
passw = request.args.get('password')
copyfile ("/etc/wpa_supplicant/wpa_supplicant.conf.copy","/etc/wpa_supplicant/wpa_supplicant.conf")
supp = ["network={\n","\tssid=\""+ ssid+"\"\n","\tpsk=\"" + passw +"\"\n" , "\tkey_mgmt=WPA-PSK\n" + "\tpriority=1\n" + "}\n"]
print supp
suppFile = open("/etc/wpa_supplicant/wpa_supplicant.conf","a")
suppFile.writelines(supp)
suppFile.close()
print "rebooting network"
#os.system('ifconfig')
os.system('sudo shutdown -r +1 "reboot in one minute"')
#sleep (5)
#os.system('sudo ifconfig wlan0 up')
#sleep (20)
except:
print ("Exception",sys.exc_info()[0])
return "failed"
return "OK - close the personal hotspot. The raspi will reboot in one minute on your house WiFi."
if __name__ == '__main__':
app.run(debug=True, host = '0.0.0.0')
Update the code running on a headless Raspberry Pi automatically, checking every 24h (python)
PythonThe outline of this updating system.
From time to time the contents of the development and testing raspberry pi are ready to be published, maybe just one file or maybe all of them. The .php files which run on <myDomain> webserver are shown here as documentation
The AD_Monitor.md5.py with the 'create' parameter will upload all the files in the list to the AD_MonitorMd5 table in a database in <myDomain.com>. This is a manual task, done occasionally by the developer.
Every reboot of the headless raspberry pi, AD_Monitor.md5.py with the 'update' parameter examines the database to see if it has a newer version of the file. If so the new version is decrypted , downloaded as a temp name, md5 is checked and if all is well, the original file is removed and the temp name is renamed to the filename.
If there is an error and the file is not downloaded and renamed correctly, then at the next reboot it will try again. Pretty much the only thing that can fail is a power failure during this process, which requires a power up reboot anyway. It could possibly run out of disk space, but unlikely with this application.
The PHP file is simply the side that actually deals with the database, forming and executing the MYSQL statements. Note the UFTUtilitiesMysqli.php is intentionally omitted here. The execQ() function is really all you need and it's obvious!
The fields in the AD_MonitorMd5 table are:-
md5 the checksum of the file
filename
lastModified time stamp
fileContents and the encoded contents of the file (Note, the file must have no trailing blanks)
inc an auto-increment identifier, not used.
There is a list of files to be managed around line 23 of the AD_Monitor.md5.py, and this needs to be added to as more files are added to the system.
The md5 checksums are managed in the python part using hashlib on the regular code
The encrypting and decrypting are managed in the php. It does not handle trailing spaces well, eliminate them.
# write md5 digest to database for each .py file in the hx711py folder
import os , os.path
import hashlib
import requests
import sys
import datetime
import time
def getEmail():
file = open("/home/pi/hx711py/data/user.email", "r")
em = file.read()
return em.strip()
def sendEmail(body):
print (body)
url1 = "https://mydomain.com/webtest/UFTSendSimpleEmail.php?subject=AD_Monitor Update error&to="+"jbrohan@gmail.com"+"&from=autonomicdysreflexia@gmail.com&body="+body
print(url)
try:
r = requests.get(url1)
except:
print ("Exception",sys.exc_info()[0])
homeDir = "/home/pi/hx711py"
directory = os.listdir(homeDir)
fileList = ["AD_Monitor.start.py","AD_Monitor.moisture.py","AD_Monitor.web.py","AD_Monitor.md5.py","AD_Monitor.start.py","AD_Monitor.hx711.py","AD_Monitor.calibrate.py"]
print fileList
print sys.argv
if len(sys.argv) > 1:
if sys.argv[1] == "update":
url = "https://mydomain.com/webtest/AD_Monitor.md5.php?update=true"
try:
r = requests.get(url)
except:
print ("Exception",sys.exc_info()[0])
sys.exit()
p = r._content
rLine = p.split("*")
for s in rLine:
t = s.split(",")
if t[0] == "":
sys.exit()
mtime = long(os.path.getmtime(t[0]))
utc_time = datetime.datetime.utcfromtimestamp(mtime)
#print utc_time, str(utc_time)
if str(utc_time) < t[2]:
print "update this one",t[0]
url = "https://mydomain.com/webtest/AD_Monitor.md5.php?getfile=true&filename="+t[0]
try:
r = requests.get(url)
print "r** =",r
#print "r** =",r.text
except:
print ("Exception",sys.exc_info()[0])
sys.exit()
p = r._content
with open("temp.download","w") as text_file:
text_file.write(p)
checksum = hashlib.md5(open("temp.download", 'rb').read()).hexdigest()
print "checksum = ",checksum, t[0], t[1]
if checksum == t[1]:
print "hoohaa"
os.remove(t[0])
os.rename("temp.download",t[0])
else:
body = "Error during Update (AD_Monitor.md5.py) done on " + time.strftime("%Y-%m-%d %H:%M")
body = body + " remote user = " + getEmail() + " offending file = " + t[0]
body = body + "download checksum = " + checksum + " storage checksum = " + t[1] + "\n"
sendEmail(body)
print body
if sys.argv[1] == "create":
url = "https://mydomain.com/webtest/AD_Monitor.md5.php?new=true"
try:
r = requests.get(url)
except:
print ("Exception",sys.exc_info()[0])
for file in fileList:
if file.endswith('.py'):
checksum = hashlib.md5(open(file, 'rb').read()).hexdigest()
print file, checksum
mtime = os.path.getmtime(file)
mtime = mtime +1
#make sure that the AD_Monitor.md5.py update picks it up
t = "{:f}".format(mtime)
#print t,t,t
#last_modified_date = datetime.fromtimestamp(mtime)
url = "https://mydomainƒuro.com/webtest/AD_Monitor.md5.php?create=true"
url = url + "&md5="+checksum
url = url + "&filename="+file
url = url + "&lastModified="+t
print url
files = {'file': open(file)}
try:
print files,"files"
r = requests.post(url, files = files)
print "r** =",r
#print "r** =",r.text
except:
print ("Exception",sys.exc_info()[0])
Update the code running on a headless Raspberry Pi automatically, checking every 24h (php)
PHP<?
//AD_Monitor.md5.php
require_once 'UFTUtilitiesMysqli.php';
writeLog1("start of AD_Monitor.md5.php");
$var = print_r($_GET, true);writeLog1($var);
writeLog1("now the dollar post stuff...");
$var = print_r($_POST, true);writeLog1($var);
writeLog1("now thedollar files stuff...");
$var = print_r($_FILES, true);writeLog1($var);
if (isset($_GET['test'])){
$b = encrypt("abcdef");
$c = decrypt($b);
echo $b." " . $c;
}
if (isset($_GET['new'])){
$q = "DELETE FROM AD_MonitorMd5 WHERE inc > 0;";
execQ($q);
return;
}
if (isset($_GET['create'])){
$md5 = $_GET['md5'];
$filename = $_GET['filename'];
$lastModified = $_GET['lastModified'];
$q = "DELETE FROM AD_MonitorMd5 WHERE filename = '$filename';";
writeLog1($q);
execQ($q);
$d = date(intval($lastModified));
$dd = gmdate("Y-m-d H:i:s",$d);
//echo $d." ".$lastModified."--".$dd;
//writeLog1($filename);
//unlink($filename);
//var_dump($_POST);
move_uploaded_file($_FILES["file"]["tmp_name"], "tempTransfer");
$fileContents = encrypt(file_get_contents("tempTransfer"));
// unlink("tempTransfer");
writeLog1("\nLength offileContents = ".strlen($fileContents));
$q = "INSERT INTO AD_MonitorMd5 (md5,filename,lastModified,fileContents) VALUES ('$md5','$filename','$dd','$fileContents');";
//writeLog1($q);
$result = execQ($q);
writeLog1("\n result",$result);
return;
}
if (isset($_GET['update'])){
// print out the relevant data to decide which files to download!
$q = "SELECT filename,lastModified,md5 FROM AD_MonitorMd5;";
$result = execQ($q);
$num=getNum($result);
for ( $i = 0; $i < $num; $i++ ){
$row = getRow($result);
echo $row['filename'].",";
echo $row['md5'].",";
echo $row['lastModified']."*";
}
}
if (isset($_GET['getfile'])){
$filename = $_GET['filename'];
$q = "SELECT fileContents FROM AD_MonitorMd5 WHERE filename = '$filename';";
$result = execQ($q);
$row = getRow($result);
//var_dump($row);
echo decrypt($row['fileContents']);
}
function writeLog1 ( $text ){
//$text = "\n".$text;
$log = fopen ( "logfile.txt", "a+" );
fwrite ( $log, $text, strlen ($text) );
fclose ( $log );
//echo $text;
}
?>
Read two ble beacons to get temperature and relative humidity
PythonThe approach we have taken here is not perfect, it's not even good, but it does extract the data accurately and should work properly for any client who just buys one (of these two models) and turns it on near his head. No need to identify it.
The actual calls to read ble data usually use characteristics and rely on a connection. With Beacons there may be no connection, just pick off the airwaves their advertising packets and use them if they are the right ones. There may be many other advertisers, but our solution is limited to having only one each of these beacons within range (maybe later we will improve this).
The program structure is a s follows.
- prepare the bluetooth adapter on the raspberry pi device.
- run hcitool lescan and hcidump --raw periodically to capture the advertising packets being emitted locally, store these into the ramdisk.
The way this works is that hcitool lescan identifies the beacons and opens a pathway inside the bluetooth adapter to listen for those advertising packets.
sudo hcidump captures these advertising packets and with the --raw > /mnt/ramdisk/hcidump.txt &' command writes all the advertising packets it sees to the ramdisk.
The pid for each command is obtained and the process killed after 1 sec. Note the maximum delay between advertising packets of the BlueMaestro is 800ms, so 1s delay is ample.
At this point we have the /mnt/ramdisk/hcidump.txt file containing all those advertising packets. The next step is to go through them and pick out the appropriate ones for our sensors and then locate the data points and convert these into decimal and output these as strings to /mnt/ramdisk/MandT.txt and /mnt/ramdisk/MandT2.txt
The readem() function basically assembles the packets from the lines using the fact that the first char of a packet output by hcidump is ">".
The packets are scanned for the device id and then when this is found the temperature and moisture is extracted.
I do not reccommend this approach and would be delighted for someone to show me a wiser approach to getting data from beacons.
import time
import os
import subprocess
import commands
import re
mjDevice = ""
def getPid(process):
pid = subprocess.check_output(["pidof","-s",process])
return pid
def gettem():
cmd1 = 'hcitool lescan --duplicates > /dev/null &'
cmd2="sudo kill -15 "
cmd3 = 'sudo hcidump --raw > /mnt/ramdisk/hcidump.txt &'
os.system(cmd3)
time.sleep(0.5)
os.system (cmd1)
time.sleep(1)
pid = getPid('hcitool')
#print("pid found ",pid)
#print (cmd2 + pid)
os.system(cmd2 + pid)
time.sleep(0.5)
#print("stop Dump")
pidDump = getPid('hcidump')
#print("pidDump found ",pidDump)
#print (cmd2 + pidDump )
os.system(cmd2 + pidDump)
def readem():
global mjDevice
with open("/mnt/ramdisk/hcidump.txt") as f:
packet = ""
count = 0
for line in (f):
if line[0] == '>':
packet = re.sub(r"[\n\t\s]*", "", packet)
#print " ",packet
if "33011764" in packet:
temperature = packet[55:59]
temp = float(eval("0x"+temperature)/10.0)
#print temp
moisture = packet[59:63]
moist = float(eval("0x"+moisture)/10.0)
#print moist
fout = open( "/mnt/ramdisk/MandT.txt", 'w' )
fout.write( "&t1="+str(temp) + '&m1=' + str(moist) + '\n' )
fout.close()
if "4D4A5F48545F5631" in packet:
mjDevice = packet[15:27]
#print "f",packet
#print mjDevice
if mjDevice in packet and mjDevice != "":
if mjDevice + "0D1004" in packet:
#print "got One"
search = mjDevice + "0D1004"
#print "search", search
pos = packet.find(search)
if pos > -1:
#print packet
#print "pos", pos
#print packet[pos:pos+8]
temperature = packet[pos+18+2:pos+18+2+2] + packet[pos+18:pos+18+2]
moisture = packet[pos+22+2:pos+22+2+2] + packet[pos+22:pos+22+2]
#print "t", temperature, "m", moisture
temp2 = float(eval("0x"+temperature)/10.0)
moist2 = float(eval("0x"+moisture)/10.0)
fout2= open( "/mnt/ramdisk/MandT2.txt", 'w' )
fout2.write( "&t2="+str(temp2) + '&m2=' + str(moist2) + '\n' )
fout2.close()
packet = ""
packet = packet + line
print " this program reads a Blue Maestro Moisture meter"
print " and writes the Temp and Moist as floats 5.1 to "
print " the ramdisk file /mnt/ramdisk/MandT.txt where it "
print " can be read by darshan.py to send to the database."
one = 1
while one > 0:
os.system("sudo hciconfig hci0 down")
os.system("sudo hciconfig hci0 up")
gettem()
readem()
time.sleep(60)
read the Load-Cell Hx711
PythonLoadcells are conveniently connected to an hx711 digitizer.
The load cell has a metal element attached by the white glue in such a way that as the aluminium bar bends as weight is applied to one end the metal element changes its resistance. The Hx711 uses Wheatstone bridge approach to measure its resistance to 24 bits. In this program we read the 24 bits by raising and lowering the CLK line 24 times and each time reading the binary value on the DATA pin. This produces a 24 bit measurement of the resistance of the element on the aluminium load cell. A simple two point calibration procedure gives and offset and slope for converting these readings into grams.
The approach is not terribly accurate anyway, and in a varying temperature setting it's quite poor. If the counting to 24 gets off a bit then the results are wild. In this implementation we use a median rather than the mean. We make 71 readings with a short pause between each reading and then sort the results and pick the middle one to output every 2.5 minutes.
This python program is also responsible for sending up to 3 moisture and temperature values from bluetooth low energy beacons along with the weight to the database.
Database fields:-
id is the salted md5 hash of the user email. This will later be compared to a salted md5 hash computed in an Android app. This works well.
weight in g
time as a Unix timestamp, and 3 sets of temperature of relative humidity from the moisture meters.
The simplest way to connect two running python programs is through a file, in this case I use a Ram Disk made in the boot-up sequence in /etc/rc.local as
printf "make a 2Mb ramdisk"
if [!-e / mnt/ramdisk]; then
sudo mkdir /mnt/ramdisk
fi
sudo mount -t tmpfs -o size=2M none /mnt/ramdisk
touch /mnt/ramdisk/testfile
Note that this ramdisk survives a reboot.
The program AD_Monitor.moisture.py writes its temperature and humidity results to the ramdisk every 1 minute, and they are picked up every 2.5 minutes. There appears to me to be no great need to do things more precisely.
The calibration data is stored as binary representation and read using the pickle library.
The php target darshan.php in mydomain.com is a simple database program where the values are inserted into the database.
import RPi.GPIO as gpio
import time
import requests
import sys
import md5
import pickle
import re
import os.path
DT =11
SCK=8
HIGH=1
LOW=0
sample=0
val=0
gpio.setwarnings(False)
gpio.setmode(gpio.BCM)
gpio.setup(SCK, gpio.OUT)
orig_stdout = sys.stdout
f = open('/home/pi/hx711py/rc.local.log', 'w')
sys.stdout = f
print "setup as BCM DT = ",DT," SCK = ",SCK
def getEmail():
file = open("/home/pi/hx711py/data/user.email", "r")
em = file.read()
return em.strip()
def hashAString(string):
return md5.new(string+"a").hexdigest()
def readCount():
i=0
Count=0
gpio.setup(DT, gpio.OUT)
gpio.output(DT,1)
gpio.output(SCK,0)
gpio.setup(DT, gpio.IN)
while gpio.input(DT) == 1:
i=0
for i in range(24):
gpio.output(SCK,1)
Count=Count<<1
gpio.output(SCK,0)
#time.sleep(0.001)
if gpio.input(DT) == 0:
Count=Count+1
#print Count
gpio.output(SCK,1)
Count=Count^0x800000
time.sleep(0.001)
gpio.output(SCK,0)
return Count
#begin()
# # run the script darshanCalibrate.py to calibrate at eh factory first!!
try:
with open ("/home/pi/hx711py/data/calibration.txt","rb") as f1:
myList = pickle.load(f1)
except:
print ("Exception",sys.exc_info()[0])
print "You probably forgot to calibrate the load cell... run AD_Monitor.calibrate.py"
exit()
slope = myList[0]
offset = myList[1]
print "slope = "+ str(slope) + " offset = "+str(offset)
flag=0
sumSamples = 0
nSamples = 0
listSamples = []
while 1:
time.sleep(0.05)
count= readCount()
w=0
w=(count-offset)/slope
#print w,"g"
sumSamples=sumSamples+w
nSamples=nSamples+1
listSamples.append(w)
if nSamples > 70:
avg = sumSamples / nSamples
listSamples.sort()
medianPos = nSamples / 2
median = listSamples[medianPos]
median = round(median,2)
id = hashAString(getEmail())
url = "https://mydomain.com/webtest/darshan.php?id="+id+"&weight="+str(median)
#print url
with open( "/mnt/ramdisk/MandT.txt") as f3:
for line in (f3):
url = url + line.strip()
#print url
if os.path.exists("/mnt/ramdisk/MandT2.txt"):
#print url
with open( "/mnt/ramdisk/MandT2.txt") as f4:
for line in (f4):
url = url + line.strip()
#print url
try:
r = requests.get(url)
except:
print ("Exception",sys.exc_info()[0])
nSamples = 0
sumSamples = 0
listSamples = []
time.sleep(95)
Comments