Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
stasel committed Jul 8, 2019
0 parents commit b8d435b
Show file tree
Hide file tree
Showing 9 changed files with 998 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
node_modules

85 changes: 85 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# NodeJS GSM API

A simple and easy to use NodeJS API to communicate with serial GSM Modems.

## Features
* Read device information: serial, manufacturer, model.
* Read network information: Carrier, signal strength, subscriber id.
* Read and send SMS Messages.
* Add, Read and modify SIM card contacts.
* Make calls (without audio).

## Requirements
* Access to a serial USB GSM Modem
* Working SIM Card
* NodeJS 11 and later

## Usage
```JavaScript
const GSM = require("gsm")
const gsm = new GSM("/dev/gsmmodem")
await gsm.connect()

let manufacturer = await gsm.getManufacturerInformation())
console.log(manufacturer) // QUALCOMM INCORPORATED

let unreadMessages = await gsm.readSMS(GSM.MessageStorage.sim, GSM.MessageFilter.unread)
console.log(unreadMessages) // List of unread SMS messages

await gsm.sendSMS("+31111222333","Hello from NodeJS")
```

## Dependencies
* [serialport](https://www.npmjs.com/package/serialport)

## Resources
* [
Send and Receive SMS Messages Using Raspberry Pi and Python
](https://hristoborisov.com/index.php/projects/turning-the-raspberry-pi-into-a-sms-center-using-python/)
* [AT Commands Reference Guide ](https://www.sparkfun.com/datasheets/Cellular%20Modules/AT_Commands_Reference_Guide_r0.pdf) ([Local Copy](docs/AT_Commands_Reference_Guide_r0.pdf))
* [
Introduction to AT commands and its uses
](https://www.codeproject.com/Articles/85636/Introduction-to-AT-commands-and-its-uses)

## Testing the modem
### Method 1: Using regular bash
Terminal window 1 will read
```bash
$ cat /dev/gsmmodem

OK

Manufacturer: QUALCOMM INCORPORATED
Model: +CGMM:HSPA MODEM
Revision: +CGMR:V1.2
IMEI: 869478036086138
+GCAP: +CGSM,+DS,+ES

OK
```

Terminal window 2 will write
```bash
$ echo "AT" > /dev/gsmmodem
$ echo "ATI" > /dev/gsmmodem
```

### Method 2: Using cu
```bash
$ apt install cu
$ cu -l /dev/gsmmodem

AT

OK

ATI

Manufacturer: QUALCOMM INCORPORATED
Model: +CGMM:HSPA MODEM
Revision: +CGMR:V1.2
IMEI: 869478036086138
+GCAP: +CGSM,+DS,+ES

OK
```
65 changes: 65 additions & 0 deletions TestModem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const GSM = require('./gsm')
const gsm = new GSM('/dev/gsmmodem')

async function main() {

// Connect
console.log("Connecting to serial modem...")
await gsm.connect()
console.log("Connected to serial modem")
await gsm.check()
console.log("Modem OK\n")

// Print modem info
console.log("Modem Information:")
console.log(` Manufacturer: ${await gsm.getManufacturerInformation()}`)
console.log(` Model ID: ${await gsm.getModelIdentification()}`)
console.log(` Version: ${await gsm.getRevisionIdentification()}`)
console.log(` Serial: ${await gsm.getSerialNumber()}`)
console.log("")

// Print network info (Works only with a working SIM card and GSM signal)
console.log("Network Information:")
console.log(` Carrier: ${await gsm.getCurrentOperator()}`)
console.log(` Signal: ${(await gsm.getSignalQuality()).description }`)
console.log(` Subscriber ID: ${await gsm.getSubscriberId()}`)
console.log(` Phone Number: ${await getPhoneNumber(gsm) || "Unknown"}`)

// Polling on the list of unread messages and write all messages to stdout
while(true) {
let unreadMessages = await gsm.readSMS(GSM.MessageStorage.sim,GSM.MessageFilter.unread)
console.log(`Unread messages: ${unreadMessages.length}`)
if(unreadMessages.length > 0) {
for(let msg of unreadMessages) {
console.log(`From ${msg.sender} at ${msg.time.toISOString()}:`)
console.log(`${msg.text}\n\n`)
}
await gsm.deleteAllMessages(GSM.MessageStorage.sim, GSM.MessageDeleteFilter.readSentAndUnsent)
}
await timeout(10000)
}
}

async function getPhoneNumber(gsm) {
try {
return await gsm.getSubscriberNumber()
}
catch {
// Some operators store the subscriber phone number in the own number phone book. But it may fail
try {
const result = await gsm.readPhoneBook(GSM.PhoneBookStorage.ownNumber,1,1)
return result[0].number
}
catch {
return undefined
}
}
}

const timeout = (ms) => {
return new Promise(resolve => {
setTimeout(resolve,ms)
})
}

main().then(() => console.log("Bye")).catch(console.error).finally(() => { gsm.disconnect() })
Binary file added docs/AT_Commands_Reference_Guide_r0.pdf
Binary file not shown.
97 changes: 97 additions & 0 deletions gsm/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
ReturnCode = Object.freeze({
ok: "OK",
connect: "CONNECT",
ring: "RING",
noCarrier: "NO CARRIER",
error: "ERROR",
noDialtone: "NO DIALTONE",
busy: "BUSY",
noAnswer: "NO ANSWER"
})

ServiceClass = Object.freeze({
data: 0,
fax: 1,
voice: 8
})

CharacterSet = Object.freeze({
IRA: "IRA",
GSM: "GSM",
UCS2: "UCS2"
})

PhoneBookStorage = Object.freeze({
sim: "SM",
fixedDialing: "FD",
dialedCalls: "DC",
missedCalls: "MC",
receivedCalls: "RC",
ownNumber: "ON",
mobileEquipment: "ME",
emergencyNumbers: "EN",
lastDialed: "LD"
})

PhoneNumberType = Object.freeze({
national: 129, // national numbering scheme
international: 145, // international numbering scheme (contains the character "+")
text: 208 // Text based ex: 'Vodafone'
})

MessageStorage = Object.freeze({
internal: "ME",
sim: "SM",
statusReport: "SR",
all: "MT"
})

MessageFormat = Object.freeze({
PDU: 0,
text: 1
})

MessageFilter = Object.freeze({
unread: { pdu: 0, text: "REC UNREAD" },
read: { pdu: 1, text: "REC READ" },
storedUnsent: { pdu: 2, text: "STO UNSENT" },
storedSent: { pdu: 3, text: "STO SENT" },
all: { pdu: 4, text: "ALL" }
})

MessageDeleteFilter = Object.freeze({
/**
* Delete all read messages from storage, leaving unread messages and stored
* mobile originated messages (whether sent or not) untouched
*/
read: 1,

/**
* Delete all read messages from storage and sent mobile originated messages,
* leaving unread messages and unsent mobile originated messages untouched
*/
readAndSent: 2,

/**
* Delete all read messages from storage, sent and unsent mobile originated
* messages, leaving unread messages untouched
*/
readSentAndUnsent: 3,

/**
* Delete all message from storage
*/
all: 4
})

module.exports = {
ReturnCode,
ServiceClass,
CharacterSet,
PhoneBookStorage,
PhoneNumberType,
MessageStorage,
MessageFormat,
MessageFilter,
MessageDeleteFilter
}
138 changes: 138 additions & 0 deletions gsm/errors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@

/**
* ME Error Result Codes
*/
const MEError = Object.freeze({
0: "phone failure",
1: "No connection to phone",
2: "phone-adaptor link reserved",
3: "operation not allowed",
4: "operation not supported",
5: "PH-SIM PIN required",
10:"SIM not inserted",
11: "SIM PIN required",
12: "SIM PUK required",
13: "SIM failure",
14: "SIM busy",
15: "SIM wrong",
16: "incorrect password",
17: "SIM PIN2 required",
18: "SIM PUK2 required",
20: "memory full",
21: "invalid index",
22: "not found",
23: "memory failure",
24: "text string too long",
25: "invalid characters in text string",
26: "dial string too long",
27: "invalid characters in dial string",
30: "no network service",
31: "network timeout",
32: "network not allowed - emergency calls only",
40: "network personalization PIN required",
41: "network personalization PUK required",
42: "network subset personalization PIN required",
43: "network subset personalization PUK required",
44: "service provider personalization PIN required",
45: "service provider personalization PUK required",
46: "corporate personalization PIN required",
47: "corporate personalization PUK require",

// Easy CAMERA® related errors
50: "Camera not found",
51: "Camera Initialization Error",
52: "Camera not Supported",
53: "No Photo Taken",
54: "NET BUSY...Camera TimeOut",
55: "Camera Error",

// General purpose error
100: "Unknown error",

// GPRS related errors to a failure to perform an Attach:
103: "Illegal MS (#3)*",
106: "Illegal ME (#6)*",
107: "GPRS service not allowed (#7)*",
111: "PLMN not allowed (#11)*",
112: "Location area not allowed (#12)*",
113: "Roaming not allowed in this location area (#13)*",

// GPRS related errors to a failure to Activate a Context and others:
132: "service option not supported (#32)*",
133: "requested service option not subscribed (#33)*",
134: "service option temporarily out of order (#34)*",
148: "unspecified GPRS error",
149: "PDP authentication failure",
150: "invalid mobile class",

// Network survey errors:
257: "Network survey error (No Carrier)*",
258: "Network survey error (Busy)*",
259: "Network survey error (Wrong request)*",
260: "Network survey error (Aborted)* ",

// Easy GPRS® related errors:
400: "generic undocumented error",
401: "wrong state",
402: "wrong mode",
403: "context already activated",
404: "stack already active",
405: "activation failed",
406: "context not opened",
407: "cannot setup socket",
408: "cannot resolve DN",
409: "timeout in opening socket",
410: "cannot open socket",
411: "remote disconnected or timeout",
412: "connection failed",
413: "tx error",
414: "already listening",

// FTP related errors:
420: "ok",
421: "connect",
422: "disconnect",
423: "error",
424: "wrong state",
425: "can not activate",
426: "can not resolve name",
427: "can not allocate control socket",
428: "can not connect control socket",
429: "bad or no response from server",
430: "not connected",
431: "already connected",
432: "context down",
433: "no photo available",
434: "can not send photo"
})

/**
* Message Service Failure Result Codes
*/
const MSError = Object.freeze({
300: "ME failure",
301: "SMS service of ME reserved",
302: "operation not allowed",
303: "operation not supported",
304: "invalid PDU mode parameter",
305: "invalid text mode parameter",
310: "SIM not inserted",
311: "SIM PIN required",
312: "PH-SIM PIN required",
313: "SIM failure",
314: "SIM busy",
315: "SIM wrong",
316: "SIM PUK required",
317: "SIM PIN2 required",
318: "SIM PUK2 required",
320: "memory failure",
321: "invalid memory index",
322: "memory full",
330: "SMSC address unknown",
331: "no network service",
332: "network timeout",
340: "no +CNMA acknowledgement expected",
500: "Unknown error",
})

module.exports = { MEError, MSError }
Loading

0 comments on commit b8d435b

Please sign in to comment.