shigetsu has asked for the wisdom of the Perl Monks concerning the following question:

We're using Device::Modem to connect to a modem of type Siemens TC35i, Revision 03.01. It is being used to send SMS to a list of recipients. All AT commands go well except for

$modem->atsend("AT+CGMS=$recipient\cm$msg\cz");

$recipient consists of valid mobile number and $msg contains a message such as "The quick brown fox jumps over the lazy dog". Whatever result I receive via

$modem->answer();

and per SMS, some leading characters get truncated; I tried chomp and autoflush, but neither of them works. When I type the string manually via the minicom console, then the entire string gets delivered.

Code excerpt:

#!/usr/bin/perl use strict; use warnings; use Device::Modem; use Getopt::Std; $| = 1; my (%opts, @recipients); my ($modem, $msg); my $device = '/dev/ttyS1'; my $pin = ''; parse_args(); check_len_msg(); main(); sub main { foreach my $recipient (@recipients) { init(); set_opts(); send_cmds($recipient); disconnect(); } } sub parse_args { getopts('S:', \%opts); @recipients = @ARGV; chomp(@recipients); usage() unless defined $opts{S}; $msg = $opts{S}; } sub usage { print <<EOT; $0 [-S] recipient(s) EOT exit(1); } sub check_len_msg { die "message exceeds size of 130 character!\n" if length($msg) > 130; } sub init { $modem = Device::Modem->new(port => $device); if ($modem->connect(baudrate => 9600)) { print "Connecting...\n"; } else { print "Sorry, no connection with serial port!\n"; } } sub set_opts { $modem->is_active(); $modem->echo(0); $modem->verbose(1); } sub send_cmds { my $recipient = shift; $modem->atsend("AT\r"); my $answer = $modem->answer()."\n"; print $answer; $modem->atsend("AT+CSCS=GSM\r"); $answer = $modem->answer()."\n"; print $answer; $modem->atsend("AT+CPIN=?\r"); $answer = $modem->answer()."\n"; print $answer; $modem->atsend("AT+CPIN?\r"); $answer = $modem->answer()."\n"; $answer =~ s/(?:\+CPIN:\s\w+)\s*(.*)/$1/; print $answer; $modem->atsend("AT+CMGC=?\r"); $answer = $modem->answer()."\n"; print $answer; $modem->atsend("AT+CMGF=1\r"); $answer = $modem->answer()."\n"; print $answer; $modem->atsend("AT+CMGS=$recipient\cm$msg\cz"); $answer = $modem->answer()."\n"; print $answer; } sub disconnect { $modem->disconnect(); }

Replies are listed 'Best First'.
Re: Lost characters using Device::Modem
by roboticus (Chancellor) on Nov 24, 2006 at 15:12 UTC
    shigetsu:

    It could be a timing issue. If you're missing the first few characters, then put in a small time delay before sending the message. It could be that the receiving hardware can't quite prepare for the message before your program is sending it out.

    As an example, a long, long time ago, I had a similar problem with a different bit of equipment. In that case, it was due to a timing issue. You see, when you send a carriage return to a teletype (excedrin headache ASR-33), you need to wait a little while before sending the next character, unless you wanted to see it smudged through the center column as the printhead is returning to the left margin.

    A few years later, working with some video terminals, I encountered something similar: Some sequences (clear screen, IIRC) needed an extra character time before you sent anything else, or the next character would be dropped. At least it wouldn't smudge somewhere on the screen... 8^)

    Later still, working on a cow feeding system--(Don't ask!)--I encountered yet more systems where you needed to give a pause between particular control sequences. It's a relatively common thing to encounter in lots of embedded systems. Give it a try to see if that helps. (Just to prove it to yourself, I'd go ahead and use a significant delay, such as a couple seconds, to see if it clears things up. If so, then trim the time down until you get as much speed as you can reliably get. Then back off a bit.)

    --roboticus

    Update: After looking at your code, I notice that you're using the AT command codes to talk to your modem. You could insert the sequence "ATD;,\015" into your command sequence to provide a delay. (You can adjust the delay by setting the appropriate value in the S8 register. See an AT command reference for details.

      roboticus:

      Indeed, that did the trick. Thanks.

Re: Lost characters using Device::Modem
by shmem (Chancellor) on Nov 24, 2006 at 15:16 UTC
    I was bitten by the same bug some time ago. Try delaying, this delays by char:
    map {$modem->atsend($_) and select undef,undef,undef,0.1 } split //, "AT+CGMS=$recipient\cm$msg\cz";

    Seems like the modem's busy echoing (or not echoing ;) chars back while new chars arrive. Or the buffer is to short. Or something else ;-)

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Lost characters using Device::Modem
by jbert (Priest) on Nov 24, 2006 at 18:41 UTC
    Whilst you can approach timing problems by adding delays, I think it can lead to a fragile solution and also a sub-optimal one (lower speed of transfer than is possible).

    The link between the modem and the computer should implement flow control, where the receiver tells the sender when to stop and start sending. This is normally done either in-band (with XON/XOFF signalling - those ctrl-S and ctrl-Q ASCII characters that you can find locking up your xterms :-) or on seperate lines of the serial connection (RTS/CTS - ready-to-send, clear-to-send), which is a little better since it makes your connection 8-bit clean and doesn't take up any comms bandwidth.

    See the &Kn section of the link above.

    Disclaimer: I haven't done this sort of thing for a long time, but fixing timing problems with delays isn't a good idea. ( 0.1s between chars? You've just limited yourself to 10chars/second, approx 80baud :-(

      jbert:

      I totally agree with you--you should use flow control whenever possible. Unfortunately, it's not always possible. In order to implement flow control, all devices in the chain must support it. It's not uncommon in the embedded world to connect with things that have no flow control. You may just have minimum/maximum timings on a specification sheet.

      For example, let's suppose that the modem on the SMS system, once it gets the proper "doit" string from the originator triggers a relay that connects the modem to a transmitter. That relay could take a few milliseconds to engage, and you could easily lose the first character or two while the relay is actuating.

      --roboticus

        Oh, agreed. If you're talking to a device which won't play ball with flow control, you don't have much choice. But, as you say, hopefully you then have a spec sheet and can put in the "theoretically correct" delays and timings, or something close.
Re: Lost characters using Device::Modem
by polettix (Vicar) on Nov 26, 2006 at 23:36 UTC
    In Telit GM862 manual it is clearly stated that the command is AT+CMGS=<da>, after which the device responds with a prompt that lets you type the message in. I haven't checked the standards, but I suspect that most modems behave like this.

    You're simply not waiting for the prompt. To be on the safe side, I'd suggest to send the command, wait for the prompt and then send the message.

    Flavio
    perl -ple'$_=reverse' <<<ti.xittelop@oivalf

    Don't fool yourself.