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
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.
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.
