date: 3:02:23, July 21, 2024
my ip: (
your ip: ()
CCBot/2.0 (
Welcome, my name is Emmanuel Roberto Richiardone, this is my place on the web.

You can contact me at e(AT)

My PGP/GPG public key
More about me here

  Docs, Tips & tricks
  Freebsd tips
  GNU/Linux tips

Outside links:
  Photos on Smugmug

Other links:


RSS feed enabled!

  July 2024  
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        

search with google
all the web

. o .
. . o

go to page: 1 2 ->    single page
Home control with local touchscreen, HTTP remote control, analog and digital IOWed, 4 Aug 21
My need was for an unified device to locally and remotely control home and garden. You'll find a lot of stuff based on Raspberry, so I went that way. Device is based on Raspberry Pi 4 with touch screen display and some custom hardware.

About the software platform I choosed "Home Assistant Core", installed on Raspbian Debian on ARM that I know quite well.
The choice has these pros:
  • Good home automation opensource solution
  • Modern solution with integration with anything related to smart home (Philips hue, IKEA TRÅDFRI, Sonos, MQTT, etc)
  • Manually customisable in Python
  • Nice HTTP interface
  • Customizable to present sensor data, action button and historic view
  • Possibility to build scene and automation mechanisms to activate action upon any conditions
  • Possibility to define zones (area)


The Rasperry Pi
The interface board
  • Stripboard sized 31x26 holes double sided, hole distance 2.54mm (mine is 90x70mm)
  • Female header connector 2.54mm, we need minimum two lines of 20 pitch
  • Some female header connector 2.54mm for the wire connection to the male pin headers
  • Some male pin header connector 2.54mm, one of 40 pin is enough.
  • 4 pcs 3-pin screw connection terminal block
  • 3 pcs 2-pin screw connection terminal block
  • 2 pcs fuse holders
  • Fuse 0.5 A
  • Fuse 1 A
  • MCP3008 microchip analog to digital 2x8 DIP
  • 4N35 optocoupler 2x3 DIP
  • 2pcs 1N5711 Schottky diode
  • 5pcs Resistor 1k Ohm
  • Resistor 2k Ohm
  • Resistor 4.7k Ohm
  • 3pcs Resistor 10k Ohm
  • 2pcs resistor 470k Ohm
  • Variable resistor aka precision potentiometer, from 0 to 10k Ohm
  • Capacitor 10uF
  • Capacitor 470pF
  • Some isolated wires AWG24 of different colors
  • 2pcs distance bolt M2.5 with screw, length 12mm + 6mm

Sensors and actuators
For the boxing
Tools needed
  • Soldering iron with sharp tip
  • Tin soldering 0.5
  • Electrician's scissors
  • Cutter
  • Screwdriver Philips and flat
  • Small flat screwdriver
  • Black marker pen
  • Internet connection
  • Some coffee and beer


The Raspberry Pi 4 is fixed on the back of touchscreen display 7". The display is connected to the Pi using his DSI display flat cable.
There is a custom board with some components, IC, fuses and screw connectors, fixed over the Raspberry Pi 4 and directly connected on the GPIO header.
The connectors represent the external interface, where are attached input devices (sensors and contact) and output control using a relay board. The external interface has the following specification:

- n.4 isolated digital output using a ready board including optocoupler and relay with both N.O. and N.C (max 10A 250VAC). My usage will be to control a boiler, a well pump, an irrigation valve, and some lights.

- n.1 1-wire protocol interface on 3.3V, can manage a number of sensors connected on the bus identified by serial ID. My usage will be to connect two temperature sensor.
- n.1 I2C protocol interface on 3.3V, can manage a number of sensor connected on the bus identified by programmed ID. My usage will be to connect a humidity/pressure/temperature sensor.
- n.3 Analog input, reading values in voltage, powered at 3,3V. My usage will be to connect two AC power sensor and a water pressure meter.
- n.1 Digital input, optoisolated with variable resistor, input is high when input is closed to 5V. My usage will be to connect a boiler status indicator.
- n.2 Digital input, protected with schottky diode, with input that have to go from GND to 5V and back. My usage will be to connect a hall water flow meter generating a square wave.

- The available voltages are 3,3V and 5V, depending on the input type.
- Total maximum current allowed is 0.5A for 3,3V and 1A for 5V, they are fused.

- Using a Pi version 4, on the DSI cable is passing video combined with touch commands using I2C protocol with address 0x38.
- The I2C used is dedicated and is leaving free the I2C on the GPIO header.


I made an approximative calculation about needed current to size correctly the power supply.
For the 5V I divide the power supply line for the Raspberry Pi 4 and for the external interface, so that I am using the Raspberry internal circuitry and fuse to protect itself and the display, and a new fuse for the sensors and devices.
For the 3.3V I use the internal Raspberry Pi 4 regulator but I add a new fuse toward external devices.

 Raspberry Pi 4 		700mA @ 5V
 7" touchscreen display  	550mA @ 5V
 4 channel relay board		each relay when activated 60mA @ 5V,
                                near 0 otherwise. Total from 0 to 240mA
 1-wire pullup resistor		1mA @ 3.3V
 1-wire temperature sensor	2* 1mA @ 3.3V
 I2C humidity/bar/temp sensor	1mA @ 3.3V
 Analog to digital circuit	1mA @ 3.3V
 AC power sensor (n.2)		<1mA @ 3.3V
 Water pressure meter		<1mA @ 5V
 Digital input circuits		<10mA @ 5V
 Hall water flow meter 		<10mA @ 5V

Length of input sensor lines
The length of input sensor lines depends mainly on the bus caracteristic and kind of wire.
Expecting to use UTP cable with AWG24 (0.5 mm2) wires, the voltage loss of an input sensor that is consuming 10mA placed with lines 50 meters long is about 0.1V.
For the 1-wire temperature sensor the bus length on this kind of wire still works 50 m away. 1-wire should use a star topology when using long wire wire more than one sensor.
If needed, it could be necessary to add a low value resistors (under 100Ohm) on the data wire near the sensors to prevent ringing of signal.

The Raspberry Pi 4 internal fuse should allow 1250 mA + at least 45 mA from the GPIO header 3.3V
The external fuse for 5V should allow 500 mA. So I choosed for a 1 A.
The external fuse for 3.3V should allow 45 mA. So I choosed for a 0.5 A.

Summing all, the power supply should deliver at least 9W.


Devices commands (output GPIO + relay)
- 4 channel relay board with optocoupler and indicator led
(I am in fact using an 8 channel board because I have that one available, but connect only 4 relays)
Used to control:
  • Heating boiler activation, manual or automatic (thermostat)
  • Well pump command
  • Irrigation valve command
  • Lights command

If it's the case REMOVE JD-VCC to VCC jumper from the board. This jumper connects the input VCC with the relay VCC supply: we are using VCC of 3.3V for GPIO connections and 5V for relay supply. The GND is connected only with the 5V supply, NOT toward the Raspberry GPIO.
To activate the relay coil, the gpio should go to 0 (GND).
See this relay board as sample schematic reference
sample relay schematic


= Internal temperature board =

Nothing to be done externally of the board.

= 1-wire data protocol =

Works on 3 pin: VCC, GND, DQ
Pull-up resistor 4.7k Ohm

4.7k between positive and data wire protocol
positive to the +3.3


Used for:
- Outdoor temperature sensor DS18B20
Temperature range -55°C e +125°C
Power supply +3 - +5.5Vdc , negative ground

Cable length should be maximum 10 meters; 1-wire protocol is capacitive, so don't use shielded cables to ground. More sensors can be added in parallel and shares the same pull-up resistor.
1-wire on Raspberry Pi 4 uses as default the pin 7 - GPIO 4
1-wire components

= I2C bus =

Works on 4 pin: VCC, GND, SCL, SDA Directly connected to the Raspberry GPIO header. Using only sensors locally placed with short wires. Used for:
- Sensor BME280
Indoor temperature, humidity, barometer sensor (I2C)

I2C uses on Raspberry Pi 4 the pin 3 - GPIO 2 for SDA and pin 5 - GPIO 3 for SCL. Works on 3.3V.
bme280 components

= Analog inputs =

The Raspberry Pi 4 has only digital GPIO, as an input you can measure if the pin is connected to a 3.3V or 0V, no intermediate values are readeable. The analog inputs are based on a MCP3004/MCP3008 chip, measure voltage levels from 0 to 3.3V. The chip is connected to the Raspberry using SPI bus.
My circuit can manage 3 inputs: n.2 AC current consumption and n.1 water pressure.

MCP3008 Analog to Digital converter

Used for:
- AC current consumption with sensor SCT-013-30, up to 30A that corresponds to 1V.
The sensor includes burden resistor, and comes with two wire connection on 3.5 audio jack (L and K). The AC wire to be measured is one, so the hot wire OR the neutral wire, never both.

As the voltage output is a sine wave corresponding of the AC current, and is centered on 0V, I need to adjust it to be readeable from the MCP3008.
Using the SCT013-030 30A model, the maximum 30A is 6.5 kW on 220 VAC line, and outputs 1V RMS on the sensor. 1V RMS is 1.414 Vpp, so on a oscilloscope between the L and K wires you will see a wave going from -1.42V to +1.42V if you measures a load consuming 6.5 kW at 220VAC. The scale is linear, so i.e. you see a wave between -0.21V to +0.21V measuring a load consuming 1kW at 220VAC.

The MCP3008 measures positive voltage values, and the range is from 0V to 3.3V as it is powered at 3.3V. I need a simple voltage divider to get a reference voltage. Dividing the half of 3.3V is alright as it is +1.65V, so that the measured sine wave will go from +0.23V to +3.07V (+1.65 -1.42 and +1.65 +1.42), a range that can be easily read from the MCP3008.

Note: For the AC load I really need only to measure only peak value. I evalutated using a rectifier circuit with diode and capacitor, but the input signal can have very low amplitude so it's not a good option. I could use a precision rectifier introducing an operational amplifier. But finally I decided to not modify the wave more than changing the reference voltage and to work on software side, to simplify and more flexibility on the board circuit.


- Water pressure sensor for home distribution pressure that should be 3 bar (0.3 MPa (Mega Pascal))

The sensor I choosed has following specs:
Max. 0.5 MPa = 5 bar
Powered at 5V
Thread G1/4
Reading value: 0-0.5 MPa
Maximum value: 1.5 MPa
Damaging value: 3.0 MPa
Temperature: 0-85 C
Output Voltage: 0.5V = 0 MPa, 4.5V = 0.5 MPa

The sensor has 3 pin connection: VCC, GND, analog voltage.

The sensor works with +5V, so I need some voltage divider to match the 3.3V required for the MCP3008. The analog voltage ranges approximely from 0.5V to 4.5V. It is needed a simple voltage divider 2:1 to match the maximum 3.3V voltage reading of MCP3008 and GPIO, so I put a 2K / 1K Ohm on the analog voltage pin.
Note that some 5V power supply are not exactly this value, but some more. For example 5.5V; in that case, the voltage divider 2:1 with an input of 5.5V gives 3.7V. But it's not an issue with the maximum input voltage of the MCP3008, as the sensor output at max pressure 0.5 MPa (5 bar) is 4.5V, that is 3V after the voltage divider.


SPI uses on Raspberry Pi 4 the pin 19 - GPIO 10, pin 21 - GPIO 9, pin 23 - GPIO 11 and pin 24 GPIO 8.
analog input circuit components

= Digital inputs =

The Raspberry Pi GPIO are 3.3V based. The inputs I am using are 5V based, so for powering the sensor it's not an issue, but some work has to be done to adapt to the Pi GPIO input.
Remembering that all the Raspberry inputs are digital, so they can read 0V or 3.3V, there are some alternatives:
  1. when using sensor that generates a square wave between 5V and 0V, it can be used a simple circuit with a protection fast diode;
  2. when reading a signal that is closed to 5V or is open (floating), it can be used a optocoupler circuit;
  3. when reading a signal that is closed to 3.3V or is open, it can be used GPIO directly.

I implemented the solution 1. and 2., to be more generic and have some degree of protection.

Fast diode input circuit

- Schottky diode 1N5711
- Resistors 10k, 1k

schottky high
schottky low

Schottky diode
I am needing a schottky diode for the fast switching speed. I am working with low voltages so there are no inconveniences.
Hereafter you can see the difference between using a diode 1N4001 and a Schottky 1N5711 as seen on an oscilloscope.

Step using general purpose diode:
general purpose diode general purpose diode 2

Step using 1N5711 diode:
schottky diode schottky diode 2

Used for:
- Hall effect water flow meter sensor (pulse)

The sensor works with 3 pin: VCC, GND, pulse
The sensor is pulled to ground to signal a pulse and float high to the external voltage.

diode input circuit components

Optocoupler input circuit

The optocoupler circuit has a variable resistor on the base to filter noise inputs and trigger the optocoupler transistor for some degree of input signals. It has to be regulated after input line installation.

- 4N35 chip
- Resistors
- Capacitor
optocoupler high
optocoupler low

In the circuit diagram the variable resistor is the 1k between the optocoupler and GND with the capacitor in parallel. The circuit shown set the GPIO input to 3.3V when the switch is opened.

On the board construction, for extra flexibility I put the GND side of the eccitating LED (pin 2) on a input connector, and added a jumper on the board.
This way I can close the line to 5V or close it to GND, according to the jumper and wire setup.
Moreover, I have the possibility to connect the line before and after the LED in the 4N35 chip toward an external powered line as input, removing the jumper.

Used for:
- Generic input, actually a simple switch closed on signalling boiler event

optocoupler components


Here is a schematic of the board:
board schematic
And finally the board looks like this:

board 2 board 3
Final board:
board 4 board 5


Follow these steps:
  1. Make two hole in the strip board where the Raspberry Pi has the two bottom holes.
  2. Labeling with marker
  3. Assembling over LCD touscreen
  4. Putting over raspberry pi some heat dissipators
  5. Put screw on the rasp header
    n.2 distance bolt M2.5 with screw, length 12mm + 6mm
  6. Connect flat cable of LCD screen
  7. Put last 2 screw in the holes of the strip board
  8. Connect 5V and GND wire for LCD screen

Putting it over the Raspberry Pi
attach 1 attach 2
Connecting display
attach 3 attach 4
attach 5 attach 6

powering 1 powering 2
Cabling 1 Cabling 2

ABS box with size 200 x 120 x 75 mm , you'll find it in beige or black variant.
Modify the box to make a display support and making some vent holes and cable passage.

Boxing 1 Boxing 2
Boxing 3 Boxing 5
Boxing 7 Boxing 8


pin 3 - GPIO 02 - i2c bus SDA
pin 5 - GPIO 03 - i2c bus SCL

pin 7 - GPIO 04 - 1-wire 	externally pullup'd

pin 11 - GPIO 17 - digital input 3 closed to 5V or closed to GND
                                   (according to the jumper, GPIO goes low;
                                   if opened, GPIO goes high)
pin 13 - GPIO 27 - digital input 1 	GND or 5V
pin 15 - GPIO 22 - digital input 2 	GND or 5V

pin 19 - GPIO 10 - SPI MOSI 	analog input 1 (from 0 to 4.5V), analog input 2 and 3 (from -1,5V to +1,5V)
pin 21 - GPIO 09 - SPI MISO 
pin 23 - GPIO 11 - SPI CLK
pin 24 - GPIO 08 - SPI CE 

pin 35 - GPIO 19 - digital output 4 (relay NO, NC)
pin 37 - GPIO 26 - digital output 3 (relay NO, NC)
pin 38 - GPIO 20 - digital output 2 (relay NO, NC)
pin 40 - GPIO 21 - digital output 1 (relay NO, NC)


Starts from raspbian image.
I started with version 10.7 Buster, but later I add testing repository (Bullseye 11) to fix Python 3.8 requirement (I installed 3.9.1). See software installation part about it.

The initial setup is not covered in this guide. We need a very small installation of armbian with a read-only root partition and writable home. We start without any graphical interface, I will install a simple window manager hereafter.
Some notes about the base setup are reported hereafter.

Partitioning with read only root
/ is ro		 size ~6 GB
/boot is ro 	 size 256 MB
/home is rw	 size the rest of the microSD (in my case ~24 GB)
Note: the partitions should be aligned for the microSD to maximise leveling on the read/write partition, considering first 4MiB and so on.

Note: all following setup commands are done from root (i.e. "su -" or "sudo su" )
As we are in readonly filesystem, before anything mount as readwrite:
root@pi:~# mount -oremount,rw /
root@pi:~# mount -oremount,rw /boot
tmpfs /tmp
cd /var
ln -s /tmp log
ln -s /tmp tmp

root@pi:~# vi /etc/fstab

proc            /proc   proc    defaults          0       0
/dev/mmcblk0p1  /boot   vfat    defaults,ro  0       2
/dev/mmcblk0p2  /       ext4    defaults,ro  0       1
/dev/mmcblk0p3  /home    ext4   defaults,noatime        0       0
tmpfs           /tmp    tmpfs   nosuid,nodev,mode=1777,size=400M         0       0
fix some temporary dir
cd /var
rm -R log
rm -R tmp
ln -s /tmp log
ln -s /tmp tmp
Final setup is something like this
root@pi:~# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/root       5.3G  2.3G  2.8G  45% /
devtmpfs        805M     0  805M   0% /dev
tmpfs           934M     0  934M   0% /dev/shm
tmpfs           934M  8.5M  926M   1% /run
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           934M     0  934M   0% /sys/fs/cgroup
tmpfs           200M   52K  400M   1% /tmp
/dev/mmcblk0p3   24G   45M   23G   1% /home
/dev/mmcblk0p1  253M   54M  199M  22% /boot
tmpfs           187M     0  187M   0% /run/user/1000

I have a user called "user", plus the root account.

= Debian testing Bullseye =

On Buster I have some issue with matchbox, midori and python versions.
I opted to add Bullseye version 11 repository in apt, adding following lines:
root@pi:~# vi /etc/apt/sources.list

deb buster main contrib non-free rpi
deb testing main contrib non-free rpi
Upgrade with commands
root@pi:~# apt-get update
root@pi:~# apt-get upgrade
root@pi:~# apt-get dist-upgrade
root@pi:~# apt-get upgrade
root@pi:~# apt-get clean
Take some time. Choose upgrade settings according to your preferences.

I had to solve some kept-back packages and purge libc6-dev for conflicts:
root@pi:~# apt-get purge libc6-dev
root@pi:~# apt-get --with-new-pkgs upgrade

Update eventually installed pip modules
root@pi:~# pip3 list --outdated --format=freeze | grep -v '^-e' | cut -d = -f 1  | xargs -n1 pip3 install -U

After that we have installed raspbian 11 Bullseye, along with python3.9.1


Note: all following setup commands are done from root (i.e. "su -" or "sudo su" )
As we are in readonly filesystem, before anything mount as readwrite:
root@pi:~# mount -oremount,rw /
root@pi:~# mount -oremount,rw /boot
Let's load at boot io modules, uncommenting
root@pi:~# vi /boot/config.txt


To rotate display if necessary, uncomment line:

You also need the following line in /etc/modules
root@pi:~# vi /etc/modules



Home Assistant is installed manually, as a Python 3 virtual env using a "homeassistant" user and group.
I follow the manual guide here but installed it to self home in /home/homeassistant instead of /srv/homeassistant, because in my setup /home is on the only writable partition.

Still as root, install required libraries
root@pi:~# apt-get install python3 python3-dev python3-venv python3-pip 
root@pi:~# apt-get install libffi-dev libssl-dev libjpeg-dev zlib1g-dev 
root@pi:~# apt-get install autoconf build-essential libopenjp2-7 libtiff5

I was unable to build inside the venv the RPi.GPIO pip module, so I install it from apt repository
apt-get install python3-rpi.gpio

Create user homeassistant and folders
useradd -rm homeassistant -G dialout,gpio,i2c
Set virtual env. I uses the flag "--system-site-packages" because I was unable to build inside the venv the RPi.GPIO pip module.
sudo -u homeassistant -H -s
cd /home/homeassistant
python3 -m venv . --system-site-packages
source bin/activate
The prompt changes to (homeassistant) homeassistant@raspberrypi:~ $

Install packages. It will take some time.
python3 -m pip install wheel
pip3 install homeassistant
Start home assistant for the first time. It will take some time because it installs required pip3 packages.
Check to be able the reach the webpage at address. Do nothing for now.


Check using another terminal that is not installing depencies anymore, i.e.:
root@pi:~# ps -fax

 8142 pts/1    S      0:00  |   _ sudo -u homeassistant -H -s
 8143 pts/1    S      0:00  |       _ /bin/bash
 8402 pts/1    Sl+    0:07  |           _ /home/homeassistant/bin/python3 /home/homeassistant/bin/hass
 1177 pts/2    Ss     0:00  _ /bin/bash
22206 pts/2    R+     0:00      _ ps -fax
Press Ctrl-C in the command line to exit hass

Create the service file to start home assistant at boot:
root@pi:~# cd /etc/systemd/system/
root@pi:/etc/systemd/system# vi home-assistant.service

Description=Home Assistant

ExecStart=/home/homeassistant/bin/hass -c "/home/homeassistant/.homeassistant"

Now you will need to restart the systemctl and read the file with the following commands
root@pi:~# systemctl --system daemon-reload
root@pi:~# systemctl enable home-assistant
root@pi:~# systemctl start home-assistant
root@pi:~# systemctl status home-assistant
Home assistant logfile is located here:
less /home/homeassistant/.homeassistant/home-assistant.log


Install a minimal window manager, Matchbox, intended for mobile devices and that only shows one window at a time.
root@pi:~# apt-get install matchbox
Install a fast browser, I choosed midori
root@pi:~# apt-get install midori
Install some required Xorg utilities
root@pi:~# apt-get install x11-xserver-utils xinit
root@pi:~# adduser user video
Create a xinit configuration file in user home directory to start window manager and browser
root@pi:~# vi /home/user/.xinitrc


/usr/bin/xset -dpms 
/usr/bin/xset s off 
/usr/bin/xset s noblank

/usr/bin/matchbox-window-manager -use_cursor no -use_titlebar no &
/usr/bin/matchbox-panel --size 20 --orientation north --no-menu --no-session &
/usr/bin/mb-applet-launcher /usr/share/pixmaps/matchbox-keyboard.png /usr/bin/matchbox-keyboard &
/usr/bin/midori -a
Allow xinit to start as an user from command line
root@pi:~# vi /etc/X11/Xwrapper.config
Modify allowed_users to:
In start xinit at startup as user "user" using /etc/rc.local
root@pi:~# vi /etc/rc.local

/bin/su user -c /usr/bin/xinit &
exit 0
matchbox-keyboard has an xml file to configure the keyboard layout. The included ones from repositories are too bad.
I started from the one shared from JimmyN here

I made some modification on my keyboard file. You have to place the file in the .matchbox user folder as file keyboard.xml .
You can download my configuration keyboard.xml here

Check that the graphical installation works using
su user -c xinit
You should see the Midori browser with automatic login in Home Assistant, and a top bar with on the left a keyboard icon and on the right a clock.
Touching the keyboard opens the on screen virtual keyboard, touching it again hides the keyboard. Here are two screenshot made with scrot.

homepage 1 homepage 2

I will set autologin for local access to web interface. You do it from the terminal editing the main configuration file.
Set autologin for local access to HTTP interface from localhost.
root@pi:~# vi /home/homeassistant/.homeassistant/configuration.yaml
Add following configuration block:
# autologin if one user
    - type: trusted_networks
        - ::1
      allow_bypass_login: true
    - type: homeassistant

Setup some settings in both the webpage of home assistant and command line. For simplicity I do it from external PC (not on local graphic user interface).
Accessing the page for the first time will create one user (the first one is an administrator) and a wizard.

In my setup, first user (and administrator) will have username admin

Graphically, I setup Home Assistant with two panels: the first one is the realtime main operative window; the second one has some historical diagnose and system informations.


On/off Digital Input and Digital Output are rapidly set in the Home Assistant configuration file. Note that the number ports are GPIO BCM indexes, not pin.
root@pi:~# vi /home/homeassistant/.homeassistant/configuration.yaml
Add following configuration blocks:
# Digital Input
  - platform: rpi_gpio
      17: DI3 Boiler Alert
    invert_logic: false

  - platform: rpi_gpio
      22: DI2 Free
    invert_logic: true

# Digital Output
  - platform: rpi_gpio
    invert_logic: true
      21: DO1 Boiler
      20: DO2 Outdoor Lights
      26: DO3 Light outside the gate
      19: DO4 Water Pump

= Digital Input used for Water Flow Counter =

Digital Input 1 (DI1_Water_Flow_Counter) is used as a pulse counter. The specifications of my sensor says it can measure from 1 L/min to 30 L/min . The pulse frequency to flow rate is: frequency (Hz) / 7.5 = flow in L/min .

Moreover using the oscilloscope I saw that the minimum steady value (high or low) was more than 2 ms, and with a duty cyle of 50%. Checking with maths, the maximum frequency can be calculated as 30 L/min * 7.5 = 225 Hz , so the minimum period declared is 1/225 Hz = 0.0044 s = 4.4 ms . So the declared minimum steady value is 2.2 ms, that is corresponding with practical measures.
Each seconds there should be a maximum of 7.5 * 30 = 225 square waves, and 1 L corresponds to 450 pulses.
The rpi_gpio has a bouncetime of ms. It is not required to campionate completely the signal, but only high and low values, so 2 ms is enough.

In Home Assistant, the statistics counter is incremented both when value is high and was low, and is low and was high, so the counter has to to be divided instead by 7.5 * 2 = 14.
The maximum value of the counter is the daily one, that can reach a maximum of 24*60*30*450 = 19,440,000 pulses, that is lower than the maximum int value in Python (2147483647).

I first tried to configure the flow sensor calculation completely in Home Assistant, using the following configuration blocks
root@pi:~# /home/homeassistant/.homeassistant/configuration.yaml

  - platform: rpi_gpio
      27: DI1 Water Flow Counter
    bouncetime: 2

  - platform: statistics
    entity_id: binary_sensor.DI1_Water_Flow_Counter
    name: "Water Flow Daily Count"
    sampling_size: 10000000
      hours: 24

  - platform: statistics
    entity_id: binary_sensor.DI1_Water_Flow_Counter
    name: "Water Flow Second Count"
    sampling_size: 500
      seconds: 1

  - platform: template
        friendly_name: Water Daily Usage
        value_template: >-
          {{ ( states('sensor.water_flow_daily_count') | float / 900 ) | round(0) }}
        unit_of_measurement: 'L'
        icon_template: hass:water

  - platform: template
        friendly_name: Water Flow Usage
        value_template: >-
          {{ ( states('sensor.water_flow_second_count') | float / 14 ) | round(0) }}
        unit_of_measurement: 'L/min'
        icon_template: hass:water

After doing that and made some testing, I found the shown values were very different from truth. I believe that this solution with statistics on binary_sensor is not fast enough and was losing counts.
Python sleep() and time() functions are based on OS accuracy; on a non-realtime Linux kernel like my case, the minimum sleep interval is lower than 1 ms but not deterministic, so enough for my purpose.

So I evaluated to create a custom integration for that, but it was too complicated over an external simple python script. I'll propose a simple Python script running from Debian. The script has to always run as a service and communicates with Home Assistant using two temporary files, because I need to always check the square wave to gives near instant value and daily counter.
The script is updating values with an interval of 10 seconds, writing some file in temp filesystem to be read from Home Assistant. An update longer than 1 second helps me compensate the delay introduced by file handling.
I checked sensor counting and correctness of water flow calculation, and seems to be realistic.

Install Python schedule library
root@pi:~# pip3 install schedule
Create a script:
root@pi:~# vi /home/homeassistant/

# Raspberri Pi - Hall effect Water Flow counter on GPIO
# pip3 install rpi_gpio schedule

import RPi.GPIO as GPIO
import time, sys, schedule

FILE_OUT_INSTANT = '/tmp/water_flow.instant'
FILE_OUT_DAILY = '/tmp/water_flow.daily'

GPIO.setup(FLOW_SENSOR, GPIO.IN, pull_up_down = GPIO.PUD_UP)

daycounter = 0
lastcount = 0

def countPulse(channel):
   global daycounter
   daycounter = daycounter+1

def resetDaily(job):
   global daycounter
   daycounter = 0

GPIO.add_event_detect(FLOW_SENSOR, GPIO.RISING, callback=countPulse, bouncetime=2)
schedule.every()"00:00").do(resetDaily, '')

while True:
   lastcount = daycounter    
   lastcount = daycounter - lastcount
   with open(FILE_OUT_INSTANT, 'w') as file:
      file.write(str(round(lastcount/UPDATE_INTERVAL/7.5, 1)))
   with open(FILE_OUT_DAILY, 'w') as file:
      file.write(str(round(daycounter/450, 1)))

Set permission:
root@pi:~# chown homeassistant:homeassistant /home/homeassistant/
root@pi:~# chmod +x /home/homeassistant/
Create a systemd service:
root@pi:~# vi /etc/systemd/system/water_flow.service

Description=Water Flow Counter



Now you will need to restart the systemctl and read the file with the following commands
root@pi:~# systemctl --system daemon-reload
root@pi:~# systemctl enable water_flow
root@pi:~# systemctl start water_flow

Add in configuration.yaml file:
root@pi:~# vi /home/homeassistant/.homeassistant/configuration.yaml
the following block:
  - platform: command_line
    name: Water Flow Instant Counter
    command: /bin/cat /tmp/water_flow.instant
    unit_of_measurement: "L/min"
    scan_interval: 10

  - platform: command_line
    name: Water Flow Daily Counter
    command: /bin/cat /tmp/water_flow.daily
    unit_of_measurement: "L"
    scan_interval: 60

= Board status: temperature, uptime, system load, memory =

Internal temperature can be read from command line (the value has to be divided by 1000):
root@pi:~# cat /sys/class/thermal/thermal_zone0/temp
To use the internal temperature I use Command Line integration. You need to add the following to configuration.yaml
root@pi:~# vi /home/homeassistant/.homeassistant/configuration.yaml
Add following configuration block
  - platform: command_line
    name: Board Temperature
    command: /bin/cat /sys/devices/virtual/thermal/thermal_zone0/temp
    unit_of_measurement: "°C"
    value_template: '{{ value | multiply(0.001) | round(1) }}'

I also put a simple uptime command in the historic panel, to view system load and uptime.
Add following block
  - platform: command_line
    name: Board Uptime
    command: /usr/bin/uptime

With an AWK filter on free command, I also display information about memory of the board.
Add following block to /home/homeassistant/.homeassistant/configuration.yaml
  - platform: command_line
    name: Board Memory
    command: '/usr/bin/free -h | /usr/bin/awk -F " " ''FNR==2{print "Total: "$2" Used: "$3" Free: "$4}'' '

= 1-wire - temperature sensor =

From command line, verify that are present wl_gpio and wl_therm
root@pi:~# lsmod | grep -i w1_

w1_therm               24576  0
w1_gpio                16384  0
wire                   36864  2 w1_gpio,w1_therm
You should see temperature sensor here
root@pi:~# ls /sys/bus/w1/devices/
With more sensors, you will see multiple /sys/bus/w1/devices/28-xxxxxxxxxxxxx directories, each one having the unique serial number as the directory name.
You can select a sensor entering the directory (xxxxxxxxxxxxx is the serial number):
cd /sys/bus/w1/devices/28-xxxxxxxxxxxxx
cat w1_slave
Copy the serial numbers.
Add to configuration.yaml the entry for the 1-wire sensor and devices. The devices are indicated according their serial numbers: they can be renamed with a more useful friendly name.
root@pi:~# vi /home/homeassistant/.homeassistant/configuration.yaml

  - platform: onewire

In the documentation it is stated that the sensors can be renamed using the "names" parameter according to the serial number, that should be "sensor.28_012032ca73c6_temperature". However in my setup it seems to not be working. So I used the customize directive to change label.
root@pi:~# vi /home/homeassistant/.homeassistant/configuration.yaml

      friendly_name: Outside South
      friendly_name: Outside North

= i2c - temperature, humidity, pressure sensor =

Install i2c programs to analyse the sensor addressing.
root@pi:~# apt-get install i2c-tools
Show i2c master adapter
root@pi:~# i2cdetect  -l

i2c-1   i2c             bcm2835 (i2c@7e804000)                  I2C adapter
List the i2c sensors detected
# i2cdetect -y 1

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- 76 -- 

1 sensors detected in 0x76
So the address of my BME280 sensor is 0x76.
In Home Assistant configuration file add:
root@pi:~# vi /home/homeassistant/.homeassistant/configuration.yaml

  - platform: bme280
    name: Indoor
    i2c_address: 0x76
    operation_mode: 2
    time_standby: 2
    oversampling_temperature: 2
    oversampling_pressure: 4
    oversampling_humidity: 1
    filter_mode: 4
    delta_temperature: -1
      - temperature
      - humidity
      - pressure

The BME280 sensor usually is self heating himself and gives higher temperature from the true. The "delta_temperature" parameter can be set to adjust constant error in temperature measuring.
Using the two 1-wire themometer plus an external analog one, I considered that around 25 degree it has an approximate error of 1 degrees. So I correct removing 1 degrees. When doing this calibration, made adjustement after enough time the sensor was powered on.

I set the oversampling, IIR filter and standby time settings to values that in some testing done seems to give me stable readings.


Check that the spi_bcm2835 and spidev modules are loaded. If not, use modprobe spi_bcm2835 spidev
root@pi:~# lsmod | grep spi_bcm2835
spi_bcm2835            24576  0

root@pi:~# lsmod |grep spidev
spidev                 20480  0
You should then have some devices ready
root@pi:~# ls  -l  /dev/spi*
crw-rw---- 1 root spi 153, 0 Jan 10 12:31 /dev/spidev0.0
crw-rw---- 1 root spi 153, 1 Jan 10 12:31 /dev/spidev0.1
To check if GPIO and wires are correctly wired, you can use this tool, after putting a wire between the MISO pin 19 and MOSI pin 21.
The result is some hex values as shown.
root@pi:~# wget
root@pi:~# gcc -o spidev_test spidev_test.c
root@pi:~# ./spidev_test -D /dev/spidev0.0
spi mode: 4
bits per word: 8
max speed: 500000 Hz (500 KHz)

40 00 00 00 00 95 
F0 0D 
Install Python spidev library
root@pi:~# pip3 install spidev
Give user homeassistant permission to use SPI device:
root@pi:~# usermod -a -G spi homeassistant

= SPI Channel 0 - Water Pressure =

Create the following Python script to get values from the MCP3008 channel 0. It is an instant reading of the pressure value.
You have to check the minimum voltage of the sensor at 0 Mpa with a voltmeter on the terminal block and set it to variable VMIN.
The PMAX can be set to 0.5 to use the Mpa unit, or 5 to use the Bar unit.
root@pi:~# vi /home/homeassistant/

# Raspberri Pi - Water Pressure reading with MCP3008 over SPI
# pip3 install spidev

from spidev import SpiDev

# minimum voltage measured on sensor data
VMIN = 0.490
# maximum voltage as declared
VMAX = 4.5
# maximum pressure as declared, can be set to 0.5 for MPa or 5 for Bar
# PMAX = 0.5 # MPa
PMAX = 5 # Bar
# MCP3008 channel
# VRef of MCP3008
VREF = 3.3
# voltage divider ratio
VDIV = 2/3

class MCP3008:
  def __init__(self, bus = 0, device = 0):
    self.bus, self.device = bus, device
    self.spi = SpiDev()
  def open(self):, self.device)
    self.spi.max_speed_hz = 1000000
  def read(self, channel = 0):
    adc = self.spi.xfer2([1, (8 + channel) << 4, 0])
    data = ((adc[1] & 3) << 8) + adc[2]
    return data
  def close(self):

  mcp = MCP3008()
  realv = ( * VREF / (VDIV)) / 1024
  realv = (realv - VMIN) * PMAX / (VMAX - VMIN)
  if realv < 0:
    print(round(realv, 3))

Set correct permissions to the file
root@pi:~# chmod +x /home/homeassistant/
root@pi:~# chown homeassistant:homeassistant /home/homeassistant/
Add in configuration.yaml file:
root@pi:~# vi /home/homeassistant/.homeassistant/configuration.yaml

  - platform: command_line
    name: Water Pressure
    command: /home/homeassistant/
    unit_of_measurement: "Bar"
    scan_interval: 60

= Channel 1, 2 - AC power consumption =

Also here I am using a Python script. It waits some time to read the consumption from the sine wave peak value.

The AC sensor gives a sine waves of the frequency of the line with the amplitude corresponding to the load: I need to get the maximum and the minimum of the wave. I can also calculate the wave period. Considering a frequency of 50Hz, for Nyquist we are needing a sampling of at least 100 sample per second, that is one each 10 ms. But I wanted to be more accurate to don't miss the peak, so I wanted to use a sample each 1 ms, that as explained before with digital counter is also a interval well managed in Python over Linux kernel.

With SCT013-030 consumption of 30A (around 6.6 kW at 220VAC) corresponds to 1V RMS, that is -1.42V to +1.42V
Using 3.3V voltage divider at +1.65V, so that the measured sine wave will go from +0.23V to +3.07V
Scale is linear, so ideally VMAX goes from +1.65V to +3.07V
Formula to get W from voltage is: (VMAX - (VREF/2)) * 30 * VOLTAGE / 1.42

VREF_2 can be adjusted manually to reflect value read without any load (for example in my case I read 1.66 V
root@pi:~# vi /home/homeassistant/

# Raspberri Pi - AC power consumption reading with MCP3008 over SPI
# pip3 install spidev schedule

from spidev import SpiDev
import asyncio
import sys
import time

# MCP3008 channel
CHANNEL = int(sys.argv[1])
# VRef on MCP3008
VREF = 3.3
VREF = 3.3
# adjusted because without load always reading 1.66
VREF_2 = 1.66

# Current scale
# AC Voltage
VOLTS = 220
# 1 ms interval
SAMPLE_INT = 0.001

# wait for 20 ms to get vmax
FILE_OUT_INSTANT = '/tmp/ac{}.instant'.format(CHANNEL)

class MCP3008:
  def __init__(self, bus = 0, device = 0):
    self.bus, self.device = bus, device
    self.spi = SpiDev()

  def open(self):, self.device)
    self.spi.max_speed_hz = 1000000

  def read(self, channel = 0):
    adc = self.spi.xfer2([1, (8 + channel) << 4, 0])

    data = ((adc[1] & 3) << 8) + adc[2]
    return data

  def close(self):

async def check_ac_task(mychan):
  global mcp, vmax, flag

  vmax = VREF_2
  vmin = VREF_2
  vprec0 = VREF_2
  vprec1 = VREF_2
  vprec2 = VREF_2

  while flag:
    realv = (int( * VREF / 1024)

    # I'm going down, update the vprec
    if(realv < vprec0):
      vprec0 = realv
    if(realv < vprec1):
      vprec1 = realv
    if(realv < vprec2):
      vprec2 = realv

    # then update the vmax
    if(realv > vmax):
      vmax = realv

    await asyncio.sleep(SAMPLE_INT)

async def run_for_interval(loop, task):
  global flag
  await asyncio.sleep(WAIT_INTERVAL_MS)
  flag = False

if __name__ == '__main__':
    vmax = 0
    flag = True
    mcp = MCP3008()

    loop = asyncio.get_event_loop()
    task = loop.create_task(check_ac_task(CHANNEL))
    loop.create_task(run_for_interval(loop, task))

    # 30A (= 6.6 kW at 220VAC) corresponds to 1V RMS, that is -1.42V to +1.42V
    # using 3.3V voltage divider at +1.65V, so that the measured sine wave will go from +0.23V to +3.07V
    # scale is linear, so ideally VMAX goes from +1.65V to +3.07V
    # formula to get W from voltage is: (vmax - (VREF/2)) * MAXCURR * VOLTS / 1.42

    # prints W
    print(round(((vmax - (VREF_2)) * MAXCURR * VOLTS / 1.42), 3))


Set permissions to the file
root@pi:~# chmod +x /home/homeassistant/
root@pi:~# chown homeassistant:homeassistant /home/homeassistant/
Add in configuration.yaml file:
root@pi:~# vi /home/homeassistant/.homeassistant/configuration.yaml

  - platform: command_line
    name: AC probe 1 current consumption
    command: /home/homeassistant/ 1
    unit_of_measurement: "W"
    scan_interval: 10

  - platform: command_line
    name: AC probe 2 current consumption
    command: /home/homeassistant/ 2
    unit_of_measurement: "W"
    scan_interval: 10


= Climate Thermostat =

Home assistant includes automation mechanism. A simple one is a climate thermostat platform to regulate boiler activation according to indoor temperature.
Add the following to the Home Assistant configuration file
root@pi:~# vi /home/homeassistant/.homeassistant/configuration.yaml

  - platform: generic_thermostat
    name: Main Thermostat
    heater: switch.DO1_Boiler
    target_sensor: sensor.indoor_temperature
    min_temp: 10
    max_temp: 30
    target_temp: 22
    away_temp: 10
    ac_mode: false
    initial_hvac_mode: "heat"

= Outdoor lights on at night

User Automation from web interface. I created two automations: first one to light on, second one to light off. Fill the following fields:

Name -> "Turn on the lights when the sun is set"
Mode -> Single
Trigger type -> Sun
Event -> Sunset
Offset -> -00:30
Action type -> Call service
Service -> switch.turn_on
Name of entities -> switch.do3_gate_lights
Click Save

The second automation to turn the lights off:

Name -> "Turn off the lights when the sun is rising"
Mode -> Single
Trigger type -> Sun
Event -> Sunrise
Action type -> Call service
Service -> switch.turn_off
Name of entities -> switch.do3_gate_lights
Click Save


screen 3 screen 4
screen 6 wall

Here you can download complete configuration.yaml file for Home Assistant.


The RAM memory used on Raspberry Pi is no more that 300 MB, so the version with 2 GB is enough to run this solution, also considering the space allocated for memory filesystems (i.e. /tmp).


The device as-is can be rated with IP20 protection. With some improvement on the side holes, adding a fine grating, and adding a custom gasket on the display, could arrive to IP42.
This is a base for many other deployment, for example some ideas:
  • Implement some history statistic in the history panel about how much time o volumes has been used.
  • Automatically alerts according to some events, for example water pressure is low since too many hours.
  • Add a speater to the Raspberry Pi and make some audio notification, from simple "cuckoo clock" at each hour to voice notification on events.
  • Use wireless connection to manage smart home commercial devices.


Can't be able to build the solution without these helps, presented here in no particular order:

phperr 0.7Sat, 13 Feb 10
Current version of my CMS phperr 0.7 is available. Added web2.0 sharing, multipage as usual without using any database, and RSS fixes. Available upon conditions of GPL v2 license.
Really simple Javascript RSS parserThu, 16 Jul 09
I was looking for a javascript parser, that works without frills and was simple. Not finding it, I write a few lines reported below:
var title = 0;
var lines = 0;

function parselevel(level, skiptext, maxlines){
    if(level.nodeName == "title"){
      title = '<b>' + level.textContent + '</b><br/>';

    if(level.nodeName == "description"){
      if(title != 0){
        if(maxlines == lines){
        document.write('<a ');
          document.write('style="background-color: #90e090;"');
        document.write('>' + title + '' + level.textContent + '</i></a>');
        document.write('<br />');
	title = 0;
        lines = lines + 1;

  for(var i=0; i < level.childNodes.length; i++){
    parselevel(level.childNodes[i], skiptext, maxlines);

function parserss(rssfile, skiptext, maxlines){
  var xmlDoc=document.implementation.createDocument("","",null);

  var root = xmlDoc.documentElement;

  title = 0;
  lines = 0;

  parselevel(root, skiptext, maxlines);
You call it from html page with:
<script src="rss.js" type="text/javascript"></script>
<script language="javascript"><!--
window.onload=parserss("YOURRSS.XML", /TITLE_TO_SKIP/, 5)
Change YOURRSS.XML with the name of rss file (it must be a local file for Gecko browsers, I download it locally with cron and fetch), TITLE_TO_SKIP is a string that exclude some RSS field to be printed, and the last is the number of RSS field to print.
phperr 0.6Sun, 18 Jan 09
New version of this simple CMS, phperr 0.6, is available. Fixed minor graphical details and added RSS capability. As usual, available upon conditions of GPL v2 license.
Random picture from PicasaSat, 9 Aug 08
Here I report the simple php code:

$userid = 'errimg'; // set your userid of picasa
$thumbsize = '108'; // set resolution of thumbnail among 54, 108 or 216

function startElement($parser, $name, $attrs){
    global $record;
    global $canstart;
    if($name == 'TITLE' && $attrs["TYPE"] == 'text' && $canstart)
        $record = true;
    if($name == 'ENTRY')
        $canstart = true;
function endElement($parser, $name){
    global $tmparray;
    global $record;
    global $curfield;
    global $canstart;

    $record = false;
    if($name == 'TITLE' && $canstart){
        $tmparray[] = $curfield;
        $curfield = '';
function elementContent($parser, $data){
    global $curfield;
    global $record;

         $curfield .= $data;
function startIdElement($parser, $name, $attrs){
    global $canstart;
    global $tmparray;
    global $thumbsize;

    if($name == 'MEDIA:THUMBNAIL' && $attrs['HEIGHT'] == '108' && $canstart)
        $tmparray[] = $attrs['URL'];
    if($name == 'ENTRY')
        $canstart = true;
function endIdElement($parser, $name){

function getAlbums($userId){
    global $tmparray;
    global $canstart;

    $tmparray = array();
    $canstart = false;

    $url = ''.urlencode($userId).'?kind=album';
    $xml = file_get_contents($url);

    $xml_parser = xml_parser_create();
    xml_set_element_handler($xml_parser, "startElement", "endElement");
    xml_set_character_data_handler($xml_parser, 'elementContent');
    xml_parse($xml_parser, $xml);

function showAlbumContent($userId, $albumName){
    global $tmparray;
    global $canstart;

    $tmparray = array();
    $canstart = false;

    $url = ''.urlencode($userId).'/album/'.urlencode($albumName);
    $xml = file_get_contents($url);
    $xml_parser = xml_parser_create();
    xml_set_element_handler($xml_parser, "startIdElement", "endIdElement");
    xml_parse($xml_parser, $xml);


  $album_title = $tmparray[array_rand($tmparray, 1)];
  $album_title = ereg_replace("[^A-Za-z0-9]", "", $album_title);
  showAlbumContent($userid, $album_title);
     $random_pic = $tmparray[array_rand($tmparray, 1)];
     print '<a href="'.$userid.'/'.$album_title
        .'"><img src="'.$random_pic .'" alt=""></a>';

phperrDec 2007
I forgot to link here the humble CMS running this site, called with fantasy phperr.
You can get it here (version 0.5); keep in mind that is very my-site oriented, but the code lines are few and it can be easily more developed.
Contents are stored in text files (no database required) and are edited in html stile blog; it support upload and management of remote files. The number of pages are unlimited and created as you access them (in wiki stile). Border menus are are also remotely editable in php.
WaNDA-toolsDec 2007
The WaNDA Project has reached a stable status. It allows you to generate a multiplatform offline version of Wikipedia. More info are available at the project page on Sourceforge, where you can get it.

There's also a page at Politechnic of Turin, linux@studenti, and my MSc thesis in italian about it, here available in texts section.

The code is a mix of PHP for processing and multiplatform ECMAscript for consulting the offline content.
ErmesJun 2006
"Ermes" is a Windows multicast network application for video conferencing; using video compressions techniques, it streams a desktop area from a server machine to unlimited numbers of clients.

The communication is done with two streams: the audio stream has priority over video, so position of mouse cursor is included with it for improved clearness of speech. The video stream compression is settable to suit situation needs. The programs should run on all Windows NT family (XP, etc..). It works in either client or server mode.
Full sources for Visual Studio 2005 (.NET) are released under GPL v2; the software is mainly written in C# with managed C++.

Brief documentation in italian with schemes of components: Ermes.pdf
zenzeroJan 2005
"zenzero" e` il nome in codice di un breve progetto per il controllo di un autoveicolo.
Il veicolo e` costruito con Lego Mindstorms, il software di controllo e` basata su Statetra e LegoStatetra.

La documentazione del progetto: zenzero.pdf
Il sorgente Statetra del sofware di gestione: zenzero_str.tar.bz2
8051 MicrocontrollerAug 2004
Microcontrollore 8051: un testo in italiano che contiene sia un'esaustiva descrizione dell'architettura che una guida alla programmazione in assembler su processori embedded di questo tipo.
La piattaforma di sviluppo di riferimento e` il Keil A51, l'integrato ed i compenenti annessi e` della Phytec.

Il testo e` presente qui Microcontrollore_8051.pdf
Gli esempi inclusi nel testo sono disponibili qui

Il progetto e` stato sviluppato per la laurea di primo livello in collaborazione con fosk.

top   go to page: 1 2 ->    single page
apache php i prefer firefox W3C html 4.0 compliant W3C css compliant
This website uses only essential technical cookies.
Emmanuel Roberto Richiardone (e AT richiardone DOT eu)

All contents, where applicable and except otherwise specified, are present under GPLv2 or GFDL licenses.
E. Richiardone (e AT richiardone DOT eu)

page viewed 27262 times and generated in 0.009072 s