NodeMCU Lua setup using a Mac

I recently bought a NodeMCU. This is a small ESP8266 based card with built in WiFi, MicroUSB, and a Lua interpreter that can be used for developing IoT (Internet of Things) devices.

I had trouble getting it working initially, so I wanted to share how I fixed this on a Mac.

Install Mac drivers

If you’re not using a Mac, you can skip this part.

You plug the NodeMCU into the Mac via USB. The Mac won’t support this by default, and you need to install a driver. The driver you need to install is the Silcon Labs CP210x USB to UART Bridge.

Once installed, plug in the NodeMCU and check that the device /dev/tty.SLAB_USBtoUART exists.

To connect to the NodeMCU, you’ll need some tools. To test, download CoolTerm. In Options, for Port select SLAB_USBtoUART, and for Baudrate select 115200.

If you are lucky you’ll get a prompt, if not you may need to build and install some new firmware.

If you do get a prompt type…

print "Hello";

… and Lua should echo “Hello” back to you.

Build and install the NodeMCU firmware

If you need to build new firmware, there is a very useful online site called NodeMCU-Build.com that I used to build the firmware with the right modules I wanted for my project.

Once you’ve build the firmware, you’ll need to install the Python esptool.py to flash the firmware to the NodeMCU.

git clone https://github.com/themadinventor/esptool.git
cd esptool
sudo python ./setup.py install

Check the flash size of your NodeMCU.

To flash the firmware hold down “FLASH” and press “RST” on the NodeMCU, then use the following command (remembering to disconnect CoolTerm first if connected)…

esptool.py --port /dev/tty.SLAB_USBtoUART write_flash -fm dio 0x00000 ~/Downloads/nodemcu-master-12-modules-2017-07-07-21-24-03-float.bin

As esptool.py runs, you should see something like this…

esptool.py v2.0.1
Connecting........_
Detecting chip type... ESP8266
Chip is ESP8266
Uploading stub...
Running stub...
Stub running...
Configuring flash size...
Auto-detected Flash size: 4MB
Flash params set to 0x0240
Compressed 754992 bytes to 505060...
Wrote 754992 bytes (505060 compressed) at 0x00000000 in 44.5 seconds (effective 135.6 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting...

Connect to it using CoolTerm, then press the “RST” button. You should see some messy characters, then something like following (depending on what you built into your firmware)…

NodeMCU custom build by frightanic.com
.branch: master
.commit: c8ac5cfb912ff206b03dd7c60ffbb2dafb83fe5e
.SSL: true
.modules: adc,bit,cron,crypto,encoder,file,gpio,http,i2c,net,node,ow,pcm,sjson,sntp,spi,struct,tmr,u8g,uart,websocket,wifi,tls
 build .built on: 2017-07-12 09:30
 powered by Lua 5.1.4 on SDK 2.1.0(116b762)
lua: cannot open init.lua
>

Upload Lua code to the NodeMCU

The best way to get Lua code onto the NodeMCU is to use the Python luatool.

git clone https://github.com/4refr0nt/luatool
cd luatool

To test we’ll upload a simple Lua script that will blink the NodeMCU’s onboard LED, save this as “blink.lua”.

LED_PIN = 0

gpio.mode(LED_PIN, gpio.OUTPUT)
value = true

tmr.alarm(0, 500, 1, function ()
    gpio.write(LED_PIN, value and gpio.HIGH or gpio.LOW)
    value = not value
end)

Upload it to the NodeMCU using the following command (making sure Coolterm is disconnected first)…

python luatool/luatool.py --port /dev/tty.SLAB_USBtoUART --src blink.lua --dest init.lua --dofile

You should see the following…

->file.open("init.lua", "w") -> ok
->file.close() -> ok
->file.remove("init.lua") -> ok
->file.open("init.lua", "w+") -> ok
->file.writeline([==[LED_PIN = 0]==]) -> ok
->file.writeline([==[]==]) -> ok
->file.writeline([==[gpio.mode(LED_PIN, gpio.OUTPUT)]==]) -> ok
->file.writeline([==[value = true]==]) -> ok
->file.writeline([==[]==]) -> ok
->file.writeline([==[tmr.alarm(0, 500, 1, function ()]==]) -> ok
->file.writeline([==[gpio.write(LED_PIN, value and gpio.HIGH or gpio.LOW)]==]) -> ok
->file.writeline([==[value = not value]==]) -> ok
->file.writeline([==[end)]==]) -> ok
->file.flush() -> ok
->file.close() -> ok
->dofile("init.lua") -> send without check
--->>> All done <<<--

The onboard LED on the NodeMCU should now be blinking.

The NodeMCU executes the init.lua script by default, so when we upload we tell luatool to upload our blink.lua script as init.lua.

Using Node.js To Send A Heartbeat To A Python Server

I’ve been looking back at some old code and I found an old piece on using Perl to send a heartbeat to a Python server.

Recently I’ve been using the excellent node.js to do some cool JavaScript on the server side instead of in a client on a web broswer. I thought it would be fun to covert this simple script over to JavaScript.

This example is based on recipe 13.11 in the Python Cookbook.

The script needs to send a UDP datagram to a server listening on port 43278. In this example the server will be listening on localhost, 127.0.0.1.

As with the Perl script, we’ll set a few “constants” to let us change things easily if we want to. As JavaScript doesn’t have constants in this example we’ll just use variables. Firstly we nee to require “dgram” so we can use UDP datagrams. We’ll set the server ip address, the port to use, the time in seconds to send the heartbeat, a debug flag if we want to see a message on the console everytime we send a message, and the message itself “PyHB”.

var dgram = require('dgram');
var message = new Buffer("PyHB");
var server_ip = '127.0.0.1';
var server_port = 43278;
var beat_period = 5;
var debug = 1;

We’ll need to send out our heartbeat every beat_period seconds. To do this we can use JavaScript’s setInterval function to execute code every X milliseconds. As we’ve defined our beat_period in seconds, we’ll need to mulitply this value by 1000 to get the time in milliseconds.

setInterval(function() {
// datagram code goes here.
}, beat_period * 1000);

The code to send the datagram is really simple, 3 lines infact. We create a client socket, send the message then close the client socket.

var client = dgram.createSocket("udp4");
client.send(message, 0, message.length, server_port, server_ip);
client.close();

Putting this together, along with a bit of debugging information, we get the following code.

var dgram = require('dgram');
var message = new Buffer("PyHB");
var server_ip = '127.0.0.1';
var server_port = 43278;
var beat_period = 5;
var debug = 1;
console.log("Sending heartbeat to IP " + server_ip + " , port " + server_port);
console.log("press Ctrl-C to stop");
setInterval(function() {
var client = dgram.createSocket("udp4");
client.send(message, 0, message.length, server_port, server_ip);
client.close();
if (debug)
console.log("Time: " + new Date());
}, beat_period * 1000);

I hope that was a useful introduction to sending messages using UDP in Node.js.

Using Perl To Send Heartbeat To A Python Server

I’ve been brushing up on my networking and Python skills lately.

One example in the Python Cookbook is recipe 13.11 Detecting Inactive Computers. Here there are two scripts, one that sends a heartbeat, and another that listens for heartbeats. The idea is a simple UDP packet containing the short string “PyHB” is sent every 5 seconds or so by a client. A server then listens for these datagrams, stores the addresses of the clients which it receives messages from, and if it fails to receive a message in a certain time period it flags this up on the console.

I thought I’d just check how easy it would be to create a Perl client to talk to the Python backend. As it’s all through sockets it should be fairly easy.

To start with we define a few constants that cover the address of the server we want to connect to, the port to send the datagram to, how often we want to send it and if we want debugging information displayed or not.

use constant SERVER_IP => '127.0.0.1';
use constant SERVER_PORT => 43278;
use constant BEAT_PERIOD => 5;
use constant DEBUG => 1;

Now we need to create the socket, in this case we’ll create a new IO::Socket object.

my $hbsocket = IO::Socket::INET->new(Proto => 'udp',
PeerPort => SERVER_PORT,
PeerAddr => SERVER_IP)
or die "Creating socket: $!n";

Next we need to send our message, the string “PyHB” over the socket.

$hbsocket->send('PyHB') or die "send: $!";

Finally we need to wrap this up in a loop and sleep BEAT_PERIOD seconds before repeating our message.

Here’s the final script.

#!/usr/bin/perl -w
use strict;
use IO::Socket;
use constant SERVER_IP => '127.0.0.1';
use constant SERVER_PORT => 43278;
use constant BEAT_PERIOD => 5;
use constant DEBUG => 1;
print "Sending heatbeat to IP " . SERVER_IP ." , port " . SERVER_PORT . "n";
print "press Ctrl-C to stopn";
while (1) {
my $hbsocket = IO::Socket::INET->new(Proto => 'udp',
PeerPort => SERVER_PORT,
PeerAddr => SERVER_IP)
or die "Creating socket: $!n";
$hbsocket->send('PyHB') or die "send: $!";
print "Time: " . localtime(time) . "n" if (DEBUG);
sleep(BEAT_PERIOD);
}

Just set the value of DEBUG to 0 if you don’t want to see the time each time we send a message to the server.

To test this you’ll have to be running the server from recipe 13.11 in the Python Cookbook. You can download a zip file of the examples in the Python Cookbook from the O’Reilly website to test this.

Python, XML and iTunes

As I’ve taken the week off work, I thought as well as spending time with my family, I’d brush up my Python skills as they’ve been a bit neglected of late.

I’ve never tried XML parsing with Python so thought I’d cover that. Apple’s iTunes has the ability to export information about your music in XML and I’d been meaning to take a look at that for a while. Why not combine the two, so here’s my take on parsing iTunes export information with Python.

I thought i’d work on a small subset of my library, the ones I’ve actually paid to download from iTunes compared to the ones converted from CD.

Rob's purchased iTunes tracks

The exported XML data is a bit peculiar. I would have assumed it to be values enclosed by sensible tag names e.g <artist>Human League</artist>. However, it’s actually a bunch of neighbouring tags and values like this <key>Artist</key><string>Sheb Wooley</string>

Here’s a snippet from the actual data export I ran…

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Major Version</key><integer>1</integer>
<key>Minor Version</key><integer>1</integer>
<key>Application Version</key><string>6.0.5</string>
<key>Features</key><integer>1</integer>
<key>Music Folder</key><string>file://localhost/D:/Documents%20and%20Settings/Windows%20User/My%20Documents/My%20Music/iTunes/iTunes%20Music/</string>
<key>Library Persistent ID</key><string>C5DD29C89369B278</string>
<key>Tracks</key>
<dict>
<key>312</key>
<dict>
<key>Track ID</key><integer>312</integer>
<key>Name</key><string>The Purple People Eater</string>
<key>Artist</key><string>Sheb Wooley</string>
<key>Album</key><string>20th Century Rocks: 50's Rock 'n Roll - At the Hop</string>
<key>Genre</key><string>Pop</string>
<key>Kind</key><string>Protected AAC audio file</string>
<key>Size</key><integer>2260837</integer>
<key>Total Time</key><integer>135533</integer>
<key>Disc Number</key><integer>1</integer>
<key>Disc Count</key><integer>1</integer>
<key>Track Number</key><integer>5</integer>
<key>Year</key><integer>2001</integer>
<key>Date Modified</key><date>2006-09-28T09:54:23Z</date>
<key>Date Added</key><date>2006-09-28T09:54:10Z</date>
<key>Bit Rate</key><integer>128</integer>
<key>Sample Rate</key><integer>44100</integer>
<key>Play Count</key><integer>11</integer>
<key>Play Date</key><integer>-1042489964</integer>
<key>Play Date UTC</key><date>2007-01-24T08:55:32Z</date>
<key>Normalization</key><integer>7764</integer>
<key>Compilation</key><true/>
<key>Artwork Count</key><integer>1</integer>
<key>Persistent ID</key><string>302B45E87F01479F</string>
<key>Track Type</key><string>File</string>
<key>Protected</key><true/>
<key>Location</key><string>file://localhost/D:/Documents%20and%20Settings/Windows%20User/My%20Documents/My%20Music/iTunes/iTunes%20Music/Compilations/20th%20Century%20Rocks_%2050's%20Rock%20'n%20Roll%20-/05%20The%20Purple%20People%20Eater.m4p</string>
<key>File Folder Count</key><integer>4</integer>
<key>Library Folder Count</key><integer>1</integer>
</dict>
<key>313</key>
<dict>
<key>Track ID</key><integer>313</integer>
<key>Name</key><string>Daisy Daisy</string>
<key>Artist</key><string>Johnny O'Tolle &#38; His Naughty Band</string>
<key>Album</key><string>Gay 90's</string>
<key>Genre</key><string>Vocal</string>
<key>Kind</key><string>Protected AAC audio file</string>
<key>Size</key><integer>2346412</integer>
<key>Total Time</key><integer>125084</integer>
<key>Disc Number</key><integer>1</integer>
<key>Disc Count</key><integer>1</integer>
<key>Track Number</key><integer>2</integer>
<key>Track Count</key><integer>10</integer>
<key>Year</key><integer>2006</integer>
<key>Date Modified</key><date>2006-09-28T09:59:52Z</date>
<key>Date Added</key><date>2006-09-28T09:59:38Z</date>
<key>Bit Rate</key><integer>128</integer>
<key>Sample Rate</key><integer>44100</integer>
<key>Play Count</key><integer>6</integer>
<key>Play Date</key><integer>-1038647848</integer>
<key>Play Date UTC</key><date>2007-03-09T20:10:48Z</date>
<key>Artwork Count</key><integer>1</integer>
<key>Persistent ID</key><string>302B45E87F01490F</string>
<key>Track Type</key><string>File</string>
<key>Protected</key><true/>
<key>Location</key><string>file://localhost/D:/Documents%20and%20Settings/Windows%20User/My%20Documents/My%20Music/iTunes/iTunes%20Music/Johnny%20O'Tolle%20&#38;%20His%20Naughty%20Band/Gay%2090's/02%20Daisy%20Daisy.m4p</string>
<key>File Folder Count</key><integer>4</integer>
<key>Library Folder Count</key><integer>1</integer>
</dict>

This makes parsing the data a bit trickier than I had hoped for. I was hoping to use a nice simple XPath expression, but data like this looks like it’s more a job for a SAX based approach.

I took a look in O’Reilly’s excellent Programming Python, and found a nice SAX parser example to modify.

As it’s just a quick test, I’m making a few assumptions on the XML data that a production system would have to handle. In this case, I’m assume a tag order of Track ID, Name and Artist. Using this order, each time we see one of those tags come past, we can make up a Track object and store the relevant data. In this case, when we see Track ID we need a new Track object to store the data in. When we see Name, we store the track name in the object and when we see Artist we save the artist, push the Track object to our list of Tracks and clear the current Track object.

That’s a bit long winded, so here’s the code.

import xml.sax.handler
class ITunesHandler(xml.sax.handler.ContentHandler):
def __init__(self):
self.parsing_tag = False
self.tag = ''
self.value = ''
self.tracks = []
self.track = None
def startElement(self, name, attributes):
if name == 'key':
self.parsing_tag = True
def characters(self, data):
if self.parsing_tag:
self.tag = data
self.value = ''
else:
# could be multiple lines, so append data.
self.value = self.value + data
def endElement(self,name):
if name == 'key':
self.parsing_tag = False
else:
if self.tag == 'Track ID':
# start of a new track, so a new object
# is needed.
self.track = Track()
elif self.tag == 'Name' and self.track:
self.track.track = self.value
elif self.tag == 'Artist' and self.track:
self.track.artist = self.value
# assume this is all the data we need
# so append the track object to our list
# and reset our track object to None.
self.tracks.append(self.track)
self.track = None
class Track:
def __init__(self):
self.track = ''
self.artist = ''
def __str__(self):
return "Track = %snArtist = %s" % (self.track,self.artist)

In the real world, the Track class would offer a lot more functionality, in this case, it’s just for holding data and providing a pretty printer.

Now we need to parse the XML and display the results, here’s the code…

parser = xml.sax.make_parser()
handler = ITunesHandler()
parser.setContentHandler(handler)
parser.parse('D:\Documents and Settings\Windows User\Desktop\Purchased.xml')
for track in handler.tracks:
print track

Let’s run that code and see what we get…

Track = The Purple People Eater
Artist = Sheb Wooley
Track = Daisy Daisy
Artist = Johnny O'Tolle & His Naughty Band
Track = Don't Dilly Dally
Artist = Kidzone
Track = Jump In My Car
Artist = David Hasselhoff
Track = Puff, the Magic Dragon
Artist = Peter, Paul And Mary
Track = You Give Love a Bad Name
Artist = Bon Jovi
Track = Heart of Glass
Artist = Blondie
Track = Grace Kelly
Artist = Mika
Track = Standing In the Way of Control
Artist = Gossip
Track = Physical
Artist = Olivia Newton-John
Track = Don't You Want Me
Artist = The Human League
Track = Have a Drink On Me
Artist = Lonnie Donegan
Track = My Old Man's a Dustman
Artist = Lonnie Donegan

That’s great! OK, I’m not going to win any awards for my taste in music, but at least I can now think about building music services that use this data.

Using A Bluetooth GPS From Python

Following on from my earlier posting on programming bluetooth from Python, I tried talking to a bluetooth GPS unit from Python.

I have a cheap MSI Starfinder SF100 bluetooth GPS that transmits NMEA format positioning information over a bluetooth socket. So, the first thing we have to do is to is to open a bluetooth socket.

import bluetooth
# bluetooth address of the GPS device.
addr = "00:08:1B:C2:AA:6D"
# port to use.
port = 1
# create a socket and connect to it.
socket = bluetooth.BluetoothSocket(bluetooth.RFCOMM)
socket.connect((addr, port))

Fingers crossed we should now be connected to the GPS unit. In the real world we’d also be checking for exceptions to make sure we really have been able to connect, but this is just a simplified example.

Now we need to receive data from the GPS.

data = ""
while True:
data = socket.recv(1024)

Here we’re initialising a holding variable called data where the received data from the bluetooth socket goes, then we create an infinate loop and continually receive data from the bluetooth socket we creataed earlier.

We should now have some data coming in, so to test it we can just print it out.

print data

Here’s what we get back.

$GPRMC,225406.537,A,5046.3972,N,00017.
3365,E,0.00,,300107,,,A*7E
$GPGGA,225407.537,5046.3972,N,00017.3365,E,1,05,1.7,55.5,
M,,,,0000*33
$GPGSA,A,3,27,10,29,28,08,,,,,,,,3.9,1.7,3.5*35
$GPRMC,225407.537,A,5046.3972,N,00017.3365,E,0.00,,300107,,,A*7F
$GPGGA,225408.537,5046.3972,N,00017.3365,E,1,05,1.7,55.5,M,,,,0000*3C
$GPGSA,A,3,27,1
0,2
9,28,08,,,,,,,,3.9,1.7,3.5*35
$GPGSV,2,1,08,10,70,218,31,08,64,067
,35,29,49,287,30,27,37,058,40*70
$GPGSV,2,2,08,26,35,283,,28,34,133,45,24,27,254,22,21,12,324,*72
$GPRMC,225408.537,A,5046.3972,N,00017.3365,E,0.00,,300107,,,A*70
$GPGGA,225409.537,5046.3972,N,00017.3365,E,1,06,1.4,55.5,M,,,
,0000*3D

Oh dear, although we are receiving data over the bluetooth socket, it’s in random packet sizes. The NMEA data we want is sent in lines terminated with a carriage return and a line feed. So how can we get the data in this format?

Well we need to use Python’s string manipulation methods. We can use the splitlines method to split the data into a list of lines. As we can’t be sure we have a complete line at the end of the list we need to check there is a carriage return and linefeed there. By default splitlines helpfully removes these, so we need to tell it to keep them in place, after all we can use the strip method later to remove them manually. So all we need to do now is to check the last line in the list has the carriage return and linefeed, if it doesn’t we need to make sure the next data received from the bluetooth socket is appended to the end before we repeat the process again. Imagine we create a holding variable called olddata at the same time as data and this is to hold a copy of the last line. Here’s the code…

# make sure we actually have some data.
if len(data) > 0:
# append the old data to the front of data.
data = olddata + data
# split the data into a list of lines, but make
# sure we preserve the end of line information.
lines = data.splitlines(1)
# iterate over each line
for line in lines:
# if the line has a carriage return and a
# linefeed, we know we have a complete line so
# we can remove those characters and print it.
if line.find("rn") != -1 :
line = line.strip()
print line
# empty the olddata variable now we have
# used the data.
olddata = ""
# else we need to keep the line to add to data
else :
olddata = line

We now have some useful data coming in…

$GPRMC,225406.537,A,5046.3972,N,00017.3365,E,0.00,,300107,,,A*7E
$GPGGA,225407.537,5046.3972,N,00017.3365,E,1,05,1.7,55.5,M,,,,0000*33
$GPGSA,A,3,27,10,29,28,08,,,,,,,,3.9,1.7,3.5*35
$GPRMC,225407.537,A,5046.3972,N,00017.3365,E,0.00,,300107,,,A*7F
$GPGGA,225408.537,5046.3972,N,00017.3365,E,1,05,1.7,55.5,M,,,,0000*3C
$GPGSA,A,3,27,10,29,28,08,,,,,,,,3.9,1.7,3.5*35
$GPGSV,2,1,08,10,70,218,31,08,64,067,35,29,49,287,30,27,37,058,40*70
$GPGSV,2,2,08,26,35,283,,28,34,133,45,24,27,254,22,21,12,324,*72
$GPRMC,225408.537,A,5046.3972,N,00017.3365,E,0.00,,300107,,,A*70
$GPGGA,225409.537,5046.3972,N,00017.3365,E,1,06,1.4,55.5,M,,,,0000*3D

As you can see the NMEA strings are simply comma seperated blocks of data.

The most useful string is the one with the actual position in. This string is the one starting with $GPRMC. If we look out for this line and split it’s data, we can get our latitude and longitude and use it as we please.

gpsstring = line.split(',')
if gpsstring[0] == '$GPRMC' :
print "Lat: " + gpsstring[3] + gpsstring[4]
print "Long: " + gpsstring[5] + gpsstring[5]

This gives us…

Lat:  5046.3972N
Long: 000017.3365

Which translates to 50 deg 46.3972′ N and 0 deg 17.3365 E.

There you have it, it’s time to start geocoding you data.

Programming Bluetooth Using Python

I discovered the pybluez project that brings bluetooth connectivity to Python today. It’s been around for a while and is compatible with both Windows (running XP) and Linux (running the bluez stack).

I installed it using the ready made windows installer and it ran first time. It’s very simple and easy to use.

I tweaked the example inquiry.py file by Albert Huang and set it up to show all the services on nearby devices.

Here’s the code…

import bluetooth
print "looking for nearby devices..."
nearby_devices = bluetooth.discover_devices(lookup_names = True, flush_cache = True, duration = 20)
print "found %d devices" % len(nearby_devices)
for addr, name in nearby_devices:
print " %s - %s" % (addr, name)
for services in bluetooth.find_service(address = addr):
print " Name: %s" % (services["name"])
print " Description: %s" % (services["description"])
print " Protocol: %s" % (services["protocol"])
print " Provider: %s" % (services["provider"])
print " Port: %s" % (services["port"])
print " Service id: %s" % (services["service-id"])
print ""
print ""

Here’s a quick run through of the code.

Firstly we need to import the bluetooth class to give us access to all that lovely bluetooth functionality.

We now look for nearby devices using bluetooth.discover_devices. I’m adding a few parameters as well. lookup_names is set to True so we can get the devices names instead of the just their addresses, and flush_cache is also set to True to make sure we always look for fresh information. Finally we set duration to 20, meaning we look for devices for up to 20 seconds. This is a bit excessive, but useful when testing.

We should have a list of devices now, assuming some were found, so we now loop over them.

We print out the name and address then look for services offered by the device by calling the bluetooth.find_service method and passing in the device’s address.

This returns a list of dictionaries describing the services available. We iterate over this and print out details.

That’s the code, now here’s a few results. I ran this on the train home and discovered 7 active bluetooth devices offereing a variety of services. I won’t bore you with them all, so here’s the entry for my Nokia N93.

Perl - 00:16:BC:30:D8:76
Name:        AVRCP Target
Description: Audio Video Remote Control
Protocol:    L2CAP
Provider:    Symbian Software Ltd.
Port:        23
Service id:  None
Name:        Hands-Free Audio Gateway
Description:
Protocol:    RFCOMM
Provider:    None
Port:        28
Service id:  None
Name:        Headset Audio Gateway
Description:
Protocol:    RFCOMM
Provider:    None
Port:        29
Service id:  None
Name:        SyncMLClient
Description:
Protocol:    RFCOMM
Provider:    None
Port:        10
Service id:  None
Name:        OBEX File Transfer
Description:
Protocol:    RFCOMM
Provider:    None
Port:        11
Service id:  None
Name:        Nokia OBEX PC Suite Services
Description:
Protocol:    RFCOMM
Provider:    None
Port:        12
Service id:  None
Name:        SyncML DM Client
Description:
Protocol:    RFCOMM
Provider:    None
Port:        13
Service id:  None
Name:        Nokia SyncML Server
Description:
Protocol:    RFCOMM
Provider:    None
Port:        14
Service id:  None
Name:        OBEX Object Push
Description:
Protocol:    RFCOMM
Provider:    None
Port:        9
Service id:  None
Name:        Dial-Up Networking
Description:
Protocol:    RFCOMM
Provider:    None
Port:        2
Service id:  None
Name:        Imaging
Description:
Protocol:    RFCOMM
Provider:    None
Port:        15
Service id:  None

As you can see it offers a lot of interesting services, but two have caught my eye and call for a bit more investigation later.

Firstly there is the Imaging service on port 15. I wonder what that does? Does it take input from a remote camera?

Secondly there is AVRCP Target the Audio Visual Remote Control Target from Symbian. I wonder if this means I can use a seperate bluetooth device to control my Nokia N93 when it’s playing back video?

A quick google reveals a (confidential?) specification document for AVRCP

Using Series 60 Python To Talk To A Webserver

I’ve been continuing my journey into Python by modifying my earlier location script to now post details to my website when run.

I’m using the standard urllib now and moving to slightly more advanced Python language features such as dictionaries (basically the equivalent of a Perl hash).

The code gets the location, calls a CGI script on my website and prints the returned message from that script to the console.

Here’s the code…

# we need access to the location and urllib modules so have to
#import them.
import location
import urllib
# get the mmc, mnc, lac and cellid by calling the gsm_location
# method from the location module.
(mmc, mnc, lac, cellid) = location.gsm_location()
# add the location to a python dictionary called params.
params = urllib.urlencode({'mmc': mmc, 'mnc': mnc, 'lac': lac, 'cellid': cellid})
# open a filehandle to a cgi tracking script that takes
# the contents of dictionary params as parameters.
f = urllib.urlopen("http://www.robertprice.co.uk/cgi-bin/test/cellid.pl?%s" % params)
# get the results, hopefully a message saying OK.
print f.read()

All the CGI script on my website does is to record the parameters passed to it and return a simple text string saying it ran OK.

As you can see it’s really simple to Series 60 Python to communicate over the phone’s network connection. I have to say, this language is turning out to be better than I had expected, and far easier than J2ME.

My First Python Script

It’s a full day since I started to try to teach myself Python for my Nokia 3230 phone.

My first script is running, and though it isn’t anything impressive, I’m still very pleased with the results.

So what is this amazing script? Well it just gets the current location of the phone by CellID, using the location module supplied with Python, and displays it on the console.

Here’s the code…

# we need access to the location module so have to import it.
import location
# get the mmc, mnc, lac and cellid by calling the gsm_location
# method from the location module.
(mmc, mnc, lac, cellid) = location.gsm_location()
# print out the retrieved details to the console.
print "mmc %sn" % mmc
print "mnc %sn" % mnc
print "lac %sn" % lac
print "cellid %sn" % cellid

screenshot of the python location script results
As you can see it’s not going to win any prizes at PyCon, but it does provide some very practical information for me, namely my location.

But what are those 4 variables returned?

  • MCC = Mobile Country Code
  • MNC = Mobile Network Code
  • LAC = Location Area Code
  • Cell ID = The Cell’s ID 🙂

Stay tuned for more Python adventures over the coming weeks.