Erlang

Erlang on Raspberry Pi, starting with a fresh Raspbian image (even Lite will do). Make sure you run raspi-config to enlarge the file system to fill your memory card. Erlang takes about 40 minutes or more to compile (when you run "make"). If you are curious, try the OTP 21 Release Candidate. sudo apt-get update sudo apt-get install wget sudo apt-get install libssl-dev sudo apt-get install ncurses-dev Go to this page: https://github.com/erlang/otp/releases/ and select Source code (tar.gz) tar -xzvf OTP-21.0-rc2.tar.gz cd OTP-21.0-rc2/ ./configure make sudo make install cd .. rm OTP-21.0-rc2.tar.gz sudo rm -R OTP-21.0-rc2/

If you want the latest stable release: sudo apt-get update sudo apt-get install wget sudo apt-get install libssl-dev sudo apt-get install ncurses-dev wget http://www.erlang.org/download/otp_src_20.3.tar.gz tar -xzvf otp_src_20.3.tar.gz cd otp_src_20.3/ ./configure make sudo make install cd .. rm otp_src_20.3.tar.gz sudo rm -R otp_src_20.3/ If you don't want to install everything that comes with the standard Erlang package, you can save space and time by just putting a file with the name SKIP in every library you don't want/need in your raspberry pi. These are the applications I usually skip. Do this before you run ./configure and make. touch lib/cosEvent/SKIP touch lib/cosEventDomain/SKIP touch lib/cosFileTransfer/SKIP touch lib/cosNotification/SKIP touch lib/cosProperty/SKIP touch lib/cosTime/SKIP touch lib/cosTransactions/SKIP touch lib/diameter/SKIP touch lib/eldap/SKIP touch lib/ic/SKIP touch lib/megaco/SKIP touch lib/orber/SKIP touch lib/otp_mibs/SKIP touch lib/parsetools/SKIP touch lib/reltool/SKIP touch lib/snmp/SKIP touch lib/wx/SKIP touch lib/xmerl/SKIP

You start the interactive shell with erl and quit with Ctrl-g q. Read about Erlang at and  and

Graphics
There is a very nice graphics library written in Erlang. It makes a perfect match if your application is written in Erlang and you are using a small screen where you don't want a window manager. The screen in the image is an Adafruit 5" Touchscreen 800x480. Erlang ex11 is writing directly to the screen (X11 server or Mir). Experience soft realtime graphics. Get it here

Here's an implementation of ex11 on a mobile phone, note the concurrent animations. Each digit is a separate Erlang process. 

To help create new widgets there is a small program included in ex11 to convert DXF-files to erlang ex11 drawing primitives. Download or draw your own graphics in a CAD system and save as DXF. Convert the DXF file with dxf2erl:start("filename.dxf"). from the erlang console.

The DXF file can be created in LibreCAD, QCAD , FreeCAD , OpenSCAD and other CAD systems. Entities with higher thickness will be drawn last. Closed polylines will be filled areas in ex11.

Ports
Erlang is using ports to communicate with all the different hardware that you are connecting to your Raspberry Pi. In the image to the right I am using an ex11 line graph widget to show the load on my mains meter in the basement. The Erlang Port I'm using is programmable in the language Forth, read more about it here. The fact that it is programmable makes it possible to read multiple sensors and command multiple actuators simultaneously and it is even possible to programmatically re-program the port from a running Erlang application. It is also very handy to debug because you can start the port standalone and issue commands to it in its own console window.

Sockets
You can also use TCP sockets to communicate with devices. This is a simple owserver client. It can call dir and print a directory listing from a onewire network. First, you have to install owfs: sudo apt-get install owfs Configure owfs in /etc/owfs.conf

Then, save a file, onewire.erl with the following content: -module(onewire). -export([start/0,init/0,loop/1]).

start -> spawn_link(?MODULE,init,[]).

init -> io:format("New process: ~p~n", [?MODULE]), inets:start, {ok,Socket} = gen_tcp:connect("localhost", 4304, [binary, {packet, 0}]), Version = 0, Type = 10, Control_flags = 36, Size = 1024, Offset = 0, Raw = <<"/",0>>, Payload = size(Raw), ok = gen_tcp:send(Socket,<>), loop(Socket).

loop(Socket) -> receive {tcp,Socket,Bin} -> Dir = get_directory(Bin), io:format("In ~p: Dir is: ~p~n",[?MODULE,Dir]), ?MODULE:loop(Socket); {ow_dir,Raw,Pid} -> Version = 0, Type = 10, % get Control_flags = 36, Size = 1024, Offset = 0, Payload = size(Raw), ok = gen_tcp:send(Socket,<>), receive {tcp,Socket,Bin} -> Dir = get_directory(Bin), Pid ! {ow_dir,Dir} after 10000 -> ok end, ?MODULE:loop(Socket); {tcp_closed,_} -> ok; Any -> io:format("~p got unknown msg: ~p~n",[?MODULE, Any]), ?MODULE:loop(Socket) end.

%**************************************************************************************** % Function get_directory(Bin) %**************************************************************************************** get_directory(Bin) -> get_dir(Bin,<<>>). get_dir(<<>>,Dir) -> Dir; get_dir(<<0,0,0,0,0,0,0,0, Return_value:32/signed-integer,Rest/binary>>,_Dir) when Return_value /= 0 -> io:format("Return value is: ~p and Rest is: ~p~n",[Return_value,Rest]); get_dir(<<_Version:32,_Payload:32,0:32,_Control_flags:32,Size:32,_Offset:32,Raw:Size/binary,0,Rest/binary>>,Dir) -> get_dir(Rest,<>); get_dir(<<_Version:32,Payload:32,Return_value:32,_Control_flags:32,_Size:32,_Offset:32,Raw:Payload/binary>>,Dir) -> io:format("Return value is: ~p and Raw is: ~p~n",[Return_value,Raw]), <>. Compile the file with: erlc onewire.erl Start an Erlang console with erl and type onewire:start, and you should see a listing of your onewire network.

Another Erlang module can send this message to onewire: {ow_dir,<<"/",0>>,self} and get back a directory listing.

PIGPIO
Another nice socket server is the pigpio daemon http://abyz.co.uk/rpi/pigpio/. Easy to integrate in your Erlang application. More about this at https://github.com/skvamme/pigpio

-module(pigpio). -author(skvamme). -export([start/0,init/0,loop/1]). -define(PIGPIO_IP,"192.168.0.20"). -define(PIGPIO_PORT,8888). -define(UINT,32/little).

start -> Pid = spawn_link(?MODULE,init,[]), {ok,Pid}.

init -> io:format("New process: ~p~n", [?MODULE]), inets:start, {ok,Socket} = gen_tcp:connect(?PIGPIO_IP, ?PIGPIO_PORT, [binary,{packet, 0}]), ok = gen_tcp:send(Socket,c(setmode,25,0)), ok = gen_tcp:send(Socket,c(getmode,25)), %callback:make(self,33554432), % Start a callback to monitor pin 25 on a dedicated socket %timer:send_interval(10*1000, {timer}), loop(Socket).

loop(Socket) -> receive % {timer} -> ok = gen_tcp:send(Socket,c(br1)), %        ?MODULE:loop(Socket); {tcp,Socket,Data} -> parse(Socket,Data), ?MODULE:loop(Socket); {kwh,Sec} when Sec > 0 -> W = 3600000000 div Sec, io:format("W: ~p~n",[W]), ?MODULE:loop(Socket); Any -> io:format("~p got unknown msg: ~p~n",[?MODULE, Any]), ?MODULE:loop(Socket) end.

%****************************************************************************** % pigpio response %****************************************************************************** parse(_Socket,<<>>) -> ok; parse(Socket,<<0:?UINT,P1:?UINT,P2:?UINT,0:?UINT,Rest/binary>>) -> io:format("setmode pin ~p mode ~p~n",[P1,P2]), parse(Socket,Rest); parse(Socket,<<1:?UINT,P1:?UINT,_P2:?UINT,P3:?UINT,Rest/binary>>) -> io:format("getmode pin ~p mode ~p~n",[P1,P3]), parse(Socket,Rest); parse(Socket,<<2:?UINT,P1:?UINT,P2:?UINT,0:?UINT,Rest/binary>>) -> io:format("setpullupdown pin ~p pud ~p~n",[P1,P2]), parse(Socket,Rest); parse(Socket,<<3:?UINT,_P1:?UINT,_P2:?UINT,P3:?UINT,Rest/binary>>) -> io:format("read ~p~n",[P3]), parse(Socket,Rest); parse(Socket,<<4:?UINT,P1:?UINT,P2:?UINT,0:?UINT,Rest/binary>>) -> io:format("write pin ~p level ~p~n",[P1,P2]), parse(Socket,Rest); parse(Socket,<<10:?UINT,_P1:?UINT,_P2:?UINT,P3:?UINT,Rest/binary>>) -> io:format("readbits 0-31 ~.2B~n",[P3]), parse(Socket,Rest); parse(Socket,<<17:?UINT,_P1:?UINT,_P2:?UINT,P3:?UINT,Rest/binary>>) -> io:format("hver ~p~n",[P3]), parse(Socket,Rest); parse(_Socket,Response) -> io:format("Error: ~w~n",[Response]).

%****************************************************************************** % pigpio commands %****************************************************************************** %c(hver) -> <<17:?UINT,0:?UINT,0:?UINT,0:?UINT>>; %c(br1) -> <<10:?UINT,0:?UINT,0:?UINT,0:?UINT>>; c(read,Gpio) -> <<3:?UINT,Gpio:?UINT,0:?UINT,0:?UINT>>; c(getmode,Gpio) -> <<1:?UINT,Gpio:?UINT,0:?UINT,0:?UINT>>. c(setmode,Gpio,Mode) -> <<0:?UINT,Gpio:?UINT,Mode:?UINT,0:?UINT>>; % Input = 0, Output = 1 c(write,Gpio,Level) -> <<4:?UINT,Gpio:?UINT,Level:?UINT,0:?UINT>>; c(setpullupdown,Gpio,Pud) -> <<2:?UINT,Gpio:?UINT,Pud:?UINT,0:?UINT>>. % Off = 0, Down = 1, Up = 2