Skip to content

Instantly share code, notes, and snippets.

@gfwilliams
Created January 5, 2021 16:13
Show Gist options
  • Save gfwilliams/9a2a182dc7e07d0181dccbae74f403b5 to your computer and use it in GitHub Desktop.
Save gfwilliams/9a2a182dc7e07d0181dccbae74f403b5 to your computer and use it in GitHub Desktop.
Tweaked SIM900 library for Espruino
/* Copyright (c) 2015 Gordon Williams, Tobias Schwalm. See the file LICENSE for copying permission. */
/*
Library for interfacing to the SIM900A.
Uses the 'NetworkJS' library to provide a JS endpoint for HTTP.
```
Serial1.setup(115200, { rx: B7, tx : B6 });
console.log("Connecting to SIM900 module");
var gprs = require('SIM900').connect(Serial1, B4, function(err) {
if (err) throw err;
gprs.connect('APN', 'USERNAME', 'PASSWORD', function(err) {
if (err) throw err;
gprs.getIP(function(err, ip) {
if (err) throw err;
console.log('IP:' + ip);
require("http").get("http://www.pur3.co.uk/hello.txt", function(res) {
console.log("Response: ",res);
res.on('data', function(d) {
console.log("--->"+d);
});
});
});
});
});
```
*/
var at;
var socks = [];
var sockData = ["","","","",""];
var MAXSOCKETS = 5;
var rst;
var busy = false;
function unregisterSocketCallbacks(sckt) {
at.unregister('>');
at.unregisterLine(sckt + ', SEND OK');
at.unregisterLine(sckt + ', SEND FAIL');
}
var netCallbacks = {
create: function(host, port) {
/* Create a socket and return its index, host is a string, port is an integer.
If host isn't defined, create a server socket */
var sckt;
if (host===undefined) {
sckt = MAXSOCKETS;
socks[sckt] = "Wait";
sockData[sckt] = "";
at.cmd("AT+CIPSERVER=1,"+port+"\r\n", 10000, function(d) {
if (d=="OK") {
socks[sckt] = true;
} else {
socks[sckt] = undefined;
throw new Error("CIPSERVER failed");
}
});
return MAXSOCKETS;
} else {
sckt = 0;
while (socks[sckt]!==undefined) sckt++; // find free socket
if (sckt>=MAXSOCKETS) throw new Error('No free sockets.');
socks[sckt] = "Wait";
sockData[sckt] = "";
at.cmd('AT+CIPSTART='+sckt+',"TCP",'+JSON.stringify(host)+','+port+'\r\n',10000, function(d) {
if (d=="OK") {
at.registerLine(sckt + ', CONNECT OK', function() {
at.unregisterLine(sckt + ', CONNECT OK');
at.unregisterLine(sckt + ', CONNECT FAIL');
socks[sckt] = true;
});
at.registerLine(sckt + ', CONNECT FAIL', function() {
at.unregisterLine(sckt + ', CONNECT FAIL');
at.unregisterLine(sckt + ', CONNECT OK');
at.unregisterLine(sckt + ', CLOSE');
socks[sckt] = undefined;
});
} else {
socks[sckt] = undefined;
return "";
}
});
}
return sckt; // jshint ignore:line
},
/* Close the socket. returns nothing */
close: function(sckt) {
if(socks[sckt]) {
// ,1 = 'fast' close
at.cmd('AT+CIPCLOSE='+sckt+",1\r\n",1000, function(/*d*/) {
socks[sckt] = undefined;
});
}
},
/* Accept the connection on the server socket. Returns socket number or -1 if no connection */
accept: function() {
// console.log("Accept",sckt);
for (var i=0;i<MAXSOCKETS;i++)
if (sockData[i] && socks[i]===undefined) {
//console.log("Socket accept "+i,JSON.stringify(sockData[i]),socks[i]);
socks[i] = true;
return i;
}
return -1;
},
/* Receive data. Returns a string (even if empty).
If non-string returned, socket is then closed */
recv: function(sckt, maxLen) {
if (sockData[sckt]) {
var r;
if (sockData[sckt].length > maxLen) {
r = sockData[sckt].substr(0,maxLen);
sockData[sckt] = sockData[sckt].substr(maxLen);
} else {
r = sockData[sckt];
sockData[sckt] = "";
}
return r;
}
if (!socks[sckt]) return -1; // close it
return "";
},
/* Send data. Returns the number of bytes sent - 0 is ok.
Less than 0 */
/* Send data. Returns the number of bytes sent - 0 is ok.
Less than 0 */
send: function(sckt, data) {
if (busy || at.isBusy() || socks[sckt]=="Wait") return 0;
if (!socks[sckt]) return -1; // error - close it
busy = true;
at.register('>', function() {
at.unregister('>');
at.write(data);
return "";
});
at.registerLine(sckt + ', SEND OK', function() {
at.unregisterLine(sckt + ', SEND OK');
at.unregisterLine(sckt + ', SEND FAIL');
busy = false;
return "";
});
at.registerLine(sckt + ', SEND FAIL', function() {
at.unregisterLine(sckt + ', SEND OK');
at.unregisterLine(sckt + ', SEND FAIL');
busy = false;
return -1;
});
at.write('AT+CIPSEND='+sckt+','+data.length+'\r\n');
return data.length;
}
};
//Handle +RECEIVE input data from SIM900A
function receiveHandler(line) {
var colon = line.indexOf(":\r\n");
if (colon<0) return line; // not enough data here at the moment
var parms = line.substring(9,colon).split(",");
parms[1] = 0|parms[1];
var len = line.length-(colon+3);
if (len>=parms[1]) {
// we have everything
sockData[parms[0]] += line.substr(colon+3,parms[1]);
return line.substr(colon+parms[1]+3); // return anything else
} else {
// still some to get - use getData to request a callback
sockData[parms[0]] += line.substr(colon+3,len);
at.getData(parms[1]-len, function(data) { sockData[parms[0]] += data; });
return "";
}
}
function receiveHandler2(line) {
var colon = line.indexOf(":");
if (colon<0) return line; // not enough data here at the moment
var parms = line.substring(3,colon).split(",");
parms[1] = 0|parms[1];
var len = line.length-(colon+1);
if (len>=parms[1]) {
// we have everything
sockData[parms[0]] += line.substr(colon+1,parms[1]);
return line.substr(colon+parms[1]+1); // return anything else
} else {
// still some to get - use getData to request a callback
sockData[parms[0]] += line.substr(colon+1,len);
at.getData(parms[1]-len, function(data) { sockData[parms[0]] += data; });
return "";
}
}
var gprsFuncs = {
"debug" : function() {
return {
socks:socks,
sockData:sockData
};
},
// initialise the SIM900A
"init": function(callback) {
var s = 0;
var cb = function(r) {
switch(s) {
case 0:
if(r === 'IIIIATE0' ||
r === 'IIII' + String.fromCharCode(255) + 'ATE0' ||
r === 'ATE0') {
return cb;
} else if(r === 'OK') {
s = 1;
at.cmd('AT+CPIN?\r\n', 1000, cb);
} else if(r) {
callback('Error in ATE0: ' + r);
}
break;
case 1:
if(r === '+CPIN: READY') {
return cb;
} else if (r === 'OK') {
s = 2;
// check if we're on network
at.cmd('AT+CGATT=1\r\n', 1000, cb);
} else if(r) {
callback('Error in CPIN: ' + r);
}
break;
case 2:
if(r === 'OK') {
s = 3;
at.cmd('AT+CIPSHUT\r\n', 1000, cb);
} else if(r) {
callback('Error in CGATT: ' + r);
}
break;
case 3:
if(r === 'SHUT OK') {
s = 4;
at.cmd('AT+CIPSTATUS\r\n', 1000, cb);
} else if(r) {
callback('Error in CIPSHUT: ' + r);
}
break;
case 4:
if(r === 'OK') {
return cb;
} else if(r === 'STATE: IP INITIAL') {
s = 5;
at.cmd('AT+CIPMUX=1\r\n', 1000, cb);
}
else if(r) {
callback('Error in CIPSTATUS: ' + r);
}
break;
case 5:
if (r&&r.substr(0,3)=="C: ") {
return cb;
} else if(r === 'OK') {
s = 6;
at.cmd('AT+CIPHEAD=1\r\n', 1000, cb);
} else if(r) {
callback('Error in CIPMUX: ' + r);
}
break;
case 6:
if(r === 'OK') {
return cb;
} else if(r) {
callback('Error in CIPHEAD: ' + r);
} else {
callback(null);
}
break;
}
};
at.cmd("ATE0\r\n",3000,cb);
},
"reset": function(callback) {
if (!rst) return gprsFuncs.init(callback);
digitalPulse(rst, false, 200);
setTimeout(function() {
gprsFuncs.init(callback);
}, 15000);
},
"getVersion": function(callback) {
at.cmd("AT+GMR\r\n", 1000, function(d) {
callback(null,d);
});
},
"connect": function(apn, username, password, callback) {
var s = 0;
var cb = function(r) {
switch(s) {
case 0:
if(r === 'OK') {
s = 1;
at.cmd('AT+CIICR\r\n', 2000, cb);
} else if(r) {
callback('Error in ' + s + ': ' + r);
}
break;
case 1:
if(r === 'OK') {
return cb;
}
else if (r) {
callback('Error in ' + s + ': ' + r);
} else {
callback(null);
}
break;
}
};
at.cmd('AT+CSTT="' + apn + '", "' + username + '", "' + password + '"\r\n', 1000, cb);
},
"getIP": function(callback) {
var ip;
var cb = function(r) {
if(r && r != 'ERROR' && r != 'OK') {
ip = r;
return cb;
} else if(r === 'ERROR') {
callback('CIFSR Error');
} else if(!r) {
callback(null, ip);
}
};
at.cmd('AT+CIFSR\r\n', 2000, cb);
}
};
function sckClosed(ln) {
var sckt = ln[0];
unregisterSocketCallbacks(sckt);
socks[sckt] = undefined;
busy = false;
}
exports.connect = function(usart, resetPin, connectedCallback) {
rst = resetPin;
gprsFuncs.at = at = require('AT').connect(usart);
require("NetworkJS").create(netCallbacks);
at.register("+RECEIVE", receiveHandler);
at.register("+D", receiveHandler2);
for (var i=0;i<MAXSOCKETS;i++)
at.registerLine(i+", CLOSE", sckClosed);
gprsFuncs.reset(connectedCallback);
return gprsFuncs;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment