-
Notifications
You must be signed in to change notification settings - Fork 1
/
arduino_nokia_6310i.ino
274 lines (221 loc) · 8.58 KB
/
arduino_nokia_6310i.ino
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*
This code sends (on boot) an SMS with the context of the TEXT #define
to the RECIPIENT, using the SMS Center SMSC (Please don't forget
to set those first!!!)
For details on the connections and how this works in a high level, please
view my post at https://ilias.giechaskiel.com/posts/arduino_sms/index.html
The code was heavily based on the http://www.gnokii.org/ library, mostly
functions NK6510_SendSMS and sms_encode of common/phones/nk6510.c and
fbus_send_message and fbus_txt_send_frame of common/links/fbus.c
There are two points where this code is brittle, denoted by FIXME.
Both of them have to do with lengths (one of the SMSC, and the other
of the actual content of the text). Because it was hard to trace where
the values come from in gnokii, I set them manually based on the debug
output for the same message.
In other words, if the code below fails, create a text file containing
the message you want to send, and run the command
gnokii.exe --sendsms $RECIPIENT < $TEXT
where the variables are set appropriate, e.g. to +XXXXXXXXXXXX and
text.txt respectively.
*/
#define FBUS_TRANSMIT_MAX_LENGTH 256
#define BAUD 115200
#define phone Serial1
/* Phones should have the +XX country code*/
#define SMSC ""
#define RECIPIENT ""
#define TEXT "Sending texts from my Arduino is awesome!"
#define DEBUG
boolean first;
unsigned int request_sequence_number;
unsigned char smsc[20];
unsigned char rec[20];
#define HEADER_BYTES 15
#define LEN_POS 5
unsigned char sms[FBUS_TRANSMIT_MAX_LENGTH] = {0x1E, /* Frame start */
0x00, /* Dst=Phone */
0x0C, /* Src=Serial */
0x02, /* Message Type=SMS */
0x00, /* Length Byte 1 */
0x00, /* Length Byte 2 (To be set) */
/* Further FBus header */
0x00,
0x01,
0x00,
/* Send SMS Header */
0x02,
0x00,
0x00,
0x00,
0x55,
0x55
};
void setup()
{
Serial.begin(BAUD);
phone.begin(BAUD);
char_semi_octet_pack(SMSC, smsc + 1);
/* FIXME: Not sure why the return value above is not used,
but this works at least with my Greek number */
smsc[0] = 0x06;
request_sequence_number = 0;
first = true;
/* Initialise the Fbus by sending "U" 128 times */
for (int x = 0; x < 128; x++)
phone.write("U");
send_sms(RECIPIENT, TEXT);
}
void loop()
{
}
void send_sms(char* num, char* text)
{
unsigned int numBytes;
rec[0] = char_semi_octet_pack(num, rec + 1);
memset(sms + HEADER_BYTES, 0, FBUS_TRANSMIT_MAX_LENGTH - HEADER_BYTES);
numBytes = HEADER_BYTES + sms_encode(text, sms + HEADER_BYTES);
sms[LEN_POS] = numBytes - 6 + 2; /* Excludes initial header, but includes part of footer*/
numBytes = create_footer(sms, numBytes);
/* Send the full frame to the phone */
for (unsigned char j = 0; j < numBytes; j++) {
#ifdef DEBUG
Serial.print(sms[j], HEX);
Serial.print(" ");
#endif
phone.write(sms[j]);
}
#ifdef DEBUG
Serial.println();
#endif
phone.flush();
}
static int sms_encode(char* text, unsigned char *req)
{
int text_length = strlen(text);
unsigned char c, w, n, shift = 0, bittext_length = 0;
int pos = 0, udh_length_pos, len;
req[pos++] = 0x01; /* one big block */
req[pos++] = 0x02; /* message type: submit */
req[pos++] = 0x00; /* length (will be set at the end) */
req[pos++] = 0x01 | 0x10; /* SMS Submit | validity indicator */
req[pos++] = 0x00; /* reference */
req[pos++] = 0x00; /* pid */
req[pos++] = 0x00; /* dcs */
req[pos++] = 0x00; /* fixed byte */
req[pos++] = 0x04; /* total blocks */
/* Block 1. Remote Number */
len = rec[0] + 4;
if (len % 2) len++;
len /= 2;
req[pos] = 0x82; /* type: number */
req[pos + 1] = (len + 4 > 0x0c) ? (len + 4) : 0x0c; /* offset to next block starting from start of block (req[18]) */
req[pos + 2] = 0x01; /* first number field => remote_number */
req[pos + 3] = len; /* actual data length in this block */
memcpy(req + pos + 4, rec, len);
pos += ((len + 4 > 0x0c) ? (len + 4) : 0x0c);
/* Block 2. SMSC Number */
len = smsc[0] + 1;
req[pos] = 0x82; /* type: number */
req[pos + 1] = (len + 4 > 0x0c) ? (len + 4) : 0x0c; /* offset to next block starting from start of block (req[18]) */
req[pos + 2] = 0x02; /* first number field => remote_number */
req[pos + 3] = len;
memcpy(req + pos + 4, smsc, len);
pos += ((len + 4 > 0x0c) ? (len + 4) : 0x0c);
/* Block 3. User Data */
req[pos++] = 0x80; /* type: User Data */
for (n = 0; n < text_length; n++) {
unsigned char c = text[n] & 0x7f;
c >>= shift;
w = text[n+1] & 0x7f;
w <<= (7-shift);
shift += 1;
c = c | w;
if (shift == 7) {
shift = 0x00;
n++;
}
req[pos + 3 + bittext_length] = c;
bittext_length++;
}
/* FIXME: I am not sure why these are different */
req[pos++] = bittext_length + 4;
req[pos++] = bittext_length;
req[pos++] = bittext_length;
pos += bittext_length;
udh_length_pos = pos - bittext_length - 3;
/* padding */
if (req[udh_length_pos] % 8 != 0) {
memcpy(req + pos, "\x55\x55\x55\x55\x55\x55\x55\x55", 8 - req[udh_length_pos] % 8);
pos += 8 - req[udh_length_pos] % 8;
req[udh_length_pos] += 8 - req[udh_length_pos] % 8;
}
/* Block 4. Validity Period */
req[pos++] = 0x08; /* type: validity */
req[pos++] = 0x04; /* block length */
req[pos++] = 0x01; /* data length */
req[pos++] = 0xA9; /* validity */
req[2] = pos - 1;
return pos;
}
int char_semi_octet_pack(char *number, unsigned char *output)
{
char *in_num = number; /* Pointer to the input number */
unsigned char *out_num = output; /* Pointer to the output */
int count = 0; /* This variable is used to notify us about count of already
packed numbers. */
/* The first byte in the Semi-octet representation of the address field is
the Type-of-Address. This field is described in the official GSM
specification 03.40 version 6.1.0, section 9.1.2.5, page 33. We support
only international, unknown and alphanumeric number. */
*out_num++ = 0x91; /* international */
if (*in_num == '+') in_num++; /* skip leading '+' */
/* The next field is the number. It is in semi-octet representation - see
GSM specification 03.40 version 6.1.0, section 9.1.2.3, page 31. */
while (*in_num) {
if (count & 0x01) {
*out_num = *out_num | ((*in_num - '0') << 4);
out_num++;
}
else
*out_num = *in_num - '0';
count++; in_num++;
}
/* We should also fill in the most significant bits of the last byte with
0x0f (1111 binary) if the number is represented with odd number of
digits. */
if (count & 0x01) {
*out_num = *out_num | 0xf0;
out_num++;
}
return (2 * (out_num - output - 1) - (count % 2));
}
int create_footer(unsigned char* buffer, unsigned int len)
{
int count, seqnum = 0;
unsigned char checksum;
buffer[len++] = 0x01; /* This is the last frame */
seqnum = 0x40 + request_sequence_number;
request_sequence_number = (request_sequence_number + 1) & 0x07;
/* For the very first time sequence number should be ORed with
0x20. It should initialize sequence counter in the phone. */
if (first) {
seqnum |= 0x20;
first = false;
}
buffer[len++] = seqnum;
/* If the message length is odd we should add pad byte 0x00 */
if (len % 2)
buffer[len++] = 0x00;
/* Now calculate checksums over entire message and append to message. */
/* Odd bytes */
checksum = 0;
for (count = 0; count < len; count += 2)
checksum ^= buffer[count];
buffer[len++] = checksum;
/* Even bytes */
checksum = 0;
for (count = 1; count < len; count += 2)
checksum ^= buffer[count];
buffer[len++] = checksum;
return len;
}