Raspberry PI open hotspot for your company site(s) only

Raspberry HotspotThe problem is really simple, you want/need to give open Wifi to your customers (let’s say inside a shop), but to you own company website (or websites) only. And nothing else, no other resources in the (internal or external) network.

The solution is simple and it comes in a tiny format… you will just need a Raspberry PI with a Wifi USB dongle that supports AP mode. Your company website should have an exclusive IP address

Side note: as normal, I’m not liable for any kind of mess, data loss, massive meteorite smash or other apocalyptic event in your world due to this guide.

Have the PI installed with the latest Raspbian, booted and logged in as root (sudo -s or equivalent).

Update the software sources:

apt-get update

Install the required software

apt-get install hostapd dnsmasq

Configure the wireless interface with a static IP address,
edit /etc/network/interfaces

iface wlan0 inet static
address 10.0.0.1
netmask 255.255.255.0
broadcast 255.0.0.0
# pre-up iptables-restore < /etc/iptables.rules

and restart the interface

ifdown wlan0
ifup wlan0

Here I choosed the 10.0.0.1 address to isolate the Wifi guests from the 192.168.1.x internal network. You should adapt it according to your existing set-up.

edit /etc/default/hostapd

and replace
#DAEMON_CONF=””

with
DAEMON_CONF=”/etc/hostapd/hostapd.conf”

now edit (it’s a new file) /etc/hostapd/hostapd.conf

For a full list of switches and whistles please do refer to http://w1.fi/cgit/hostap/plain/hostapd/hostapd.conf, we go with a very minimalistic (but functional) configuration

interface=wlan0
ssid=WIFI-FREE-AS-BEER
channel=0
macaddr_acl=0
auth_algs=1
wmm_enabled=0
driver=nl80211
hw_mode=g
ieee80211n=1

Here we can start the service.

service hostapd start

and I got the dreadful failed in red font… a lsusb command quickly showed the infamous RTL8188CUS chip:
Bus 001 Device 004: ID 0bda:8176 Realtek Semiconductor Corp. RTL8188CUS 802.11n WLAN Adapter

Thanks to the good people of the Internets you get a quick fix (you are downloading an external binary… so cross your fingers before installation, and nothing bad will happen to your PI… well, it worked for me).

wget http://dl.dropbox.com/u/1663660/hostapd/hostapd
chmod 755 hostapd
mv /usr/sbin/hostapd /usr/sbin/hostapd.ori
mv hostapd /usr/sbin/

and change in /etc/hostapd/hostapd.conf
driver=nl80211
to
driver=rtl871xdrv

service hostapd start

service [….] Starting advanced IEEE 802.11 management: ok
hostapdioctl[RTL_IOCTL_HOSTAPD]: Invalid argument

Even with the warning output the service managed to start and work correctly.

By now there should be an open network called WIFI-FREE-AS-BEER available to log in, but the process will stall in the Obtaining IP Address stage. So it’s time to move to the DHCP and DNS server.

Edit /etc/dnsmasq.conf, and place at the end of the file the lines

address=/#/aaa.bbb.ccc.ddd
interface=wlan0
dhcp-range=10.0.0.10,10.0.0.250,12h015/05/raspberry-pi-open-hotspot-for-your-company-sites-only/
no-resolv
log-queries
log-dhcp

adjust the aaa.bbb.ccc.ddd to the exclusive public IP address of your company website. Basically we are configuring Dnsmasq to answer all name resolution queries to your public IP address, and setting DCHP leases to the Hostspot clients from IP 10.0.0.10 to 10.0.0.250 valid for 12h periods.

From now on it should be possible to log in to the Hotspot, but no data flow, so let’s take care of this now. First activate the kernel IP forwarding

echo 1 > /proc/sys/net/ipv4/ip_forward

and then adjust iptables rules

iptables -F
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
iptables -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i wlan0 -o eth0 -p tcp -d aaa.bbb.ccc.ddd --dport 80 -j ACCEPT
iptables -A FORWARD -i wlan0 -o eth0 -p tcp -d aaa.bbb.ccc.ddd --dport 443 -j ACCEPT
iptables -A FORWARD -i wlan0 -o eth0 -p udp -d 10.0.0.1 --dport 53 -j ACCEPT
iptables -A FORWARD -i wlan0 -o eth0 -p udp -d 10.0.0.1 --dport 67:68 -j ACCEPT
iptables -A FORWARD -i wlan0 -j DROP 

remember to replace aaa.bbb.ccc.ddd with your the exclusive public IP address like in dnsmasq. From this point there should be a fully functional system. You can login to the Hotspot, and any http/https request will be landing in your company website. All other network traffic (except for the DHCP and name resolution will be blocked).

Now, to wrap up just make all this stuff survive reboots:

echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf

update-rc.d hostapd defaults
update-rc.d dnsmasq defaults

iptables-save > /etc/iptables.rules

and uncomment in /etc/network/interfaces the line
# pre-up iptables-restore < /etc/iptables.rules

There is just one thing left, avoid the captive portal detection and the respective sign in to network message. If you are using some kind of URL mapping/decoupling system (really hope you do) it’s pretty easy.

For Android, test for http://clients3.google.com/generate_204 request and send a 204 header and 0 bytes:

if (isset($script_parts) && $script_parts[0] == 'generate_204') {
    header('HTTP/1.1 204 No Content');
    header('Content-Length: 0');
    die();
}

For iOS lalaland test for the user agent ‘CaptiveNetworkSupport’ and send a 200 response:

if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/CaptiveNetworkSupport/i', $_SERVER['HTTP_USER_AGENT'])) {
    header("HTTP/1.1 200 OK");
    die();
}

That’s it folks, and I wonder what will be the next use for this tiny big computer?

UPDATE
After all been working well and good for a long time, maybe after a reboot a problem surfaced. Maybe a whim of the bits gods, the system was using the dnsmasq on internal lookups for all interfaces ignoring the interface directive.

So for example if one ssshed into the raspberry and tried to wget google.com one would get our company site…. not good.

Simple fix, manually edit /etc/resolv.conf, you can use Google public DNS (not censored) or your LAN Router IP (that normally uses the upstream DNS of your provider).

# Google IPv4 nameservers
nameserver 8.8.8.8
nameserver 8.8.4.4

and to not be automatic overwritten by dhcpclient updates set the immutable bit:

chattr +i /etc/resolv.conf

UPDATE 2
Noticed that the raspberry was missing /etc/network/interfaces (no file at all and I don’t recall to delete it). Maybe the problem was due to this and Maybe it’s time for a new SD card and fresh install.

Tune-up a Ikea Expedit shelve with a LED Strip

IKEA ExpeditI was tired of looking into my plain, dull, boring, tedious, monstrous Ikea Expedit shelve in the living room. And, it seems that the new trend at the independent furniture shops (ok, the Pakistani at Lisbon downtown) is to apply LED strips to all kind of furnishings: beds, tables, chairs, stools, sinks, whatever… and boom!!! Eureka, the aha moment, why not leave the bed and table alone, but cheer up my shelve! Also it made sense in a functional way, since at mid-light room I can barely see the stuff that i get from the inner guts of it.

Next day, was shopping day for LED strips in Ebay the global marketplace. Now bear with me, the shopping process is crucial for a good development of your project. There are several decisions you must take. First, the length of strip that you need, for the full IKEA Expedit we are talking about 1.47cm * 5 = 7.35m (and give yourself some cut margin and error margin). Next the LED type, the protection rating and color. For the LED types there are two mainstream options 3528 and 5050. They get their numbers because the dimensions of the chip, 3.5mm * 2.8mm for the 3528 and 5.0mm x 5.0mm for the 5050. So the 5050 is bigger than the 3528, are substantially brighter, with an average of 18 lumen Vs 6 lumen, but also more power angry 0.24W vs .08W. So, for decorative lighting your best bet should be the 3528 and for functional lighting or bright environments you should go for the 5050. Also, take care of the number of LEDs per meter, it should be clear now that a meter of strip with 60 3528s should output the same amount of light that of a meter with 20 5050s (but the first will look much more smooth)… now for the protection rating, you can check the full spec here, but i will break it down for you. The first number is for solid particle resistance and the second for the liquid protection. The common IP20, stands for 2 (protection against fingers or similar?!) and 0 (not protected against water), other common available IP in LEDS strips is IP65 (6 – No ingress of dust; complete protection against contact and 5 – Water projected by a nozzle (6.3 mm) against enclosure from any direction shall have no harmful effects). Usually higher the IP, higher the cost of the strip. For me, with 2 young, active and curious cats in the house, the choice was obvious (IP65)… finally, the color is mainly a question of personal taste, but if you want to change the color on the fly, the way to go is a RGB 5050 LEDs strip as each chip has the 3 main colors that combine into the color that you want (or some psychedelic effects).

This guide is for the 4×4 IKEA Expedit shelve, but is easy adaptable to other piece of furniture.

You will start with:
– 2 rolls of 3028 IP65 warm white
– a transformer (more on this later)
– wire for the connections, don’t worry too much about the thickness as the flow of power will be limited, but worry to get a flexible wire, the flexible the better
– electric block connector (for parallel connections, if series you don’t need this but a little more solder..)
– insulation tape
– a virgin IKEA expedit shelve

Tools:
– a multimeter to check continuity
– a soldering iron and some solder
– a wire stripping tool, or a knife or scissor or use or teeth (don’t use the teeth)
– a screwdriver for the electric block connectors
– a X-Acto knife
– a ruler to check out the dimensions is not a bad idea

Other stuff needed:
– a bottle of beer, so when you get thirsty don’t leave the workstation

You start to cut down five 1.47m segments Expedit Suppliesout of the LEDs rolls. The LEDs strips have a scissor mark that repeats itself along the way after some number of LEDs (mine was between each 3 LED). So look for the nearest mark and cut it with precision on the middle. It’s important that you cut cleanly at the middle as will be easer in the soldering step.

Now, you must decide what kind of connection you should do. You can go parallel or series. For parallel you have the advantage of less soldering to do, if one section stops to work the others will continue to, but you will use more wire, block connectors, and it will be a pain to hide the electric circuit if the fitment is in the middle of the room. The series will use less wire, no block connector, it will be easier to hide completely in the back side of the fitment, but much more soldering to do, and if one of the strips go bad (or the connection between) the upstream will also not work.

You should keep in mind a very important detail about LED strips. When you cut a piece in the pre-designated cutting zones, there is a transistor right before the connectors. This transistor will automatically close the circuit for you if there is no upstream continuity. So if you connect in series and connect a 12v transformer (in the right polarity) to the first LED segment it should light up, you don’t need to close the circuit in the other side. If you connect another segment to the first one (again check the polarity, the LEDs strip should have plus and minus markings) it behaves the same, lights up without the need of closing the circuit at the far end.

Expedit Wiring Diagram

Now, for the “funny” stuff, to solder the circuit. With the X-Acto knife remove the protection material (epoxy or silicone) above the solder points. Then with the hot iron melt way the remaining of the protection material. Drop a bit of solder into the solder points, then drop another bit into the end of the stripped wire. Then press and hold the wire against the solder point, and heat it up until both bits of solder melt and fuse. A quick demonstration video:

When it’s all done and fitted, means that is time to calculate the power consumption, and transformer requirements. Each LED uses 0.08W and one meter has 60 LEDs, for the sake of simplicity we will round the 1.47m segments to 1.5m. So each segment has 90 LEDs, 0,08W * 90 = 7.2W per segment. At 12v it gives (7.2 / 12) 0.6amps per segment. For 5 strips in parallel, the total output is 3amps.

To be safe the transformer should output 12v and 3amps (if it’s rated in Watts should be 36W). If you use a less powerfull transformer (let’s say a 1 amp) it can heat up and meltdown. If you feed the circuit with less voltage it will the lights will dim, and as you step down the voltage, eventually it will not light at all. The cost per hour of usage at full throttle is pretty cheap, as 36W/h at 0.20 cents the Kw/h sums at a cost of 0.0072 cents hour….

If all goes well the final result should be this:

expedit_leds

Raspberry GPIO

Finally the fun stuff. Messing around with the General Purpose Input Output of the PI. So, first things first. First you must identify what revision PI you are working. Visually, looking at it, if there are 2 mounting holes is a revision 2, if there are no mounting holes it’s a revision 1. A much more scientific approach is to install WiringPi.

sudo bash
apt-get install git-core
apt-get update
apt-get upgrade
git clone git://git.drogon.net/wiringPi
cd wiringPi
git pull origin
cd wiringPi
./build

Then run

gpio -v

Raspberry Pinout DiagramAnd there you got the answer in the last line of the output. “This Raspberry Pi is a revision 2 board.”. So, first thing is to pull from the Internets a wiring diagram.

Now, for the actual cabling from the PI, in my opinion the best route is to get a set of male to female jumper cables. You can also find for sale some ribbon cables specific for the PI. In my opinion, unless you want to connect some device or accessory already wired to match the PI GPIO, the best option are the jumper cables, it’s very easy to visually  match the PI pin to a breadboard location, you only use the number of cables needed and they are easily routed trough the venting holes of the average PI case and easy to solder on a protoboard.

I ordered my jumper cables from Ebay, but has i waited for them, the inner MacGyver took over… with a old floppy drive flat cable is not so difficult to improvise. Just cut a segment of the cable with a sharp knife, so in one end you have the plastic connector and in the other you have the wires with a clean cut and no short circuits between them. One problem, is the floppy cable 34 pins versus 26 in the PI. What i did is simple to cut out of the way the first 8 cables of the ribbon (and just forget encasing the PI, the connector protrudes almost the same length as the SD card). Then with cable connected in the right way (not bad idea to test first at the end of the cable with a multimeter) the first cable of the ribbon corresponds to pin 1 of the PI and so on. Some pictures of this setup:

IMG_20131108_023031IMG_20131108_035153

Just another note, i have been messing with the GPIO, juggling and testing different setups without powering off the PI between rewiring, without no problem at all, but please use good judgment and common sense.

1 – Lighting up a LED with the PI (simple output example)

Connect physical pin 11 of the PI to the positive bus of the breadboard and physical pin 6 (ground) to the negative bus of the breadboard, and connect a led and >= 68Ω resistor (why?) closing the circuit between positive and negative bus strips (remember that a led is one way device, so long leg on the positive side and small leg to the negative). The diagram to make even easier:

Raspberry First Led

And now in the PI command line, the command to put the pin in output mode, switch off and on the LED:

gpio mode 0 out
gpio write 0 0
gpio write 0 1

Wait! But isn’t the cable connected to pin 11? What is this stuff with pin 0?
Well.. the logic in wiringPI is to abstract pin numbers in software so it remains “immune” to any hardware changes. As the pin diagram above (can be checked running gpio readall also), physical pin #11 (third column of gpio readall output) corresponds to wiringPI pin #0 (first column of gpio readall)  and to the GPIO #17 (the internal pin number used in the chip). So, regarding the logic of hardware abstraction with the gpio command, we can call a pin by the wiringPI numbering or the GPIO numbering (with the -g switch, ex: gpio -g write 17 0) but not by physical pin-out number…

2 – Reading state (simple input example)

To read state from a pin, you must devise a circuit that pulls up or pulls down the pin that you are going to read, then change the state of the circuit with a pushbutton (or any other suitable mean). This is a fucking bad explanation… so let’s look to the schematic:

raspberrypi_input_schemWe have physical pin #1, the 3.3v output (that’s the one that should be used for input reading) going to a 10kΩ resistor then split by a) a pushbutton that connects to physical pin #6 (ground) and b) a 1kΩ resistor that connects to physical pin #11. When the pushbutton is not pressed, no current passes trough there to ground, so the current goes to the 1kΩ resistor and pulls up pin #11. In this state, all the readings from pin #11 will be High. When the pushbutton is pressed the current will flow freely to ground (pin #6) and the reading from pin #11 will be Low. This is a bit counter intuitive, reading High (or 1) when the pushbutton is NOT pressed and Low (or 0) when the pushbutton IS pressed, but is very easy to take care in the software stage.

The diagram on the RaspberryPI and breadboard:

raspberry_input

And a very simple Python script to output when the push button is pressed:

import RPi.GPIO as GPIO
import time
buttonPin = 11
GPIO.setmode(GPIO.BOARD)
GPIO.setup(buttonPin,GPIO.IN)

while True:
    input = GPIO.input(buttonPin)
    if input == 0:
        print("Button pressed")
        exit();
time.sleep(0.1)

Save and run as “python script_name”. When the pushbutton is pressed it should output “Button pressed”. Note that with the RPi.GPIO module in the GPIO.BOARD mode, we are addressing the physical number of the pin in the board.

There! Raspberry PI, welcome to the real world. Just baby steps… laying the foundations for further developments.

Some references:
http://elinux.org/RPi_Low-level_peripherals
http://wiringpi.com/

Raspberry PI configuring Wi-Fi command-line

This was a saga… i bought myself a second hand USB dongle, a Dynamode WL-700N-XS ultra compact (nano) 802.11b/g/n compatible Wi-Fi adapter, based in tbe Realtek 8188CU chipset. A fully updated USB Wi-Fi adapters list is mantained here.

Dynamode Wireless USB Nano 150mbps WL-700N-XSThis chipset is pretty plug an play on the PI with the latest Raspbian Wheezy, reported to work directly with a decent power source, no driver compilation or obscure installation, just supported out of the box by the Linux kernel. Just perfect.

I confidently connect the dongle in the PI, the USB device was properly recognized:

# lsusb
Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
Bus 001 Device 004: ID 0bda:8176 Realtek Semiconductor Corp. RTL8188CUS 802.11n WLAN Adapter

also on dmesg

# dmesg
[    3.171681] usb 1-1.2: new high-speed USB device number 4 using dwc_otg
[    3.293726] usb 1-1.2: New USB device found, idVendor=0bda, idProduct=8176
[    3.302266] usb 1-1.2: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[    3.311142] usb 1-1.2: Product: 802.11n WLAN Adapter
[    3.317650] usb 1-1.2: Manufacturer: Realtek
[    3.323401] usb 1-1.2: SerialNumber: 00e04c000001
...
[   15.830604] usbcore: registered new interface driver rtl8192cu

and ifconfig, reports a wlan0, so by now everything looked great. I followed an tutorial about configuring wireless on PI, and no connection, then another, and no connection… shit!!! Then of course i dump the PI tutorials (you can guess the technical level as low when you find “reboot to load the new values”….). Moved to good old Linux documentation, as Raspbian is just another Debian clone.

So before messing with /etc/network/interfaces and /etc/wpa_supplicant/wpa_supplicant.conf the best debug tool is the command ‘iwlist wlan0 scan‘, that should print the available wireless networks. And with this dongle i was getting none (even at 10 centimeters of the wireless router). Long story short, after testing the dongle in other computers (and even other OS – yes, i washed my hands already) i found out the dongle is simply damaged and working rather randomly.

After replacing the dongle (thanks Delaman) by another of the exact same model, things started to work properly, iwlist wlan0 scan started to work right and i could see my wireless network, and the neighbors networks also.

From this point i could confidently resume the wireless network setup. First thing the /etc/network/interfaces:

auto lo
iface lo inet loopback

iface eth0 inet dhcp

auto wlan0
iface wlan0 inet dhcp
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

iface default inet dhcp

the important lines here are those 3 referring to the wlan interface and should be added to the configuration file.

Then the /etc/wpa_supplicant/wpa_supplicant.conf that holds the network security configuration. First i tried some of the suggested configurations in the Internets, but i was just getting the error: “failed to parse ssid ‘MY_NETWORK_NAME’” and the likes. So, go with the wpa_passphrase command to generate a network block configuration:

wpa_passphrase YOUR_NETWORK_NAME password

Now copy and replace the generated network block to /etc/wpa_supplicant/wpa_supplicant.conf, it should look something like this (a quite simple and clean configuration):

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1

network={
        ssid="NETWORK_NAME"
        #psk="password"
        psk=generated_by_wpa_passphrase
}

You can heep the first two lines, as they provide an interface to the wpa_supplicant via the wpa_cli command.

Don’t have to reboot, i tested this while ethernet connected. Restart the wlan0 interface and reload the configuration into the supplicant thing:

ifdown wlan0
ifup wlan0
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf

And there, ifconfig shows an active Wi-Fi connection on wlan0. From other computer, the wlan0 IP responds on pings and is possible to SSH. Now disable the ethernet connection:

ifdown eth0

disconnect the ethernet cable, and there your PI is free to move around without the network cable.

Raspberry PI follow-up

raspberry_pi_logoSo, i did get a (actually 2) Raspberry PI and did get it up and running, it was time to do anything useful with it…

Time to setup a Samba server for network recording of security camera feeds. I went along with SWAT, a web based graphical interface to samba configuration. Like all Debian based software, the installation process is pretty straight forward,

apt-get update
apt-get install swat

And auto-magically it installs everything that you need, it evens adds the needed configuration line to /etc/inetd.conf 🙂

[global]
        netbios name = INTRANET
        server string = %h server
        map to guest = Bad Password
        syslog = 0
        log file = /var/log/samba/log.%m
        max log size = 1000
        dns proxy = No
        guest account = nobody
        usershare allow guests = Yes
        panic action = /usr/share/samba/panic-action %d
        idmap config * : backend = tdb

[public1]
        comment = Samba Public 1
        browsable = yes
        public = yes
        writable = yes
        read only = No
        guest ok = Yes
        path = /media/usb0/samba/cam1/

[public2]
        comment = Samba Public 2
        browsable = yes
        public = yes
        writable = yes
        read only = No
        guest ok = Yes

Then just point your browser to the PI ip at port 901. Curious enough, i found the SWAT tool too complex for the simple configuration that i wanted: i trust all users in the network, so my need was just two shares that anyone could read/write. So, i ditch SWAT and went on to good ol’style configuration file editing. The final /etc/samba/smb.conf that is working for me:

Fired up samba
# service samba restart

And the cams had no problem finding the samba shares and recording into them.

Next step was to get an easy way to navigate and download recordings. Of course you can also use the samba shares to navigate and read, but specially to outside access it would implied to configure a VPN access to the network (you don’t want your security camera feeds exposed in the Internets with read/write permissions to the world, right?). I went for HTTP with some kind of a file explorer software that allows users/permissions, file/directory browsing, and file download. For the server part i opted for lighttpd, a small footprint server, and for the voodoo PHP (all pretty familiar technology to me). Again the installation is for dummies:

apt-get install lighttpd
apt-get install php5-cgi

Then just a tiny adjustment at /etc/lighttpd/lighttpd.conf:

index-file.names            = ( "index.php", "index.html", "index.lighttpd.html" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )

fastcgi.server = ( ".php" => ((
                     "bin-path" => "/usr/bin/php-cgi",
                     "socket" => "/tmp/php.socket"
                 )))

and restart it. For the software i went for the super nice, cool and powerful AjaXplorer. Just download it and untar to /var/www directory. Then point your browser to PI and log in with admin/admin (changed the password) and then it was just a matter of setting up a user account and a repository pointing to /media/usb0/samba/ (the parent directory of both samba shares).

Now, only one thing left, clean up and report. What to use? Of course PHP again. But this one in command line, so i installed the CLI version.

apt-get install php5-cli

And i did i script that cleans up old recordings and send me a daily report email using basic functions and the great PHPMailer class.

require('phpmailer/class.phpmailer.php');

function deleteDir($dir, $days) {
    $now      = time();
    $diff     = 60*60*24*$days;
    $treshold = $now - $diff;

    $d = dir($dir);
    while (false !== ($entry = $d->read())) {
        if ($entry != '.' && $entry != '..') {
            $year  = substr($entry, 0, 4);
            $month = substr($entry, 4, 2);
            $day   = substr($entry, 6, 2);

            if (mktime(0, 0, 0, $month, $day, $year) < $treshold)
                exec("/bin/rm -rf ".$dir.$entry);
        }
    }

    $d->close();
}

function getDirUsage($dir) {
    exec("/usr/bin/du -sh ".$dir, $output, $return);

    if ($return > 0)
        return 0;

    $output = $output[0];
    $output = explode("\t", $output);

    return $output[0];
}

/*
 * DELETE OLD FILES, +30d
 */

deleteDir('/media/usb0/samba/cam1/video/', 30);
deleteDir('/media/usb0/samba/cam2/video/', 30);

/*
 * GET USED/FREE SPACE
 */

exec ('df -h', $output);
foreach ($output as $line) {
    if (strpos($line, '/media/usb0')) {
        $disk_line = $line;
        break;
    }
}

$disk_line = explode(" ", $disk_line);
$disk_line = array_values(array_filter($disk_line));

$disk_used_space = $disk_line[2];
$disk_used_perc  = $disk_line[4];
$disk_free_space = $disk_line[3];

/*
 *  GET YESTERDAY RECORDINGS USAGE
 */

$yesterday  = date("Ymd", time() - 60 * 60 * 24);
$cam1_space = getDirUsage('/media/usb0/samba/cam1/video/'.$yesterday.'/');
$cam2_space = getDirUsage('/media/usb0/samba/cam2/video/'.$yesterday.'/');

/*
 *  GET YESTERDAY RECORDINGS USAGE
 */

$yesterday  = date("Ymd", time() - 60 * 60 * 24);
$cam1_space = getDirUsage('/media/usb0/samba/cam1/video/'.$yesterday.'/');
$cam2_space = getDirUsage('/media/usb0/samba/cam2/video/'.$yesterday.'/');

/*
 * SEND REPORT EMAIL
 */

$mail = new PHPMailer();
$mail->IsSMTP();                            // telling the class to use SMTP
$mail->SMTPAuth = true;                     // enable SMTP authentication
$mail->Port     = 25;                       // set the SMTP port
$mail->Host     = "mail.domain.com";        // SMTP server
$mail->Username = "username";               // SMTP account username
$mail->Password = "password";               // SMTP account password

$mail->From     = "email@domain.com";
$mail->FromName = "Descriptive email";
$mail->AddAddress("my_email@domain.com");

$mail->CharSet = "UTF-8";
$mail->Subject  = "Cam Report";
$mail->Body     = "YESTERDAY RECORDINGS\n".
                  "Cam 1: $cam1_space\n".
                  "Cam 2: $cam2_space\n".
                  "\n\n".
                  "HDD SPACE STATUS\n".
                  "Free: $disk_free_space\n".
                  "Used: $disk_used_space ($disk_used_perc)\n";
$mail->WordWrap = 50;

if(!$mail->Send())
        error_log($mail->ErrorInfo);

Then just run it daily with cron
30 3 * * * /usr/bin/php /path/to/script/cams.php > /dev/null

For now that’s all, but i guess there will be more updates on the Raspberry PI as i have still some ideas floating in my head.