Tag Archives: TC65

The M2MP protocol

Today, I’m going to talk about a little protocol that I’ve implemented in few softwares (on TC65, .Net client and .Net server). It won’t be very interesting for most of you (99.99%). But still, I wanted to write it because it has been very useful and it might prevent you from writing the next stupid M2M protocol specifications.

Motivations

I made this simple protocol to solve very simple and common problems on M2M equipments, we want to :

  • Transmit and receive in real-time
  • Transmit as few data as possible
  • Send and receive any kind of data

So the basic ideas are to :

  • Keep a TCP connection open all the time by sending regular keep-alive frames.
  • Define named channels that are only transmitted once
  • Send byte array, and two-dimensionnal byte arrays on these channels

We rely on the TCP protocol for packet checksuming, packet ordering and every little things it does very well.

Basically, we send these kind of frames :

  • Identification frames
  • Channel name to id definition
  • Data transmission of single byte arrays (Byte[]).
  • Data transmission of two-dimentionnal byte arrays (Byte[][]).

The protocol

Identification
The client ask for authentication :

1
2
3
[ 1 ] { 0x01 } // Identification header
[ 1 ] { 0x05 } // Size of the identification
[ 5 ] { 'h', 'e', 'l', 'l', 'o' } // Identifier

The server replies “ok” or “not ok”

1
2
[ 1 ] { 0x01 } // Identification request
[ 1 ] { 0x01 } // OK, 0x00 for not ok

Ping / Keep alives
Client sends a ping request :

1
2
[ 1 ] { 0x02 } // client ping request header
[ 1 ] { 0x15 } // the number (here 0x15) can be incremented or random

Server answers the ping request :

1
2
[ 1 ] { 0x02 } // client ping server response header
[ 1 ] { 0x15 } // where 0x15 is the number of the ping request

Server sends a ping request :

1
2
[ 1 ] { 0x03 } // server ping request header
[ 1 ] { 0x16 } // the number (here 0x16) can be incremented or random

Client answers the ping request :

1
2
[ 1 ] { 0x03 } // server ping client response header
[ 1 ] { 0x16 } // where 0x16 is the 0x16 number of the ping request

Defining a named channel
This applies to both client to server and server to client communcation.

1
2
3
4
[ 1 ] { 0x20 } // channel definition frame header
[ 1 ] { 0x0D } // where 13 is the size of the following message
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 12 ] { 'c', 'h', 'a', 'n', 'n', 'e', 'l', ' ', 'n', 'a', 'm', 'e' } // the name of the channel : "channel name"

Sending some data
This applies to both client to server and server to client communcation.

For 0 to 254 sized messages :

1
2
3
4
[ 1 ] { 0x21 } // one byte sized data transmission frame header
[ 1 ] { 0x06 } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 5 ] { 0x01, 0x02, 0x03, 0x04, 0x05 } // data transmitted on the channel

For 255 to 65534 sized data messages :

1
2
3
4
[ 1 ] { 0x41 } // two bytes sized data transmission frame header
[ 2 ] { 0x00, 0x06 } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 5 ] { 0x01, 0x02, 0x03, 0x04, 0x05 } // data transmitted on the channel

For 65 535 octets to 4 294 967 294 sized data messages :

1
2
3
4
[ 1 ] { 0x61 } // two bytes sized data transmission frame header
[ 4 ] { 0x00, 0x00, 0x00, 0x06 } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 5 ] { 0x01, 0x02, 0x03, 0x04, 0x05 } // data transmitted on the channel

For data arrray transmission, it’s the same except frame header are 0x22, 0x42, 0x62 instead of 0x21, 0x41, 0x61. If you transmit data array like this one Byte[][] data = { { 0x01, 0x02 }, { 0x03, 0x04, 0x05 }, { 0x06, 0x07, 0x08, 0x09 } } :

1
2
3
4
5
6
7
8
9
[ 1 ] { 0x22 } // one byte sized data transmission frame header
[ 1 ] { 0x0D } // size of the data
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 1 ] { 0x02 } // size of the first array
[ 2 ] { 0x01, 0x02 } 
[ 1 ] { 0x03 } // size of the second array
[ 3 ] { 0x03, 0x04, 0x05 } 
[ 1 ] { 0x04 } // size of the third array
[ 4 ] { 0x06, 0x07, 0x08, 0x09 }

For bigger arrays, you need to define :

1
2
3
4
5
6
7
8
9
[ 1 ] { 0x42 } // one byte sized data transmission frame header
[ 1 ] { 0x10 } // size of the data (16)
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 2 ] { 0x00, 0x02 } // size of the first array
[ 2 ] { 0x01, 0x02 } 
[ 2 ] { 0x00, 0x03 } // size of the second array
[ 3 ] { 0x03, 0x04, 0x05 } 
[ 2 ] { 0x00, 0x04 } // size of the third array
[ 4 ] { 0x06, 0x07, 0x08, 0x09 }

and so on :

1
2
3
4
5
6
7
8
9
[ 1 ] { 0x62 } // one byte sized data transmission frame header
[ 1 ] { 0x16 } // size of the data (22)
[ 1 ] { 0x03 } // where 3 is the id of the channel
[ 4 ] { 0x00, 0x00, 0x00, 0x02 } // size of the first array
[ 2 ] { 0x01, 0x02 } 
[ 4 ] { 0x00, 0x00, 0x00, 0x03 } // size of the second array
[ 3 ] { 0x03, 0x04, 0x05 } 
[ 4 ] { 0x00, 0x00, 0x00, 0x04 } // size of the third array
[ 4 ] { 0x06, 0x07, 0x08, 0x09 }

Sample data types transmitted

Strings
Easy… Just the bytes value of the string

Position
when moving :

1
2
3
4
5
[ 4 ] Timestamp : UInt32 (since 1970 or 2009 if you wish)
[ 4 ] Longitude : Float
[ 4 ] Latitude : Float
[ 2 ] Speed : UInt16
[ 2 ] Altitude : UInt16

= 16 bytes

when stopped :

1
2
3
[ 4 ] Timestamp : UInt32 (since 1970 or 2009 if you wish)
[ 4 ] Longitude : Float
[ 4 ] Latitude : Float

= 12 bytes

Battery
Percentage :

1
[ 1 ] Percentage (Byte)

Voltage :

1
[ 2 ] Voltage in mV (UInt16)

Sample communication

This is just to show how is working a sample communication

1
2
3
4
--> [] { 0x01, 0x04, 0x01, 0x02, 0x03, 0x04 } // identification frame
<-- [] { 0x01, 0x01 } // identification accepted
--> [] { 0x20, 0x08, 0x00, 'b', 'a', 't', 't', 'e', 'r', 'y' } // We define the channel "battery" with id 0x00
--> [] { 0x21, 0x02, 0x00, 70 } // We send 70 (meaning 70%) on the "battery" channel.

On top of that

On top of that, I added some settings management and status specifications.

Settings management
The server can get and set some settings on the “_set” channel.
If it’s a two-dimentionnal array and the first element is “s” (like “set”) it means that it sets some settings, the next elements will be “setting1=value1”, “setting2=value2”, etc.
If it’s a two-dimentionnal array and the first element is “g” (like “get”) it means that it needs the client to report the value of some settings, like “setting1”, “setting2”
If it’s a single dimentionnal array that contains “ga” (like “get all”) it means that the server wants the client to report the value of every settings.
If the client doesn’t know some settings that were sent to it, it can report it using “u”.
The client can also send “c” (in the same format as the get) asynchronously if a setting changed on the client side.

Sample transmission:
Setting values:

1
 --("_set")--> { "s", "setting1=value1", "setting2=value2" }

It’s the responsibility of the server to send an acknowledge request to know that the client has actually successfully received this settings.

Getting values:

1
2
 --("_set")--> { "g", "setting1", "setting2" }
<--("_set")--  { "g", "setting1=value1", "setting2=value2" }

Setting and gettings value :

1
2
 --("_set")--> { "sg", "setting1=value1", "setting2=value2" }
<--("_set")--  { "g", "setting1=value1", "setting2=value2" }

Gettings all the values :

1
2
 --("_set")--> "ga"
<--("_set")--  { "g", "setting1=value1", "setting2=value2" }

Sending a value that is unknown to the client :

1
2
 --("_set")--> { "sg", "setting1=value1", "setting98=value98", "setting99=value99" }
<--("_set")--  { "g", "setting1=value1", "setting98", "setting99" }

Same since here except we don’t specify the get. The client might decide to tell us the setting is unknown.

1
2
 --("_set")--> { "s", "setting1=value1", "setting98=value98", "setting99=value99" }
<--("_set")--  { "u", "setting98", "setting99" }

Status parameters management
The server can get some status parameters on the “_sta” channel. Status are pretty much like settings except that they describe parameters that cannot be changed from the server.

Getting a parameter:

1
2
 --("_sta")--> { "g", "version" }
<--("_sta")--  { "g", "version=0.0.1" }

Getting all parameters:

1
2
 --("_sta")--> { "ga" }
<--("_sta")--  { "g", "hw=tc65i/1.1", "sw=sampleprogram/0.0.1", "cap=sms,applicationA/1.0" }

Value changed on the client side

1
<--("_set")-- { "c", "setting1=value1" }

Capacities
Previously, capacities where requested by server with the “?” message on the “_cap” channel but they are now handled through the standard status message and the “cap” parameter.

1
2
 --("_sta")--> { "g", "cap" }
<--("_sta")--  { "g", "cap=sms_relay_send_1,gpio_1" }

Command execution

1
2
3
4
// To send a command
 --("_cmd")--> { "e", "<commandId>", "command", "arg1", "arg2" }
// To acknowledge the command
<--("_cmd")--  { "a", "<commandId>" }

In the future commands could algo be acknowledge as a group by sending an acknowledge request.

Files transmission
The server sends:

1
<--("_fi:u")-- { byte[5] = "file1", byte[4] = (uint32_t) file1Size, byte[16] = file1Hash, byte[5] = "file2", byte[4] = (uint32_t) file2Size, byte[16] = file2Hash }

If the client wants to get the detailed subhashes:

1
2
 --("_fi:h")--> { byte[5] = "file1", byte[4] = (uint32_t) size, byte[4] = (uint32_t) offset1, byte[4] = (uint32_t) offset2 }
<--("_fi:h")--  { byte[5] = "file1", byte[4] = (uint32_t) size, byte[4] = (uint32_t) offset1, byte[16] = hash, byte[4] = (uint32_t) size, byte[16] = hash }

Then if the client has a different value of the files:

1
2
 --("_fi:g:file1")--> { byte[4] = (uint32_t) offset, byte[4] = (uint32_t) size }
<--("_fi:g:file1")--  { byte[4] = (uint32_t) offset, byte[ x ] = data, byte[16] = file1Hash }

Publish/Subscribe (MQTT like)
You could subscribe to a channel “toto” using:

1
2
 --("_pubsub")--> { "s" , "toto" }
<--("_pubsub")--  { "sa", "toto", [ 0x01 ] }

You could unsubscribe to a channel “toto” using:

1
2
 --("_pubsub")--> { "u", "toto" }
<--("_pubsub")--  { "ua", "toto", [ 0x01 ] }

And the rest is left like this.

Gateway acting and sub equipements
Any equipment can act as a gateway by encapsulating data of an sub-equipment in a channel name like this “_eq/[sub equipment identifier]” but this has never been used for the moment.

Every packet coming from the sub-equipment is relayed to the server as a data packet.
If the equipment, identified locally by “mac:112233445566” sends an identification to the server, this will work like this :
Subequipment to Equipment:

1
[ 0x01 0x04 't' 'o' 't' 'o' ]

This will be relayed to the server as data message like this :

1
--("_eq/mac:112233445566")--> [ 0x01, 0x04, 't', 'o', 't', 'o' ]

Meaning in raw data :

1
[0x21 0x07 0x03 0x01 0x04 't' 'o' 't' 'o' ]

(where 0x03 is the generated channel id)

Stupid protocols

I’ve seen a lot of protocols from big companies doing some really stupid things like :

  • Fixed size frames for not fixed size data.
  • 4 zero filled bytes preamble. Why the hell would we need preamble in a TCP connection ?
  • 8 bytes timestamp in millisecond when data has a 1 second precision.
  • 4 bytes integer for specifying some number that never exceed 32.
  • Checksums on top of the TCP checksumming mechanism.
  • Redundant data at the beginning and the end.
  • Disconnecting very frequently (TCP establishment + identification costs a lot).

TC65 : Settings management

Someone recently asked me and the javacint group how we should handle settings. So I’ll give you two answers :

  • ejw’s reply : You should use some simple DataInputStream and DataOutputStream objects.
  • Mine : If you only store simple values (numbers and text), you should use a simple text file. It enables you to easily see and modify settings outside the TC65.

So, my little gift of today will be a simple settings management class. The idea is that this file is in a very simple format (that can be read in any PC) and it only stores settings that have changed. This is very important, it allows you to change the default behavior at next software update but also enable you to override some of the settings for each chip (like the last IMSI identifier of the SIM card).

I did something wrong in this example by writing all the methods with a starting Uppercase letter. The reason is that I do like a it a lot better like that (this is the C# style) but style, the good thing to do is to stick to each language’s coding conventions. I can’t really update this code as I have modified this class to a more complex version where other class register to this one to receive settings change event.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
package Common;
 
import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import com.siemens.icm.io.file.*;
 
/**
 * Settings management class
 * @author Florent Clairambault
 */
public class Settings {
 
        // The cached settings
	private Hashtable _settings;
 
        // File of the settings
	private final String _fileName = "a:/settings.txt";
 
        // Singleton instance of the settings
        private static Settings _instance;
 
	/**
	 * Get Default instance of the Settings class
	 * @return The default instance of the Settings class
	 */
	public static synchronized Settings getInstance() {
		if ( _instance == null )
			_instance = new Settings();
		return _instance;
	}
 
	/**
	 * Free the singleton instance
	 */
	public static synchronized void freeInstance() {
		_instance = null;
	}
 
	/**
	 * Load settings
	 */
	public synchronized void load() {
		StringBuffer buffer = new StringBuffer();
		Hashtable settings = getDefaultSettings();
		try {
 
 
			FileConnection fc = (FileConnection) Connector.open( "file:///" + _fileName, Connector.READ );
			InputStream is = fc.openInputStream();
 
			while ( is.available() > 0 ) {
				int c = is.read();
 
				if ( c == '\n' ) {
					loadTreatLine( settings, buffer.toString() );
					buffer = new StringBuffer();
				} else
					buffer.append( (char) c );
			}
			is.close();
			fc.close();
 
		} catch ( IOException ex ) {
			// The exception we shoud have is at first launch : 
			// There shouldn't be any file to read from
 
			if ( Logger.E_CRITICAL )
				Logger.Log( 19, "Settings.Load", ex );
		}
		_settings = settings;
	}
 
	/**
	 * Treat each line of the file
	 * @param def Default settings
	 * @param line Line to parse
	 */
	private static void loadTreatLine( Hashtable settings, String line ) {
		if ( Logger.E_VERBOSE )
			Logger.Log( 78, "loadTreatLine( [...], \"" + line + "\" );" );
		String[] spl = Common.strSplit( '=', line );
		String key = spl[0];
		String value = spl[1];
 
		// If default settings hashTable contains this key
		// we can use this value
		if ( settings.containsKey( key ) ) {
			settings.remove( key );
			settings.put( key, value );
		}
 
	}
 
 
	/**
	 * Get default settings
	 * @return Default settings Hashtable
	 */
	private Hashtable getDefaultSettings() {
		Hashtable defaultSettings = new Hashtable();
 
		// General M2MSoft settings :
		defaultSettings.put( "code", "8888" );
		defaultSettings.put( "servers", "87.106.206.30:3000" );
		defaultSettings.put( "apn", "gprs,m2minternet,\"\",\"\",,0" );
		defaultSettings.put( "imsi", "0000" );
		defaultSettings.put( "watchdogtimer", "20" );
		defaultSettings.put( "version", "0" );
		defaultSettings.put( "phoneManager", "+33686955405" );
 
		return defaultSettings;
	}
 
	/**
	 * Reset everything
	 */
	public synchronized void resetEverything() {
		try {
			FileConnection fc = (FileConnection) Connector.open( "file:///" + _fileName, Connector.READ_WRITE );
			if ( fc.exists() )
				fc.delete();
                        _settings = new Hashtable();
		} catch ( Exception ex ) {
			if ( Logger.E_CRITICAL )
				Logger.Log( 16725, "Settings.ResetEverything", ex );
		}
	}
 
	/** 
	 * save setttings
	 */
	public synchronized void save() {
 
		// If there's no settings, we shouldn't have to save anything
		if ( _settings == null )
			return;
 
		try {
			Hashtable defSettings = getDefaultSettings();
			Enumeration e = defSettings.keys();
			FileConnection fc = (FileConnection) Connector.open( "file:///" + _fileName, Connector.READ_WRITE );
			if ( fc.exists() )
				fc.delete();
			//fc = (FileConnection) Connector.open("file:///" + _fileName, Connector.READ_WRITE);
			fc.create();
			OutputStream os = fc.openOutputStream();
 
			while ( e.hasMoreElements() ) {
				String key = (String) e.nextElement();
				String value = (String) _settings.get( key );
				<a href="http://www.google.com/search?hl=en&q=allinurl%3Astring+java prix viagra pharmacie france.sun.com&btnI=I%27m%20Feeling%20Lucky">String defValue = (String) defSettings.get( key );
 
				if ( // if there is a default value
					defValue != null && // and
					// the value isn't the same as the default value
					defValue.compareTo( value ) != 0 ) {
					String line = key + "=" + value + '\n';
 
					if ( Logger.E_DEBUG )
						Logger.Log( 131, "Settings.save.line = \"" + line + "\"" );
 
					os.write( line.getBytes() );
				}
 
			}
			os.flush();
			os.close();
			fc.close();
		} catch ( Exception ex ) {
			if ( Logger.E_CRITICAL )
				Logger.Log( 131, "Settings.save", ex );
		}
 
	}
 
	/**
	 * Init (and ReInit) method
	 */
	private void checkLoad() {
		if ( _settings == null )
			load();
	}
 
	/**
	 * Get a setting's value as a String
	 * @param key Key Name of the setting
	 * @return String value of the setting
	 */
	public synchronized String getSetting( String key ) {
		checkLoad();
		if ( _settings.containsKey( key ) )
			return (String) _settings.get( key );
		else
			return null;
	}
 
	/**
	 * Sets a setting
	 * @param key Setting to set
	 * @param value Value of the setting
	 */
	public synchronized void setSetting( String key, String value ) {
		checkLoad();
		if ( _settings.containsKey( key ) )
			_settings.remove( key );
 
		_settings.put( key, value );
 
		OnSettingChanged( key );
	}
 
	/**
	 * Get a setting's value as an int
	 * @param key Key Name of setting
	 * @return Integer value of the setting
	 * @throws java.lang.NumberFormatException When the int cannot be parsed
	 */
	public int getSettingInt( String key ) throws NumberFormatException {
		String value = getSetting( key );
 
		if ( value == null )
			return -1;
 
		return Integer.parseInt( value );
	}
}

Latter on, I added some settings consumer capacities. It allows to use this class in some sort of “third party” developed components. Each component had to register itself to the settings management class, the settings management class could then request the default value settings it will provide, and it could set/get settings. The settings event class also launched an event when a setting was changed.

My dear visitors

Hi my dear visitors,

Some of you seem to like (or fall by mistake in) my unfunny, unspiritual and crappy english speaking blog. I’m very glad you have such bad taste. You are more than 2 500 to come each month. You mostly come about some stuffs around the TC65, SVN, C# .Net or Sharepoint.

By the way, you consumed more than 200 GB of bandwidth this month, mostly on downloading the TC65 SDK. I’m really surprised, I guess a lot of people use this chip even if nearly nobody writes about it.

TC65 HTTP POST request

This is just a quick code to show how to do a simple http POST request from a TC65 chip.

    private void httpCheck() {
        String data = "Parameter1=000009960623185&Parameter2=$GPWPL,4807.038,N,01131.000,E,WPTNME*5C";
        String url = "http://test.webingenia.com/postIdent";
        HttpConnection conn = null;
        InputStream is = null;
        OutputStream os = null;
        try { // Test HTTP connection

            // We prepare the POST request
            conn = (HttpConnection) Connector.open( url );
            conn.setRequestMethod( HttpConnection.POST );
            conn.setRequestProperty( "Content-Type", "application/x-www-form-urlencoded" );
            os = conn.openOutputStream();
            os.write( data.getBytes() );

            // We launch the request
            System.out.println( "Response code : " + conn.getResponseCode() );

            // We display the generated content
            is = conn.openInputStream();
            int ch;
            System.out.println( "Output : " );
            while ( (ch = is.read()) != -1 )
                System.out.print( (char) ch );

        } catch ( Exception ex ) {
            System.out.println( "Http : ex : " + ex.getClass() + " : " + ex.getMessage() );
            ex.printStackTrace();
        } // Whatever happens, we close everything
        finally {
            try {
                if ( conn != null )
                    conn.close();
                if ( is != null )
                    is.close();
                if ( os != null )
                    os.close();
            } catch ( Exception ex ) {
                System.out.println( "Http : ex2 : " + ex.getClass() + " : " + ex.getMessage() );
                ex.printStackTrace();
            }
        }
    }

Server-Side HTTP Request tester

I made this simple test website for those of you who would want to check if their HTTP request made from a simple equipment like the TC65 are sent correctly.

The idea is simple :

You can contact me if you don’t understand something, if something doesn’t work or if you would like me to add a feature.

.Net, Flex and WebORB.Net

I’ve been working on a project where we had to use Flex on a .Net environment and it had to be realtime. I was a little worried at first that it could be tricky to set up. But with WebORB.Net it’s pretty easy. We used the integrated RTMP (Real Time Messaging Protocol) messaging server. It’s almost like using WCF. The most important differences are that, by default, objects are transmitted as Hashtable and calls can’t be synchronous. We can bind the object to right .Net object within the WebOrb management console but we decided to do it ourself using reflection (because we don’t like to depend too much on the management console).

RTMP
The result is pretty impressive as it makes a really powerful real-time management system. And it’s freaking fast. Like WCF, it can handle concurrent calls. It’s in fact easier because everything is asynchronous but still, it does manage that. These calls are received on parallel threads.

The only problem with WebORB.Net is that you don’t have too much examples in the documentation. Our biggest problem was that we didn’t even knew how to detect what was the current calling connection on a method. This is pretty easy but you can’t exactly guess it. This is a dummy sample to show you how it’s done :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class FlexConnectionsServer:ApplicationAdapter {
	private int _connectionIdCounter = 0;
	private Dictionary<IConnection,int> _connectionsToId = new Dictionary<IConnection,int>();
 
	// Returns the CURRENT connection (must be used from a method called by the flex client)
	private IConnection CurrentConnection { get { return ConnectionHub.getConnectionLocal(); } }
	private int CurrentID { get { return _connectionsToId[ CurrentConnection ]; } }
 
	// When the RTMP service is loaded, we start the time dispatcher thread
	public override bool appStart( IScope app ) {
		new Thread( Thread_TimeDispatcher ).Start();
	}
 
	// This method is called when a client connects
	public override bool appConnect( IConnection conn, object[] parms ) {
		lock ( _connectionsToId ) {
			_connectionsToId.Add( conn, _connectionIdCounter++ );
		}
 
		return true;
	}
 
	// This method is called when a client disconnects
	public override void appDisconnect( IConnection conn ) {
		lock ( _connectionsToId ) {
			_connectionsToId.Remove( conn );
		}
	}
 
	// This is a sample method call
	public String GetMyName() {
		return String.Format( "Client{0}", CurrentId );
	}
 
	// This thread dispatches the current time to every client
	private void Thread_TimeDispatcher() {
		while( true ) {
			var t = DateTime.UtcNow;
			SendCommand( "CurrentTime", new Object[] { t } );
			Thread.Sleep( 1000 );
		}
	}
 
	// Call a remote method on each client connection
	private void SendCommand( String command, Object[] params ) {
		lock ( _connectionsToId ) {
			foreach( var kvp in _connectionsToId )
				( kvp.Key as IServiceCapableConnection ).Invoke( command, params );
		}
	}
}

It’s a little bit like using WCF in Single instance mode.

IIS 7.0 Setup
There was something really weird that happened with my IIS 7.0 on my Windows 7 x64. Using the RTMP connection, I frequently had null return values on .Net method calls. We firstly find a quick way to solve it : Recall each time we get a null value. It’s really dirty, but It worked as a very temporary solution.
Then, when debugging on the IIS process, I noticed a lot of NullReferenceException exceptions displayed on the debugger console. They were about the WebORB “LicenseManager” class, and I had as much exceptions as I had null values on the Flex client. I tried a lot of things that didn’t do anything. And then, I changed the “Managemd pipeline mode” of the Application Pool from “Integrated” to “Classic”, and the problem was solved. The really weird stuff here is that WebORB recommends to use the “Integrated” mode.

MidnightCoders support
We asked MidnightCoders to send us a community edition license, and they instantly answered. We got our license within the 48 hours after our first mail (I was the one slowing it down).

Quick note on the project
To give you a better view of the project I’m talking about :
GPRS Equipment <--(M2MP protocol)–> [Generic M2M Server] <--(WCF)--> [Business logic software] <--(WCF)--> [Flex connections server] <--(RTMP)--> Flex
We’ve got a little equipment (TC65 based as you can guess) that connects to a generic server using our own (real-time and bandwidth optimized) protocol. A business logic software connects to this server using a WCF service. And then we have the WebORB.Net / IIS that connects to this business logic software.

WCF helps make very interesting .Net applications because it simplifies all the communication but can also create deadlocks you might not have seen. Take this example : You have a lock on the WCF Server on the clients list object, you make a call to the WCF client and this client makes a call to the WCF Server that also requires a lock. If you were doing all this client/server calls on the same process this wouldn’t be a problem. But as you use WCF client-to-server and server-to-client calls are made on differents threads. You create a deadlock (unless you set the OperationContract attribute to IsOneWay and/or you fix the deadlock source).

If you’re doing real-time applications, I would recommend to define all the frequently used methods with the “IsOneWay=true” option.

06/11/2009 Update : I added a sample server-to-client code because some people were interested by it.

TC65 Module Exchange Suite (MES) problems

WARNING: All the Cinterion related content from this blog will be removed to go to the javacint wiki soon. Please get used to going there.

Update (2011-06-28):

You should look at the jOBEXFtp program if you don’t want to spend hours fixing a problem with a crappy software.

When you want to locally deploy software on your TC65 chip, you need the Module Exchange Suite (MES). If it doesn’t work, it won’t tell you why, finding out why can be quite tricky.

If you want to find out why the communication with your TC65 failed, the best way is to use the portmon tool and see what is exchanged between the MESServer.exe and the chip.

I collected 4 portmon captures that could help you diagnose your MES communication problems :

  • Here is a successful MES files listing at 19200 bps. You can see that it sends the “AT+CGMM” at different bit rates unless it gets a good response. Then it sets some serial communication options. And then it sends the “AT^SQWE=100” commands which fails and then establish a successful link using the “AT^SQWE=3” command.
  • Here is a failed MES file listing. Here, after the “”AT^SQWE=3″” is sent, MES sends a 26 bytes message and receives a 41 bytes message from the chip. Then it should request the file listing. But here, MES doesn’t request anything. Maybe MES doesn’t work well, or maybe the reply from the chip doesn’t satisfy his requirements.
    Anyway, after that it should send a 27 bytes message containing : “x-obex/folder-listing”, and it should get the file listing. But it doesn’t. It reads on the serial port like if it was still waiting for something. And finally, it closes the connection (with “+++”).
  • Here is a totally failed communication. Here it’s because the modem gets stuck on waiting for the modem hardware flow control (“IOCTL_SERIAL_GET_MODEMSTATUS”) when it’s only a 3 wires (TX,RX,GND) communication. So MES never reads or writes anything.
  • Here is communication that failed by not receiving anything. Everything seems ok but no data was received. It’s quite likely that the RX cable is disconnected.

If you have the same kind of problem, you can send me your portmon capture file.

SMSOTAP v1.2.1

WARNING: All the Cinterion related content from this blog will be removed to go to the javacint wiki soon. Please get used to going there.

I have just made a little update to the SMSOTAP program so that the little message generation wizzard can add the NETUSER and NETPWD parameters in the OTAP SM sent to the TC65. Thank you John for this little feature request. This pretty useful, without it you could end up adding a new SM and editing each message.

By the way, I have tested this program on Linux with mono and it works fine.

SMSOTAP 1.2

I made some few changes to the SMSOTAP program for the TC65 :

  • I removed the time limit, it’s stable enough to do not force you to update it frequently.
  • It now uses class 1, PID 7d messages instead of class 0, PID 00 compatibility mode (it doesn’t change anything).
  • It generates OTAP SMS only with the parameters you specifiy and use as few SMS as possible. Most of the time, you can use only one OTAP SMS
  • It will prevent you from sending SMS above 140 chars in 8 bits and 160 chars in 7 bits.
  • I fixed a little bug, I replaced “APORNUM:” by “APNORNUM:”

Configuration file from the previous v1.1 version will still work on this one.

[ Download SMSOTAP 1.2 ]

Thanks to Martijn for his comments on the program. You can send me comments if you would like to see new features added or some bugs corrected.

One weird bug on the XT75

WARNING: All the Cinterion related content from this blog will be removed to go to the javacint wiki soon. Please get used to going there.

You might have faced this error with your X765 chip. The program crashes with this error :

1
2
3
^EXIT 00010000,02d6414253485f4c434c2c70726f6365647572655265636f72645f702d3e6e756d6265724f66526567697374657265645461736b73203c3d204d41585f4e4f5f524547495354455245445f5441534b53
 
^SHUTDOWN

If you convert the hex array to some text, that will give you :

1
#ABSH_LCL,procedureRecord_p->numberOfRegisteredTasks <= MAX_NO_REGISTERED_TASKS

Which might mean something to someone. But the point is, you won’t find any help. Mostly because it’s an uncommon error.

The error comes from the GPRS connection management. I had it when I was using a wrong APN (with the “AT^SJNET” command) to connect to a host. In my program, connection failed with a classic IOException (“Profile not found”) but 1 or 2 minutes later, the chip was ALWAYS crashed (with the “^EXIT” URC). So, the only solution I found to correct this problem was to automatically detect which APN is required.

This might also improve the ease of deployment of your programs. I like putting as much as possible auto-detection / auto-configuration code as possible. It takes a little time to write it but saves a lot of troubles (last minute configuration, human errors, human explanations, etc.).

The APN auto-detection code is like that :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public static String AutoDetectApn( ATCommand atc ) {
 
    String[] apnList = {
        "\"gprs\",\"objcobytel.com\",\"\",\"\",\"88.191.11.20\",0", // Bouygues Telecom : "Objet communiquant" / "ObjetCo" / "ObjCo"
        "\"gprs\",\"m2minternet\",\"\",\"\",\"88.191.11.20\",0",
        "\"gprs\",\"internet-entreprise\",\"orange\",\"orange\",\"88.191.11.20\",0",
        "\"gprs\",\"a2bouygtel.com\",\"\",\"\",\"88.191.11.20\",0",
        "\"gprs\",\"b2bouygtel.com\",\"\",\"\",\"88.191.11.20\",0",
        "\"gprs\",\"ebouygtel.com\",\"\",\"\",\"88.191.11.20\",0",
        "\"gprs\",\"movistar.es\",\"movistar\",\"movistar\",\"88.191.11.20\",0",
        "\"gprs\",\"orange\",\"orange\",\"orange\",\"88.191.11.20\",0",
        "\"gprs\",\"orange.fr\",\"orange\",\"orange\",\"88.191.11.20\",0",
        "\"gprs\",\"websfr\",\"\",\"\",\"88.191.11.20\",0"
    };
 
 
   synchronized (atc) {
        System.out.println( "Waiting 30s..." );
        try {
            Thread.sleep( 30000 );
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
 
        for (int i = 0; i < apnList.length; ++i) {
            try {
                String apn = apnList[i];
                System.out.println( "Trying apn " + apn + "..." );
                atc.send( "AT^SJNET=" + apn + "\r" );
 
                SocketConnection conn = (SocketConnection) Connector.open( "socket://88.191.11.20:80" );
                conn.close();
                return apn;
            } catch (Exception ex) {
                System.out.println( "Failed : " + ex.getClass() + " : " + ex.getMessage() );
            }
        }
    }
 
 
    // We couldn't find any APN
    return null;
 
}

And it is only launched when the SIM Card has been changed. To detect that, I have some simple code like this :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{ // APN auto-detection
 
    String imsi;
 
    synchronized (atc) {
         imsi = Common.getIMSI( atc );
    }
 
    if (settings.confLastImsi.compareTo( imsi ) != 0) {
        System.out.println( "Sim card changed ! Auto-detecting APN..." );
 
        synchronized (atc) {
            String apn = Common.AutoDetectApn( atc );
 
            if (apn != null)
                reg.confAPN = apn;
        }
 
        settings.confLastImsi = imsi;
 
        // Whatever happens, we still need to save the current IMSI
        settings.Save();
    }
}

Reference :
The forum that helped me solve this problem
Translating Hex to Text