wtorek, 16 kwietnia 2013

RPi - Running services at boot

I wanted to start a few services while my Rasperry Pi is booting. Since my days as linux "administrator" are gone, I needed to do some research how to achieve that.

Services to be started: H2 database server, serial port listening service, tomcat - in that order.

Naturally, all applications have been installed.
First step - prepare script files:

/etc/init.d/h2 file:
#!/bin/bash

### BEGIN INIT INFO
# Provides:          h2
# Required-Start:
# Required-Stop:
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: h2 database
# Description:       h2 database
### END INIT INFO


test -f /lib/lsb/init-functions || exit 1
. /lib/lsb/init-functions

H2_HOME=/opt/h2
DAEMON=h2server
ARGS=


#PID=$(get_pid $DAEMON)

case "$1" in
 start)
  log_begin_msg "Starting h2 database"
   $H2_HOME/bin/$DAEMON $ARGS & #>/dev/null
   log_end_msg 0
   ;;
 stop)
   log_begin_msg "Stopping $DAEMON"
   kill `ps aux | awk '/h2.*jar/{print $2}'` > /dev/null
   ;;
 restart)
   $0 stop
   sleep 1
   $0 start
   ;;
 *)
   echo "usage: $0 {start|stop|restart}"  
esac


/etc/init.d/rxreader file:
#!/bin/bash

### BEGIN INIT INFO
# Provides:          rxreader
# Required-Start: $tomcat
# Required-Stop: $tomcat
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: rxreader
# Description:       rxreader
### END INIT INFO


test -f /lib/lsb/init-functions || exit 1
. /lib/lsb/init-functions

RXRD_HOME=/opt/rxreader
DAEMON=read
ARGS=


case "$1" in
 start)
  log_begin_msg "Starting h2 database"
   #[ -z "$PID" ] && 
   $RXRD_HOME/bin/$DAEMON $ARGS & #>/dev/null
   log_end_msg 0
   ;;
 stop)
   log_begin_msg "Stopping $DAEMON"
   kill `ps aux | awk '/rxreader/{print $2}'` > /dev/null
   ;;
 restart)
   $0 stop
   sleep 1
   $0 start
   ;;
 *)
   echo "usage: $0 {start|stop|restart}"  
esac

/etc/init.d/tomcat file:
#!/bin/bash

### BEGIN INIT INFO
# Provides:          tomcat
# Required-Start: $h2
# Required-Stop: $h2
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: tomcat
# Description:       tomcat
### END INIT INFO


test -f /lib/lsb/init-functions || exit 1
. /lib/lsb/init-functions

TOMCAT_HOME=/opt/apache-tomcat-7
DAEMON=startup.sh
ARGS=


case "$1" in
 start)
  log_begin_msg "Starting apache tomcat"
   $TOMCAT_HOME/bin/$DAEMON $ARGS & #>/dev/null
   log_end_msg 0
   ;;
 stop)
   log_begin_msg "Stopping $DAEMON"
   $TOMCAT_HOME/bin/shutdown.sh
   ;;
 restart)
   $0 stop
   sleep 1
   $0 start
   ;;
 *)
   echo "usage: $0 {start|stop|restart}"  
esac

When file are in /etc/init.d directory we have to run update-rc command, which installs (or removes) System-V style init script links:
sudo update-rc.d h2 defaults
sudo update-rc.d tomcat defaults
sudo update-rc.d rxreader defaults

Now you can restart RPi or start services manually:
sudo service h2 start
sudo service rxreader start
sudo service tomcat start

wtorek, 22 stycznia 2013

Java and RPi - continued


After I had installed both jdk (open and oracle's), I started wondering what is the difference between them, when it comes to performance. Recently I found some time in the evening and wrote simple tests, which are not very accurate, but give the general notion about performance.

Let's take a look into tests:
ArrayListAppendTest
List<Long> list = new ArrayList<Long>();
for (long l = 0; l <1000000; l++) {
   list.add(l);
}

ArrayListInsertMiddleTest
List<Long> list = new ArrayList<Long>();
for (long l = 0; l < 1000; l++) {
   list.add(l);
}
for (long l = 0; l < 10000; l++) {
   list.add(500, l);
} 

ArrayListSortTest
List list = new ArrayList();
for (int i = 100000; i > 0; i--) {
   String str = String.format("String nr %5s", i);
   list.add(str);
}
Collections.sort(list);

LinkedListAppendTest, LinkedListInsertMiddleTest, LinkedListSortTest are exactly the same as for ArrayList, but with subtle change...

DoubleAddTest
double val = 0.0;
for (int i = 0; i< 1000000; i++) {
   val += (double) i;
}

SinusTest
for(int i = 0; i < 1000000; i++) {
   double val = Math.sin((double) i);
}

StringAppendTest
String [] strings = new String[1000];
for (int i = 0; i < strings.length; i++) {
   strings[i] = String.format("Str %d", i);
}
String concat = "";
for (int i = 0; i < strings.length; i++) {
   concat += strings[i];
}

All tests *ISTest use various input streams, *OSTest - output streams.

When it comes to reflection tests, I tried to instantiate class using classe's newInstance method, in case of CallMethod test I'm calling one argument method using reflection. Simple...

For comparison I executed test on my ancient computer (Amd64 3500+). After that I compiled and run tests on Raspberry Pi using proper JDK.

And the resulst are... interesting (execution time is in ms):

Test nameJDK Oracle 1.7 @ AMD64 3500Oracle JDK 1.8 ea @ RPiOpen JDK 1.7 @ RPiRatio
ArrayListAppendTest 348,6 2632 10965,1 4,2
ArrayListInsertMiddleTest 72,6 285,7 1024,7 3,6
ArrayListSortTest 19,6 292,9 2394,4 8,2
BufferedFileOS 12,2 45,4 856,4 18,9
DoubleAddTest 0,2 35,8 390,5 10,9
FileISTest 25,3 183,8 231,5 1,3
FileOSTest 96,8 524,9 1618,9 3,1
LinkedListAppendTest 636,1 3393,7 16634,3 4,9
LinkedListInsertMiddleTest 23,3 99,5 2159 21,7
LinkedListSortTest 44,8 270,9 2225,9 8,2
ReflectionCallMethodTest 116,3 803,2 3496,7 4,4
ReflectionNewInstanceTest 55,5 202,5 542,4 2,7
SinusTest 268,6 562,5 3722 6,6
StringAppendTest 49,3 254,4 489,1 1,9

As you can see Open JDK in those test is far behind oracle's (in column Ratio you can see, how much slower Open JDK was :/).
I think I stick to JDK 1.8 ea...

sobota, 19 stycznia 2013

Java and RPi

If you gave a java developer a Raspberry Pi board, what programming language would he choose? You can make additional assumption, that he would be interested in writing a web application for RPi.
He would choose:
  1. C++
  2. Python
  3. or maybe Java?

Please send your answer anywhere you wish, in the meantime we start installing Java on RPi.

Most searches in google, if you want to find phrase "java raspberry pi", ends with plenty of links to articles, how to install the only proper one version, namely oracle java. But I would like to install for starters an open version.

Actually, installation process is not very sophisticated:
sudo apt-get install openjdk-7-jdk

When it's done, you can verify it quickly:
myuser@raspberrypi ~ $ java -version
java version "1.7.0_07"
OpenJDK Runtime Environment (IcedTea7 2.3.2) (7u7-2.3.2a-1+rpi1)
OpenJDK Zero VM (build 22.0-b10, mixed mode)

When it comes to web application, web container could be quite handy when running application. So far I haven't used jetty, therefore:
sudo apt-get install jetty8

When installation is completed (ignore an error while starting jetty) you need to update you startup scripts. But first, the only valid text editor should be installeed:
sudo apt-get install vim

Before we update a configuration file, let's see where java has landed:
update-alternatives --list java

and we get:
/usr/lib/jvm/java-7-openjdk-armhf/jre/bin/java

so java home is: /usr/lib/jvm/java-7-openjdk-armhf

Knowing all that important stuff we can modify a script file:
sudo vim /etc/default/jetty8

first thing, we shall allow jetty to start along wiht the system:
# change to 0 to allow Jetty to start
NO_START=0

after that, setting JAVA_HOME is proper thing to do:
# Home of Java installation.
JAVA_HOME=/usr/lib/jvm/java-7-openjdk-armhf

If we completed our work now, jetty could be accessed only on... localhost. In order to access web server from anywhere, we need to set JETTY_HOST as well:
# Listen to connections from this network host
# Use 0.0.0.0 as host to accept all connections.
# Uncomment to restrict access to localhost
#JETTY_HOST=$(uname -n)
JETTY_HOST=0.0.0.0

Let's restart jetty:
sudo /etc/init.d/jetty8 restart

and what we get is default page for our jetty server:



However...
I would like to have the possibility to use oracle's jdk as well.
But...
It's not so easy for me... At least not on a wheezy version I have installed nor with jdk 1.7.0_10.
The problem is that I had wanted to have more efficient system, therefore I've installed wheezy with hardware ABI, and current oracle's jdk can use only slower soft-bloat ABI.

Fortunatelly, Oralce released "early access" jdk 1.8 with JavaFX included (you can get that package from http://jdk8.java.net/fxarmpreview/)

Whern you download it and unpack in /usr/lib/jvm directory, following command should be executed:
myuser@raspberrypi ~ $ sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.8.0/bin/java 1
myuser@raspberrypi ~ $ sudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.8.0/bin/javac 1

One more thing to - setting which java alternative should be used:
myuser@raspberrypi ~ $ sudo update-alternatives --config java
There are 3 choices for the alternative java (providing /usr/bin/java).

  Selection    Path                                            Priority   Status
------------------------------------------------------------
* 0            /usr/lib/jvm/java-7-openjdk-armhf/jre/bin/java   1063      auto mode
  1            /usr/lib/jvm/java-7-openjdk-armhf/jre/bin/java   1063      manual mode
  2            /usr/lib/jvm/jdk1.8.0/bin/java                   1         manual mode

Press enter to keep the current choice[*], or type selection number: 2

myuser@raspberrypi ~ $ sudo update-alternatives --config javac
There are 3 choices for the alternative javac (providing /usr/bin/javac).

  Selection    Path                                         Priority   Status
------------------------------------------------------------
* 0            /usr/lib/jvm/java-7-openjdk-armhf/bin/javac   1063      auto mode
  1            /usr/lib/jvm/java-7-openjdk-armhf/bin/javac   1063      manual mode
  2            /usr/lib/jvm/jdk1.8.0/bin/javac               1         manual mode

Press enter to keep the current choice[*], or type selection number: 2

Version check is simple:
myuser@raspberrypi ~ $ java -version
java version "1.8.0-ea"
Java(TM) SE Runtime Environment (build 1.8.0-ea-b36e)
Java HotSpot(TM) Client VM (build 25.0-b04, mixed mode)

Last thing to is is changing jetty configuration. I did that, and for now everything seems to work fine.

sobota, 5 stycznia 2013

New Year's Resolution

New year has come and with many New Year's Resolutions I've made one affecting this blog, namely I would like to try writing my posts in English (naturally I would like to translate all previous entries as well).
Why? There are many reasons. First of all I have to improve it. Not only my blog naturally ;) I haven't used written English for a long time (come on, short e-mails at work do not count). Besides, I really like this language and don't treat it as a tool.
Moreover, maybe I write something valuable one day... I that case more people could use that information (whatever).

At least my primordial assumption that this blog serves mainly me, is still valid.

Enjoy!

czwartek, 27 grudnia 2012

Logging to Raspberry without password

Once upon a time I was spending a lot of time working with Linux servers. It's a distant past, but I still can recall some tips. One of them, quite useful when it comes to playing with RPi, is logging without password. I don't mean to give up passwords and allow anyone to enter my realms, but rather to automate the whole process from trusted machine.

How to achieve that?

On your workstations you need to generate authentication keys:
myuser@myhost:~$ ssh-keygen 
Generating public/private rsa key pair.
Enter file in which to save the key (/home/myuser/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/myuser/.ssh/id_rsa.
Your public key has been saved in /home/myuser/.ssh/id_rsa.pub.
The key fingerprint is:
2d:2c:d5:d1:cc:cc:1a:aa:a3:11:28:da:1b:1a:2a:3a


When you have them, the next step is copying your public key to RPi:
myuser@myhost:~$ ssh-copy-id -i ~/.ssh/id_rsa.pub myuser@raspberrypi
myuser@raspberrypi's password: 
Now try logging into the machine, with "ssh 'myuser@raspberrypi'", and check in:

  ~/.ssh/authorized_keys

to make sure we haven't added extra keys that you weren't expecting.

And that's all.
From now on you will be automatically logged to your precious board.

środa, 26 grudnia 2012

Malinowo :)

W tym roku Mikołaj był przesłodki. Jak malina. Więc zaczyna się zabawa z nową płytką, czyli Raspberry Pi. Oby tylko czasu starczyło...

Pracę z Raspberry Pi wypada rozpocząć od przygotowania karty SD. Minimalny rozmiar karty do 2GB, ja użyję 8GB klasy 10.

Po włożeniu karty do czytnika, sprawdzamy urządzenie skojarzone z kartą:
df -h

Zakładajac, że jest to sdb, należy odmontować kartę:
sudo umount /dev/sdb1
a następnie za pomocą dd skopiować obraz na kartę
sudo dd bs=1M if=./2012-12-16-wheezy-raspbian.img of=/dev/sdb
Pomimo wielu zalet, dd ma zasadniczą wadę - nie informuje o postępie kopiowania. Jednakże można zmusić dd do współpracy. Najpierw trzeba poznać PID procesu. W tym celu otwieramy drugą konsolę:
ps aux | grep dd.*wheez
a następnie wysłać sygnał do procesu:
sudo kill -USR1 <PID>
W pierwszej konsoli dd łaskawie ujawni swoje tajemnice:

Kiedy karta jest gotowa, można za pomocą Gparted rozszerzyć partycję, tak by zajmowała całą kartę, jednakże nie jest to konieczne. Przy pierwszym starcie uruchomi się raspi-config, który umożliwia wstępną konfigurację malinki:


Z przydatnych ustawień:
  • expand_rootfs
  • change_pass
  • change_locale
  • change_timezone
  • ssh
  • boot_behaviour
Jeśli czegoś nie ustawimy, można konfigurator uruchomić ponownie:
sudo raspi-config
Po zakończeniu konfiguracji można się zalogować do systemu.

Pierwszym krokiem po uruchomieniu, jest nadanie stałego IP.
W pliku /etc/network/interfaces należy wpisać (wybierając odpowiednie IP :) )
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address 192.168.1.10
gateway 192.168.1.1
netmask 255.255.255.0
network 192.168.1.0
broadcast 192.168.1.255

# w moim przypadku nie używam sieci bezprzewodowej
#allow-hotplug wlan0
#iface wlan0 inet manual
#wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf
#iface default inet dhcp

Jeśli nie chcemy używać domyślnego użytkownika (pi), można dodać własnego:
sudo adduser myuser
A następnie nadać mu nieco większe uprawnienia:
sudo visudo
wpisując
myuser ALL=(ALL) NOPASSWD: ALL

Możemy jeszcze dodać naszego użytkownika do tych samych grup, do których należy pi. W tym celu (bedąc zalogowanym jako pi) wykonujemy polecenie:
sudo usermod -a -G adm,dialout,cdrom,sudo,audio,video,plugdev,games,users,netdev,input myuser
albo mniej zrozumiałe polecenie:
sudo usermod -a -G `groups | sed -e 's/pi //g;s/ /,/g'` myuser

Po przelogowaniu się na naszego nowego użytkownika możemy sprawdzić czy się udało:
groups

wtorek, 11 grudnia 2012

Szeregowo i kolorowo...

Dużo czasu minęło od ostatniego posta... Spręża brak, więc na dobry początek coś łatwego, szybkiego i... mało potrzebnego.

Potrzebowałem zobaczyć, jak skomplikowane jest komunikowanie się aplikacji uruchomionej na komputerze z Arduino. Oczywiście wybór padł na  złącze szeregowe :/

Pytanie, czym sterować, żeby się nie narobić... Albo serwo, a może jakaś dioda? A może dioda RGB? Akurat miałem pod ręką małe trójkolowe coś w wersji smd, więc...

Najpierw mała aplikacja w Processingu, ot trzy suwaki...


Każdy z "suwaków" aktualizuje poszczególne składowe, które następnie są łączone w jeden łańcuch i wysyłane przez port szeregowy do Arduino:

float mr = map(r, 0, 255, 0, 255);
float mg = map(g, 0, 255, 0, 255);
float mb = map(b, 0, 255, 0, 255);

String rgbString = str((int)mr) + " " + str((int)mg) + " " + str((int)mb) + "\n";
print("rgbString:"); println(rgbString);
port.write(rgbString);

Arduino zaś odbiera łańcuch i parsuje poszczególne składowe:

if (Serial.available() > 0) {
    redValue   = Serial.parseInt();
    greenValue = Serial.parseInt();
    blueValue  = Serial.parseInt();
        
    if (Serial.read() == '\n') {
        redValue   = constrain(redValue, 0, 255);
        greenValue = constrain(greenValue, 0, 255);
        blueValue  = constrain(blueValue, 0, 255);

        analogWrite(redPin,  redValue);
        analogWrite(greenPin,greenValue);
        analogWrite(bluePin, blueValue);
    }
}

Próby wysyłania poszczególnych składowych osobno, jako sekwencja bajtów, się nie sprawdziła, więc póki co pozostaje tworzenie łańcucha znaków...

A działa to mniej więcej tak:


Kody źródłowe: