From db32d2a7e5fde378b670ae20961dae2e022a1f35 Mon Sep 17 00:00:00 2001 From: ntruchsess <> Date: Mon, 24 Mar 2014 09:31:45 +0000 Subject: [PATCH] I2C_DS1307: new module, initial checkin' git-svn-id: https://svn.fhem.de/fhem/trunk@5313 2b470e98-0d58-463d-a4d8-8e2adae1ed80 --- fhem/FHEM/10_FRM.pm | 1 + fhem/FHEM/52_I2C_DS1307.pm | 386 +++++++++++++++++++++++++++++++++++++ fhem/FHEM/52_I2C_LCD.pm | 7 +- 3 files changed, 390 insertions(+), 4 deletions(-) create mode 100644 fhem/FHEM/52_I2C_DS1307.pm diff --git a/fhem/FHEM/10_FRM.pm b/fhem/FHEM/10_FRM.pm index 57d9e197e..a8f14b955 100755 --- a/fhem/FHEM/10_FRM.pm +++ b/fhem/FHEM/10_FRM.pm @@ -43,6 +43,7 @@ my @clients = qw( FRM_STEPPER OWX I2C_LCD + I2C_DS1307 I2C_PC.* I2C_MCP23017 I2C_BMP180 diff --git a/fhem/FHEM/52_I2C_DS1307.pm b/fhem/FHEM/52_I2C_DS1307.pm new file mode 100644 index 000000000..ae1b2c132 --- /dev/null +++ b/fhem/FHEM/52_I2C_DS1307.pm @@ -0,0 +1,386 @@ +package main; + +use strict; +use warnings; + +#add FHEM/lib to @INC if it's not allready included. Should rather be in fhem.pl than here though... +BEGIN { + if ( !grep( /FHEM\/lib$/, @INC ) ) { + foreach my $inc ( grep( /FHEM$/, @INC ) ) { + push @INC, $inc . "/lib"; + } + } +} + +##################################### + +my %sets = ( + "datetime" => "", + "now" => "" +); + +my %gets = ( + "second" => "", + "minute" => "", + "hour" => "", + "day" => "", + "month" => "", + "year" => "", + "datetime" => "", + "date" => "", + "time" => "" +); + +sub I2C_DS1307_Initialize($) { + my ($hash) = @_; + + $hash->{DefFn} = "I2C_DS1307_Define"; + $hash->{InitFn} = "I2C_DS1307_Init"; + $hash->{SetFn} = "I2C_DS1307_Set"; + $hash->{AttrFn} = "I2C_DS1307_Attr"; + + $hash->{I2CRecFn} = "I2C_DS1307_Receive"; + + $hash->{AttrList} = "IODev poll_interval $main::readingFnAttributes"; +} + +sub I2C_DS1307_Define($$) { + my ( $hash, $def ) = @_; + my @a = split( "[ \t][ \t]*", $def ); + + $hash->{STATE} = "defined"; + + if ($main::init_done) { + eval { I2C_DS1307_Init( $hash, [ @a[ 2 .. scalar(@a) - 1 ] ] ); }; + return I2C_DS1307_Catch($@) if $@; + } + return undef; +} + +sub I2C_DS1307_Init($$) { + my ( $hash, $args ) = @_; + my $u = + "wrong syntax: define I2C_DS1307 [
]"; + + return $u if (defined $args and int(@$args) > 1 ); + + my $name = $hash->{NAME}; + my $address = defined $args ? shift @$args : 0b1101000; #default address + $hash->{I2C_Address} = $address; + if (! (defined AttrVal($name,"stateFormat",undef))) { + $main::attr{$name}{"stateFormat"} = "datetime"; + } + eval { + main::AssignIoPort( $hash, AttrVal( $name, "IODev", undef ) ); + $hash->{DS1307} = Device::DS1307->new($address); + $hash->{DS1307}->attach(I2C_DS1307_IO->new($hash)); + $hash->{STATE} = "Initialized"; + }; + return I2C_DS1307_Catch($@) if $@; + I2C_DS1307_Poll($hash); + return undef; +} + +sub I2C_DS1307_Attr($$$$) { + my ( $command, $name, $attribute, $value ) = @_; + my $hash = $main::defs{$name}; + eval { + if ( $command eq "set" ) + { + ARGUMENT_HANDLER: { + $attribute eq "IODev" and do { + if ( $main::init_done and ( !defined( $hash->{IODev} ) or $hash->{IODev}->{NAME} ne $value ) ) + { + main::AssignIoPort( $hash, $value ); + my @def = split( ' ', $hash->{DEF} ) if defined $hash->{DEF}; + I2C_DS1307_Init( $hash, \@def ) if ( defined( $hash->{IODev} ) ); + } + last; + }; + $attribute eq "poll_interval" and do { + $hash->{POLL_INTERVAL} = $value; + if ( $main::init_done ) + { + I2C_DS1307_Poll($hash); + } + last; + } + } + } + }; + my $ret = I2C_DS1307_Catch($@) if $@; + if ($ret) { + $hash->{STATE} = "error setting $attribute to $value: " . $ret; + return "cannot $command attribute $attribute to $value for $name: " . $ret; + } +} + +sub I2C_DS1307_Set(@) { + my ( $hash, @a ) = @_; + return "Need at least one parameters" if ( @a < 2 ); + shift @a; + my $command = shift @a; + if ( !defined( $sets{$command} ) ) { + my @commands = (); + foreach my $key ( sort keys %sets ) { + push @commands, + $sets{$key} ? $key . ":" . join( ",", $sets{$key} ) : $key; + } + return "Unknown argument $command, choose one of " . join( " ", @commands ); + } + my $ds1307 = $hash->{DS1307}; + return unless defined $ds1307; + + eval { + COMMAND_HANDLER: { + $command eq "datetime" and do { + $ds1307->setDatetime(join (' ',@a)); + main::readingsSingleUpdate( $hash, "datetime", $ds1307->getDatetime(), 1 ); + last; + }; + $command eq "now" and do { + $ds1307->setTime(time()); + main::readingsSingleUpdate( $hash, "datetime", $ds1307->getDatetime(), 1 ); + last; + }; + } + }; + return I2C_DS1307_Catch($@) if $@; + return undef; +} + +sub I2C_DS1307_Poll { + my ( $hash ) = @_; + RemoveInternalTimer($hash); + eval { + $hash->{DS1307}->read(); + }; + my $ret = I2C_DS1307_Catch($@) if $@; + if ($ret) { + $hash->{STATE} = "error reading DS1307: " . $ret; + main::Log3 $hash->{NAME},4,"error reading DS1307: ".$ret; + } + InternalTimer(gettimeofday()+$hash->{POLL_INTERVAL}, 'I2C_DS1307_Poll', $hash, 0) if defined $hash->{POLL_INTERVAL}; +} + +# package: +# i2caddress => $data->{address}, +# direction => "i2cread", +# reg => $data->{register}, +# nbyte => scalar(@{$data->{data}}), +# data => join (' ',@{$data->{data}}) + +sub I2C_DS1307_Receive { + my ( $hash, $package ) = @_; + + $hash->{DS1307}->receive( + split (' ',$package->{data}) + ); + main::readingsSingleUpdate( $hash, "datetime", $hash->{DS1307}->getDatetime(), 1 ); +} + +sub I2C_DS1307_Catch($) { + my $exception = shift; + if ($exception) { + $exception =~ /^(.*)( at.*FHEM.*)$/; + return $1; + } + return undef; +} + +package I2C_DS1307_IO; + +use strict; +use warnings; + +sub new { + my ( $class, $hash ) = @_; + return bless { hash => $hash, }, $class; +} + +sub i2c_write { + my ( $self, $address, @data ) = @_; + my $hash = $self->{hash}; + if ( defined( my $iodev = $hash->{IODev} ) ) { + main::CallFn( + $iodev->{NAME}, + "I2CWrtFn", + $iodev, + { + i2caddress => $address, + direction => "i2cwrite", + data => join( ' ', @data ), + } + ); + } + else { + die "no IODev assigned to '$hash->{NAME}'"; + } +} + +sub i2c_read { + my ( $self, $address, $reg, $nbyte ) = @_; + my $hash = $self->{hash}; + if ( defined( my $iodev = $hash->{IODev} ) ) { + main::CallFn( + $iodev->{NAME}, + "I2CWrtFn", + $iodev, + { + i2caddress => $address, + direction => "i2cread", + reg => $reg, + nbyte => $nbyte, + } + ); + } + else { + die "no IODev assigned to '$hash->{NAME}'"; + } +} + +package Device::DS1307; + +use strict; +use warnings; + +# DS1307 ADDRESS MAP + +use constant DS1307_SECONDS => 0x00; +use constant DS1307_MINUTES => 0x01; +use constant DS1307_HOURS => 0x02; +use constant DS1307_DAY => 0x03; +use constant DS1307_DATE => 0x04; +use constant DS1307_MONTH => 0x05; +use constant DS1307_YEAR => 0x06; +use constant DS1307_CONTROL => 0x07; +use constant DS1307_RAM => 0x08; +# RAM 56 x 8 ? +# ... +# 0x3F + +# DS1307 Control Register: +use constant DS1307_OUT => 0x40; # BIT 7 OUT +use constant DS1307_SQWE => 0x10; # BIT 4 SQWE +use constant DS1307_RS1 => 0x02; # BIT 1 RS1 +use constant DS1307_RS0 => 0x01; # BIT 0 RS0 + +sub new { + my ( $class, $address, $timezone, $century ) = @_; + return bless { + address => $address, + time => time(), + }, $class; +} + +sub getDatetime { + my ( $self ) = @_; + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->{datetime}); + return sprintf "%04d-%02d-%02d %02d:%02d:%02d", ($year+1900,$mon+1,$mday,$hour,$min,$sec); +} + +sub setDatetime { + my ( $self, $value ) = @_; + $self->{datetime} = main::time_str2num($value); + $self->write(); +} + +sub setTime { + my ( $self, $value ) = @_; + $self->{datetime} = $value; + $self->write(); +} + +sub attach { + my ( $self, $io ) = @_; + $self->{io} = $io; +} + +sub read { + my ( $self ) = @_; + $self->{io}->i2c_read( $self->{address}, 0, 7 ); +} + +sub receive { + my ($self, @data) = @_; + + my $sec = shift @data; + my $min = shift @data; + my $hour = shift @data; + my $wday = shift @data; + my $mday = shift @data; + my $mon = shift @data; + my $year = shift @data; + + #$self->{time} = mktime(sec, min, hour, mday, mon, year, wday = 0, yday = 0, isdst = -1) + $self->{datetime} = main::mktime($sec, $min, $hour, $mday, $mon, $year, $wday, 0, -1); +} + +sub write { + my ( $self ) = @_; + if (defined $self->{io}) { + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($self->{datetime}); + $self->{io}->i2c_write( + $self->{address}, #slave address + 0, #register + $sec, #data... + $min, + $hour, + $wday, #DS1307 week starts on Sunday + $mday, + $mon, + $year, + 0); #control + } +}; + +1; + +=pod +=begin html + + +

I2C_DS1307

+ +
+ +=end html +=cut diff --git a/fhem/FHEM/52_I2C_LCD.pm b/fhem/FHEM/52_I2C_LCD.pm index 2d8776b8a..8ab2466a2 100644 --- a/fhem/FHEM/52_I2C_LCD.pm +++ b/fhem/FHEM/52_I2C_LCD.pm @@ -73,14 +73,14 @@ I2C_LCD_Init($$) $hash->{sizex} = shift @$args; $hash->{sizey} = shift @$args; - $hash->{address} = shift @$args if (@$args); + $hash->{I2C_Address} = shift @$args if (@$args); my $name = $hash->{NAME}; - if (defined $hash->{address}) { + if (defined $hash->{I2C_Address}) { eval { main::AssignIoPort($hash,AttrVal($hash->{NAME},"IODev",undef)); require LiquidCrystal_I2C; - my $lcd = LiquidCrystal_I2C->new($hash->{address},$hash->{sizex},$hash->{sizey}); + my $lcd = LiquidCrystal_I2C->new($hash->{I2C_Address},$hash->{sizex},$hash->{sizey}); $lcd->attach(I2C_LCD_IO->new($hash)); $lcd->init(); $hash->{lcd} = $lcd; @@ -124,7 +124,6 @@ I2C_LCD_Attr($$$$) { } } }; - return $@ if $@; my $ret = I2C_LCD_Catch($@) if $@; if ($ret) { $hash->{STATE} = "error setting $attribute to $value: ".$ret;