Dieses Tutorial ist veraltet und wandert bald ins Archiv.
Die neuere Anleitung findet ihr hier:
Da ich mittlerweile einen Pi Zero W mit BME280 habe und auf den MCP3208 umgestiegen bin, hier ein kleines Tut für mich selbst als Gedankenstütze, ihr dürft natürlich auch gucken und Tips geben:
Der Pi Zero W hat den Vorteil, dass man die Leitungen direkt anlöten kann, was einem Wackelkontakte erspart.
Hardware:
Das Board bzw. die Relais selbst laufen mit 5 Volt, die Optokoppler werden mit den 3,3 Volt des Raspberry angesteuert.
Die BME280 sind im Moment bissl teuer, aber das legt sich hoffentlich bald wieder.
Ach der BME280 benötigt 3,3 Volt und GND, zudem die BCM GPIOs 2 und 3 für I2C.
Der MCP3208 könnte zwar auch mit 5 Volt arbeiten, da die GPIOs jedoch nur 3,3 Volt vertragen, betreiben wir ihn auch nur mit 3,3 Volt.
Zum Betrieb des MCP3208 benötigen wir zusätzlich zu den 3,3 Volt und GND auch noch die BCM GPIOs 8, 9 ,10 und 11 für SPI.
Der MCP3208 hat eine 12-Bit-Auflösung, wenn wir also an einen Kanal 3,3 Volt anlegen, bekommen wir den Wert 4095 geliefert. (0 bis 4095)
Ich setze mal voraus, dass der Pi mit PiOS läuft, up to date ist und läuft, SPI und I2C mit raspi-config aktiviert wurde, deutsche Tastatur eingestellt ist, wir uns in /home/pi befinden und im Terminal als User pi angemeldet sind.
Ich benutze hier den GPIO BCM 24, um das Relaisboard zu schalten, wie z.B. hier zu sehen ist, oder im Terminal mit gpio readall
Zuerst erzeuge ich schon beim boot die nötigen Dateien im RAM, damit sie vorhanden sind und dem User pi gehören und die SD-Karte nicht kaputtschreiben.
sudo crontab -e
@reboot /home/pi/strom_aus.py
@reboot touch /dev/shm/strom_status.txt && chown pi:pi /dev/shm/strom_status.txt
@reboot touch /dev/shm/volt.txt && chown pi:pi /dev/shm/volt.txt
@reboot touch /dev/shm/zeit.txt && chown pi:pi /dev/shm/zeit.txt
@reboot touch /dev/shm/temperatur.txt && chown pi:pi /dev/shm/temperatur.txt
@reboot touch /dev/shm/luftdruck.txt && chown pi:pi /dev/shm/luftdruck.txt
@reboot touch /dev/shm/luftfeuchte.txt && chown pi:pi /dev/shm/luftfeuchte.txt
##############################################################################
Mit Strg+x speichern, mit j bestätigen und Enter drücken.
Das Script strom_aus.py wird ausgeführt, damit bei einem Stromausfall o.ä. die Last beim neu booten erst einmal aus bleibt.
nano strom_aus.py
#!/usr/bin/env python3
from RPi import GPIO as gpio
gpio.setwarnings(False)
gpio.setmode(gpio.BCM)
gpio.setup(24, gpio.OUT)
gpio.output(24, gpio.HIGH)
Mit Strg+x speichern, mit j bestätigen und Enter drücken.
Ausführbar machen mit chmod +x /home/pi/strom_aus.py
Da können wir auch gleich das Script zum anschalten noch machen:
nano strom_an.py
#!/usr/bin/env python3
from RPi import GPIO as gpio
gpio.setwarnings(False)
gpio.setmode(gpio.BCM)
gpio.setup(24, gpio.OUT)
gpio.output(24, gpio.LOW)
Mit Strg+x speichern, mit j bestätigen und Enter drücken.
Ausführbar machen mit chmod +x /home/pi/strom_an.py
Python-Spidev für den MCP3208 installieren:
sudo apt install python3-spidev
Ein Script mit nano MCP3208.py anlegen:
#!/usr/bin/env python3
from spidev import SpiDev
class MCP3208:
def __init__(self, bus = 0, device = 0):
self.bus, self.device = bus, device
self.spi = SpiDev()
self.open()
self.spi.max_speed_hz = 1000000 # 1MHz
def open(self):
self.spi.open(self.bus, self.device)
self.spi.max_speed_hz = 1000000 # 1MHz
def read(self, channel = 0):
adc = self.spi.xfer2([ 6 | (channel&4) >> 2, (channel&3)<<6, 0])
data = ((adc[1]&15) << 8) + adc[2]
return data
def close(self):
self.spi.close()
Alles anzeigen
Strg+x zum speichern, mit j und Enter bestätigen.
Ausführbar machen mit chmod +x /home/pi/MCP3208.py
Dann das eigentliche Script zum auslesen des MCP3208 und zum reagieren auf die unterschiedliche Spannung:
nano mcp3208_auslesen.py
#!/usr/bin/env python3
from MCP3208 import MCP3208
from datetime import datetime as DateTime
from RPi import GPIO as gpio
import time
import datetime
import subprocess
adc = MCP3208()
MIN_VOLTAGE = 12.8
MAX_VOLTAGE = 13.8
POWER_PIN = 24
VOLTAGE_FILENAME = "/dev/shm/volt.txt"
POWER_PIN_OFF_SCRIPT_FILENAME = "/home/pi/strom_aus.py"
POWER_PIN_ON_SCRIPT_FILENAME = "/home/pi/strom_an.py"
def read_value(filename, convert):
with open(filename, encoding="ascii") as file:
return convert(next(file))
def main():
with open("/dev/shm/zeit.txt", "w") as z:
z.write(str((datetime.datetime.now().strftime('%d-%m-%Y %H:%M:%S'))))
gpio.setwarnings(False)
gpio.setmode(gpio.BCM)
gpio.setup(POWER_PIN, gpio.OUT)
print("\n{:%A %d %B %Y %H:%M}\n{}".format(DateTime.now(), "-" * 30))
value = adc.read( channel = 0 )
print("Anliegende Spannung an Kanal 0: %.2f Volt" % (value / 290.69) )
with open("/dev/shm/volt.txt", "w") as v:
v.write(str("{0:.2f}".format(value / 290.69)))
voltage = (value / 290.69)
if voltage < MIN_VOLTAGE:
if gpio.input(24) == gpio.HIGH:
print("\nDer Strom ist aus, die Batteriespannung beträgt nur {0:.2f} Volt,\nich warte bis die Batteriespannung wieder über {1:.2f} Volt ist ...\n".format(voltage, MAX_VOLTAGE))
with open("/dev/shm/strom_status.txt", "w") as s:
s.write(str("1"))
elif gpio.input(24) == gpio.LOW:
print("\nIch Schalte den Strom aus, weil nur noch {0:.2f} Volt Batteriespannung anliegen!".format(voltage))
with open("/dev/shm/strom_status.txt", "w") as s:
s.write(str("1"))
subprocess.run([POWER_PIN_OFF_SCRIPT_FILENAME], check=True)
else:
print("Fehler beim auslesen des GPIO {}".format(POWER_PIN))
else:
print("\nDie Batteriespannung ist wieder über {0} Volt, überprüfe ob es auch mehr als {1} Volt sind.\nDas Ergebnis ist eine Batteriespannung von {2:.2f} Volt.".format(MIN_VOLTAGE, MAX_VOLTAGE, voltage))
voltage = read_value(VOLTAGE_FILENAME, float)
if voltage > MAX_VOLTAGE:
print("\nDie Batteriespannung ist wieder über {} Volt, mal sehen, ob der Strom schon an ist...\n".format(MAX_VOLTAGE))
if gpio.input(24) == gpio.HIGH:
print("\nDer Strom ist aus, die Batteriespannung beträgt {0:.2f} Volt, ich schalte den Strom jetzt ein!\n".format(voltage))
subprocess.run([POWER_PIN_ON_SCRIPT_FILENAME], check=True)
with open("/dev/shm/strom_status.txt", "w") as s:
s.write(str("0"))
elif gpio.input(24) == gpio.LOW:
print("\nDer Strom ist schon an, die Batteriespannung beträgt {0:.2f} Volt.\nDer Strom bleibt an !".format(voltage))
with open("/dev/shm/strom_status.txt", "w") as s:
s.write(str("0"))
else:
print("Fehler beim auslesen des GPIO {}".format(POWER_PIN))
else:
print("\n{0:.2f} Volt Batteriespannung.\nDer Stromstatus bleibt !\n".format(voltage))
if __name__ == "__main__":
main()
Alles anzeigen
Auch hier wieder mit Strg+x speichern, j und Enter.
Ausführbar machen mit chmod +x /home/pi/mcp3208_auslesen.py
Damit wird das Relaisboard bei unter 12,8 Volt ausgeschaltet und bei über 13.8 Volt eingeschaltet.
sudo apt install python3-smbus um die Software für den BME280 bzw. smbus zu installieren.
Das Script für den BME280:
nano bme280.py
#!/usr/bin/env python3
import smbus
import time
import datetime
import subprocess
from ctypes import c_short
from ctypes import c_byte
from ctypes import c_ubyte
DEVICE = 0x76 # Standard-Geräteaddresse am I2C
BUS = smbus.SMBus(1)
def get_short(data, index):
return c_short((data[index+1] << 8) + data[index]).value
def get_ushort(data, index):
return (data[index+1] << 8) + data[index]
def get_char(data, index):
result = data[index]
if result > 127:
result -= 256
return result
def get_uchar(data, index):
result = data[index] & 0xFF
return result
def read_bme280id(addr=DEVICE):
reg_id = 0xD0
(chip_id, chip_version) = BUS.read_i2c_block_data(addr, reg_id, 2)
return chip_id, chip_version
def read_bme280_all(addr=DEVICE):
reg_data = 0xF7
reg_control = 0xF4
reg_config = 0xF5
reg_control_hum = 0xF2
reg_hum_msb = 0xFD
reg_hum_lsb = 0xFE
oversample_temp = 2
oversample_pres = 2
mode = 1
oversample_hum = 2
BUS.write_byte_data(addr, reg_control_hum, oversample_hum)
control = oversample_temp << 5 | oversample_pres << 2 | mode
BUS.write_byte_data(addr, reg_control, control)
cal1 = BUS.read_i2c_block_data(addr, 0x88, 24)
cal2 = BUS.read_i2c_block_data(addr, 0xA1, 1)
cal3 = BUS.read_i2c_block_data(addr, 0xE1, 7)
dig_t1 = get_ushort(cal1, 0)
dig_t2 = get_short(cal1, 2)
dig_t3 = get_short(cal1, 4)
dig_p1 = get_ushort(cal1, 6)
dig_p2 = get_short(cal1, 8)
dig_p3 = get_short(cal1, 10)
dig_p4 = get_short(cal1, 12)
dig_p5 = get_short(cal1, 14)
dig_p6 = get_short(cal1, 16)
dig_p7 = get_short(cal1, 18)
dig_p8 = get_short(cal1, 20)
dig_p9 = get_short(cal1, 22)
dig_h1 = get_uchar(cal2, 0)
dig_h2 = get_short(cal3, 0)
dig_h3 = get_uchar(cal3, 2)
dig_h4 = get_char(cal3, 3)
dig_h4 = (dig_h4 << 24) >> 20
dig_h4 = dig_h4 | (get_char(cal3, 4) & 0x0F)
dig_h5 = get_char(cal3, 5)
dig_h5 = (dig_h5 << 24) >> 20
dig_h5 = dig_h5 | (get_uchar(cal3, 4) >> 4 & 0x0F)
dig_h6 = get_char(cal3, 6)
wait_time = 1.25 + (2.3 * oversample_temp) + ((2.3 * oversample_pres) + 0.575) + ((2.3 * oversample_hum)+0.575)
time.sleep(wait_time/1000)
data = BUS.read_i2c_block_data(addr, reg_data, 8)
pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
hum_raw = (data[6] << 8) | data[7]
var1 = ((((temp_raw>>3)-(dig_t1<<1)))*(dig_t2)) >> 11
var2 = (((((temp_raw >> 4) - dig_t1) * ((temp_raw >> 4) - dig_t1)) >> 12) * dig_t3) >> 14
t_fine = var1+var2
temperature = float(((t_fine * 5) + 128) >> 8)
var1 = t_fine / 2.0 - 64000.0
var2 = var1 * var1 * dig_p6 / 32768.0
var2 = var2 + var1 * dig_p5 * 2.0
var2 = var2 / 4.0 + dig_p4 * 65536.0
var1 = (dig_p3 * var1 * var1 / 524288.0 + dig_p2 * var1) / 524288.0
var1 = (1.0 + var1 / 32768.0) * dig_p1
if var1 == 0:
pressure = 0
else:
pressure = 1048576.0 - pres_raw
pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1
var1 = dig_p9 * pressure * pressure / 2147483648.0
var2 = pressure * dig_p8 / 32768.0
pressure = pressure + (var1 + var2 + dig_p7) / 16.0
humidity = t_fine - 76800.0
humidity = (hum_raw - (dig_h4 * 64.0 + dig_h5 / 16384.0 * humidity)) * \
(dig_h2 / 65536.0 * (1.0 + dig_h6 / 67108864.0 * humidity * (1.0 + dig_h3 / 67108864.0 * humidity)))
humidity = humidity * (1.0 - dig_h1 * humidity / 524288.0)
if humidity > 100:
humidity = 100
elif humidity < 0:
humidity = 0
return temperature/100.0, pressure/100.0, humidity
temperature,pressure,humidity = read_bme280_all()
print(datetime.datetime.now().strftime('%d-%m-%Y %H:%M:%S'))
print("Temperatur : ", temperature, "°C")
print("Luftdruck : {:.2f}".format(pressure), "hPa")
print("Luftfeuchtigkeit : {:.2f}".format(humidity), "%")
with open("/dev/shm/temperatur.txt", "w") as t:
t.write(str("{0:.2f}".format(temperature)))
with open("/dev/shm/luftdruck.txt", "w") as p:
p.write(str("{0:.2f}".format(pressure)))
with open("/dev/shm/luftfeuchte.txt", "w") as f:
f.write(str("{0:.2f}".format(humidity)))
Alles anzeigen
Speichern mit Strg+x, mit j bestätigen und Enter drücken.
Ausführbar machen mit chmod +x /home/pi/bme280.py
In Zeile 26 und 28 ist bei mir der Umrechnungswert durch den Spannungsteiler 290,69 der muss angepasst werden, je nach dem, wie hoch die Spannung ist, die ihr messen wollt, aber keinesfalls dürfen mehr als 3,3 Volt beim GPIO ankommen.
Mit ein wenig rechnen ganz einfach, beträgt die Ausgabe des MCP3208 z.B. 3718 und die angelegte Spannung vor dem Spannungsteiler ist 12,79 Volt, so ist 3718 / 12.79 = 290,69
Jetzt noch einen crontab für pi anlegen:
crontab -e
* * * * * /usr/bin/python3 /home/pi/bme280.py
* * * * * /usr/bin/python3 /home/pi/mcp3208_auslesen.py
##########################################
Speichern mit Strg+x, mit j bestätigen und Enter drücken.
Jetzt noch ein sudo reboot und wenn ich nichts vergessen habe, sollte es klappen.
Installation des Apache2:
sudo apt install libapache2-mod-php7.3, der Rest wird automatisch mitinstalliert, einfach j und Enter drücken.
sudo nano /var/www/html/index.php
<html><head><title>Spannung, Temperatur, Luftfeuchte, Luftdruck</title>
<span style="font-size:24pt">
<link rel="stylesheet" href="../menu_style.css" type="text/css" />
<meta http-equiv="pragma" content="no-cache">
</head>
<body bgcolor="#000000" text="#FFFFFF" link="#FFFFFF" vlink="#FFFFFF alink="#FFFFFF"">
<ul class="menu red">
<li class="current">
<a href="../index.php" target="_self">Spannung, Temperatur, Luftfeuchte, Luftdruck</a></li>
</ul>
<?php
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Expires: Sat, 01 Jan 2000 00:00:00 GMT"); // Datum in der Vergangenheit
?>
<?php
$volt = file_get_contents('/dev/shm/volt.txt');
echo $volt;
?> Volt
<br>
<?php
// Ausgabe z.B. somefile.txt wurde zuletzt modifiziert: December 29 2002 22:16:23.
$filename = '/dev/shm/volt.txt';
if (file_exists($filename)) {
echo "Spannung zuletzt gemessen:<br />\n " . date ("d F Y H:i:s", filemtime($filename));
}
?>
<br><br>
<?php
$strom = file_get_contents('/dev/shm/strom_status.txt');
if (intval($strom) == 0){
echo "Strom ist an\n";
} else {
echo "Strom ist aus\n"; }
?>
<br>
<?php
$filename = '/dev/shm/strom_status.txt';
if (file_exists($filename)) {
echo "Zuletzt geändert :<br />\n " . date ("d F Y H:i:s", filemtime($filename));
}
?>
<br><br>
<?php
$temperatur = file_get_contents('/dev/shm/temperatur.txt');
echo $temperatur;
?> ° Celsius
<br>
<?php
$luftfeuchte = file_get_contents('/dev/shm/luftfeuchte.txt');
echo $luftfeuchte;
?> % Luftfeuchtigkeit
<br>
<?php
$luftdruck = file_get_contents('/dev/shm/luftdruck.txt');
echo $luftdruck;
?> hPa Luftdruck
<br><br>
<?php
// Ausgabe z.B. somefile.txt wurde zuletzt modifiziert: December 29 2002 22:16:23.
$filename = '/dev/shm/temperatur.txt';
if (file_exists($filename)) {
echo "Temperatur , Luftdruck und Luftfeuchte zuletzt gemessen:<br />\n " . date ("d F Y H:i:s", filemtime($filename));
}
?>
<br>
Alles anzeigen
Strg+x, mit j bestätigen, Enter.
sudo nano /var/www/html/menu_style.css
ul.menu {
list-style-type:none;
width:auto;
position:relative;
display:block;
height:33px;
font-size:.6em;
background:url(images/bg.png) repeat-x top left;
font-family:Verdana,Helvetica,Arial,sans-serif;
border:1px solid #000;
margin:0;
padding:0;
}
ul.menu li {
display:block;
float:left;
margin:0;
padding:0;
}
ul.menu li a {
float:left;
color:#A79787;
text-decoration:none;
height:24px;
padding:9px 15px 0;
font-weight:normal;
}
ul.menu li a:hover,.current {
color:#fff;
background:url(images/bg.png) repeat-x top left;
text-decoration:none;
}
ul.menu .current a {
color:#fff;
font-weight:700;
}
/*RED*/
ul.menu.red{
background-color:#B11718;
}
ul.menu.red li a:hover, .menu.red li.current {
background-color:#DE3330;
}
Alles anzeigen
Strg+x, mit j bestätigen, Enter drücken.
Jetzt könnt ihr noch ein favicon.ico hinzufügen und einen Unterordner /images/ in dem die bg.png (siehe Anhang) liegt.
sudo mkdir /var/www/html/images
Die Forensoftware erlaubt keine .ico als Dateiendung, also nach dem runterladen umbenennen oder selbst eines erstellen.
Die Dateien www-data schenken:
sudo chown -R www-data:www-data /var/www/html/
Das ganze sollte dann so ausehen:
Und weil man es nicht oft genug schreiben kann, niemals dürfen mehr als 3,3 Volt am GPIO ankommen.
Wollt ihr höhere Spannungen messen, benötigt ihr einen Spannungsteiler.
Dazu komme ich dann später mit einem Update zur Hardware.
Hier lässt sich ein Spannungsteiler bequem online berechnen.
Z.B. bei maximalen 15 Volt, die am Kanal des MCP3208 angelegt werden, könnte das so aussehen:
Hier noch ein Bild der Verkabelung:
Sorry für die schlechten Paintskills. ¯\_(ツ)_/¯