zabsalahid / serialport-gsm Goto Github PK
View Code? Open in Web Editor NEWSerialPort-GSM is a simplified plugin for communicating with gsm modems. (Primarily for sms) (Focused in PDU mode)
License: MIT License
SerialPort-GSM is a simplified plugin for communicating with gsm modems. (Primarily for sms) (Focused in PDU mode)
License: MIT License
Hey @karianpour !
Upon checking, getNetworkSignal
does not return and had resulted to timeout.
item.logic = (newpart) => {
let resultData;
if (newpart.substr(0, 5) == '+CSQ:') {
let signal = newpart.split(' ')
signal = signal[1].split(',')
resultData = {
status: 'success',
request: 'getNetworkSignal',
data: { 'signalQuality': signal[0] }
}
}
if ((newpart == ">" || newpart == "> " || newpart == 'OK') && resultData) {
return {
resultData,
returnResult: true,
}
} else if (newpart == "ERROR") {
return {
resultData: {
status: 'ERROR',
request: 'getNetworkSignal',
data: 'Cannot Get Signal'
},
returnResult: true,
}
}
}
resultData is always set to undefined every time item.logis() is called, will not enter
((newpart == ">" || newpart == "> " || newpart == 'OK') && resultData)
part.
When sending a SMS, with a length of anywhere in-between 142-148 (inclusive) characters, the modem (im using sim800L) sends back "+CMS ERROR: 325", from which i understand is an input-error.
According to the internet, the SMS limit is 160 characters, and I believe this might be a serialport-gsm error, I am not sure though, and I might have setup the communication wrong.
The module is using firmware version 1418B04SIM800L24 and my serialport-gsm options are:
let options = { baudRate: 115200, dataBits: 8, stopBits: 1, parity: 'none', rtscts: false, xon: false, xoff: false, xany: false, autoDeleteOnReceive: true, enableConcatenation: true, incomingCallIndication: false, incomingSMSIndication: false, pin: '', customInitCommand: '', //logger: console }
On top of that, when sending 141 characters exactly, no error is thrown, but the SMS never gets sent, but anything below that seems to work. I am also able to send longer messages of around 440 characters no problem. Is anyone else experiencing this problem? I apologize if it is a mistake on my part.
Own "thread" to disacuss about codeing style and guidelines :-)
My two topics/questions would be:
What's your opinions?
Hi all,
@zabsalahid @karianpour @Apollon77
What do you guys understand about this issue (see below)? I don't see that serialport-gsm code handles this possibility?
This issue pertains to guarantees that a URC from the modem will not occur while the modem (and any host state machine) is in the middle of processing a current command.
// Good behavior:
ATZ
OK
+CMTI... <--- URC comes within 100ms after a command from the host completes
// Bad behavior
ATZ
+CMTI... <---URC comes asynchronously in the middle of a command
OK
Here is a discussion that highlights the issue and how it's handled: https://embeddedfreak.wordpress.com/2008/08/19/handling-urc-unsolicited-result-code-in-hayes-at-command/
It mentions that a modem can 'crash' if an AT command is sent to the modem at the same time the modem sends a URC. To prevent commands from the host to the modem colliding with a URC, the host should ALWAYS wait 100ms after an OK from a previous command to give the modem time to send any cached URCs.
Thank you
Paul
Hi.
Thanks for sharing your module. It is very helpful.
I set a RPi 3 B to monitoring some sensors. I use your module with a sim800L to provide a 24/7 SMS device to alert abnormal events. So my code must run 24/7.
This is a basic example that runs perfectly.
/* server.js */
let serialportgsm = require('serialport-gsm')
let modem = serialportgsm.Modem()
let options = {
baudRate: 115200,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false,
xon: false,
rtscts: false,
xoff: false,
xany: false,
buffersize: 0,
autoDeleteOnReceive: true,
enableConcatenation: true
}
modem.open('/dev/ttyS0', options, (data)=>{console.log(data)});
modem.on('open', () => {
modem.initializeModem(msg => console.log('initialize msg:', msg));
modem.setModemMode(msg => console.log('set pdu msg:', msg), 'PDU');
});
modem.on('close', msg => console.log('on close msg:' , msg));
modem.on('error', msg => console.log('on error msg:' , msg));
But when sim800L is turned off (it might happen sometimes), Rpi CPU usage and temperature will raise.
This is screenshot of s-tui
on RPi.
Console logs:
pi@monitor:~/gsm-v2 $ node server.js
open msg: null
initialize msg: { status: 'success',
request: 'modemInitialized',
data: 'Modem Successfully Initialized' }
set pdu msg: { status: 'success', request: 'modemMode', data: 'PDU_Mode' }
When sim800L is turned on back again, code will run perfectly again and CPU usage will drop to normal.
My question:
How can I handle this turned off situations?
How to use serialport-gsm with multiple ports gsm
I have a GSM Modem with 16 port.. Can you guys.. give example.. thanks..
2019-04-25 18:11:09,6, GSM1: Checking memory size
2019-04-25 18:11:10,7, GSM1: -> AT+CMGD=?
2019-04-25 18:11:10,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:11:10,7, GSM1: <- +CMGD: (0),(0-4) OK
2019-04-25 18:11:10,6, GSM1: Used memory is 1, list: 0
@zabsalahid You can also assign it to me
Line 54 should output sigStrength
not msg
@zabsalahid @Apollon77 @karianpour
Apologies for taking your time, but your modem experience is appreciated. Hopefully, this issue is a more straightforward discussion.
You guys used 'PDU' mode. Can I ask if there was any particular technical reason to use PDU vs. Text Mode?
My thought is that Text mode is not opaque enough (i.e., what is a Modem response and what is a multiline text message). As an example if of an ambiguous text mode messages:
Example of simple clear SMS:
+CMGR: “REC UNREAD”,“+8615021012496”,,“13/12/13,15:06:37+32”,145,4,0,0,“+861380021050 0”,145,27
<This is a test from Quectel>
OK
But how about the three line message "Hello, OK, AT+ATZ" (admittedly a contrived example):
+CMGR: “REC UNREAD”,“+8615021012496”,,“13/12/13,15:06:37+32”,145,4,0,0,“+861380021050 0”,145,27
Hello
OK
AT+ATZ
OK
Which 'OK' is part of the message and which is the 'OK' that terminates the AT+CMGR? If host code is waiting for OK, the message is believed to be just 'Hello' followed by some sort of command sequence error from the modem echoing some non issued 'AT+ATZ' command
PDU format is clear and unambigious:
+CMGR: 1,,33
07919107739667F9040B910000000000F000009150708004806910C8329BFD563C978A207515A46A15
OK
I found that if do not give callback
parameter to the functions, it will return a promise which is a very nice practice.
But there is a bug in the list function, it reject
if it succeeds and resolve
if it fails.
In index.js
line 13 and 15, resolve
and reject
should be swapped.
SerialPortGSM.list((error, results) => {
if (error) {
resolve(error)
} else {
reject(results)
}
should be changed to :
SerialPortGSM.list((error, results) => {
if (error) {
reject(error)
} else {
resolve(results)
}
Hi!
I have multiple modems connected to a single computer and some of them have timeout issues.
What I don't understand is, it's the same code as other modems and when I use another software like SMSCaster or Diafaan, I have no timeout.
With Diafaan I can see the options and it's the same baud rate as all other modems that I have.
In this image singleport.js and 4.js are the same, just that 4 is hardcoded to COM4 and singleport.js to COM3
I've tried to put a timeout of more than 3 minutes, still does timeout
I've tried all baud rates
@zabsalahid what do you think?
2019-04-25 18:11:44,7, GSM1: -> AT+CREG?
2019-04-25 18:11:44,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:11:44,7, GSM1: <- +CREG: 2,1, 1C00A7, 15E5 OK
2019-04-25 18:11:44,6, GSM1: Modem is registered to the network
@zabsalahid please assign to me, I would add it the next days
I am desperatly trying to upload some audio file to the internal memory of a SIM800C, so far this is what I've been able to do, but the uploaded file seems void, and doesnt play any sound. But with AT+FSLS=C:\\
I can see the file is there.
Here is what I have tried:
modem.executeCommand('AT+FSCREATE=tts2.amr',(result) => { log.debug(result); });
modem.executeCommand('AT+FSWRITE=tts2.amr,0,5030,10',(result) => { log.debug(result); });
modem.executeCommand('AT+FSLS=C:\\',(result) => { log.debug(result); });
I've seen this post but it doesnt suit to me, because it uses a tool (AmrFile Download.exe) to do the job manually, I need to do it programmatically. I thought if that tool could do it so can I, there must be a way, but have not yet found...
What really bugs me is how is the file located from my computer, and its content read before even loading it.
I've kept the audio file tts2.amr
in the same directory than the nodejs script since the AT commands shows me no way to specify a path for the source file it only considers the destination, so I've no clue how to do it, and I feel it doesnt work like that.
Hello Guys Need help. (Tulongan nyo naman ako pano paganahin) Thank you.
serialport-gsm = 2.3.1
usb modem = globe tattoo huawei e359
output
code
let modem = serialportgsm.Modem()
let options = {
baudRate: 9600,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false,
xon: false,
rtscts: false,
xoff: false,
xany: false,
buffersize: 0,
autoDeleteOnReceive: true,
enableConcatenation: true,
incomingCallIndication: true
}
modem.open('COM5', options, (data,err,msg)=>{console.log('Opened ',data,err,msg)});
modem.on('open', () => {
modem.initializeModem((msg,err,data) => {console.log('initialize msg:', msg, err)});
modem.setModemMode((msg,err,data) => {console.log('set pdu msg:', msg, err,data)}, 'PDU');
});
modem.on('close', msg => console.log('on close msg:' , msg));
modem.on('error', msg => console.log('on error msg:' , msg));```
How to deal with this? I can't receive message because message never went to SIM inbox but rather than to GPRS/USB modem. I tried calling getSimInbox(), I got only messages from SIM, however my recent received message was in GPS/USB modem.
let modemPort="COM4"; // default serial port for modemy
const gsmModem = serialportgsm.Modem();
const options = {
baudRate: 9600,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false,
xon: false,
rtscts: false,
xoff: false,
xany: false,
// autoDeleteOnReceive: true,
enableConcatenation: true,
incomingCallIndication: true,
onNewMessageIndicator: true,
onNewMessage:true,
// autoOpen: true,
}
app.post('/openModem',(req,response,next)=>{
try {
gsmModem.open(req.body.modemPort, options,async (err,data) => {
if (err) {
console.log(`Error opening modem`);
response.send({status: false, message: 'Opening modem failed'})
modemStatus = false
console.log('Modem status ', modemStatus)
return
}
else{
console.log('Opening port: ', portStatus)
gsmModem.initializeModem(async (msg,err) =>
{
if(err)
{
console.log("Initialize Modem Failed")
response.send({status: false, message: 'Initialize Modem Failed'})
gsmModem.close((err) => {
console.log('port closed', err);
})
return
}
console.log('initialize msg:', msg.data)
gsmModem.setModemMode(async(msgMode,err) => {
if (err) {
console.log("Gateway", `Error Setting Modem Mode`);
response.send({status: false, message: 'Error Setting Modem Mode'})
gsmModem.close((err) => {
console.log('port closed', err);
})
return
}
gsmModem.getOwnNumber(async (msg,err) => {
if(err)
{
console.log("Gateway", `Error getting own number`);
response.send({status: false, message: 'Error getting own number'})
gsmModem.close((err) => {
console.log('port closed', err);
})
}
else{
gsmModem.getSimInbox((result, err) => {
if(err) {
console.log(`Failed to get SimInbox ${err}`);
} else {
console.log(`Sim Inbox Result: ${JSON.stringify(result)}`);
}
});
response.send({status: true, message: 'Modem succesfully connected'})
}
})
}, "PDU");
});
}
});
} catch (error) {
return next(error)
}
})
gsmModem.on('onNewMessageIndicator', data => {
//indicator for new message only (sender, timeSent)
console.log(data)
})
gsmModem.on('onNewMessage', data => {
//whole message data
console.log(data)
})
Hi, I'm trying to get incoming SIM card messages, but they always get this kind of answer.
This is my code, options as in the example.
let modem = serialportgsm.Modem();
modem.open('COM16', options);
modem.on('open', (data) => {
modem.initializeModem((_) => {
modem.getSimInbox((res) => {
console.log(res);
})
})
})
But on I have incoming messages, what confirm desktop utilities.
Can someone help me ?
In modem.js, there is a call to modem.checkPINRequired where the error is not handled properly:
modem.checkPINRequired((result, error) => {
if (!error && result.data.pinNeeded && modem.pin.length) {
If there is an error, the 'result' will be '{error: "ERROR", request: "checkPINRequired", status: "Error"}'
, where as the second parameter 'error' will be null. Checking for error and result.data.pinNeeded at the same time caused issue (calling pinNeeded on null).
If I need to send 10,000 full-length messages back and forth every second,
would I need some sort of special hardware, something at the telco, both?
Or can it be achieved with a regular modem connected by USB?
Theoretically (160 + 2) * 10,000 = 1620000 bytes or 1.62 megabytes per second.
It's not much compared to even 3G, but they may be apples & oranges (unsure).
If it can be done, would this package be limited by its' design in some way?
My understanding of baud rates might be wrong. Ping @Apollon77
The link to read about error codes in the readme seems to be returning a 404.
Hey, an issue for exchange @zabsalahid @karianpour ...
I was wondering why I never found RING/+CLIP and +CMTI data on new messages and now I found them ;-) My Modem is providing 3 USB ports ... ttyUSB0 for the AT commands, ttyUSB1 for data and ttyUSB2 is only sending such special infos like +CLIP, RING, ^CEND, ...
How are your modems?
Did you had such a case? Is there any chance to "forward" the infos to the first port?
Modem Write: AT^GETPORTMODE
Modem Received: AT^GETPORTMODE
Activate Message Processing for: AT^GETPORTMODE
Modem Received:
Modem Received: ^GETPORTMODE:TYPE:WCDMA:Qualcomm ,MDM:0 ,DIAG:1,PCUI:2
Modem Received:
Modem Received: OK
What is this showing for your modems?
The timeout is not fired while we do not listen to item.on('timeout', ...)
event.
I prepared a pull request for that.
> AT+COPS?
< +COPS: 0,0,"Elisa Corporation"
<
< OK
> AT+COPS=?
< +COPS: (2,"Elisa Corporation","elisa","24405"),(3,"SONERA","SONERA","24491"),(3,"FINNET","FINNET","24412"),,(0-4),(0-2)
<
< OK
Hi,
a few days ago I got good results with this library, e.g. I was able to get a response from modem.on("onNewMessage")
.
Now, when I run the script in /examples
of this repo, I can send messages.
However, when I try getting messages in my inbox through:
modem.on("onNewMessage", console.log);
modem.getSimInbox(console.log)
// {"status":"success","request":"getSimInbox","data":[]}
modem.executeCommand('AT+CPMS="SM"', console.log);
// {"status":"success","request":"executeCommand","data":{"result":" 0,20,0,20,0,20"}}
modem.executeCommand('AT+CPMS="ME"', console.log);
// Result {"status":"success","request":"executeCommand","data":{"result":" 0,20,0,20,0,20"}}
modem.executeCommand('AT+CPMS="MT"', console.log);
//{"status":"ERROR","request":"executeCommand","data":"Execute Command returned Error: +CMS ERROR: 302"}
I checked this page too already: https://www.developershome.com/sms/cpmsCommand.asp
I'm using a Huawei E3531i-2. My options are
const { SIM_PIN } = process.env;
module.exports = {
baudRate: 115200,
dataBits: 8,
stopBits: 1,
parity: "none",
rtscts: false,
xon: false,
xoff: false,
xany: false,
autoDeleteOnReceive: true,
enableConcatenation: true,
incomingCallIndication: true,
incomingSMSIndication: true,
pin: SIM_PIN,
customInitCommand: "",
logger: console
};
$ lsusb
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 006: ID 12d1:1001 Huawei Technologies Co., Ltd. E161/E169/E620/E800 HSDPA Modem
Bus 001 Device 002: ID 2109:3431 VIA Labs, Inc. Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Any ideas?
Hello,
first I'd like to thank your for this module.
I'm testing it under windows with a Huawei gsm modem, I can send messages with sendSMS function, but I was enable to receive messages and the onNewMessage and onNewMessageIndicator are never fired.
Notice: getSimInbox() function shows the list of messages including the one I sent.
Any suggestion please?
Thank you,
Here's my code:
let serialportgsm = require('serialport-gsm');
var firstPort;
let modem = serialportgsm.Modem();
let options = {
baudRate: 115200,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false,
xon: false,
rtscts: false,
xoff: false,
xany: false,
buffersize: 0
}
serialportgsm.list((err, result) => {
firstPort = result[0].comName;
console.log("Port to be used: ", firstPort);
modem.open(firstPort, options, function(err){
if (err) {
console.log("Error opening port:", err);
}
});
})
modem.on('open', data => {
console.log("Port opened.");
modem.initializeModem( res => {
console.log("Modem initialized.\n");
modem.getSimInbox(function(res){
console.log("inbox:", res);
});
})
})
modem.on('close', result => {
console.log("Port closed");
})
modem.on('error', err => {
console.log("Error", err);
})
modem.on('onSendingMessage', result => {
console.log("Sending progress:", result);
})
modem.on('onNewMessageIndicator', result => {
console.log("new msg indicator:", result);
})
modem.on('onNewMessage', messageDetails => {
console.log("new msg: ", messageDetails);
})
Can you give a whole example for use it, including open modem, init modem, send sms. Thanks!
The following code doesn't work.
let serialportgsm = require('serialport-gsm')
serialportgsm.list((err, result) => {
console.log(result)
})
let modem = serialportgsm.Modem()
let options = {
baudRate: 115200,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false,
xon: false,
rtscts: false,
xoff: false,
xany: false,
autoDeleteOnReceive: true,
enableConcatenation: true,
incomingCallIndication: true
}
modem.open('/dev/ttyUSB1', options, data => {
modem.getOwnNumber(data3 => {console.log(data3)});
modem.setModemMode(data2 => {console.log(data2)}, 'PDU');
modem.sendSMS('1xxxxxxxxxx', 'Hello there Zab!', true)
})
2019-04-25 18:12:08,6, GSM1: I have to make a voice call to 4915153859448, with (3 times) DTMF 123
2019-04-25 18:12:08,7, GSM1: -> ATD+4915153859448;
2019-04-25 18:12:08,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:08,7, GSM1: <- OK
2019-04-25 18:12:09,7, GSM1: -> AT+CLCC
2019-04-25 18:12:09,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:09,7, GSM1: <- +CLCC: 1,0,2,0,0,"+4915153859448",145 OK
2019-04-25 18:12:10,7, GSM1: -> AT+CLCC
2019-04-25 18:12:10,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:10,7, GSM1: <- +CLCC: 1,0,2,0,0,"+4915153859448",145 OK
2019-04-25 18:12:11,7, GSM1: -> AT+CLCC
2019-04-25 18:12:11,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:11,7, GSM1: <- +CLCC: 1,0,2,0,0,"+4915153859448",145 OK
2019-04-25 18:12:11,7, GSM1: -> AT+CLCC
2019-04-25 18:12:11,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:12,7, GSM1: <- +CLCC: 1,0,2,0,0,"+4915153859448",145 OK
2019-04-25 18:12:12,7, GSM1: -> AT+CLCC
2019-04-25 18:12:12,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:12,7, GSM1: <- +CLCC: 1,0,3,0,0,"+4915153859448",145 OK
2019-04-25 18:12:13,7, GSM1: -> AT+CLCC
2019-04-25 18:12:13,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:13,7, GSM1: <- +CLCC: 1,0,3,0,0,"+4915153859448",145 OK
2019-04-25 18:12:14,7, GSM1: -> AT+CLCC
2019-04-25 18:12:14,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:14,7, GSM1: <- +CLCC: 1,0,3,0,0,"+4915153859448",145 OK
2019-04-25 18:12:14,7, GSM1: -> AT+CLCC
2019-04-25 18:12:14,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:15,7, GSM1: <- +CLCC: 1,0,3,0,0,"+4915153859448",145 OK
2019-04-25 18:12:15,7, GSM1: -> AT+CLCC
2019-04-25 18:12:15,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:16,7, GSM1: <- +CLCC: 1,0,3,0,0,"+4915153859448",145 OK
2019-04-25 18:12:16,7, GSM1: -> AT+CLCC
2019-04-25 18:12:16,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:16,7, GSM1: <- +CLCC: 1,0,3,0,0,"+4915153859448",145 OK
2019-04-25 18:12:17,7, GSM1: -> AT+CLCC
2019-04-25 18:12:17,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:17,7, GSM1: <- +CLCC: 1,0,0,0,0,"+4915153859448",145 OK
2019-04-25 18:12:21,7, GSM1: -> AT+VTS=1;+VTS=2;+VTS=3
2019-04-25 18:12:21,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:31,7, GSM1: put_command expected (OK)|(NO CARRIER)|(BUSY)|(NO ANSWER)|(ERROR)|(DELAYED), timeout occurred. 1.
2019-04-25 18:12:31,7, GSM1: <- COMMAND NOT SUPPORT
2019-04-25 18:12:34,7, GSM1: -> AT+VTS=1;+VTS=2;+VTS=3
2019-04-25 18:12:34,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:37,5, smsd: Moved file /var/spool/sms/outgoing/voicecalltest to /var/spool/sms/checked/vC3vLX
2019-04-25 18:12:44,7, GSM1: put_command expected (OK)|(NO CARRIER)|(BUSY)|(NO ANSWER)|(ERROR)|(DELAYED), timeout occurred. 2.
2019-04-25 18:12:44,7, GSM1: <- COMMAND NOT SUPPORT
2019-04-25 18:12:47,7, GSM1: -> AT+VTS=1;+VTS=2;+VTS=3
2019-04-25 18:12:47,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:58,7, GSM1: put_command expected (OK)|(NO CARRIER)|(BUSY)|(NO ANSWER)|(ERROR)|(DELAYED), timeout occurred. 3.
2019-04-25 18:12:58,7, GSM1: <- COMMAND NOT SUPPORT
2019-04-25 18:12:59,7, GSM1: -> AT+CHUP
2019-04-25 18:12:59,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:12:59,7, GSM1: <- OK
In my case the gsm modem is not supporting the DTMF tones, but for others this will work.
Proposal would be:
More for DTMF can also be found here: http://sgreg.fi/blog/article/mixing-the-sim800l-gsmgprs-module
I am using ubuntu 19.04 with 8 GB memory and Core i5 processor. The modem is Hawaii E303. Sending message is working but takes to much time, about 1 or more minutes.
There is what I have code:
let serialportgsm = require("serialport-gsm");
let modem = serialportgsm.Modem();
let options = {
baudRate: 115200,
dataBits: 8,
stopBits: 1,
parity: "none",
rtscts: false,
xon: false,
xoff: false,
xany: false,
autoDeleteOnReceive: true,
enableConcatenation: true,
incomingCallIndication: true,
incomingSMSIndication: true,
pin: "",
customInitCommand: "",
logger: console
};
modem.open("/dev/ttyUSB2", options, () => console.log("Connected"));
modem.on("open", data => {
// initialze the modem
modem.initializeModem(function() {
console.log("The GSM Modem is ready now.");
});
// set the the modem mode to PDU
modem.setModemMode(function() {
console.log("Setting the mode to PDU");
}, "PDU");
// Sending message
// modem.sendSMS("+93747912248", "I am fine!", true, function() {
// console.log("sending message");
// });
modem.getNetworkSignal(result => {
console.log(`Signal Strength = ${result}`);
});
//send message(number, text, alert(true or false), callback)
modem.sendSMS("+93747912248", `Hello there zab!`, false, function(response) {
console.log("message status", response);
});
});
So what I want to know is, is it the normal process that takes the time or any suggestion?
When I execute modem.setModemMode(callback)
I get the error TypeError: message.getScts is not a function
.
I checked the matter and I found that in my modem there is a Submit
message ( it is kind of un-sent message), the rest of the messages are Deliver
.
I guess that in modem.js
line 755 we have to check the type of message
parameter. If it does not have getScts
, then assigning null
:
dateTimeSent: (message.getScts ? new Date(message.getScts().getIsoString()) : null) || null,
I have tried this module in May last year and it was working.
Now I resuming my project and the module is showing error when initializing.
`
2020-02-18 16:43:03.649 - info: javascript.0 (18086) script.js.test.SIM2: Modem Sucessfully Opened
2020-02-18 16:43:04.265 - info: javascript.0 (18086) script.js.test.SIM2: [{'comName':'/dev/ttyAMA0'},{'manufacturer':'MediaTek Inc','pnpId':'usb-MediaTek_Inc_Product-if02-port0','vendorId':'0e8d','productId':'00a5','comName':'/dev/ttyUSB0'},{'manufacturer':'MediaTek Inc','pnpId':'usb-MediaTek_Inc_Product-if03-port0','vendorId':'0e8d','productId':'00a5','comName':'/dev/ttyUSB1'},{'manufacturer':'MediaTek Inc','pnpId':'usb-MediaTek_Inc_Product-if04-port0','vendorId':'0e8d','productId':'00a5','comName':'/dev/ttyUSB2'},{'manufacturer':'MediaTek Inc','pnpId':'usb-MediaTek_Inc_Product-if05-port0','vendorId':'0e8d','productId':'00a5','comName':'/dev/ttyUSB3'},{'manufacturer':'Texas Instruments','serialNumber':'__0X00124B0018DF33FC','pnpId':'usb-Texas_Instruments_TI_CC2531_USB_CDC___0X00124B0018DF33FC-if00','vendorId':'0451','productId':'16a8','comName':'/dev/ttyACM0'}]
2020-02-18 16:43:33.652 - info: javascript.0 (18086) script.js.test.SIM2: Error Initializing Modem - Error: timeout:
{"_events":{},"_eventsCount":1,"_maxListeners":100,"command":"ATZ","add_time":"2020-02-18T15:43:03.650Z","id":2,"timeout":30000,"inProgress":true,"execute_time":"2020-02-18T15:43:03.651Z"}
2020-02-18 16:44:35.380 - info: javascript.0 (18086) script.js.test.SIM2: Event Close: {"modem":"/dev/ttyAMA0","status":"Offline"}
`
Any thoughts?
$ sudo npm install serialport-gsm
brings me into this beautiful error:
`
npm WARN deprecated [email protected]: The sprintf package is deprecated in favor of sprintf-js.
@serialport/[email protected] install /home/myserver/node_modules/@serialport/bindings
prebuild-install --tag-prefix @serialport/bindings@ || node-gyp rebuild
prebuild-install WARN install No prebuilt binaries found (target=13.12.0 runtime=node arch=x64 libc= platform=linux)
make: Entering directory '/home/myserver/node_modules/@serialport/bindings/build'
CXX(target) Release/obj.target/bindings/src/serialport.o
../src/serialport.cpp: In function ‘void EIO_AfterGet(uv_work_t*)’:
../src/serialport.cpp:329:96: error: no matching function for call to ‘v8::Object::Set(v8::Localv8::String, Nan::imp::FactoryBasev8::Boolean::return_t)’
results->Set(Nan::Newv8::String("cts").ToLocalChecked(), Nan::Newv8::Boolean(data->cts));
^
In file included from /home/myserver/.cache/node-gyp/13.12.0/include/node/node.h:67:0,
from ../../../nan/nan.h:56,
from ../src/./serialport.h:6,
from ../src/serialport.cpp:1:
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3547:37: note: candidate: v8::Maybe v8::Object::Set(v8::Localv8::Context, v8::Localv8::Value, v8::Localv8::Value)
V8_WARN_UNUSED_RESULT Maybe Set(Local context,
^~~
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3547:37: note: candidate expects 3 arguments, 2 provided
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3550:37: note: candidate: v8::Maybe v8::Object::Set(v8::Localv8::Context, uint32_t, v8::Localv8::Value)
V8_WARN_UNUSED_RESULT Maybe Set(Local context, uint32_t index,
^~~
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3550:37: note: candidate expects 3 arguments, 2 provided
../src/serialport.cpp:330:96: error: no matching function for call to ‘v8::Object::Set(v8::Localv8::String, Nan::imp::FactoryBasev8::Boolean::return_t)’
results->Set(Nan::Newv8::String("dsr").ToLocalChecked(), Nan::Newv8::Boolean(data->dsr));
^
In file included from /home/myserver/.cache/node-gyp/13.12.0/include/node/node.h:67:0,
from ../../../nan/nan.h:56,
from ../src/./serialport.h:6,
from ../src/serialport.cpp:1:
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3547:37: note: candidate: v8::Maybe v8::Object::Set(v8::Localv8::Context, v8::Localv8::Value, v8::Localv8::Value)
V8_WARN_UNUSED_RESULT Maybe Set(Local context,
^~~
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3547:37: note: candidate expects 3 arguments, 2 provided
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3550:37: note: candidate: v8::Maybe v8::Object::Set(v8::Localv8::Context, uint32_t, v8::Localv8::Value)
V8_WARN_UNUSED_RESULT Maybe Set(Local context, uint32_t index,
^~~
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3550:37: note: candidate expects 3 arguments, 2 provided
../src/serialport.cpp:331:96: error: no matching function for call to ‘v8::Object::Set(v8::Localv8::String, Nan::imp::FactoryBasev8::Boolean::return_t)’
results->Set(Nan::Newv8::String("dcd").ToLocalChecked(), Nan::Newv8::Boolean(data->dcd));
^
In file included from /home/myserver/.cache/node-gyp/13.12.0/include/node/node.h:67:0,
from ../../../nan/nan.h:56,
from ../src/./serialport.h:6,
from ../src/serialport.cpp:1:
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3547:37: note: candidate: v8::Maybe v8::Object::Set(v8::Localv8::Context, v8::Localv8::Value, v8::Localv8::Value)
V8_WARN_UNUSED_RESULT Maybe Set(Local context,
^~~
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3547:37: note: candidate expects 3 arguments, 2 provided
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3550:37: note: candidate: v8::Maybe v8::Object::Set(v8::Localv8::Context, uint32_t, v8::Localv8::Value)
V8_WARN_UNUSED_RESULT Maybe Set(Local context, uint32_t index,
^~~
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3550:37: note: candidate expects 3 arguments, 2 provided
../src/serialport.cpp: In function ‘void EIO_AfterGetBaudRate(uv_work_t*)’:
../src/serialport.cpp:378:106: error: no matching function for call to ‘v8::Object::Set(v8::Localv8::String, Nan::imp::IntegerFactoryv8::Integer::return_t)’
results->Set(Nan::Newv8::String("baudRate").ToLocalChecked(), Nan::Newv8::Integer(data->baudRate));
^
In file included from /home/myserver/.cache/node-gyp/13.12.0/include/node/node.h:67:0,
from ../../../nan/nan.h:56,
from ../src/./serialport.h:6,
from ../src/serialport.cpp:1:
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3547:37: note: candidate: v8::Maybe v8::Object::Set(v8::Localv8::Context, v8::Localv8::Value, v8::Localv8::Value)
V8_WARN_UNUSED_RESULT Maybe Set(Local context,
^~~
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3547:37: note: candidate expects 3 arguments, 2 provided
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3550:37: note: candidate: v8::Maybe v8::Object::Set(v8::Localv8::Context, uint32_t, v8::Localv8::Value)
V8_WARN_UNUSED_RESULT Maybe Set(Local context, uint32_t index,
^~~
/home/myserver/.cache/node-gyp/13.12.0/include/node/v8.h:3550:37: note: candidate expects 3 arguments, 2 provided
bindings.target.mk:114: recipe for target 'Release/obj.target/bindings/src/serialport.o' failed
make: *** [Release/obj.target/bindings/src/serialport.o] Error 1
make: Leaving directory '/home/myserver/node_modules/@serialport/bindings/build'
gyp ERR! build error
gyp ERR! stack Error: make
failed with exit code: 2
gyp ERR! stack at ChildProcess.onExit (/usr/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:194:23)
gyp ERR! stack at ChildProcess.emit (events.js:315:20)
gyp ERR! stack at Process.ChildProcess._handle.onexit (internal/child_process.js:275:12)
gyp ERR! System Linux 4.15.0-99-generic
gyp ERR! command "/usr/bin/node" "/usr/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
gyp ERR! cwd /home/myserver/node_modules/@serialport/bindings
gyp ERR! node -v v13.12.0
gyp ERR! node-gyp -v v5.1.0
gyp ERR! not ok
npm WARN enoent ENOENT: no such file or directory, open '/home/myserver/package.json'
npm WARN myserver No description
npm WARN myserver No repository field.
npm WARN myserver No README data
npm WARN myserver No license field.
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! @serialport/[email protected] install: prebuild-install --tag-prefix @serialport/bindings@ || node-gyp rebuild
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the @serialport/[email protected] install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /home/myserver/.npm/_logs/2020-05-09T10_46_50_424Z-debug.log
`
I like this library. And I like to use async/await style coding. Most of the methos in modem
class does not support them, do you agree to implement them? I can do it. If yes, do you like to have the backward compatibility, or you prefer to change a major version and have callback
argument as the last one on each method?
If my sim card receive a CMTI for a MMS PUSH, it sends a CMGR and my modem will be unresponsive, and only restart solve this problem. Maybe i should handle differently these messages? What could be the problem?
2|service- | Modem Received:
2|service- | Modem Received: +CMTI: "SM",1,"MMS PUSH",2,1
2|service- | Ignore Data: +CMTI: "SM",1,"MMS PUSH",2,1
2|service- | Modem Write: AT+CMGR=1
2|service- | Modem Received: AT+CMGR=1
2|service- | Activate Message Processing for: AT+CMGR=1
2|service- | Modem Received:
2|service- | Modem Received: +CMTI: "SM",2,"MMS PUSH",2,2
2|service- | Modem Received:
2|service- | Modem Received: +CMTI: "SM",1,"MMS PUSH"
2|service- | Modem Write: AT+CMGR=2
2|service- | Modem Write: AT+CMGR=1
Event Sending Message: [object Object]
Callback Send: Message ID: 4KsGbb1o4m4whKOVli3pEb0mv,Message Failed +CMS ERROR: 21 To: 214 {"status":"fail","request":"SendSMS","data":{"messageId":"4KsGbb1o4m4whKOVli3pEb0mv","message":"?1515","recipient":"214","response":"Message Failed +CMS ERROR: 21"}}
Hi, I'm getting this error when sending sms.
I suggest to move some logic parts of the code in modem.dataReceived
to the caller function.
For example modem.checkSimMemory
function is calling modem.executeCommand
function, there we add an item
in the modem.queue
, and then in the modem.dataReceived
we get data and we check for modem.queue[0].command == 'AT+CPMS="SM"'
if it matches, if yes then comes the logic to proceed further.
I suggest to move the logic block in dataReceived
function which is the follow :
if (newpart.trim().substr(0, 6) === '+CPMS:') {
modem.parseSimCardResponse(newpart, result => {
resultData = result
})
}
if ((newpart == ">" || newpart == 'OK') && resultData) {
returnResult = true
}
as and arrow function inside modem.checkSimMemory
function.
I can do it if you agree.
Hai, I currently got this issue while I'm trying to send message
{ status: 'fail',
request: 'SendSMS',
data:
{ messageId: 'SU4RVub9mcBTX81YPvdFEoukx',
message: 'Hello',
recipient: '+628999999',
response: 'Message Failed +CMS ERROR: 69' } }
My code is down bellow:
const serialportgsm = require('serialport-gsm');
var gsmModem = serialportgsm.Modem()
let options = {
baudRate: 115200,
dataBits: 8,
parity: 'none',
stopBits: 1,
xon: false,
rtscts: false,
xoff: false,
xany: false,
autoDeleteOnReceive: true,
enableConcatenation: true,
incomingCallIndication: true,
incomingSMSIndication: true,
pin: '',
customInitCommand: 'AT^CURC=0'
}
let phone = {
name: "My-Name",
number: "+62899999",
message: "My message",
mode: "PDU"
}
// Port is opened
gsmModem.on('open', () => {
console.log(`Modem Sucessfully Opened`);
// now we initialize the GSM Modem
gsmModem.initializeModem((msg, err) => {
if (err) {
console.log(`Error Initializing Modem - ${err}`);
} else {
console.log(`InitModemResponse: ${JSON.stringify(msg)}`);
console.log(`Configuring Modem for Mode: ${phone.mode}`);
// set mode to PDU mode to handle SMS
gsmModem.setModemMode((msg, err) => {
if (err) {
console.log(`Error Setting Modem Mode - ${err}`);
} else {
console.log(`Set Mode: ${JSON.stringify(msg)}`);
gsmModem.sendSMS(phone.number, phone.message, false, (result) => {
console.log(result);
});
}
}, 'PDU')
}
});
gsmModem.on('onNewMessageIndicator', data => {
//indicator for new message only (sender, timeSent)
console.log(`Event New Message Indication: ` + JSON.stringify(data));
});
gsmModem.on('onNewMessage', data => {
//whole message data
console.log(`Event New Message: ` + JSON.stringify(data));
});
gsmModem.on('onSendingMessage', data => {
//whole message data
console.log(`Event Sending Message: ` + JSON.stringify(data));
});
gsmModem.on('onNewIncomingCall', data => {
//whole message data
console.log(`Event Incoming Call: ` + JSON.stringify(data));
});
gsmModem.on('onMemoryFull', data => {
//whole message data
console.log(`Event Memory Full: ` + JSON.stringify(data));
});
gsmModem.on('close', data => {
//whole message data
console.log(`Event Close: ` + JSON.stringify(data));
});
});
gsmModem.open('COM24', options);
setTimeout(() => {
gsmModem.close(() => process.exit);
}, 90000);
Hopefully you can help me, thanks!
hey, I found your library and tried it with my Vodafone GSM USB stick which in fact is a Huawei K3765. I use this stick so far with "smstools3" which was working fine after some initial stuff some time ago,
Here I have a log what they are sending roughly in this easy case to check for sms on sim, but it shown the initialization stuff. maybe it helps.
2019-04-25 18:14:37,6, GSM1: Checking device for incoming SMS
2019-04-25 18:14:37,6, GSM1: Checking if modem is ready
2019-04-25 18:14:37,7, GSM1: -> AT
2019-04-25 18:14:37,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:14:38,7, GSM1: <- OK
2019-04-25 18:14:38,6, GSM1: Pre-initializing modem
2019-04-25 18:14:38,7, GSM1: -> ATE0+CMEE=1;+CLIP=1;+CREG=2
2019-04-25 18:14:38,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:14:38,7, GSM1: <- OK
2019-04-25 18:14:38,6, GSM1: Checking if modem needs PIN
2019-04-25 18:14:38,7, GSM1: -> AT+CPIN?
2019-04-25 18:14:38,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:14:38,7, GSM1: <- +CPIN: READY OK
2019-04-25 18:14:38,6, GSM1: Initializing modem
2019-04-25 18:14:39,7, GSM1: -> AT^CURC=0
2019-04-25 18:14:39,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:14:39,7, GSM1: <- OK
2019-04-25 18:14:39,7, GSM1: -> AT+CSQ
2019-04-25 18:14:39,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:14:39,7, GSM1: <- +CSQ: 12,99 OK
2019-04-25 18:14:39,6, GSM1: Signal Strength Indicator: (12,99) -89 dBm (Workable), Bit Error Rate: not known or not detectable
2019-04-25 18:14:39,6, GSM1: Checking if Modem is registered to the network
2019-04-25 18:14:40,7, GSM1: -> AT+CREG?
2019-04-25 18:14:40,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:14:40,7, GSM1: <- +CREG: 2,1, 1C00A7, 15E5 OK
2019-04-25 18:14:40,6, GSM1: Modem is registered to the network
2019-04-25 18:14:40,6, GSM1: Selecting PDU mode
2019-04-25 18:14:40,7, GSM1: -> AT+CMGF=0
2019-04-25 18:14:40,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:14:40,7, GSM1: <- OK
2019-04-25 18:14:40,6, GSM1: Checking memory size
2019-04-25 18:14:41,7, GSM1: -> AT+CMGD=?
2019-04-25 18:14:41,7, GSM1: Command is sent, waiting for the answer
2019-04-25 18:14:41,7, GSM1: <- +CMGD: (),(0-4) OK
2019-04-25 18:14:41,6, GSM1: No SMS received
I will also experiment further because right now it is not sending any SMS :-(
Can you please, give me an example of how to use mode.sendUSSD() function properly. I can understand it .
Thank You
modem.open
method is missing some arguments if you do not send callback
in order to use promise.
modem.js
line 53
if (callback == undefined) {
return new Promise((resolve, reject) => {
modem.open((error, result) => {
if (error) {
should be set to
if (callback == undefined) {
return new Promise((resolve, reject) => {
modem.open(device, options, (error, result) => {
if (error) {
otherwise it traps in a infinite loop.
Hi there!
When am trying to run AT commands i only "OK" as a response
example:
modem.executeCommand('AT+CIMI',(result) => { log.debug(result); });
OUTPUT:
{
"status": "success",
"request": "executeCommand",
"data": {
"result": "OK"
}
}
how can i get the actual data?, in this case iwas requesting for the IMSI number.
Thanks again!
Hi again,
I can successfully get sent receipt but is it possible to get delivery receipts with this module ?
I am able to send sms text less than 155 characters but unable to send more than that.
Message text:
On the Insert tab, the galleries include items that are designed to coordinate with the overall look of your document. You can use these galleries to insert tables
Characters with spaces: 164
Console.log shows:
Event Sending Message: {"status":"Sending SMS","request":"sendingSMS","data":{"messageId":"3WIvATnCQinebq44RQwHBMUHN","message":"On the Insert tab, the galleries include items that are designed to coordinate with the overall look of your document. You can use these galleries to insert tables ","recipient":"00923325200***","response":"Message Currently Sending"}}
I am able to send the same text using another library. I am using the same modem options in both libraries.
https://github.com/apla/modem
Can anyone help me ?
By looking arround i saw there were special functions for USSD but have no clue how to use them, all I have tried so far have failed!
Hello,
Is it possible to supply a custom made messageId, when sending an sms ?
It should be possible to send voice files when an active call exists:
https://codestudioblog.wordpress.com/2012/06/26/episode-2-answering-machine-at-commands-and-gsm-modem-interaction/
https://stackoverflow.com/questions/17446574/how-to-send-voice-from-microphone-or-play-recorded-audio-from-hsdpa-dongle-c-sha
https://unix.stackexchange.com/questions/439119/how-to-get-audio-in-and-out-of-huawei-gsm-modems-when-doing-a-voice-call
Thank you for your help ,,
Please i get this error
{"_events":{},"_eventsCount":1,"command":"AT+CUSD=1,\"*100#\",15","add_time":"2019-08-06T11:13:05.970Z","id":14,"timeout":1000,"inProgress":true,"execute_time":"2019-08-06T11:13:06.375Z"}
at EventEmitter.item.on (F:\sp\node_modules\serialport-gsm\lib\functions\modem.js:864:27)
at EventEmitter.emit (events.js:198:13)
at Timeout.timeouts.(anonymous function).setTimeout [as _onTimeout] (F:\sp\node_modules\serialport-gsm\lib\functions\modem.js:944:14)
at ontimeout (timers.js:436:11)
at tryOnTimeout (timers.js:300:5)
at listOnTimeout (timers.js:263:5)
at Timer.processTimers (timers.js:223:10)
WHen i run
// execute a custom command - one line response normally is handled automatically
gsmModem.sendUSSD('*100#' ).then(
(success)=>{console.log( 'ssd dd ',uccess)},
).catch( (fail)=>{console.log( 'ssd fail',fail)});
Formula: 113-strengthValue*2 = dB
means strengthValue = 12 -> 89dB
Found here (sorry german page): http://www.nobbi.com/atgsm.html#+csq
I just create some issues where I struggle over, hopefully I find time also to implement it in a PR
Hi im triying to send sms with concurent, but only one sms succesfully sending on my number inbox.
and in log i see
(node:8945) UnhandledPromiseRejectionWarning: Error: Error Resource temporarily unavailable Cannot lock port
(node:8945) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
how to solve this ?
Hey,
Imagine the modem is off and I receive a text, is it possible to trigger the 'onNewMessage' event when I will turn on the modem to process the new message received when the modem was off. I thought it worked that way before I changed the configuration of my modem.
I played a bit with +CPMS and the method 'getSimInbox' to retrieve the previous messages but I would prefer to automatically trigger 'onNewMessage' with the message object.
I don't know if I am cristal clear...
Cycl0pe
Unable to get the second line of USSD.
serialport-gsm/lib/functions/ussd.js
Line 8 in 03b8548
This only the the first USSD response
Example USSD reponse:
Please select options:
1. Small package
2. Medium package.
3. Large package.
gsmModem.sendUSSD("*0#", (response, err) => {
// this only receive the first line
// Please select options:
})
gsmModem.on('onNewIncomingUSSD', (data) => {
// this only receive the first line of response
console.log('onNewIncomingUSSD', { data })
})
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.