Guida alla realizzazione di un mediacenter con RaspberryPi e Raspbmc / OpenELEC

Dal 2008 avevo un mediacenter in soggiorno, basato semplicemente su un normale pc, una distribuzione ArchLinux ridotta all’osso su una SATADOM da 2G, e ovviamente XBMC. Ultimamente, però, mi sono trovato a lavorare su un progetto basato principalmente su RaspberryPi, e quindi ho deciso finalmente di testare Raspbmc.

IMG_20141205_132918
Questo era il mio vecchio setup basato su un normale PC con ArchLinux e XBMC

 

Raspbmc

Raspbmc (http://www.raspbmc.com/) è una distribuzione linux realizzata ad-hoc per RaspberryPi, che vi permette di trasformare questo stupendo mini computer in un completo mediacenter con XBMC (Kodi, nelle ultime versioni). Sinceramente finora non l’avevo mai provata, in quanto non credevo fosse capace di visualizzare filmati in risoluzione full-hd. Mi sono dovuto ricredere con sommo piacere 🙂

OpenELEC

OpenELEC (Open Embedded Linux Entertainment Center, http://openelec.tv/) è un’altra distribuzione linux realizzata ad-hoc per RaspberryPi basata su Kodi. A mio avviso, rispetto a Raspbmc, è decisamente migliore. Attualmente, infatti, è la mia scelta predefinita.

Di cosa abbiamo bisogno?

  • 1 RaspberryPi (io ho usato il Model B+);
  • 1 alimentatore 5V 2A;
  • 1 scheda MicroSD da almeno 2GB;
  • 1 cavo HDMI;
  • 1 telecomando USB, se il vostro televisore non supporta la funzione HDMI-CEC;
  • 1 circuito a microcontrollore per la gestione dell’alimentazione, se volete dargli “quel tocco di classe” (descritto di seguito 😉 );
  • 1 “contenitore adatto”, se non vi piacciono le soluzioni “quick and dirty” 😉
  • giusto un pò di tempo (un weekend al massimo) e qualche attrezzo;

Let’s build it!

Il “contenitore adatto”

Volevo qualcosa che mi permettesse di racchiudere il tutto senza apparire troppo raffazzonato, e che avesse un minimo di stile. Alla fine avrei comunque posizionato il tutto in maniera “stealth” dietro al televisore, ma, che cavolo, anche se non si vede, anche l’occhio vuole la sua parte… Quindi ho inziato a cercare qualche contenitore delle dimensioni giuste, scartando scatole di cartone, contenitori per alimenti, contenitori dell’IKEA…. finchè non ho pensato di guardare nella mia scatola di vecchi “aggeggi di recupero”, e ho trovato questo simpatico modem adsl Trust trovato-non-so-dove: carino, nero, pieno di griglie di ventilazione, e soprattutto delle dimensioni giuste!

DSCN1656
Foto del “donatore del contenitore”: un modem ADSL Trust

 

L’alimentatore 5V 2A

Per assicurare una buona alimentazione alla RaspberryPi, ho comprato quest’alimentatore da 2A su ebay, pagandolo meno di 5 euro:

DSCN1657
Alimentatore 5V 2A. “cheap”, ma fa un buon lavoro
DSCN1658
Il circuito dell’alimentatore: le dimensioni sono perfette per il contenitore del modem

Le modifiche al case

Qui di seguito potete vedere le modifiche apportate al case del modem. Niente di complicato, solo qualche taglio e qualche fresatura con il dremel:

DSCN1662
Dremel working: sulla sinistra l’apertura per i connettori del RaspberryPi, e sul davanti l’alloggiamento per il connettore di alimentazione 220V di recupero. Si possono notare anche due viti, per il bloccaggio della scheda RaspberryPi
DSCN1661
Questo invece è il connettore 220V di recupero, che ho tirato fuori dalla mia scatola di “vecchie cose che possono tornare utili”. Probabilmente era montato su una vecchia radio…

Assemblaggio

Ecco le foto dell’assemblaggio:

DSCN1663DSCN1664DSCN1665DSCN1666

Ma… un momento… perchè l’uscita 5V dell’alimentatore non è collegata direttamente alla RaspberryPi, ma c’è quel connettore marrone e quel filo giallo? Beh, ci arriviamo tra un attimo 😉

 

Il circuito PowerController

Sul mio vecchio PC-MediaCenter c’era la comodissima funzione, grazie all’ACPI, che mi permetteva di avviare lo shutdown del sistema semplicemente premendo il tasto “power” sul case. In quel modo potevo accendere e spegnere il sistema premendo solo lo stesso tasto, e la stessa cosa poteva essere fatta dalle mie bambine, senza che mi distruggessero il filesystem di root… Ma come ottenere la stessa cosa con una Raspberry? Per definizione, un sistema embedded fa il boot non appena riceve alimentazione, ma per lo shutdown, c’è bisogno di un intervento esterno. Inoltre, anche se si effettua lo shutdown correttamente dal sistema, per poi riaccenderlo, c’è bisogno di staccare e riattaccare fisicamente l’alimentazione. La soluzione Beh, la soluzione è piuttosto semplice: basta usare un microcontrollore esterno che si occupi di dare e togliere l’alimentazione alla RaspberryPi, a “comando”. In tutta sincerità l’idea non è mia, e l’ho presa da qui: http://lowpowerlab.com/atxraspi/ La soluzione però non è completamente opensource, in quanto sul sito è possibile acquistare l’oggetto già assemblato, e quindi non viene fornito ne’ lo schema elettrico, ne’ il firmware del microcontrollore. Nessun problema, ho rifatto da zero entrambe le cose! Sotto potete vedere lo schema del circuito, basato su un microcontrollore ATTiny13 alimentato a 3.3V per essere level-compatible con la Raspberry (questa tensione ce la fornisce il regolatore LF33CDT visibile a sinistra). Il funzionamento è molto semplice: il micro ha il compito di rilevare la pressione del tasto “Power Switch”, e di pilotare il gate del mosfet, in modo da dare o togliere alimentazione alla Raspberry. Ci sono inoltre 2 pin, che vanno collegati all’header GPIO della Raspberry: il primo, “RaspberryPi Request Shutdown”, viene posto dall’ATTiny13 a livello alto quando si ha intenzione di procedere allo shutdown, mentre il secondo, “RaspberryPi Boot Status” è un pin configurato in input per leggere lo stato del boot del sistema operativo. Questi pin vengono gestiti anche attraverso uno script installato sul sistema, che vedremo in seguito. Trovate tutti i files descritti di sotto su questo repository github: https://github.com/OpenMakersItaly/raspi-power-controller

Edit: nello schema non è riportato il modello del mosfet. Ho usato il primo che avevo sotto mano, un IRFU9120NPBF. In ogni caso, per questo schema va benissimo un qualsiasi mosfet a canale P che gestisca senza problemi un carico di 2A.

 

raspi-power
Schema del circuito PowerController

 

Questo invece è il codice sorgente del firmware da caricare sul microcontrollore:


// RaspiPowerController circuit firmware
//
// MCU: ATTiny13
// Author: Salvatore Carotenuto of Startup Solutions / OpenMakersItaly
// (mailto: carotenuto@startupsolutions.it)
//
//
// Changelog:
//    2014-12-05 - started writing
//
// ---------------------------------------------------------------------

#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>

// ===== PROGRAM DEFINITIONS ===========================================

#define STATUS_STANDBY		1
#define STATUS_POWERON		2
#define STATUS_SHUTDOWN		3

#define BUTTON_PIN			0 	// PB0
#define RASPI_STATUS_PIN	1 	// PB1
#define RASPI_SHUTDOWN_PIN	2 	// PB2
#define POWER_PIN			3 	// PB3
#define LED_PIN				4 	// PB4

// note: with clock=4.8MHz, CLKDIV=y and prescaler=clk/64 we should get ~35 overflows per second
#define OVERFLOWS_PER_SECOND			35

// =====================================================================

// global variables
volatile uint8_t  timerTicks;
volatile uint8_t  sleepTimer;
uint8_t  status = 0;

// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------

// function prototypes
void wait(uint16_t periods);
void init(void);
void setPower(uint8_t status);
void setLed(uint8_t status);

// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------

// Timer0 overflow interrupt handler
ISR(TIM0_OVF_vect)
	{
	if(timerTicks == OVERFLOWS_PER_SECOND)
		timerTicks = 0;
	else
		timerTicks++;
	//
	if(sleepTimer > 0)
		sleepTimer--;
	}

// ---------------------------------------------------------------------

// suspends execution for a specified period of time
// 1 period: 1/35th of second
void wait(uint16_t periods)
	{
	sleepTimer = periods;
	while(sleepTimer != 0);
	}

// ---------------------------------------------------------------------

// device initializations
void init()
	{
	// resets timer0 value
	TCNT0 = 0x00; 

	// Clock source & prescaling select (clk/64)
	TCCR0B &= ~(1<<CS02);
	TCCR0B |= (1<<CS01);
	TCCR0B |= (1<<CS00);

	// WGM2:0 = 0b000: normal mode of operation
	TCCR0B &= ~(1<<WGM02);
	TCCR0A &= ~((1<<WGM00) | (1<<WGM01));    

	// Enables interrupt from timer 0 overflow
	TIMSK0 |= 1<<TOIE0;

	DDRB = 0x00;

	// sets LED_PIN as output and low
	DDRB |= (1 << LED_PIN);
	PORTB &= ~(1 << LED_PIN);

	// sets POWER_PIN as output and high (circuit uses a P-Channel MOSFET)
	DDRB |= (1 << POWER_PIN);
	PORTB |= (1 << POWER_PIN);

	// sets BUTTON_PIN as input, and enables pullup on it
	DDRB &= ~(1 << BUTTON_PIN);
	PORTB |= (1 << BUTTON_PIN);

	// sets RASPI_STATUS_PIN as input
	DDRB &= ~(1 << RASPI_STATUS_PIN);
	PORTB &= ~(1 << RASPI_STATUS_PIN);

	// sets RASPI_SHUTDOWN_PIN as output
	DDRB |= (1 << RASPI_SHUTDOWN_PIN);
	PORTB &= ~(1 << RASPI_SHUTDOWN_PIN);

	// default status
	status = STATUS_STANDBY;

	// reset counters
	timerTicks = 0;
	sleepTimer = 0;
	}	

// ---------------------------------------------------------------------

void setPower(uint8_t status)
	{
	if(status)
		// sets POWER_PIN as low
		PORTB &= ~(1 << POWER_PIN);
	else
		// sets POWER_PIN as high
		PORTB |= (1 << POWER_PIN);
	}

// ---------------------------------------------------------------------

void setLed(uint8_t status)
	{
	switch(status)
		{
		case 0:
			// sets LED_PIN as low
			PORTB &= ~(1 << LED_PIN);
			break;
		case 1:
			// sets LED_PIN as high
			PORTB |= (1 << LED_PIN);
			break;
		case 2:
			// toggles LED_PIN status
			PORTB ^= (1 << LED_PIN);
			break;
		}
	}

// ---------------------------------------------------------------------

int main (void)
	{
	// disables interrupts
	cli();

	// initializes device
	init();

	// enables interrupts
	sei();

	for(;;)
		{
		switch(status)
			{
			case STATUS_STANDBY:
				// checks status of BUTTON_PIN
				// if button pressed (pin low) sets
				// power and led pins as high,
				// and goes in POWERON status
				if(!(PINB & (1 << BUTTON_PIN)))
					{
					setPower(1);
					setLed(1);
					status = STATUS_POWERON;
					//
					// waits for at least 3 seconds
					wait(150);
					}
				break;
			//
			//
			case STATUS_POWERON:
				// if RASPI_STATUS_PIN is high (raspberry pi up and running)
				if(PINB & (1 << RASPI_STATUS_PIN))
					{
					// checks status of button pin
					// if button pressed (pin low) goes in SHUTDOWN status
					if(!(PINB & (1 << BUTTON_PIN)))
						{
						status = STATUS_SHUTDOWN;
						// sets shutdown pin as high
						PORTB |= (1 << RASPI_SHUTDOWN_PIN);
						}
					}
				break;
			//
			//
			case STATUS_SHUTDOWN:
				// toggles status of led
				setLed(2);
				// checks status of RASPI_STATUS_PIN pin
				// if pin is low, the raspberry pi has finished shutdown
				if(!(PINB & (1 << RASPI_STATUS_PIN)))
					{
					// turns off raspberry pi power and led, and sets shutdown pin as low
					setPower(0);
					setLed(0);
					PORTB &= ~(1 << RASPI_SHUTDOWN_PIN);
					// and goes back in standby mode
					status = STATUS_STANDBY;
					}
				else
					wait(5);
				break;
			}
		wait(2);
		} 

	return 0;
	}

 

CIMG1552
Primo piano del circuito PowerController, realizzato su millefori. Da sinistra: connettore alimentazione, mosfet, regolatore di tensione, ATTiny13
CIMG1553
Visione d’assieme di tutto il setup. I due fili giallo e bianco, collegati alla RaspberryPi, sono sui pin 20 e 26 (i penultimi della fila, vedi script di seguito)

 

RaspberryPi-B-_GPIO-pinout
Questo è il pinout del connettore header GPIO della RaspberryPi Model B+. In evidenza i pin usati per il progetto.

Installazione del sistema (Raspbmc)

L’installazione del sistema è decisamente semplice. Procuratevi una scheda microSD da almeno 2GB, e scaricate l’immagine del sistema da qui: http://www.raspbmc.com/download/ Per un setup rapido, vi consiglio di scaricare la standalone image. Questa è l’immagine completa del sistema. Nel caso aveste una connessione lenta, potete anche scaricare la network image, la quale contiene solo il minimo essenziale, e che provvede a “completarsi” e aggiornarsi direttamente una volta che si è avviata sulla RaspberryPi. Nota: anche la versione standalone si auto-aggiorna, una volta installata e connessa ad internet. Sulla mia Raspberry ha aggiornato la versione di XBMC installata all’ultima release (Kodi). Una volta scaricato e scompattato il tarball dell’immagine, vi basta “spalmare” sulla microSD il file raspbmc-final.img semplicemente aprendo una shell ed eseguendo il seguente comando:

dd if=raspbmc-final.img of=/dev/sdX bs=1M

fatto ciò, scollegate la microSD dal PC e inseritela nella Raspberry. Collegate la Raspberry ad internet con un cavo ethernet e date alimentazione. Il sistema si autoconfigurerà e scaricherà gli aggiornamenti. Dopo qualche riavvio automatico, avrete il vostro mediacenter XBMC/Kodi completo e funzionante. Ah, ovviamente questa procedura di installazione presuppone che abbiate un PC GNU/Linux. Nel caso in cui siate sfigati e abbiate solo una macchina win**ws a disposizione… beh, peggio per voi. Scherzo, ovviamente… sul sito di Raspbmc ci sono tutte le indicazioni su come installarla usando macchine windows.

Installazione del sistema (OpenELEC)

L’installazione di OpenELEC, è identica a quella della versione Raspbmc. L’unica differenza, ovviamente, sta nel fatto di scaricare la giusta immagine di sistema da qui: http://openelec.tv/get-openelec

Una volta scaricata, va scritta sulla microSD con il solito comando dd (individuando il device assegnato alla microSD):

dd if=openelec_image.img of=/dev/sdX bs=1M 

Le modifiche al sistema della Raspberry (versione Raspbmc)

Per fare in modo che la Raspberry interagisca con il circuito PowerController, c’è bisogno di fare due semplicissime modifiche al sistema operativo. La prima, consiste nel copiare questo semplice script bash in /etc/shutdowncheck.sh:

#!/bin/bash

# Original version taken from http://lowpowerlab.com/atxraspi/
# Adapted by Salvatore Carotenuto of StartupSolutions / OpenMakersItaly

# This script puts "BOOT" pin as high when it starts,
# and checks continously the status of "SHUTDOWN" pin. When this pin is put as
# high by the external controller circuit, issues a shutdown.

# GPIO26 (pin 37 on the pinout diagram of the RaspberryPi B+).
# This pin is used as input by the Raspberry.
# When the system is running and the user presses the power button,
# this pin is set high by the ATTiny13, signaling the RaspberryPi to shutdown.
SHUTDOWN=26
echo "$SHUTDOWN" > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio$SHUTDOWN/direction

# GPIO20 (pin 38 on the pinout diagram).
# This pin is used as input by the Raspberry.
# This pin is put HIGH by the RaspberryPi when the system has booted.
BOOT=20
echo "$BOOT" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio$BOOT/direction
echo "1" > /sys/class/gpio/gpio$BOOT/value

echo "RaspiPowerController shutdown script started: asserted pins ($SHUTDOWN=input,LOW; $BOOT=output,HIGH). Waiting for GPIO$SHUTDOWN to become HIGH..."

# This loop continuously checks if the shutdown button was pressed on RaspiPowerController (SHUTDOWN pin to become HIGH), and issues a shutdown when that happens.
# It sleeps as long as that has not happened.
while [ 1 ]; do
  shutdownSignal=$(cat /sys/class/gpio/gpio$SHUTDOWN/value)
  if [ $shutdownSignal = 0 ]; then
    /bin/sleep 0.5
  else
    sudo pkill xbmc.bin
    /bin/sleep 5
    sudo poweroff
  fi
done

Date i permessi di esecuzione al file /etc/shutdowncheck.sh (dando il comando sudo chmod +x /etc/shutdowncheck.sh), quindi inserite la seguente riga nel file /etc/rc.local:

/etc/shutdowncheck.sh &

Il file dovrà apparire più o meno come questo:

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

/etc/shutdowncheck.sh &
exit 0

Ovviamente tutto questo presupponeva che aveste già un sistema Raspbmc installato e funzionante. Nel caso non sappiate come installare il sistema, vi basta leggere il paragrafo di seguito.

Le modifiche al sistema della Raspberry (versione OpenELEC)

Le modifiche da apportare al sistema, in caso di sistema OpenELEC, sono le seguenti:

  1. connettersi in ssh sul sistema, ed editare il file .config/autostart.sh (il file non sarà presente, quindi va creato), ed inserire all’interno la seguente riga:
    /storage/.config/shutdowncheck.sh &
    
  2. dare i permessi di esecuzione al file appena creato, con il comando
    chmod +x .config/autostart.sh
    
  3. creare il file .config/shutdowncheck.sh e inserire il seguente contenuto:
    #!/bin/bash
    
    # GPIO26 (pin 37 on the pinout diagram of the RaspberryPi B+).
    # This pin is used as input by the Raspberry. 
    # When the system is running and the user presses the power button,
    # this pin is set high by the ATTiny13, signaling the RaspberryPi to shutdown.
    SHUTDOWN=26
    echo "$SHUTDOWN" > /sys/class/gpio/export
    echo "in" > /sys/class/gpio/gpio$SHUTDOWN/direction
    
    # GPIO20 (pin 38 on the pinout diagram).
    # This pin is used as input by the Raspberry. 
    # This pin is put HIGH by the RaspberryPi when the system has booted.
    BOOT=20
    echo "$BOOT" > /sys/class/gpio/export
    echo "out" > /sys/class/gpio/gpio$BOOT/direction
    echo "1" > /sys/class/gpio/gpio$BOOT/value
    
    echo "ATXRaspi shutdown script started: asserted pins ($SHUTDOWN=input,LOW; $BOOT=output,HIGH). Waiting for GPIO$SHUTDOWN to become HIGH..."
    
    #This loop continuously checks if the shutdown button was pressed on ATXRaspi (SHUTDOWN pin to become HIGH), and issues a shutdown when that happens.
    #It sleeps as long as that has not happened.
    while [ 1 ]; do
     shutdownSignal=$(cat /sys/class/gpio/gpio$SHUTDOWN/value)
     if [ $shutdownSignal = 0 ]; then
     /bin/sleep 0.5
     else
     pkill kodi.bin
     /bin/sleep 5
     poweroff
     fi
    done

Il sistema in azione

Ed ecco le foto ed un paio di video del sistema finito e installato:

CIMG1555
Il tutto assemblato, prima della chiusura del case. Ho dovuto un pò penare per sistemare bene il pulsante, che ho fissato con della colla a caldo
CIMG1558
ed eccolo qui, in tutta la sua bellezza. Il led rosso è quello dell’alimentatore, mentre quello bianco ad alta luminosità segnala lo stato di power on della RaspberryPi. Ho messo questo tipo di led, perchè essendo il tutto montato sul retro del TV, volevo si notasse quando acceso
CIMG1561

Il posizionamento finale per il montaggio

Il telecomando

Si, ma come fare per comandarlo? Beh, se avete un TV recente, che supporti la funzione HDMI-CEC, potete usare direttamente il telecomando del vostro TV. In caso contrario, io vi consiglio uno di questi (che ho usato sul mio vecchio setup fino ad ora):

pc-remote
simpatico, economico, e implementa sia tastiera che mouse

Si, ma i files?

Beh, domanda lecita… A casa mia ho una macchina linux che funge sia da server che da NAS, quindi in realtà tutti i miei files multimediali sono lì, e li visualizzo tramite condivisione NFS. Nel caso vogliate realizzare una soluzione standalone, potete tranquillamente usare una pendrive o un hd esterno collegato direttamente via USB alla Raspberry.

Buona raspbmc-visione a tutti!

Annunci

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...