Integracja Satel ETHM-1 z Raspberry Pi

Dwa i pół roku temu, gdy wybierałem sposób integracji systemu “inteligentnego domu” z centralą alarmową Satel Integra, zdecydowałem się na użycie modułu INT-RS . Stworzyłem wtedy skrypty w Node.js do integracji systemu automatyki domowej z centralą alarmową . Wybrałem komunikację przez port szeregowy a nie moduł IP, między innymi ze względu na koszty. Teraz, przy okazji awarii konwertera transmisji szeregowej, zdecydowałem się na zmianę medium na Ethernet, bo jest to wygodniejsze przy kablowaniu, a dodatkowo same moduły ETHM-1 można kupić używane w rozsądniejszych cenach. Nie bez znaczenia jest też fakt, że dzięki temu będę mógł też programować centralę zdalnie, bez przenoszenia laptopa i kombinowania z połączeniem szeregowym oraz używać aplikacji Satela do zdalnego sterowania centralą z telefonu komórkowego. Poza tym zawsze to coś nowego do wypróbowania.

Instalacja

Instalacja modułu ETHM-1 nie różni się znacząco od znanej z INT-RS. Przewody łączymy do tej samem magistrali manipulatorów. Mamy więc zasilanie, dwa przewody do komunikacji i ewentualnie przewód RS232, jeżeli chcemy programować centralę przez sieć. Tu od razu mała uwaga - potrzebny jest właściwy przewód, który Satel oznacza czerwoną etykietą. Zielony RJ/PIN5-LCD nie będzie działał, bo żyły są inaczej połączone. Poza tym pozostaje podłączenie przewodu Ethernet do switcha naszej sieci domowej.

Należy też w konfiguracji centrali, np. przez program DloadX, przypisać adres IP oraz włączyć opcję integracji i wybrać port (domyślnie 7094).

Integracja

Po sprawdzeniu, czy moduł odpowiada na pingi, można przejść do integracji. Pewną niespodzianką może być, jak niewiele zmian potrzebne jest, aby przystosować skrypty z wersji działającej z portem szeregowy m na tryb sieciowy. Całą klasę SatFrame pozostawiamy bez zmian, bo protokół jest taki sam. Nieznacznie zmieniamy tylko główny moduł, który ma otwierać połączenie TCP zamiast portu szeregowego.

var SatFrameClass = require("./satframe");
var dgram = require('dgram');
var http = require('http');
var child_process = require('child_process');
var dgram = require('dgram');

var net = require('net');

var bufsize=1024;
var buf=new Buffer(bufsize);
var bufpos=0;

var outputs=new Array;
var outputschange=new Array;

var armed=false;

//połączenie do modułu ETHM-1 - adres IP i port
var client = new net.Socket();
client.connect(7094, '192.168.1.17', function() {
	console.log('Connected');
});

client.on('data', function(data) {
	var receivedData = "";

	if(bufpos+data.length>bufsize) bufpos=0;
	for(w=0; w<data.length; w++ )
	{
		buf[bufpos]=data[w];
		bufpos++;
	}
	oldbufpos=bufpos;
	while( (bufpos=parseSatel(buf, bufpos))!=oldbufpos ) oldbufpos=bufpos;
});

client.on('close', function() {
	console.log('Connection closed');
	exit(1); 
});

client.on("error", function() { console.log("error"); exit(1); });

// tworzymy zapytanie o stan wyjść
var sf=new SatFrameClass.SatFrame();
sf.addParam(0x17);

//inicjujemy tablice stanów i zmian wyjść
//numery wejść w cenrali są numerowane od 1 do 128 (w przypadku Integry 128)
for( w=1; w<=128; w++ )
{
  outputs[w]=0;
  outputschange[w]=0;
}

// cykliczne zapytania o stan wyjść
setInterval( function()
{
  var fr=sf.getFrameAsBuffer();
  client.write(fr);
}, 1000 );

// szuka pełnej ramki i dba, żeby bufor się nie przepełnił
function parseSatel(b, size)
{
  var begin=-1;
  var end=-1;
  var frame=new Array;

  for( w=0; w<size && end==-1; w++ )
  {
    if( b[w]==0xfe && b[w+1]==0xfe ) begin=w;
    if( b[w]==0xfe && b[w+1]==0x0d ) end=w+1;
  }

  if( end!=-1 ) // jest pelna ramka
  {

    //przepisujemy gotowa ramke
    q=0;
    for(w=begin; w<=end; w++)
    {
      frame[q]=b[w];
      q++;
    }

    //przepisujemy reszte na poczatek bufora
    q=0;
    for(w=end+1; w<size; w++)
    {
      b[q]=b[w];
      q++;
    }

    runFrame(frame);
    return(q);

  }

  return(size);

}

// uruchamia akcje zwiazana z ramka, gdy to potrzebne
function runFrame(f)
{

  var tm = new Date();

  var test=new SatFrameClass.SatFrame();
  test.setFrame(f);
  if( !test.checkCrc() )
  {
    console.log("Niepoprawny CRC dla ramki: "+f);
    return;
  }

  if( f[2]==0x17 ) //wyjscie
  {
    for( w=0; w<128; w++ )
    {
      out=f[3+Math.floor(w/8)]&(Math.pow(2,(w%8)));
      if( out>0 ) out=1;
      outputschange[w+1]=0;
      if( out!=outputs[w+1] ) {
        outputschange[w+1]=1;
        console.log(tm+" zmiana "+(w+1)+"="+out);
        sendMain("s:"+out+":"+(w+1));
      }
      outputs[w+1]=out;
    }

  }
}

//wysłanie do modułu głównego przez UDP
function sendMain(code)
{
  var message = new Buffer(code);
  var client = dgram.createSocket("udp4");
  client.send(message, 0, message.length, 8001, "127.0.0.1", function(err) {
    client.close();
  });
}

Cała zmiana od strony programistycznej zajęła może 15 minut z testowaniem. Oczywiście dla ETHM-1, gdzie znamy początek i koniec ramek, w przeciwieństwie do transmisji szeregowej, kod można nieco uprościć, ale nie ma takiej konieczności. Dzięki temu jest bardziej uniwersalny.

To czego nie zrobiliśmy, a co jest możliwe, to kodowanie transmisji wybranym kluczem dla podniesienia poziomu bezpieczeństwa. Oczywiście wszystko zależy od potrzeb i sieci, do której podłączona jest centrala.