1 package GSM::SMS::Transport::Serial;
4 # SMS transport layer for serial GSM modems
7 use GSM::SMS::Transport::Transport;
8 @ISA = qw(GSM::SMS::Transport::Transport);
12 use Device::SerialPort;
16 # All the parameters I need to run ...
34 my $class = ref($proto) || $proto;
39 $self->{'__LOGGER__'} = GSM::SMS::Log->new( $self->{cfg}->{"log"} );
42 my $success = $self->init( $self->{cfg} );
43 return undef if $success == -1;
47 # Send a PDU encoded message
49 my($self, $msisdn, $p) = @_;
50 my $logger = $self->{'__LOGGER__'};
53 # print "MSISDN : $msisdn\n";
56 # calculate length of message
57 my $len = length($p)/2 - 1;
58 $len-=hex( substr($p, 0, 2) );
60 # $self->_at("ATE0\r", $__TO);
61 $self->_at("AT+CMGF=0\r", $__TO);
62 $self->_at("AT+CMGS=$len\r", $__TO, ">");
63 my $res = $self->_at("$p\cz", $__TO);
68 $logger->logentry("send [$p]") if $logger;
71 $logger->logentry("error sending [$p]") if $logger;
77 # Receive a PDU encoded message
78 # Will return a PDU string in $pduref from the modem IF we have a message pending
81 # -1 if no message pending
83 my ($self, $pduref) = @_;
85 my $ar = $self->{MSGARRAY};
86 my $msg = shift (@$ar); # shift because we want to delete lower index first (problem with modem)
89 # Read in pending messages
90 my $msgarr = $self->_getSMS();
91 foreach my $msg (@$msgarr) {
94 $msg = shift (@$ar); # shift same reason as above
97 $$pduref = $msg->{MSG};
99 $self->_delete($msg->{ID});
107 # Initialise this transport layer$
108 # No init file -> default initfile for transport
110 my ($self, $config) = @_;
112 my $ress = $self->_init($config);
117 $self->{MSGARRAY} = [];
124 # Close the init file
128 my $logger = $self->{'__LOGGER__'};
130 my $l = $self->{log};
132 $logger->logentry("Serialtransport stopped") if $logger;
136 # A ping command .. just return an informative string on success
140 return $self->_getSQ();
143 # give out the needed config paramters
144 sub get_config_parameters {
150 # Do we have a valid route for this msisdn
151 sub has_valid_route {
152 my ($self, $msisdn) = @_;
154 # print "in route\n";
155 # print Dumper $self->{cfg};
156 foreach my $route ( split /,/, $self->{cfg}->{"match"} ) {
158 return -1 if $msisdn =~ /$route/;
166 return $self->{cfg}->{name};
169 ###############################################################################
170 # Transport layer specific functions
173 my ($self, $cfg) = @_;
175 my $logger = $self->{'__LOGGER__'};
181 $logger->logentry('Starting Serial Transport for '.$cfg->{"name"}) if $logger;
183 # Get configuration from config file
189 my $port = $cfg->{"serial-port"} || return -1;
190 my $br = $cfg->{"baud-rate"} || return -1;
191 my $pc = $cfg->{"pin-code"} || return -1;
192 my $csca = $cfg->{"csca"} || return -1;
193 my $modem = $cfg->{"name"};
195 # Start up serial port
196 $self->{port} = Device::SerialPort->new ($port);
197 $self->{port}->baudrate($br);
198 $self->{port}->parity("none");
199 $self->{port}->databits(8);
200 $self->{port}->stopbits(1);
202 # Try to communicate to the port
203 $self->_at("ATZ\r", $__TO);
204 $self->_at("ATE0\r", $__TO);
205 my $res = $self->_at("AT\r", $__TO);
207 unless ($res =~/OK/is) {
208 $logger->logentry('Could not communicate to '.$port.'.') if $logger;
212 # Check the modem status (PIN, CSCA and network connection)
213 return -1 unless ( $self->_register );
215 $logger->logentry("Modem is alive! (SQ=".$self->_getSQ()."dBm)") if $logger;
227 $self->_at("AT+CMGF=0\r", $__TO);
229 # loop from 1 to cfg->memorylimit to get messages
230 my $limit = $self->{cfg}->{"memorylimit"} || 10;
231 for (my $i=1; $i<=$limit; $i++) {
232 my $res = $self->_at( "AT+CMGR=$i\r", $__TO );
234 next if ($res=~/ERROR/ig);
236 # find +CMGR: ..,..,..
237 my $cmgr_start = index( $res, "+CMGR:" );
238 my $cmgr_stop = index( $res, "\r", $cmgr_start );
239 my $cmgr = substr($res, $cmgr_start, $cmgr_stop - $cmgr_start);
242 my $pdu_start = $cmgr_stop + 2;
243 my $pdu_stop = index( $res, "\r", $pdu_start );
244 my $pdu = substr($res, $pdu_start, $pdu_stop - $pdu_start);
247 $cmgr=~/\+CMGR:\s+(\d*),(\d*),(\d*)/;
249 my $msg = $result->[$msgcount++] = {};
252 $msg->{'LENGTH'} = $3;
260 my ($self, $id) = @_;
264 $self->_at("AT+CMGF=1\r", $__TO);
265 my $res = $self->_at("AT+CMGD=".$id."\r", $__TO);
268 # print "DELETE $res\n";
278 my ($self, $at, $timeout, $expect) = @_;
280 my $ob = $self->{port};
298 select(undef,undef,undef, 0.1);
299 $to_read = $max - $count;
301 ($readcount, $input) = $ob->read($to_read);
305 if ( ($in=~/OK\r\n/) || ($in=~/ERROR\r\n/) ) {
309 if ( $expect ne "" ) {
310 if ( index($in, $expect) > -1 ) {
317 } while ( ($found==0) && ($counter<$timeout) );
319 select(undef,undef,undef, 0.1);
321 $to_read=$max-$count;
322 ($readcount, $input) = $ob->read($to_read);
333 my $res = $self->_at("AT+CSQ\r", $__TO);
334 $res=~/\+CSQ:\s+(\d+),(\d+)/igs;
340 $dbm = -113 if ($res == 0);
341 $dbm = -111 if ($res == 1);
342 $dbm = -109 + 2*($res-2) if (($res >= 2) && ($res <=30));
343 $dbm = 0 if ($res == 99);
348 # Register modem: PIN, CSCA, Wait for network connectivity for a certain period
353 my $logger = $self->{'__LOGGER__'};
355 my $cfg = $self->{cfg};
357 my $pc = $cfg->{"pin-code"};
358 my $csca = $cfg->{"csca"};
360 $logger->logentry( "Checking if modem ready .." ) if $logger;
362 # I should speed this 'registering' up ...
363 # ... actually I should have a command that tells me
364 # ready to send|receive ...
366 # $res = $self->_at("AT+MAGIC?\r", $__TO);
367 # return -1 if ( $res=~/OK/ );
369 # 1. Do we need to give in the PIN ?
370 $res = $self->_at("AT+CPIN?\r", $__TO, "+CPIN:");
372 if ( $res=~/\+CPIN: SIM PIN/i ) {
374 $logger->logentry("Modem needs PIN ...") if $logger;
375 $self->_at("AT+CPIN=\"$pc\"\r", $__TO);
378 $res = $self->_at("AT+CPIN?\r", $__TO , "+CPIN:");
379 if( $res!~/\+CPIN: READY/i ) {
380 # somethings wrong here!
381 $logger->logentry("Modem did not accept PIN!") if $logger;
387 $res = $self->_at("AT+CSCA=\"$csca\"\r", $__TO);
388 $res = $self->_at("AT+CSCA=\"$csca\"\r", $__TO);
390 # 3. Wait for registration on network
394 $res = $self->_at("AT+CREG?\r", $__TO , "+CREG");
399 until ( $registered || ((time - $stime) > 10 ) );
401 if( $registered==0 ) {
402 $logger->logentry("Modem could not register on network!") if $logger;
406 # 4. Wait until SIM chip is ready (give it 3 mins) - by checking +cmgf
410 $res = $self->_at("AT+CMGF=0\r", $__TO , "+CMGF");
417 until ( $simReady || ((time - $stime) > 180 ) );
420 $logger->logentry("SIM will not respond!") if $logger;
432 GSM::SMS::Transport::Serial
436 This class implements a serial transport. It uses Device::SerialPort to communicate to the modem. At the moment the modem that I recommend is the WAVECOM modem. This module is in fact the root of the complete package, as the project started as a simple perl script that talked to a Nokia6110 connected via a software modem ( as that model did not implement standard AT ) on a WINNT machine, using Win32::SerialPort and Activestate perl. Also tested with the M20 modem module from SIEMENS.
438 I first used the Nokia6110, then moved to the Falcom A1 GSM modem, then moved to the SIEMENS M20 and then moved to the WAVECOM series. Both M20 and WAVECOM worked best, but I could crash the firmware in the M20 by sending some fake PDU messages.
442 The Device::SerialPort puts a big load on your system (active polling).
444 The initialisation does not always work well and sometimes you have to
445 initialize your modem manually using minicom or something like that.
452 AT+CSCA="+32475161616"
454 +CPIN puts the pin-code in the modem; Be carefull, only 3 tries and then you have to provide the PUK code etc ...
456 +CSCA sets the service center address
460 Johan Van den Brande <johan@vandenbrande.com>