Uppstart med systemd

Ramp nummer fem är under produktion men som alltid har det hänt saker med Raspbian som kräver lite pill och pyssel. I senaste versionen verkar SysV-uppstarten vara mer eller mindre avvecklad till förmån för systemd. MRTG startar fortfarande via de klassiska init.d-scripten men mina python-snurror för ljus- och temperaturstyrning ville gå igång den vägen längre. Dags att lära sig att göra .service-filer! Jag hittade en bra guide, How To Autorun A Python Script On Boot Using systemd, som fick fart på det hela.

 

Share

LED till köks-nanot

Vi har ett litet nanokar på 30 liter i köket som än så länge fått hålla till godo med den belysning som följde med. Det är en Aquael Leddy Smart på 6W, jag är dock inte säker på om det är en Sunny eller Plant, det är lite för liten skillnad i färgtemperatur. Den räcker i vilket fall inte till på långa vägar och valet mellan att köpa några till, köpa helt annan belysning eller bygga själv var enkelt. Fram med lödpennan!

Dioder hade jag redan hemma och ebay försedde mig med en kylfläns att limma dem på och en liten väggvårta på 12V/2A för att driva det hela för totalt 150:- inkl frakt och några veckors väntan.

2016-03-12 10.20.17

Det blev fyra kallvita, två royal blue och två röda vilka tillsammans ska ge drygt 35 lumen per liter. Än så länge sitter den lite temporärt med buntband på den gamla belysningen men vi ska ordna något bättre vad det lider. Det ska bli spännande och se om vår Hygrophila polysperma orkar färga ut och visa att den är en Rosanervig!

2016-03-12 10.25.57

Share

Aktuell LED-status

Vi har numera fyra ramper igång enligt modellen som jag beskrivit här tidigare. Två går i kar med mycket växter (ett sydamerikanskt och ett asiatiskt) och två används för malawi, både huvudkaret och yngelkaret. Alla har numera tre typer av dioder, kallvit (över 10 000K), röd (660 nm) och blå (450 nm) och styr förstås värmaren i respektive kar.

Växtkaren har ca 0,3W/liter och ca 20 lumen/liter vilket passar bra för växter som inte har extrema krav (vi använder inte CO2) när vi har tänt ca 10 timmar per dygn. Malawikaren har ca 0,2W/liter och ca 10 lumen/liter då det inte är mer växter än lite anubias och vallisneria i dem.

Jag är nöjd med både färger och tillväxt på fiskar och växter i karen och nörden i mig gillar att jag kan se temperaturen i karen i vår hallspegel… 🙂

Share

Ramp #2

DSC_0013

Nu har det första karet varit igång några veckor och har alla växter och invånare på plats. LED-rampen har nio grupper för dagsljus där varje grupp består av fyra dagsljusdioder (10-12000K) och två djupröda (655-660nm). Normalt har jag sex av grupperna tända vilket verkar ge en lagom balans med tanke på algtillväxten. Jag har dessutom en grupp med sex blå dioder (460-470nm) som är tända som månljus ett par timmar före och efter dagsljuset.

Temperaturbiten har byggts på så att doppvärmaren styrs av ett av reläen. Jag vill hålla 26°C i karet så värmaren slås på när det är 25,94°C och av när det är 26,05°C. Värmarens egen termostat är satt på 27°C så fallerar min styrning blir det antingen rumstemperatur eller 27°C, inget av det är katastrofalt. Naturligtvis får jag ett mail om avvikelsen är för stor.

DSC_0014

Idag fick även det andra karet en ramp av samma modell. Då kar nummer två är tänkt för malawiciklider (jo, det fattas en del inredning) rekommenderade Oceanakvarium ett blåare ljus. Varje grupp för dagsljus består därför av fyra dagsljusdioder (10-12000K), en djupröd (655-660nm) och en blå diod (460-470nm).

Koden har delats upp i två delar, en för ljuset och en för temperaturen:

#!/usr/bin/python
import RPi.GPIO as GPIO
import time
import datetime
import syslog
import smtplib
from random import randint
from time import strftime

syslog.syslog('Aqua-light started')

# Definiera tider
moon_on = datetime.datetime(2015, 1, 1, 8, 30)
sunrise_on = datetime.datetime(2015, 1, 1, 10, 30)
noon_on = datetime.datetime(2015, 1, 1, 15, 30)
noon_off = datetime.datetime(2015, 1, 1, 16, 30)
sunset_on = datetime.datetime(2015, 1, 1, 20, 30)
moon_off = datetime.datetime(2015, 1, 1, 23, 30)

# Klasser
class sunClass:
	def __init__(self):
		#2,3,17,5,6,7,8,9,10
		self.sun = [2,17,5,7,8,10]
		self.sunLength = len(self.sun)
		self.noon = [3,6,9]
		self.noonLength = len(self.noon)
	def init(self):
		syslog.syslog('sunClass.init called')
		for x in range (0, self.sunLength):
				GPIO.setup(self.sun[x], GPIO.OUT)
		for x in range (0, self.noonLength):
				GPIO.setup(self.noon[x], GPIO.OUT)
	def off(self):
		syslog.syslog('sunClass.off called')
		for x in range (0, self.sunLength):
			GPIO.output(self.sun[x], LEDOff)
		for x in range (0, self.noonLength):
			GPIO.output(self.noon[x], LEDOff)
	def sunrise(self, runtime):
		syslog.syslog('Sunrise started')
		for x in range (0, self.sunLength):
			GPIO.output(self.sun[x], LEDOn)
			if x == 1: moon.off()
			if x != self.sunLength - 1: time.sleep(runtime/(self.sunLength-1))
		syslog.syslog('Sunrise ended')
	def sunset(self, runtime):
		syslog.syslog('Sunset started')
		for x in range (0, self.sunLength):
			GPIO.output(self.sun[x], LEDOff)
			if x == self.sunLength-2: moon.on()
			if x != self.sunLength - 1: time.sleep(runtime/(self.sunLength-1))
		syslog.syslog('Sunset ended')
	def middag(self, status):
		if (status): syslog.syslog('Noon on')
		else: syslog.syslog('Noon off')
		for x in range (0, self.noonLength):	
			if (status): 
				GPIO.output(self.noon[x], LEDOn)
			else: 
				GPIO.output(self.noon[x], LEDOff) 
	def shadow(self, runtime):
		syslog.syslog('Shadow started')
		for x in range (0, self.sunLength+3):
			if x < self.sunLength: GPIO.output(self.sun[x], LEDOff)
			if x > 2: GPIO.output(self.sun[x - 3], LEDOn)
			time.sleep(runtime / (self.sunLength+3))
		syslog.syslog('Shadow ended')

class moonClass:
	def __init__(self):
		self.moon = 11
	def init(self):
		syslog.syslog('moonClass.init called')
		GPIO.setup(self.moon, GPIO.OUT)
	def off(self):
		syslog.syslog('moonClass.off called')
		GPIO.output(self.moon, LEDOff)	
	def on(self):
		syslog.syslog('moonClass.on called')
		GPIO.output(self.moon, LEDOn)	
		
# Variabler
sun = sunClass()
moon = moonClass()
LEDOn = 0
LEDOff = 1

# Funktioner
			
# Initiera GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
sun.init()
moon.init()
syslog.syslog('GPIO initialized')

# Initiera state
now = datetime.datetime.now()
sun.off()
moon.off()
state = 0
if now.time() > moon_on.time(): state = 1
if now.time() > sunrise_on.time(): state = 2
if now.time() > noon_on.time(): state = 3
if now.time() > noon_off.time(): state = 4
if now.time() > sunset_on.time(): state = 5
if now.time() > moon_off.time(): state = 0
if state == 1 or state == 5:
	moon.on()
elif state <> 0:
	sun.sunrise(0)
if state == 3:
	sun.middag(True)
syslog.syslog('Initial state is %d' % state)

# Huvudloop
while True:
	now = datetime.datetime.now()
	if state == 0:
		# Night mode, wait for morning moon
		if now.time() > moon_on.time() and now.time() < moon_off.time():
			state = 1
			syslog.syslog('Entering state 1')
			moon.on()
	elif state == 1:
		# Morning moon, wait for sunrise
		if now.time() > sunrise_on.time():
			state = 2
			syslog.syslog('Entering state 2')
			sun.sunrise(3600)
	elif state == 2:
		# Daytime, random shadows, wait for noon
		if randint(1, 60) == 1:
			sun.shadow(60)
		if now.time() > noon_on.time():
			state = 3
			syslog.syslog('Entering state 3')
			sun.middag(True)
	elif state == 3:
		# Noon, wait for second daytime
		if now.time() > noon_off.time():
			state = 4
			syslog.syslog('Entering state 4')
			sun.middag(False)
	elif state == 4:
		# Second daytime, random shadows, wait for sunset
		if randint(1, 60) == 1:
			sun.shadow(60)
		if now.time() > sunset_on.time():
			state = 5
			syslog.syslog('Entering state 5')
			sun.sunset(3600)
	elif state == 5:
		# evening moon, wait for night 
		if now.time() > moon_off.time(): 
			state = 0
			syslog.syslog('Entering state 0')
			moon.off()
	time.sleep(60)
#!/usr/bin/python
import RPi.GPIO as GPIO
import time
import datetime
import syslog
import smtplib
from time import strftime

syslog.syslog('Aqua-temp started')

# Definiera temperaturgranser
alarmLow = 25.5
alarmHigh = 26.5
tempLow = 25.9
tempHigh = 26.1

# Klasser
		
# Variabler
heatOn = 0
heatOff = 1
heater = 22
tempLarm = False
heatStatus = False

# Funktioner
def sendMail(subject, msg):
	fromaddr = 'Rikard Elofsson <elof@elof.com>'
	toaddr  = 'Rikard Elofsson <elof@elof.com>'
	message = ('From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s' % (fromaddr, toaddr, subject, msg))
	username = 'rikard.elofsson@gmail.com'
	password = 'jättehemligt'

	server = smtplib.SMTP('smtp.gmail.com:587')
	server.starttls()
	server.login(username,password)
	server.sendmail(fromaddr, toaddr, message)
	server.quit()
	syslog.syslog('Mail sent')

def writeTemp(temp):
	tfile = open('/var/www/html/index.html', 'w')
	tfile.write('<html><head>')
	tfile.write('<title>Info f&ouml;r kar 1</title>')
	tfile.write('<META HTTP-EQUIV="refresh" CONTENT="60">')
	tfile.write('</head><body>')
	tfile.write(strftime("%Y-%m-%d %H:%M:%S"))
	tfile.write('<h1>Temperaturen i kar 1 &auml;r '+str(temp)+'&deg;C</h1>')
	tfile.write('<h2>V&auml;rmaren &auml;r ')
	if heatStatus: tfile.write('p&aring;</h2>') 
	else: tfile.write('av</h2>')
	tfile.write('<p><a href="/mrtg/kar1.html">Historik</a>')
	tfile.write('</body></html>')
	tfile.close()
	
def checkTemp():
	global tempLarm
	global heatStatus
	
	tfile = open("/sys/bus/w1/devices/28-0000065bd235/w1_slave", 'r')
	text = tfile.read()
	tfile.close()

	secondline = text.split("\n")[1]
	temperaturedata = secondline.split(" ")[9]
	temperature = float(temperaturedata[2:])
	temperature = float(int((temperature/100)+0.5))/10

	if (temperature < alarmLow or temperature > alarmHigh):
		if not tempLarm:
			syslog.syslog('Temp is wrong')
			sendMail("Temperaturen ar felaktig", "Temperaturen i kar 1 ar nu "+str(temperature))
			tempLarm = True
	else:
		if tempLarm:
			syslog.syslog('Temp is normal again')
			sendMail("Temperaturen ar normal", "Temperaturen i kar 1 ar nu "+str(temperature))
			tempLarm = False

	if (temperature <= tempLow):
		if not heatStatus:
			syslog.syslog('Temp is '+str(temperature)+'. Heater switched on')
			GPIO.output(heater, heatOn)
			heatStatus = True
	if (temperature >= tempHigh):
		if heatStatus:
			syslog.syslog('Temp is '+str(temperature)+'. Heater switched off')
			GPIO.output(heater, heatOff)
			heatStatus = False
	
	writeTemp(temperature)
	
# Initiera GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(heater, GPIO.OUT)
GPIO.output(heater, heatOff)
syslog.syslog('GPIO initialized, heater switched off')

# Huvudloop
while True:
	checkTemp()
	time.sleep(60)
Share

Temperatur #2

Skärmklipp

Det som kan mätas ska loggas, därför har jag installerat MRTG på akvariets Raspberry Pi. Med hjälp av följande mrtg.cfg:

######################################################################
# Multi Router Traffic Grapher -- Sample Configuration File
######################################################################
# This file is for use with mrtg-2.5.4c

# Global configuration
WorkDir: /var/www/mrtg
WriteExpires: Yes
RunAsDaemon: Yes
Interval: 5

Title[^]: Temperaturstatistik for 

Title[kar1]: kar 1
PageTop[kar1]: <h1>Temperaturen i kar 1</h1>
Target[kar1]: `/usr/local/bin/aquacontrol/mrtg.py`
MaxBytes[kar1]: 4000
Options[kar1]: growright,gauge,expscale
Factor[kar1]: 0.01
YLegend[kar1]: Temperatur
YTicsFactor[kar1]: 0.01
ShortLegend[kar1]: C
Legend1[kar1]: Temperatur i grader celcius
Legend2[kar1]: 
Legend3[kar1]: Maximal 5 Minute Incoming Traffic
Legend4[kar1]: Maximal 5 Minute Outgoing Traffic
LegendI[kar1]: &nbsp;Temp:
LegendO[kar1]: 

och ett litet pythonskript:

#!/usr/bin/python
from time import strftime

tfile = open("/sys/bus/w1/devices/28-0000065bd235/w1_slave", 'r')
text = tfile.read()
tfile.close()

secondline = text.split("\n")[1]
temperaturedata = secondline.split(" ")[9]
temperature = float(temperaturedata[2:])
temperature = int(temperature / 10)

print(str(temperature))
print("0")
print(strftime("%Y-%m-%d %H:%M:%S"))
print("Temperatur i kar 1")

loggas temperaturen och visas upp i fina grafer för senaste dygnet, veckan, månaden och året.

Share

Temperatur

När man har en dator till akvariet så kliar det lite i fingrarna, det finns ju mer att göra än att styra ljuset! Först ut blev en enkel sak, att mäta vattentemperaturen i karet. Jag införskaffade en DS18B20 som är en vanlig sensor med 1-wire-gränssnitt. Med hjälp av instruktionerna på Computer Laboratory var det enkelt att löda ihop den med ett pull-up-motstånd och koppla in den på min Pi.

DSC_0198

Jag fick i och för sig flytta på styrningen av ett av reläerna och ändra lite i koden till ljusstyrningen men det var ett mindre bekymmer. Då var det lite jobbigare att den inte identifierades som den skulle… Efter ett tag hittade jag lösningen, version 3.18 av kerneln ändrade hanteringen av 1-wire och man är tvungen att lägga till ett par rader i /boot/config.txt:

# 1-wire settings
dtoverlay=w1-gpio,gpiopin=4

Efter omstart gick det bra att söka upp sensorn med två modprobe-kommandon men de vill inte skriva in varje gång utan istället la jag in det permanent i /etc/modules:

w1-gpio
w1-therm

För att kunna se temperaturen enkelt och på sikt presentera den på min spegel installerade jag Apache på min Pi med

apt-get install apache2

Sedan gick det enkelt att hacka ihop ett litet Python-skript som skriver aktuell temperatur varje minut till Apaches standardfil:

#!/usr/bin/python
import time

while True:
	tfile = open("/sys/bus/w1/devices/28-0000065bd235/w1_slave", 'r')
	text = tfile.read()
	tfile.close()

	secondline = text.split("\n")[1]
	temperaturedata = secondline.split(" ")[9]
	temperature = float(temperaturedata[2:])

	temperature = temperature / 1000
	wfile = open("/var/www/index.html", 'w')
	text = wfile.write(str(temperature))
	wfile.close()

	time.sleep(60)

Nu ska det bara kompletteras med en JSON-variant och integreras i spegeln också men det får bli en annan dag.

Share

Tänt vare här!

Idag kom de nya reläkorten så kvällspysslet var givet. Nu fungerar allt som det ska och rampen är i drift, än så länge på det första akvariet i väntan på att vi inreder det andra. Ljuset är mycket bra och färgerna riktigt lyser på kardinaltetrorna.

Materialkostnaden för bygget är ungefär 1 500:- inklusive Raspberry Pi. Det som inte ingår är nätagget som jag hade kvar från en gammal dator så där tillkommer några hundralappar om man måste köpa ett från eBay. Med tanke på att det kan kosta upp emot 8 000:- att köpa en liknande ramp tycker jag det är rätt bra.

DSC_0179

Share

Håhå jaja…

När alla sladdar var på plats och strömmen påslagen tände sig alla dioder snyggt och prydligt. Synd att de inte släckte sig lika villigt. Efter lite funderande och googlande på komponenter visade det sig att de Solid State-reläer som sitter på reläkorten är för AC och bryter på nollgenomgång. Det blir svårt med lysdioder som trivs bättre med DC… Nya reläkort, denna gång med mekaniska reläer, är på väg från England. Det är som vanligt Micke som gärna är med på kort.

DSC_0173

Share