Sunday, January 20, 2013

Heating monitor display system on google code

I have been thinking for a while that maybe I should put the code for the heating monitor system on google code. And now I have done that: http://code.google.com/p/heating-monitor/

I'm hoping I can use it to keep drawings and building instructions as well.

Sunday, January 13, 2013

Central heating monitor display unit

Today I upgraded the display unit for my central heating monitoring system.This will give me a good opportunity to show the hardware since I had to dismantle the unit.

The idea is that it should give visible and audible alerts when there is some event that should be attended to. The ones I have identified are:
  1. When there is not enough hot water in the tanks to heat up the house
  2. When the current set of logs in the furnace have burned out and there is space for more heat in the tanks.
Number 1 I've scratched since I found it was a bit nagging. Normally you have plenty of time to act when heat is getting lower and after it runs out the house doesn't go cold within a couple of hours. Number 2 on the other hand is very useful since the furnace can take enough logs to heat up about 2/3 of the tanks. Then I can warm up the tanks to maximum without running to the display and check all the time. The problem is that the audio has not been working.

My initial plan was to use a piezo speaker and let the AVR output the frequency of the sound. By doing that I can even play a small tune depending on what kind of event that triggered the alert. But in the end ran out of timers in the AVR so it could not output any frequency. Instead I purchased an active piezo speaker which outputs 4khz. This I connected to a data pin on the AVR to be able to turn the sound on and off. This is not optimal but better than no sound at all. I started off by connecting the speaker to an arduino to see that it worked. With the Blink sketch i verified that the speaker turned on and off with 1 seconds interval.

Then I dismantled the display unit to get ready to solder the speaker to the stripboard that the unit is built on. As usual when I started the project I began with the stripboard. Then when it is time to find a casing it is a real pain to get one that the board fits in. But I was in luck and found one that both managed to fit the board and that had the fittings for the screws in just the right places. With some spacers I also manged to get the display and buttons to end up flush with the case front. This is how it looks inside:


Here you can see the component side of the board with the speaker soldered in place:


I then updated the coded to be able to turn of sound if needed. Maybe the sound will be a nag or there is some error that makes it beep all the time. The system measures the temperature that the furnace loads the tank. When the furnace is in full action the loading temperature is at about 87 degrees Celsius. When the logs in the furnace is running out the temperature drops, and around 76 degrees it is completely out. So I set the audible alarm to go off when the temperature has been more than 80 and then goes down to 78. That will tell me that it is a good time to fill up with more wood. The time from loading until filling up is around 3.5 hours.

Here are some examples of the different screens I show on the display:


The first picture shows the main screen with a number of temperatures and how much heat is stored in the tank. The second shows the temperatures as a list instead including the sensors mounted on the tank. The third shows a history diagram which can be set for four different values with different sample times. Currently it doesn't show anything because it has been turned off before taking the picture which clears the history. In the future I'm planning to implement storage on a dedicated logger unit which will contain an SD-card. 4th to 6th image shows the menu, the preference setup and the device setup screen.

Here are the schematics and stripboard layout for the display unit:



Monday, January 7, 2013

A hot water storage tank and central heating temperature monitoring system

This is a short description of the central heating temperature monitoring system that I have installed in my house. It's an ongoing project which only seems to grow as time goes. :-)

The system keeps track of indoor/outdoor temperature, radiator inlet/outlet temperature, wood furnace heat transfiguring and effect and the hot water storage tank temperature. The hot water tank is actually measured at 7 positions evenly distributed along the tank from inlet pipe to outlet pipe. By doing this I can actually see the level of hot water in the tank and predict when the tank will be empty.


The measuring is done via a 1-wire network with 14 connected DS18S20 temperature sensors. The network is then connected to an Arduino based display unit. The display unit gives current status and is able to log and display up to 4 of the sensors with 128 samples each. This is because of the limited RAM in the ATmega328p.


The expansion plan now is to create a logger on the 1-wire network which logs all the data on the display with some interval. The logger will have wifi connection and an SD card so that I will be able to check up on the current status from my iPhone and even from the Internet when I am not at home. The logger will also be Arduino based. I guess that maybe I should have gone with a Raspberry Pi instead but I had already gotten some parts for the project when the Pi was available. And also it will be more of a challenge to fit everything into the ATmega328 :-)

Sunday, January 6, 2013

Installing second Arch Linux partition for dual boot on Raspberry Pi without using other computer

Recently I managed to set up an AirPlay http streaming server on my raspberry pi. Now I also want to have a web server on the pi. Since it is supposed to be public I want to do it on a fresh install of Arch Linux. But I don't want to buy a new SD card for this and I don't want to loose my AirPlay setup.

So I started to look into dual booting the pi. All I could find was how to use berryboot but that seemed to require another computer and also wipes the SD card. I wanted to do it directly on the pi and as simple as possible. And I had not resized the original Arch install so I had room for another on my 4Gb SD. This is how I did it.

First I downloaded the at the moment latest image:
#wget http://mirrors.dotsrc.org/rpi/images/archlinuxarm/archlinux-hf-2012-09-18/archlinux-hf-2012-09-18.zip
Then I setup a second partition EXACTLY the same size as the current root:
#sudo parted
GNU Parted 3.1
Using /dev/mmcblk0
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) unit MiB
(parted) print
Model: SD SD4GB (sd/mmc)
Disk /dev/mmcblk0: 3796MiB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags: 

Number Start End Size Type File system Flags
1 1.00MiB 95.0MiB 94.0MiB primary fat16 boot, lba
2 95.0MiB 1886MiB 1791MiB primary ext4

(parted) mkpart ext4 1886MiB 3677MiB
(parted) quit
Then I used unzip and dd together to write the downloaded root partition to the new partition:
#su -
#unzip -p archlinux-hf-2012-09-18.zip | dd of=/dev/mmcblk0p3 bs=1M skip=99614720 iflag=fullblock,skip_bytes
DANGER: Be sure to use the right path for the of flag or else you could wipe your whole boot or root partition!!!!
The skip part is because the image has a partition table and a boot partition in front of the root partition. By looking at the partition table above we can see that the root begins at 95MiB into the image. unzip -p pipes the output to dd. This is very useful since there is not enough room to uncompress the whole image.

By mounting the new partition we can see that it seems good:
#mount /dev/mmcblk0p3 /mnt/dual
TBD: Setting up cmdline.txt and config.txt in /boot to use new partition

Saturday, January 5, 2013

FFMpeg parameters to transcode enigma2 recordings

After a lot of trial and error I found that the following ffmpeg command line work well with transcoding recordings from my enigma2 satelite box. For some reason a few SD channels transmit with a very high bitrate and encoded in mpeg2. With the transcoding to h264 I manage to get the file size down to about 1/3th the original without visibly lowered quality. In this example the recording has two different audio streams which I only copy.

start /low  ffmpeg.exe -vsync 0 -copyts -i "input.ts" -map 0:0 -map 0:1 -map 0:2 -codec:v libx264 -g 50 -crf 22 -aud 1 -mpegts_service_id 0xBB9 -codec:a copy -codec:a copy -y -f mpegts "output.ts"

The ffmpeg version I use is 20121230-git-518239c.

Friday, January 4, 2013

A python .ap and .sc generator for enigma2 recordings

In my quest to make a transcoder for recordings on my enigma2 satellite receiver I've made an .ap and .sc file generator in python. The .ap and .sc files are used for more accurate skipping in the recording when viewing on the receiver. As input and inspiration I used ProcessApSc plugin by Michel Hartman and also the OpenPli source for pvrparse.cpp.
from struct import pack, unpack
import io
import os.path
import sys

LEN = 24064
PACKET_SIZE = 188
    
def WriteBufInternal(f, sc, tm):
    f.write(pack('>QQ', sc, tm))

def FrameGenerator(ts, packetoffset=0, maxlen=-1):
    s = io.open(ts, 'rb', buffering=LEN)
    buf = bytearray(PACKET_SIZE)
    streamtype = -1
    pid = -1
    while len(s.peek())>=PACKET_SIZE: #Skip if not a whole packet left
        while ord(s.peek()[0]) != 0x47: #Sync stream
            s.read(1)
            print("Skipped 1 byte")
 
        filepos = s.tell()
        if maxlen >= 0 and filepos > maxlen: #Limit reading for debugging purposes
            break
        
        if s.readinto(buf) != PACKET_SIZE: #If not enough data quit
            s.close()
            break

        if not buf[3]&0x10: #Skip if there is no payload
            continue

        pos = buf[4] + 5 if buf[3]&0x20 else 4 #Skip adaption field
        if pos > PACKET_SIZE: #Skip large adaption field
            continue

        tpid = ((buf[1]&0x1F) << 8) | buf[2] #Read current packet pid
        if (not (buf[pos] or buf[pos+1] or not buf[pos+2]&0x01)
            and buf[pos+3]&0xf0 == 0xe0
            and buf[1]&0x040): #Find video pid
            pid = tpid
        elif pid>=0 and pid != tpid: #Wrong pid
            continue

        pts = -1
        if buf[1]&0x40: #Pusi
            if buf[pos] or buf[pos+1] or not buf[pos+2]&0x01:
                print("Broken startcode")
                continue
            if buf[pos+7]&0x80: #PTS present?
                pts = ((buf[pos+9]&0xE) << 29   | (buf[pos+10]&0xFF) << 22 |
                       (buf[pos+11]&0xFE) << 14 | (buf[pos+12]&0xFF) << 7  |
                       (buf[pos+13]&0xFE) >> 1)
            pos = buf[pos+8] + 9
        
        while pos < PACKET_SIZE - 4:
            if not (buf[pos] or buf[pos+1] or not buf[pos+2]&0x01):
                sc = buf[pos+3]
                if streamtype < 0: #Stream type is unknown
                    if sc in [0x00, 0xb3, 0xb8]:
                        streamtype = 0
                        print("Detected MPEG2 stream type")
                    elif sc in [0x09]:
                        streamtype = 1
                        print("Detected H264 stream type")
                    else:
                        pos += 1
                        continue
                
                if streamtype == 0: #MPEG2
                    if sc in [0x00, 0xb3, 0xb8]: #Picture, sequence, group start code
                        retpos = retpts = retdat = retpos2 = -1
                        if sc == 0xb3 and pts >=0 : #Sequence header
                            retpos = filepos
                            retpts = pts
                        if pos < PACKET_SIZE - 6:
                            retdat = sc | buf[pos+4] << 8 | buf[pos+5] << 16
                            if pts >= 0:
                                retdat |= (pts << 31) | 0x1000000;
                            retpos2 = filepos + pos
                        yield (retpts, retpos, retdat, retpos2)

                elif streamtype == 1:
                    if sc == 0x09:
                        retpos = retpts = retdat = retpos2 = -1
                        retdat = sc | (buf[pos+4] << 8)
                        if pts >= 0:
                            retdat |= (pts << 31) | 0x1000000
                        retpos2 = filepos + pos
                        if (buf[pos+4]&0x60) == 0:
                            if pts >= 0:
                                retpos = filepos
                                retpts = pts
                        yield (retpts, retpos, retdat, retpos2)
                                
            pos += 1

def ProcessScAp(ts, maxlen=-1):
    scf = open(ts+'.sc', 'wb')
    apf = open(ts+'.ap', 'wb')
    filesize = os.path.getsize(ts)

    lastprogress = -1
    for (pts, pos, dat, pos2) in FrameGenerator(ts, maxlen=maxlen):
        curpos = max(pos, pos2)
        if curpos >= 0:
            progress = curpos*100/filesize
        if progress > lastprogress:
            print("{0}%".format(progress))
            lastprogress = progress
        if pts >= 0 and pos >= 0:
            WriteBufInternal(apf, pos,  pts)
        if dat >= 0 and pos2 >= 0:
            WriteBufInternal(scf, pos2, dat)

    scf.close()
    apf.close()

Wednesday, January 2, 2013

AirPlay HTTP MP3 streaming server

I recently got myself a Raspberry Pi. Even though not knowing what I should use it for. It is just such a nice peace of cheap hardware that I had to have it. The plan was that a use for it will show itself eventually. And now it has...

A few years ago I got myself a Reciva based internet radio (Argon Inet) to have in the kitchen. It has internet radio, podcasts, http streaming, mp3 playing and even line in. But it does not have AirPlay.

Lately I have been using AirPlay together with my Enigma2 based Vu+ Duo satelite receiver. The satelite receiver is connected to the hifi amplifier and AirPlay has been a great way to get spotify music from my iPhone to the living room speakers. So ofcourse now I want to do the same to the kitchen radio.

This is where the Raspberry Pi comes in. By combining Shairport and VLC to set up an HTTP MP3 stream the radio can tune in on Pi's adress and play the audio received via AirPlay.

Installing Shairport

Shairport is an open source implementation of Apples AirPlay protocol. I think I have read somewhere that it is not compatible with iOS 6. But I still have iOS 5.1 on all my Apple devices so it is no problem.

Since I'm using the Pi headless I opted to use the Arch Linux distribution. I installed it on an uSD card and set up the environment according the installation instructions on their webpage. I then used this article to install the Shairport daemon. The article mentions workarounds with systemd and requiring manual installation of perl-net-sdp. I ended up also installing initscripts to write my own rc.d scripts:
systemctl enable avahi-daemon
echo snd-bcm2835 > /etc/modules-load.d/snd-bcm2835.conf
wget https://aur.archlinux.org/packages/pe/perl-net-sdp/perl-net-sdp.tar.gz
tar -zxvf perl-net-sdp.tar.gz
cd perl-net-sdp
makepkg -s --asroot
pacman -U perl-net-sdp-0.07-1-any.pkg.tar.xz
pacman -S initscripts
I think it is nice to be able to play both to line out and to an HTTP MP3 stream. So by adding the following line to the shairport script in /etc/rc.d, a Shairport daemon starts up at boot and will play to line out by selecting AirPi as output on the iPhone:
$daemon_name -d -a AirPi
Installing VLC and other tools

Using pacman I installed VLC:
pacman -S vlc
With this i ended up with VLC version 2.0.5.

Some other  tools are also needed:
pacman -S screen
pacman -S netcat (select freebsd version) 
The tool screen is used to start Shairport and VLC from init scripts and later be able to attach to the screen and read any output. The tool netcat (nc) is used to echo command to VLC remote control interface.

Connecting it all together

Shairport has a function to stream the received AirPlay audio as raw pcm stream to a names unix pipe. VLC can open this unix pipe and play it as long as there is data. But when the iPhone disconnects from the Shairport server the data stops. So Shairport needs to tell VLC to start playing again when the iPhone connects. By selecting a different server name and port than the line out Shairport daemon the two different way of playing can coexist. To accomplish this, Shairport is started with the following parameters:
shairport.pl -a AirPi2 -o 5012 --pipe=/tmp/airpi --play_prog="echo play | nc localhost 7070"
To read the pcm stream, VLC needs to be told exactly what the format is. This is done with the following VLC parameters:
--demux=rawaud --rawaud-channels 2 --rawaud-samplerate 44100 /tmp/airpi
To be able to tell VLC to restart play when an AirPlay client connects it is started with the remote control interface i used:
-I rc --rc-host localhost:7070
To make vlc transcode the pcm stream to MP3 and the making it available on http the following parameters are used:
:sout='#gather:transcode{acodec=mpga,ab=128,channels=2,samplerate=44100}:http{mux=dummy,dst=:8080/sound.mp3}' :sout-keep
The final script which I placed in /usr/local/bin is:
#!/bin/bash
screen -S sp -X quit
screen -S vlc -X quit
screen -d -m -S sp shairport.pl -a AirPi2 --pipe=/tmp/airpi -o 5012 --play_prog="echo play | nc localhost 7070"
screen -d -m -S vlc su - user -c "/usr/bin/vlc -v -I rc --rc-host localhost:7070 --demux=rawaud --rawaud-channels 2 --rawaud-samplerate 44100 /tmp/airpi :sout='#gather:transcode{acodec=mpga,ab=128,channels=2,samplerate=44100}:http{mux=dummy,dst=:8080/sound.mp3}' :sout-keep :repeat"
By making an rc.d script shairport2 which calls the above script, the server is automaticaly started at boot.

Please comment if there are errors or something is unclear.