Initial commit
commit
c5cfbf4ecb
@ -0,0 +1,152 @@
|
|||||||
|
##############################################
|
||||||
|
|
||||||
|
package main;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use Time::HiRes qw(gettimeofday);
|
||||||
|
use IPC::Open2;
|
||||||
|
|
||||||
|
sub FHZ_Write($$$);
|
||||||
|
sub FHZ_Read($);
|
||||||
|
sub FHZ_Ready($);
|
||||||
|
|
||||||
|
sub
|
||||||
|
EB_Initialize($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
# Provider
|
||||||
|
$hash->{ReadyFn} = "EB_Ready";
|
||||||
|
$hash->{ReadFn} = "EB_Read";
|
||||||
|
$hash->{WriteFn} = "EB_Write";
|
||||||
|
$hash->{Clients} = ":EB:EBN:";
|
||||||
|
|
||||||
|
my %mc = (
|
||||||
|
"1:EBN" => "^R",
|
||||||
|
);
|
||||||
|
$hash->{MatchList} = \%mc;
|
||||||
|
|
||||||
|
# Normal devices
|
||||||
|
$hash->{DefFn} = "EB_Define";
|
||||||
|
$hash->{UndefFn} = "EB_Undef";
|
||||||
|
$hash->{SetFn} = "EB_Set";
|
||||||
|
$hash->{AttrList}= "loglevel:0,1,2,3,4,5,6";
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
EB_Define($$)
|
||||||
|
{
|
||||||
|
my ($hash, $def) = @_;
|
||||||
|
my @a = split("[ \t][ \t]*", $def);
|
||||||
|
|
||||||
|
Log 3, "$hash->{NAME} define ";
|
||||||
|
if(@a < 3 || @a > 3) {
|
||||||
|
my $msg = "wrong syntax: define <name> EB {devicename}";
|
||||||
|
Log 2, $msg;
|
||||||
|
Log 2, @a;
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
my $name = $a[0];
|
||||||
|
my $dev = $a[2];
|
||||||
|
|
||||||
|
$hash->{DeviceName} = $dev;
|
||||||
|
|
||||||
|
my($pid, $chld_out, $chld_in);
|
||||||
|
$pid = open2($chld_out, $chld_in, '/root/ebd -d '.$dev);
|
||||||
|
|
||||||
|
$hash->{RDPIPE} = $chld_out;
|
||||||
|
$hash->{WRPIPE} = $chld_in;
|
||||||
|
$hash->{FD} = fileno($chld_out);
|
||||||
|
$hash->{PID} = $pid;
|
||||||
|
$hash->{STATE} = "Open";
|
||||||
|
$selectlist{"$name.pipe"} = $hash;
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
sub
|
||||||
|
EB_Undef($$)
|
||||||
|
{
|
||||||
|
my ($hash, $arg) = @_;
|
||||||
|
|
||||||
|
Log 3, "$hash->{NAME} undef";
|
||||||
|
return if ($hash->{STATE} ne "Open");
|
||||||
|
|
||||||
|
EB_Write($hash,'Q','');
|
||||||
|
|
||||||
|
kill $hash->{PID};
|
||||||
|
close($hash->{RDPIPE});
|
||||||
|
close($hash->{WRPIPE});
|
||||||
|
|
||||||
|
delete $hash->{FD};
|
||||||
|
delete $hash->{RDPIPE};
|
||||||
|
delete $hash->{WRPIPE};
|
||||||
|
delete $hash->{PID};
|
||||||
|
|
||||||
|
$hash->{STATE} = "Closed";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
sub
|
||||||
|
EB_Ready($)
|
||||||
|
{
|
||||||
|
my ($hash,$fn,$msg) = @_;
|
||||||
|
Log 5, "$hash->{NAME} ready";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
sub
|
||||||
|
EB_Write($$$)
|
||||||
|
{
|
||||||
|
my ($hash,$fn,$msg) = @_;
|
||||||
|
my ($buf);
|
||||||
|
|
||||||
|
$buf = $fn . ' ' . $msg;
|
||||||
|
Log 5, "$hash->{NAME} write: " . $buf;
|
||||||
|
$buf .= "\n";
|
||||||
|
syswrite($hash->{WRPIPE}, $buf);
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
sub
|
||||||
|
EB_Read($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
my ($buf, $res, $nextbyte);
|
||||||
|
|
||||||
|
|
||||||
|
CHAR: while (sysread($hash->{RDPIPE}, $nextbyte, 1)) {
|
||||||
|
last CHAR if $nextbyte eq "\n";
|
||||||
|
$buf .= $nextbyte;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log 5, "$hash->{NAME} read: " . $buf;
|
||||||
|
$hash->{RAWMSG} = $buf;
|
||||||
|
Dispatch($hash, $buf, undef);
|
||||||
|
return $buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################
|
||||||
|
sub
|
||||||
|
EB_Set($@)
|
||||||
|
{
|
||||||
|
my ($hash, @a) = @_;
|
||||||
|
|
||||||
|
my $name = shift @a;
|
||||||
|
my $type = shift @a;
|
||||||
|
my $arg = join(" ", @a);
|
||||||
|
|
||||||
|
return "Unknown argument $type, choose one of raw" if($type ne "raw");
|
||||||
|
|
||||||
|
$arg .= "\n";
|
||||||
|
|
||||||
|
syswrite($hash->{WRPIPE}, $arg);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -0,0 +1,292 @@
|
|||||||
|
package main;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
sub
|
||||||
|
EBN_Initialize($)
|
||||||
|
{
|
||||||
|
my ($hash) = @_;
|
||||||
|
|
||||||
|
$hash->{Match} = "^R";
|
||||||
|
$hash->{DefFn} = "EBN_Define";
|
||||||
|
$hash->{ParseFn} = "EBN_Parse";
|
||||||
|
$hash->{GetFn} = "EBN_Get";
|
||||||
|
$hash->{SetFn} = "EBN_Set";
|
||||||
|
$hash->{AttrList} = "IODev loglevel:0,1,2,3,4,5,6";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#############################
|
||||||
|
sub
|
||||||
|
EBN_Define($$)
|
||||||
|
{
|
||||||
|
my ($hash, $def) = @_;
|
||||||
|
my @a = split("[ \t][ \t]*", $def);
|
||||||
|
|
||||||
|
my $u = "wrong syntax: define <name> EBN addr type";
|
||||||
|
|
||||||
|
return "wrong syntax: define <name> EBN addr type" if(int(@a) != 4);
|
||||||
|
|
||||||
|
$modules{EBN}{defptr}{$a[2]} = $hash;
|
||||||
|
$hash->{STATE} = "Defined";
|
||||||
|
$hash->{EBTYPE} = $a[3];
|
||||||
|
$hash->{ADDR} = $a[2];
|
||||||
|
$hash->{channel1} = "Init";
|
||||||
|
AssignIoPort($hash);
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
EBN_Parse($$)
|
||||||
|
{
|
||||||
|
my ($hash, $msg) = @_;
|
||||||
|
|
||||||
|
# Msg format: R addr chan value
|
||||||
|
my @cmp = split(" ", $msg);
|
||||||
|
my $def = $modules{EBN}{defptr}{$cmp[1]};
|
||||||
|
my $changed;
|
||||||
|
my $log;
|
||||||
|
my $v1;
|
||||||
|
my $v2;
|
||||||
|
my $v3;
|
||||||
|
my $v4;
|
||||||
|
|
||||||
|
Log 5, "$hash->{NAME} parse: " . $msg;
|
||||||
|
|
||||||
|
if($def) {
|
||||||
|
my $name = $def->{NAME};
|
||||||
|
if ($cmp[2] == 0) {
|
||||||
|
IOWrite($def, "R", $cmp[1]. " 1 0");
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
# TYPE 1: Test Node
|
||||||
|
# Channel 1:
|
||||||
|
# Bit 0 : Test LED
|
||||||
|
# Bit 1 : Exp LED
|
||||||
|
# Channel 2:
|
||||||
|
# Vcc
|
||||||
|
|
||||||
|
$changed = 0;
|
||||||
|
$v1 = $cmp[3] & 0x1;
|
||||||
|
$v2 = $cmp[3] & 0x2;
|
||||||
|
$v3 = $cmp[3] & 0x4;
|
||||||
|
$v4 = $cmp[3] & 0x8;
|
||||||
|
|
||||||
|
if ($def->{EBTYPE} == 1) {
|
||||||
|
if ($cmp[2] == 1) {
|
||||||
|
if (($v1 != ($def->{channel1} & 0x1)) || ($def->{channel1} eq "Init")) {
|
||||||
|
$def->{READINGS}{testled}{TIME} = TimeNow();
|
||||||
|
$def->{READINGS}{testled}{VAL} = $v1 ? "on" : "off";
|
||||||
|
$changed = 1;
|
||||||
|
}
|
||||||
|
if (($v2 != ($def->{channel1} & 0x2)) || ($def->{channel1} eq "Init")) {
|
||||||
|
$def->{READINGS}{expled}{TIME} = TimeNow();
|
||||||
|
$def->{READINGS}{expled}{VAL} = $v2 ? "on" : "off";
|
||||||
|
$changed = 1;
|
||||||
|
}
|
||||||
|
if ($def->{channel1} eq "Init") {
|
||||||
|
IOWrite($def, "R", $cmp[1]. " 2 0");
|
||||||
|
}
|
||||||
|
$def->{channel1} = $cmp[3];
|
||||||
|
if ($changed == 1) {
|
||||||
|
$log = "testled: " . ($v1 ? "on" : "off") . " expled: " . ($v2 ? "on" : "off");
|
||||||
|
$def->{STATE} = $log;
|
||||||
|
push @{$def->{CHANGED}}, $log;
|
||||||
|
DoTrigger($def->{NAME}, undef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($cmp[2] == 2) {
|
||||||
|
$def->{READINGS}{vcc}{TIME} = TimeNow();
|
||||||
|
$def->{READINGS}{vcc}{VAL} = $cmp[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# TYPE 2: Alarm Node
|
||||||
|
# Channel 1:
|
||||||
|
# Bit 0 : Tuerkontakt
|
||||||
|
# Bit 1 : Buzzer aus
|
||||||
|
# Channel 2:
|
||||||
|
# Vcc
|
||||||
|
|
||||||
|
if ($def->{EBTYPE} == 2) {
|
||||||
|
if ($cmp[2] == 1) {
|
||||||
|
if (($v1 != ($def->{channel1} & 0x1)) || ($def->{channel1} eq "Init")) {
|
||||||
|
$def->{READINGS}{tuer}{TIME} = TimeNow();
|
||||||
|
$def->{READINGS}{tuer}{VAL} = $v1 ? "closed" : "open";
|
||||||
|
$changed = 1;
|
||||||
|
}
|
||||||
|
if (($v2 != ($def->{channel1} & 0x2)) || ($def->{channel1} eq "Init")) {
|
||||||
|
$def->{READINGS}{buzzer}{TIME} = TimeNow();
|
||||||
|
$def->{READINGS}{buzzer}{VAL} = $v2 ? "disabled" : "enabled";
|
||||||
|
$changed = 1;
|
||||||
|
}
|
||||||
|
if ($def->{channel1} eq "Init") {
|
||||||
|
IOWrite($def, "R", $cmp[1]. " 2 0");
|
||||||
|
}
|
||||||
|
$def->{channel1} = $cmp[3];
|
||||||
|
if ($changed == 1) {
|
||||||
|
$log = "tuer: ". ($v1 ? "closed" : "open") . " buzzer: " . ($v2 ? "disabled" : "enabled");
|
||||||
|
$def->{STATE} = $log;
|
||||||
|
push @{$def->{CHANGED}}, $log;
|
||||||
|
DoTrigger($def->{NAME}, undef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($cmp[2] == 2) {
|
||||||
|
$def->{READINGS}{vcc}{TIME} = TimeNow();
|
||||||
|
$def->{READINGS}{vcc}{VAL} = $cmp[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# TYPE 3: Relais Node
|
||||||
|
# Channel 1:
|
||||||
|
# Bit 0 : LED
|
||||||
|
# Bit 1 : Relais 1
|
||||||
|
# Bit 2 : Relais 2
|
||||||
|
|
||||||
|
if ($def->{EBTYPE} == 3) {
|
||||||
|
if ($cmp[2] == 1) {
|
||||||
|
if (($v1 != ($def->{channel1} & 0x1)) || ($def->{channel1} eq "Init")) {
|
||||||
|
$def->{READINGS}{led}{TIME} = TimeNow();
|
||||||
|
$def->{READINGS}{led}{VAL} = $v1 ? "on": "off";
|
||||||
|
$changed = 1;
|
||||||
|
}
|
||||||
|
if (($v2 != ($def->{channel1} & 0x2)) || ($def->{channel1} eq "Init")) {
|
||||||
|
$def->{READINGS}{relais1}{TIME} = TimeNow();
|
||||||
|
$def->{READINGS}{relais1}{VAL} = $v2 ? "on": "off";
|
||||||
|
$changed = 1;
|
||||||
|
}
|
||||||
|
if (($v3 != ($def->{channel1} & 0x4)) || ($def->{channel1} eq "Init")) {
|
||||||
|
$def->{READINGS}{relais2}{TIME} = TimeNow();
|
||||||
|
$def->{READINGS}{relais2}{VAL} = $v3 ? "on": "off";
|
||||||
|
$changed = 1;
|
||||||
|
}
|
||||||
|
$def->{channel1} = $cmp[3];
|
||||||
|
if ($changed == 1) {
|
||||||
|
$log = "led: " . ($v1 ? "on": "off") . " relais1: " . ($v2 ? "on": "off") . " relais2: " . ($v3? "on": "off");
|
||||||
|
$def->{STATE} = $log;
|
||||||
|
push @{$def->{CHANGED}}, $log;
|
||||||
|
DoTrigger($def->{NAME}, undef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
} else {
|
||||||
|
Log 3, "EBN Unknown device $cmp[1], please define it";
|
||||||
|
if ($cmp[2] == 0) {
|
||||||
|
return "UNDEFINED EBN_$cmp[1] EBN $cmp[1] $cmp[3]";
|
||||||
|
} else {
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
EBN_Get($@)
|
||||||
|
{
|
||||||
|
my ($hash, @args) = @_;
|
||||||
|
|
||||||
|
return 'EBN_Get needs two arguments' if (@args != 2);
|
||||||
|
|
||||||
|
my $get = $args[1];
|
||||||
|
my $val;
|
||||||
|
|
||||||
|
if ( $get eq "?"){
|
||||||
|
if ($hash->{EBTYPE} == 1) {
|
||||||
|
return "Unknown argument ?, choose one of testled expled";
|
||||||
|
}
|
||||||
|
if ($hash->{EBTYPE} == 2) {
|
||||||
|
return "Unknown argument ?, choose one of tuer buzzer";
|
||||||
|
}
|
||||||
|
if ($hash->{EBTYPE} == 3) {
|
||||||
|
return "Unknown argument ?, choose one of led relais1 relais2";
|
||||||
|
}
|
||||||
|
return "EBN_Get: no such reading: $get";
|
||||||
|
}
|
||||||
|
if (defined($hash->{READINGS}{$get})) {
|
||||||
|
$val = $hash->{READINGS}{$get}{VAL};
|
||||||
|
} else {
|
||||||
|
return "EBN_Get: no such reading: $get";
|
||||||
|
}
|
||||||
|
Log 3, "$args[0] $get => $val";
|
||||||
|
|
||||||
|
return $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub
|
||||||
|
EBN_Set($@)
|
||||||
|
{
|
||||||
|
my ($hash, @args) = @_;
|
||||||
|
|
||||||
|
my $set = $args[1];
|
||||||
|
my $num;
|
||||||
|
my $msg;
|
||||||
|
|
||||||
|
if ( $set eq "?"){
|
||||||
|
if ($hash->{EBTYPE} == 1) {
|
||||||
|
return "Unknown argument ?, choose one of testled expled";
|
||||||
|
}
|
||||||
|
if ($hash->{EBTYPE} == 2) {
|
||||||
|
return "Unknown argument ?, choose one of buzzer";
|
||||||
|
}
|
||||||
|
if ($hash->{EBTYPE} == 3) {
|
||||||
|
return "Unknown argument ?, choose one of led relais1 relais2";
|
||||||
|
}
|
||||||
|
return "EBN_Get: no such reading: $set";
|
||||||
|
}
|
||||||
|
return 'EBN_Set needs three arguments' if (@args != 3);
|
||||||
|
my $val = $args[2];
|
||||||
|
|
||||||
|
Log 3, "$args[0] $set => $val";
|
||||||
|
|
||||||
|
if ($hash->{EBTYPE} == 1) {
|
||||||
|
if ($val eq "on") {
|
||||||
|
$num = 1;
|
||||||
|
} else {
|
||||||
|
$num = 0;
|
||||||
|
}
|
||||||
|
if ($set eq "testled") {
|
||||||
|
$msg = ($hash->{channel1} & 0x3FE) | $num;
|
||||||
|
}
|
||||||
|
if ($set eq "expled") {
|
||||||
|
$msg = ($hash->{channel1} & 0x3FD) | ($num << 1);
|
||||||
|
}
|
||||||
|
IOWrite($hash, "W", $hash->{ADDR} . " 1 " . $msg);
|
||||||
|
}
|
||||||
|
if ($hash->{EBTYPE} == 2) {
|
||||||
|
if ($val eq "disabled") {
|
||||||
|
$num = 1;
|
||||||
|
} else {
|
||||||
|
$num = 0;
|
||||||
|
}
|
||||||
|
if ($set eq "buzzer") {
|
||||||
|
$msg = ($hash->{channel1} & 0x3FD) | ($num << 1);
|
||||||
|
}
|
||||||
|
IOWrite($hash, "W", $hash->{ADDR} . " 1 " . $msg);
|
||||||
|
}
|
||||||
|
if (($hash->{EBTYPE} == 3) || ($hash->{EBTYPE} == 4)) {
|
||||||
|
if ($val eq "on") {
|
||||||
|
$num = 1;
|
||||||
|
} else {
|
||||||
|
$num = 0;
|
||||||
|
}
|
||||||
|
if ($set eq "led") {
|
||||||
|
$msg = ($hash->{channel1} & 0x3FE) | $num;
|
||||||
|
}
|
||||||
|
if ($set eq "relais1") {
|
||||||
|
$msg = ($hash->{channel1} & 0x3FD) | ($num << 1);
|
||||||
|
}
|
||||||
|
if ($set eq "relais2") {
|
||||||
|
$msg = ($hash->{channel1} & 0x3FB) | ($num << 2);
|
||||||
|
}
|
||||||
|
IOWrite($hash, "W", $hash->{ADDR} . " 1 " . $msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
SUBDIRS = src
|
@ -0,0 +1,15 @@
|
|||||||
|
AC_INIT([src/rs485.c])
|
||||||
|
AC_PREREQ([2.63])
|
||||||
|
AM_INIT_AUTOMAKE(ebd, 0.0.1)
|
||||||
|
|
||||||
|
AC_CONFIG_HEADERS([config.h])
|
||||||
|
|
||||||
|
AC_PROG_CC
|
||||||
|
AC_PROG_INSTALL
|
||||||
|
|
||||||
|
AC_CONFIG_FILES([
|
||||||
|
Makefile
|
||||||
|
src/Makefile
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_OUTPUT
|
@ -0,0 +1,6 @@
|
|||||||
|
bin_PROGRAMS = ebd
|
||||||
|
|
||||||
|
AM_CFLAGS = $(DEPS_CFLAGS)
|
||||||
|
|
||||||
|
ebd_SOURCES = main.c rs485.c
|
||||||
|
ebd_LDADD = $(DEPS_LIBS)
|
@ -0,0 +1,245 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
void send_ack(int addr) {
|
||||||
|
unsigned char buf[16];
|
||||||
|
|
||||||
|
buf[0] = 0xAA;
|
||||||
|
buf[1] = 2;
|
||||||
|
buf[2] = addr;
|
||||||
|
buf[3] = addr;
|
||||||
|
buf[4] = 1;
|
||||||
|
buf[5] = 1;
|
||||||
|
buf[6] = 0;
|
||||||
|
buf[7] = 0;
|
||||||
|
buf[8] = 0;
|
||||||
|
buf[9] = 0;
|
||||||
|
buf[10] = 0;
|
||||||
|
buf[11] = 0;
|
||||||
|
buf[12] = 0;
|
||||||
|
buf[13] = 0;
|
||||||
|
buf[14] = 0;
|
||||||
|
|
||||||
|
rs485_crc(buf);
|
||||||
|
rs485_send(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_read_req(int addr, int channel) {
|
||||||
|
unsigned char buf[16];
|
||||||
|
|
||||||
|
buf[0] = 0xAA;
|
||||||
|
buf[1] = 0;
|
||||||
|
buf[2] = addr;
|
||||||
|
buf[3] = addr;
|
||||||
|
buf[4] = 1;
|
||||||
|
buf[5] = 1;
|
||||||
|
buf[6] = 0x40 + channel;
|
||||||
|
buf[7] = 0;
|
||||||
|
buf[8] = 0;
|
||||||
|
buf[9] = 0;
|
||||||
|
buf[10] = 0;
|
||||||
|
buf[11] = 0;
|
||||||
|
buf[12] = 0;
|
||||||
|
buf[13] = 0;
|
||||||
|
buf[14] = 0;
|
||||||
|
|
||||||
|
rs485_crc(buf);
|
||||||
|
rs485_send(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_write_req(int addr, int channel, int data) {
|
||||||
|
unsigned char buf[16];
|
||||||
|
|
||||||
|
buf[0] = 0xAA;
|
||||||
|
buf[1] = 0;
|
||||||
|
buf[2] = addr;
|
||||||
|
buf[3] = addr;
|
||||||
|
buf[4] = 1;
|
||||||
|
buf[5] = 1;
|
||||||
|
buf[6] = 0x60 + channel + ((data & 0x300 ) >> 5);
|
||||||
|
buf[7] = data & 0xFF;
|
||||||
|
buf[8] = 0;
|
||||||
|
buf[9] = 0;
|
||||||
|
buf[10] = 0;
|
||||||
|
buf[11] = 0;
|
||||||
|
buf[12] = 0;
|
||||||
|
buf[13] = 0;
|
||||||
|
buf[14] = 0;
|
||||||
|
|
||||||
|
rs485_crc(buf);
|
||||||
|
rs485_send(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void send_poll(void) {
|
||||||
|
unsigned char buf[16];
|
||||||
|
|
||||||
|
buf[0] = 0xAA;
|
||||||
|
buf[1] = 0;
|
||||||
|
buf[2] = 0;
|
||||||
|
buf[3] = 0;
|
||||||
|
buf[4] = 1;
|
||||||
|
buf[5] = 1;
|
||||||
|
buf[6] = 0;
|
||||||
|
buf[7] = 0;
|
||||||
|
buf[8] = 0;
|
||||||
|
buf[9] = 0;
|
||||||
|
buf[10] = 0;
|
||||||
|
buf[11] = 0;
|
||||||
|
buf[12] = 0;
|
||||||
|
buf[13] = 0;
|
||||||
|
buf[14] = 0;
|
||||||
|
|
||||||
|
rs485_crc(buf);
|
||||||
|
rs485_send(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mainloop(int fd) {
|
||||||
|
struct timeval timeout;
|
||||||
|
int rd;
|
||||||
|
fd_set rset;
|
||||||
|
fd_set wset;
|
||||||
|
fd_set eset;
|
||||||
|
int quit;
|
||||||
|
int alt;
|
||||||
|
int ret;
|
||||||
|
int req_dev;
|
||||||
|
int req_channel;
|
||||||
|
int req_data;
|
||||||
|
int req_write;
|
||||||
|
int ack_dev;
|
||||||
|
int dev_poll;
|
||||||
|
unsigned char rbuf[16];
|
||||||
|
|
||||||
|
alt = 0;
|
||||||
|
quit = 0;
|
||||||
|
req_dev = 0;
|
||||||
|
req_channel = 0;
|
||||||
|
ack_dev = 0;
|
||||||
|
dev_poll = 2;
|
||||||
|
|
||||||
|
while (!quit) {
|
||||||
|
FD_ZERO(&rset);
|
||||||
|
FD_ZERO(&wset);
|
||||||
|
FD_ZERO(&eset);
|
||||||
|
FD_SET(fd, &rset);
|
||||||
|
FD_SET(0, &rset);
|
||||||
|
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = 40000;
|
||||||
|
|
||||||
|
rd = select(fd + 1, &rset, &wset, &eset, &timeout);
|
||||||
|
|
||||||
|
if (rd == 0) {
|
||||||
|
if (alt) {
|
||||||
|
send_poll();
|
||||||
|
alt = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
alt = 1;
|
||||||
|
|
||||||
|
if (ack_dev) {
|
||||||
|
send_ack(ack_dev);
|
||||||
|
ack_dev = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_dev) {
|
||||||
|
if (req_write)
|
||||||
|
send_write_req(req_dev, req_channel, req_data);
|
||||||
|
else
|
||||||
|
send_read_req(req_dev, req_channel);
|
||||||
|
req_dev = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_read_req(dev_poll, 0);
|
||||||
|
dev_poll++;
|
||||||
|
if (dev_poll > 126)
|
||||||
|
dev_poll = 2;
|
||||||
|
} else {
|
||||||
|
if (FD_ISSET(fd, &rset)) {
|
||||||
|
ret = rs485_recv_loop();
|
||||||
|
if (ret < 0)
|
||||||
|
exit(-1);
|
||||||
|
if (ret == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
rs485_recv(&rbuf);
|
||||||
|
|
||||||
|
if ((rbuf[1] == 1) && (rbuf[2] == 1))
|
||||||
|
ack_dev = rbuf[4];
|
||||||
|
|
||||||
|
if ((rbuf[2] == 1) && (rbuf[6] >= 0x40) && (rbuf[6] < 0x60)) {
|
||||||
|
printf ("R %d %d %d\n", rbuf[4], (rbuf[6] - 0x40) & 0x7, rbuf[7] + (((rbuf[6] - 0x40) & 0x18) << 5));
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (FD_ISSET(0, &rset)) {
|
||||||
|
unsigned char a;
|
||||||
|
unsigned char ibuf[100];
|
||||||
|
int d, c, v;
|
||||||
|
unsigned char *p;
|
||||||
|
|
||||||
|
p = fgets(ibuf, 100, stdin);
|
||||||
|
if (p == NULL)
|
||||||
|
exit(-1);
|
||||||
|
|
||||||
|
sscanf(ibuf, "%c %d %d %d\n", &a, &d, &c, &v);
|
||||||
|
if (a == 'Q') {
|
||||||
|
quit = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (a == 'R') {
|
||||||
|
req_dev = d;
|
||||||
|
req_channel = c;
|
||||||
|
req_write = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (a == 'W') {
|
||||||
|
req_dev = d;
|
||||||
|
req_channel = c;
|
||||||
|
req_data = v;
|
||||||
|
req_write = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
|
||||||
|
char *dev = "/dev/ttyS2";
|
||||||
|
int c;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
while ((c = getopt(argc, argv, "d:")) != -1)
|
||||||
|
switch (c) {
|
||||||
|
case 'd':
|
||||||
|
dev = optarg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = rs485_open(dev);
|
||||||
|
if (fd < 0) {
|
||||||
|
printf("RS485 open failed\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainloop(fd);
|
||||||
|
|
||||||
|
rs485_close();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,143 @@
|
|||||||
|
#include <linux/serial.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
/* Driver-specific ioctls: */
|
||||||
|
#define TIOCGRS485 0x542E
|
||||||
|
#define TIOCSRS485 0x542F
|
||||||
|
|
||||||
|
struct serial_rs485 rs485conf;
|
||||||
|
struct termios options;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
unsigned char inbuf[16];
|
||||||
|
int bufp;
|
||||||
|
|
||||||
|
static uint16_t crc_ccitt_update(uint16_t crc, uint8_t data)
|
||||||
|
{
|
||||||
|
data ^= (crc & 0xFF);
|
||||||
|
data ^= data << 4;
|
||||||
|
|
||||||
|
return ((((uint16_t)data << 8) | ((crc >> 8) & 0xFF))
|
||||||
|
^ (uint8_t)(data >> 4) ^ ((uint16_t)data << 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
int rs485_crc(unsigned char buf[16])
|
||||||
|
{
|
||||||
|
uint16_t crc;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (buf[0] != 0xAA)
|
||||||
|
return -1;
|
||||||
|
if (buf[2] != buf[3])
|
||||||
|
return -1;
|
||||||
|
if (buf[4] != buf[5])
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
crc = 0xFFFF;
|
||||||
|
for (i=0; i<14; i++)
|
||||||
|
crc = crc_ccitt_update(crc, buf[i]);
|
||||||
|
buf[14] = (crc >> 8) & 0xFF;
|
||||||
|
buf[15] = crc & 0xFF;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rs485_send(unsigned char buf[16])
|
||||||
|
{
|
||||||
|
return write(fd, buf, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rs485_recv(unsigned char buf[16])
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (bufp < 16)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i=0;i<16;i++)
|
||||||
|
buf[i] = inbuf[i];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rs485_recv_loop(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char *p;
|
||||||
|
uint16_t crc;
|
||||||
|
|
||||||
|
if (bufp >=16)
|
||||||
|
bufp=0;
|
||||||
|
|
||||||
|
p = &(inbuf[bufp]);
|
||||||
|
|
||||||
|
if (read(fd, p, 1) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
bufp++;
|
||||||
|
|
||||||
|
if ((bufp == 1) && (inbuf[0] != 0xAA))
|
||||||
|
bufp = 0;
|
||||||
|
|
||||||
|
if ((bufp == 4) && (inbuf[2] != inbuf[3]))
|
||||||
|
bufp = 0;
|
||||||
|
|
||||||
|
if ((bufp == 6) && (inbuf[4] != inbuf[5]))
|
||||||
|
bufp = 0;
|
||||||
|
|
||||||
|
if (bufp == 16) {
|
||||||
|
crc = 0xFFFF;
|
||||||
|
|
||||||
|
for (i=0; i<14; i++)
|
||||||
|
crc = crc_ccitt_update(crc, inbuf[i]);
|
||||||
|
if ((inbuf[14] == ((crc >> 8) & 0xFF)) && (inbuf[15] == (crc & 0xFF)))
|
||||||
|
return 1;
|
||||||
|
else
|
||||||
|
bufp = 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rs485_close(void)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rs485_open(char *path)
|
||||||
|
{
|
||||||
|
fd = open (path, O_RDWR);
|
||||||
|
if (fd < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs485conf.flags |= SER_RS485_ENABLED;
|
||||||
|
rs485conf.flags |= (SER_RS485_RTS_ON_SEND);
|
||||||
|
rs485conf.flags |= (SER_RS485_RTS_AFTER_SEND);
|
||||||
|
//rs485conf.flags |= SER_RS485_RX_DURING_TX;
|
||||||
|
|
||||||
|
ioctl (fd, TIOCSRS485, &rs485conf);
|
||||||
|
|
||||||
|
bzero(&options, sizeof(options));
|
||||||
|
cfsetspeed(&options, B9600);
|
||||||
|
options.c_cflag &= ~PARENB;
|
||||||
|
options.c_cflag &= ~CSTOPB;
|
||||||
|
options.c_cflag &= ~CSIZE;
|
||||||
|
options.c_cflag |= CS8;
|
||||||
|
options.c_cflag |= (CLOCAL | CREAD);
|
||||||
|
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
||||||
|
options.c_oflag &= ~OPOST;
|
||||||
|
options.c_cc[VMIN] = 0;
|
||||||
|
options.c_cc[VTIME] = 1;
|
||||||
|
tcflush(fd,TCIOFLUSH);
|
||||||
|
tcsetattr(fd, TCSAFLUSH, &options);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
int rs485_crc(unsigned char buf[16]);
|
||||||
|
int rs485_send(unsigned char buf[16]);
|
||||||
|
int rs485_recv(unsigned char buf[16]);
|
||||||
|
int rs485_recv_loop(void);
|
||||||
|
int rs485_close(void);
|
||||||
|
int rs485_open(char *path);
|
Loading…
Reference in New Issue