Receiving sensor data with ZeroMQ

Mobotix cameraTo see what has to be done to get ZeroMQ working in my Domotica system, I decided to do a small test. For this test I chose a sensor that’s inside one of my camera’s, the Mobotix camera you see in the picture to the left. This Mobotix has a very good illumination sensor which I use for detecting daylight. The Mobotix can be configured to send (among a lot of other things) the sensor value at a regular interval to a list of IP addresses. My camera is configured to send it’s IP address, a PIR value and the illumination value each minute, separated by a semicolon. This results in the following data, with a 1 minute interval between each line:

192.168.120.11;0;25;
192.168.120.11;0;30;
192.168.120.11;0;35;
192.168.120.11;0;46;
192.168.120.11;0;52;
192.168.120.11;0;63;
192.168.120.11;0;68;

Let’s see if I can get this into my system by sending it to a ZeroMQ server that’s embedded in my system; so for that I need 2 things:

  • A 0MQ client, written in whatever language I want (Delphi XE2 this time);
  • A 0MQ server in my Domotica system, written in Delphi 2005.

I started with the client, following the examples found in the ZeroMQ Guide:

program MobotixNotifyPlugin;
{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils,
  IdTCPServer,
  IdIOHandler,
  IdContext,
  zmq in 'zmq.pas',
  zhelper in 'zhelper.pas';

type
  // dummy class for holding the event
  TProcObject = Class(TObject)
    procedure ServerExecute(AContext: TIdContext);
  end;

var
  PO       : TProcObject;
  Context  : Pointer;
  Requestor: Pointer;
  Msg      : string;
  Server   : TIDTCPServer;

function Version:string;
var
  major, minor, patch:Integer;
begin
  zmq_version(major, minor, patch);
  Result := Format('%d.%d.%d', [major, minor, patch]);
end;

procedure TProcObject.ServerExecute(AContext: TIdContext);
var
  IO   : TIdIOHandler;
  data : string;
  reply: String;
begin
  IO := AContext.Connection.IOHandler;
  //IO.CheckForDataOnSource(10);
  if not IO.InputBufferIsEmpty then
  begin
    data := IO.InputBuffer.ExtractToString();
    Writeln(TimeToStr(Now)+' '+data);
    s_sendmore(Requestor, 'MOBOTIX');
    s_send(Requestor, data);

    reply := s_recv(Requestor);
    Writeln(TimeToStr(Now)+' '+reply);
  end;
end;

begin
  PO := TProcObject.Create;
  Writeln('ZeroMQ library version: '+Version);
  Context := zmq_init(1);
  Requestor := zmq_socket(Context, ZMQ_REQ);
  zmq_connect(Requestor, 'tcp://192.168.10.40:5563');

  Server := TidTCPServer.Create;
  Server.DefaultPort := 808;
  Server.MaxConnections := 1;
  Server.OnExecute := PO.ServerExecute;
  Server.Active := True;

  try
    while True do
    begin
      sleep(1);
    end;
  except
    on E: Exception do
    begin
      Writeln(E.ClassName, ': ', E.Message);
      zmq_close(Requestor);
      zmq_term(Context);
      FreeAndNil(PO);
    end;
  end;

end.

Receiving the sensor data from the Mobotix is handled by the Indy TIDTCPServer class and all it does is forwarding the incoming data to my system the 0MQ way, preceded by the Device ID (‘MOBOTIX’) the Mobotix camera has in my Domotica system. That should do it, in a very very simplified way; on to the server part!

procedure TZMQServerThread.Execute;
var
  DeviceID, Data:string;
begin
  while not Terminated do
  begin
    // read Device ID and sensordata
    DeviceID := s_recv(Responder);
    Log('RX '+DeviceID);
    Data := s_recv(Responder);
    Log('RX '+Data);

    // transfer sensor data to Device object
    gInterfaceDataHandler.DistributeToDevice(DeviceID, Data);

    // send reply to 0MQ client
    s_send(Responder, 'OK');

  end;
end;

The code above only shows the code for the Execute() method of a class derived from a Delphi TThread. All this method does is receiving information from the 0MQ socket and ‘injecting’ it into a Interface data queue, which handles the data delivery to the device object. From there, events are triggered, data is made historic, etc. etc. Again, the method code above is very very simple and not good enough for use in a live system, but good enough for testing.

OK, now it’s time to see if this ZeroMQ Plugin (hint, hint) works. I first started up the Plugin and after it had received the first data sample from the Mobotix, I started up a development instance of my Domotica system. And this is the output of both the Plugin and my system:

Plugin:
19:29:02 192.168.120.11;0;629;;
19:29:02 OK
19:30:01 192.168.120.11;0;633;;
19:30:01 OK
19:31:01 192.168.120.11;0;640;;
19:31:01 OK
Domotica system:
19:29:02.419 ZEROMQ : RX MOBOTIX
19:29:02.419 ZEROMQ : RX 192.168.120.11;0;629;;
19:30:01.835 ZEROMQ : RX MOBOTIX
19:30:01.835 ZEROMQ : RX 192.168.120.11;0;633;;
19:31:01.561 ZEROMQ : RX MOBOTIX
19:31:01.561 ZEROMQ : RX 192.168.120.11;0;640;;

Yeeha, it works, I’m receiving data in my system and everything keeps on working!

You might wonder why I’m so thrilled about being able to do something that looks a lot like what I’ve been doing from the beginning (i.e. getting data into my system); well, it is what it looks like, but it’s more!

ZeroMQ enables me to do a lot of things:

  • I can start using any programming language for which there’s a ZeroMQ binding;
  • This programming language independence also gives me platform independence (try running a Delphi-win32 executable on a Linux based NAS..)
  • I can break up my system in lots of small parts, starting with removing all the hardware I/O related code from the ‘core’, resulting in less chance of the whole system collapsing;
  • And with all this, create a multi-language, multi-platform distributed home automation system!

Although this all sounds nice, I’m still far away from implementing all this; this new architecture comes with new questions and demands (and new trade-offs?).

For example, I’ll need to do some sort of authentication for these loosely coupled Plugins, because I don’t want just anything that can ‘do’ ZeroMQ to start talking to my system. And I will also need some sort of heartbeat messaging, so that the ‘core’ knows if all the Plugins are still online and performing like they should. And the Plugins will need to know where to find the ‘core’; and the core should be able to start & stop Plugins, and …  sorry, this post ends here – I’ve got work to do!

 

Tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *