Weblog of jaj

Mon, 07 Feb 2011

Using the Nokia 6510 GSM modem over IrDA

So it looks like everybody has a smartphone nowadays, however I still
use my Nokia 6510 on a daily basis.
Like many phones back then, it has an IrDA (Infrared Data Association)
interface which permits communication with the phone and I felt like
playing with it a bit.
For this I used a laptop with integrated IrDA interface, the OpenBSD
operating system and the ppp tool that ships with it, the birda
package and the smstools package.
The ircomm tool that ships with birda lets you initiate an IrDA COMM
connection with the phone and lets you attach a pseudo terminal to it:

ircomm -d /dev/tty01 -y /dev/ptypz

Here, /dev/tty01 is the actual IrDA interface that ircomm talks to and
/dev/ptypz is a pseudo terminal it allocates that lets you talk to the
GSM modem inside the device. The modem understands AT style commands.
Actually you don't talk directy to the modem but rather to some
restricted modem emulation.

For more information see:
http://en.wikipedia.org/wiki/Hayes_command_set#GSM
http://wiki.openmoko.org/wiki/Hardware:AT_Commands
http://wiki.openmoko.org/wiki/Manually_using_GSM
http://wiki.openmoko.org/wiki/Manually_using_GPRS

I use the ppp(8) tool to talk to the modem but anything else will work.
Here's a first session:

spaceman% sudo ppp           
Password:
Working in interactive mode
Using interface: tun0
Warning: No default entry found in config file.
ppp ON spaceman> set device /dev/ttypz
ppp ON spaceman> term
deflink: Entering terminal mode on /dev/ttypz
Type `~?' for help
ATE1
OK

RING

RING
ATA
OK
ATH
OK
ATD0952880000;
OK

NO CARRIER
AT
OK

ppp ON spaceman> 

Some explanations: "ATE1" enables echoing, which enables you to see what
you type, this is not required. The phone responds with "OK" when it
correctly processes a command.
I then call the phone and the modem shows that a call is incoming with
the "RING" signal. You can then accept the call with "ATA" or reject it
with "ATH". I accepted the call, talked a bit and then hung up using
"ATH".
Then I called my home phone from the mobile phone using the ATD command.
'D' here stands for dialling and the command dials the number that
follows the command. I then hung up from the home phone which the modem
signals with "NO CARRIER".

Here's another session where I try to establish a GPRS connection for
tethering. This unfortunately doesn't work, I don't really know why,
probably the provider forbids it:

ppp ON spaceman> term
AT+CGDCONT=1,"IP","gprs-service-fr.net"
OK
ATD*99#
CONNECT
ppp ON spaceman> Warning: Sending empty PAP authname!
Ppp ON spaceman> 
PPp ON spaceman> Warning: ff01:8::: Change route failed: errno: Network is unreachable
Warning: ff02:8::: Change route failed: errno: Network is unreachable
Warning: ff02:8::: Change route failed: errno: Network is unreachable
ppp ON spaceman> 

So here I first set the APN which is needed for GPRS, using the CGDCONT
command, the value depends on your provider. Then I dial the special
number *99#. The modem tries to establish the connection, which it
signals with "CONNECT". It then starts talking PPP (Point-to-Point
Protocol). The ppp(8) tool detects the fact that PPP is now spoken and
takes over to establish a PPP link, which unfortunately fails here. The
number of capital 'P's on the prompt show the state of the connection:

ppp ON awfulhak>               # No link has been established
Ppp ON awfulhak>               # We've connected & finished LCP
PPp ON awfulhak>               # We've authenticated
PPP ON awfulhak>               # We've agreed IP numbers

So, as you can see here, authentication is successful but we don't get
an IP address. When it fails the phone beeps and shows the following
error: "Subscribe to GPRS first".

One final thing I wanted to play with is SMS. Writing SMSes manually
using AT style commands is cumbersome, so I used the smstools package
which comes with a daemon that does the dirty work of talking to the
modem. I set it up to talk to the /dev/ptypz device. Once it runs you
can send SMSes using the "sendsms" command line tool, for example:

spaceman% sendsms 33668360000 'Test Message'

My phone number actually starts in "0668". "33" is the international
prefix of my country (France), so you get how you have to format the
number.

Here is the log of the smsd daemon for this command:

2011-02-07 17:06:58,5, smsd: Moved file /var/spool/sms/outgoing/send_qzxMhS to /var/spool/sms/checked
2011-02-07 17:06:58,6, GSM1: I have to send 1 short message for /var/spool/sms/checked/send_qzxMhS
2011-02-07 17:06:58,6, GSM1: Sending SMS from  to 33668360000
2011-02-07 17:06:58,6, GSM1: Checking if modem is ready
2011-02-07 17:06:59,7, GSM1: -> AT
2011-02-07 17:06:59,7, GSM1: Command is sent, waiting for the answer
2011-02-07 17:06:59,7, GSM1: <- OK
2011-02-07 17:06:59,6, GSM1: Pre-initializing modem
2011-02-07 17:06:59,7, GSM1: -> ATE0+CMEE=1;+CREG=2
2011-02-07 17:07:00,7, GSM1: Command is sent, waiting for the answer
2011-02-07 17:07:00,7, GSM1: <- OK
2011-02-07 17:07:01,7, GSM1: -> AT+CSQ
2011-02-07 17:07:01,7, GSM1: Command is sent, waiting for the answer
2011-02-07 17:07:01,7, GSM1: <- +CSQ: 20,99 OK
2011-02-07 17:07:01,6, GSM1: Signal Strength Indicator: (20,99) -73 dBm (Excellent), Bit Error Rate: not known or not detectable
2011-02-07 17:07:01,6, GSM1: Checking if Modem is registered to the network
2011-02-07 17:07:01,7, GSM1: -> AT+CREG?
2011-02-07 17:07:02,7, GSM1: Command is sent, waiting for the answer
2011-02-07 17:07:02,7, GSM1: <- +CREG: 2,1,"00D6","9C99" OK
2011-02-07 17:07:02,6, GSM1: Modem is registered to the network
2011-02-07 17:07:02,6, GSM1: Selecting PDU mode
2011-02-07 17:07:02,7, GSM1: -> AT+CMGF=0
2011-02-07 17:07:03,7, GSM1: Command is sent, waiting for the answer
2011-02-07 17:07:03,7, GSM1: <- OK
2011-02-07 17:07:03,7, GSM1: -> AT+CMGS=18
2011-02-07 17:07:04,7, GSM1: Command is sent, waiting for the answer
2011-02-07 17:07:04,7, GSM1: <- >
2011-02-07 17:07:04,7, GSM1: -> 0011000B913366388639F90000FF04F4F29C0E
2011-02-07 17:07:06,7, GSM1: Command is sent, waiting for the answer
2011-02-07 17:07:10,7, GSM1: <- +CMGS: 214 OK
2011-02-07 17:07:10,5, GSM1: SMS sent, Message_id: 214, To: 33668360000, sending time 12 sec.
2011-02-07 17:07:10,6, GSM1: Deleted file /var/spool/sms/checked/send_qzxMhS

The message got delivered correctly.
smsd is also able to receive incoming SMS, however I haven't tried it
and I'm not sure it works since the modem of this phone does not signal
anything when it receives a message.

The birda package also includes some tools to use OBEX over IrDA. This
lets you send and receive vCards with contacts (what Nokia calls
"Business Cards").
For example, to send a vCard with a contact named "John" and phone
number 06... to your phone you can use the following command:

mkobextel -n John -t 0668360001 |  irobex -d /dev/tty01

Receiving vCards sent from the phone also works.

Have fun!

Update:
I just add this here for the nostalgia

AT+CGMI
Nokia Mobile Phones

OK
AT+CGMM
Nokia 6510

OK
AT+CGMR
V 04.12
28-08-02
NPM-9
(c) NMP.

OK


posted at: 23:46 | path: | permanent link to this entry

Mon, 26 Apr 2010

Neo Freerunner GPS as OpenBSD timedelta sensor

I recently discovered the nmea(4) serial line discipline in OpenBSD which was
written by Marc Balmer in 2008.
This is a driver that can be attached to a serial device and which interprets
NMEA 0183 data typically produced by GPS devices. It extracts time data (not
position) from the GPS stream and makes it available through the OpenBSD sensors
framework. This timedelta sensor can then be used by OpenNTPD to keep your clock
in sync.
Now I have a Neo Freerunner smartphone which comes with a GPS device that
produces NMEA data and I wanted to test it out.

First of all on the phone side you have to power on the GPS device, for example
like this:

echo 1 > /sys/class/i2c-adapter/i2c-0/0-0073/pcf50633-regltr.7/neo1973-pm-gps.0/power_on

Then you need to transfer the GPS data from the phone to your OpenBSD machine
and emulate a serial device that provides the data.

socat (net/socat) does a great job at this.
On the phone side make sure GPSD is running and do something like the
this:

socat EXEC:"gpspipe -r" TCP-LISTEN:31415

alternatively you can talk to the raw device without GPSD like that:

socat /dev/ttySAC1,raw,echo=0,crnl TCP-LISTEN:31415

Now on the computer side you need to attach this to a pseudo terminal:

socat TCP:192.168.0.14:31415 /dev/ptypa,raw,echo=0,crnl

You should now be able to query the GPS device on your computer. Attach the
nmea(4) discipline to the serial device using:

ldattach nmea /dev/ttypa

If everything worked fine you should see an nmea sensor come up which you can
query using sysctl or systat for example.

Here's my relevant output from systat:

SENSOR                                          VALUE  STATUS  DESCRIPTION
nmea0.percent0                                100.00%    OK    Signal
nmea0.timedelta0                              2.237 s    OK    GPS autonomous

To use this sensor with OpenNTPD you simply need to add the following to
/etc/ntpd.conf:

sensor nmea0

ntpd is opportunistic about the timedelta sensors, meaning it will use them when
they are available but will run just as smooth when they are not.

Here's some ntpd output:

sensor nmea0 added (weight 1, correction 0.000000, refstr HARD)
sensor nmea0: offset -2.236736

I advise you to read the relevant man pages to get a better understanding of the
process.



posted at: 19:28 | path: | permanent link to this entry

Wed, 10 Feb 2010

xterm, zsh, spawn-new-terminal, *BSD

xterm defines a very convenient action called spawn-new-terminal().
Using that action you can duplicate your current terminal and obtain a
shell in your current working directory even if you're inside a program
like vim or mutt.

From the man page:

       spawn-new-terminal(params)
               Spawn  a  new xterm process.  This is available on
               systems which have a modern version of the process
               filesystem, e.g., "/proc", which xterm can read.

               Use the "cwd" process entry, e.g., /proc/12345/cwd
               to obtain the working  directory  of  the  process
               which is running in the current xterm.

You can bind this action to a key using Xresources. For example I bind
the action to Alt-n. Here's the relevant part from my .Xdefaults:

xterm*VT100.translations: #override \n\
        Meta <Key>n:spawn-new-terminal()

The problem is that on *BSD you don't have a procfs, at least not one
that looks like the one on Linux.

So to actually get this working, xterm needs another way to find out the
current working directory (CWD) of the shell that runs inside of it. I
found out that this is not so easy and after some tinkering I decided to
go for a dirty hack. The idea is, if I can't find out the cwd of a zsh process
on my own then perhaps I can ask zsh gently to tell it.

Zsh, like any other shell, lets you define functions, however certain
functions have a special meaning for zsh: they define hooks.
We use 2 hooks here: chpwd(), called whenever the CWD of zsh changes,
and zshexit(), called when the shell quits.

Here are the relevant parts from my .zshrc:

function chpwd()
{
    echo -n ${PWD} > ${HOME}/.zsh/${PPID}.pwd
}
#call chpwd once on startup
chpwd

function zshexit()
{
    rm ${HOME}/.zsh/${PPID}.pwd
}

Now for the xterm part. The relevant function is HandleSpawnTerminal()
in misc.c. Here's my modified version:

#include <sys/types.h>
#include <pwd.h>
#include <unistd.h>
#include <limits.h>
#include <err.h>

/* ARGSUSED */
void
HandleSpawnTerminal(Widget w GCC_UNUSED,
		    XEvent * event GCC_UNUSED,
		    String * params,
		    Cardinal *nparams)
{
    static char zshdir[] = ".zsh";
    static char termpath[] = "xterm";

    pid_t pid, forkpid;
    uid_t uid;
    struct passwd *passwd;

    char pathname[_POSIX_PATH_MAX];
    char zshcwd[_POSIX_PATH_MAX];

    char *home;
    FILE *fp;

    pid = getpid();

    uid = geteuid();
    passwd = getpwuid(uid);
    home = passwd->pw_dir;

    snprintf(pathname, sizeof pathname, "%s/%s/%ld.pwd", home, zshdir, (long)pid);

    fp = fopen(pathname, "r");
    if(fp == NULL)
    {
        warn("couldn't open %s", pathname);
        return;
    }
    if(fgets(zshcwd, sizeof zshcwd, fp) == NULL)
    {
        warn("couldn't read from %s", pathname);
        fclose(fp);
        return;
    }

    fclose(fp);

    /* The reaper will take care of cleaning up the child */
    forkpid = fork();
    if(forkpid < 0)
    {
        warn("Could not fork");
        return;
    }
    if(forkpid == 0)
    {
        /* We are the child */
        if(chdir(zshcwd) < 0)
            err(1, "could not chdir to %s", zshcwd);

        unsigned myargc = *nparams + 1;
        char **myargv = TypeMallocN(char *, myargc + 1);
        unsigned n = 0;

        myargv[n++] = termpath;

        while (n < myargc) {
            myargv[n++] = *params++;
        }

        myargv[n] = 0;
        execvp(termpath, myargv);

        /* If we get here, we've failed */
        err(1, "exec of '%s'", termpath);
    } else {
        /* We are the parent
         * we just live our life */
    }
}

Now you just have to reconfigure xterm with --enable-exec-xterm and
build it.



posted at: 20:23 | path: | permanent link to this entry