-
• #2
Interesting post. I receive a FONA GSM board from Adafruit tomorrow. So I have been trying to figure out the same thing. I have been studying the npm modem code to try and understand the way to handle callbacks from a AT modem.
-
• #3
Hi,
About the
console.log
- it won't hurt to use it when running off a battery - you just won't see anything. I guess you could define a function like:function log(type, message) { if (type=="good") digitalWrite(LED2,1); // green if (type=="bad") digitalWrite(LED1,1); // red console.log(">"+message); } log("good", "It worked!"); log("", "It's ok..."); log("bad", "It's smoking");
You could also try calling a callback for the response message, rather than using intervals...
I've added a timeout so it won't just break if something goes wrong, but I'm not handling it very well at the moment :)
var cmd=""; var responseText = "OK"; var responseCallback = undefined; //Function handles data coming in from Serial. Serial4.on('data',function (data) { cmd+=data; // maybe we should be looking for newline instead? if(cmd.indexOf(responseText) > 0){ console.log(cmd); if (responseCallback) responseCallback(); responseCallback = undefined; cmd=""; } }); //Function handles sending AT commands to GSM device. function Send_Command(command,response,time,caller){ if (responseCallback) { console.log("we were already doing something!"); return; } // if we didn't get anything within 'time' ms, var timeout = setTimeout(function(){ console.log("Timeout for "+command); caller(false); },time); responseText = response; responseCallback = function() { // if we actually did want to repeatedly send the command, do it here clearInterval(timeout); // clear the timeout caller(true); }; // send command Serial4.write(command); } //Start the GSM Module function Start_GSM(callback) { console.log("GSM Starting..."); pinMode(C0,"output"); digitalPulse(C0,1,2000); // this will send the AT command right after C0 has raised. // Not sure it's what you want? setTimeout might be better here Send_Command("AT\r\n","OK",500,function() { console.log("GSM Started."); callback(); }); } //Send the text message function Send_SMS(phoneNumber,message, callback){ console.log("Setting SMS Mode.."); Send_Command("AT+CMGF=1\r\n","OK",1000,function(){ answer=">"; console.log("Setting Phone Number.."); Send_Command('AT+CMGS=\""+phoneNumber+"\"\r',">",1000,function(){ console.log("Send Message"); Send_Command(message+"\x1A\r","OK",3000,function() { console.log("Message Sent"); callback(); }); }); }); } // You can then chain the callbacks together to use the library Start_GSM(function() { Send_SMS("+441234567890","Hello There", function() { Send_SMS("+441234567890","Hello There Again", function() { Stop_GSM(function() { console.log("phew..."); }); }); }); });
-
• #4
Thanks for the example I've given it a try and got a bit muddled up will review properly tonight though. I see the serial4.setup is missing which might be the issue.
-
• #5
Ahh - yes. I don't have GSM here to test it with though...
-
• #6
No problem I understand the principles anyway
-
• #7
I am also trying same test. As well as serial setup I see a few other things that I dont quite understand. Does espruino have a blocking timeout command? when setting c0 high for 2 seconds using digitalPulse does not block I tried using setTimeout(function(){},2000); after digitalPulse, the code continues and sends AT commands before the GSM unit turns on.
I am struggling to understand how the responseCallback routine works.
I am finding it really interesting to read and try to debug code. It gives a much better understanding of the way that Javascript works.
-
• #9
Fona also requires 2 second high pulse to turn on/off board. I have might not explained correctly but when I run Gordon's code the digitalPulse command does not seem to block. Hence the other AT commands to initialize the board are sent before the GSM board is powered on.
Thinking about his maybe the Send_Command in the start_GSM function needs to be fired from a SetTimeout command that waits for 2 seconds to allow for the GSM board to be powered on. That way the commands are sent and read rather than being lost.
I think most GSM boards are going to have the same command set so once the code is working it will be relevant to a number of GSM boards.
-
• #10
Yes, I had a feeling that
digitalPulse(C0,1,2000);
may not have been quite right.The best thing is probably just to replace it altogether:
digitalWrite(C0,1); setTimeout(function() { digitalWrite(C0,0); Send_Command(...) }, 2000);
Of course you might need to wait a few ms after taking C0 low again too...
-
• #11
Looks to be quite a bit not working I think im going to go back to basics on this one and give it another shot!
-
• #12
Serial4.println() allows the modem to be set up. The Serial.write does not seem to work
-
• #13
Odd.. Write has been perfect for me. I have some working code now but doesn't really look much like the stuff above. I'm trying to decide how best to receive the send command output and check its content still.
-
• #15
Took another look at the Serial.write cmd. I had the / \ mixed up. Now it seems to work.
On to the issue of reading and processing the send command output. Would a swicth statement in the serial.on("data") make sense?
if I send the command Serial.write("AT+CBC\n\r", 500, "+CBC",function(){}); to check the battery voltage I get +cbc: 0,94,3978 returned from the serial port. With a switch statement I could act on this by parsing the +cbc from the line and comparing it to the expected response from the Serial.write line (+CBC). If these agree I could then return an array for the 0,94,3878, that shows battery % charge left and the battery voltage.
I will try to code this later today if someone agrees this is a good way to proceed.
-
• #16
@user7143 yeap thats the route you should go down in my opinion.
Typically you will take the response from the on.data, build up a buffer to get a complete command and then check the command to see if it has the required response.
I was part the way implementing that and got confused over how I wanted to go about it. You will see in my original code I had an answer variable which stored the expected response agains the sent command. Note. The answer is not always OK nor is it always the expected answer.
I was hoping to do the check agains the response buffer to see if I got what I wanted in return. If I didnt I would just raise and error and shutdown the module.
There is also the scenario where you have slow response or no initial response. This is why I had the timer implemented into the send command function. This meant that I could repeatedly run the command every few seconds if I didnt get an answer back from the module. There is also the scenario where the command times out, so how long do you want to keep sending a command or waiting for a response until you decide the module is either dead or unresponsive? You can potentially end up in a loop.
As you can see there are a few things to consider and I think I rushed in to it at first without splitting all of the problems down and building out the solution. In my confusion I created quite a mess which I'm now working on to fix. Would be good if you could share your code though might help me get a bit further on my work :)
-
• #17
Will work on the Switch code and share when I have a few minutes free. This should take care of the differing responses based on different commands. A if statement before the switch statement should be able to check the expected response vs actual response.
The Digital.write code that Gordon posted above still does not seem to work correctly. I think rather than using a delay time after the 2 second high turn on code is run, the right way to handle this is to read a pin on the modem that lights up the ready LED to make sure the modem is ready to read commands.
This is fun stuff!
-
• #18
This is where I'm at after a refresh.
var cmd=""; //Function handles data coming in from Serial. Serial4.on('data',function (c) { if(c==="\r") { parseResponse(cmd); cmd=""; } cmd+=c; }); //Parse responses on data received on serial function parseResponse(buffer){ if(buffer.length < 2) return; if(buffer.indexOf("ERROR:")>0){ console.log("Error - forcing shutdown"); Stop_GSM(); } else { console.log(buffer); } } function Send_SMS(text) { Start_GSM(); setTimeout(function(){send(text);},10000); } //Start the GSM Module function Start_GSM() { Serial4.setup(9600); console.log("GSM Starting..."); pinMode(C0,"output"); digitalPulse(C0,1,2000); } function Stop_GSM(){ pinMode(C0,"output"); digitalPulse(C0,1,2000); } function send(input){ setTimeout(function(){send_command("AT+CMGS=\"number here\"\r");},2000); setTimeout(function(){send_command(input+"\x1A\r");},3000); setTimeout(function(){Stop_GSM();},10000); } function send_command(command){ //Send the command to serial port connected to GSM Serial4.write(command); } function onInit(){ Send_SMS("Heyyyy!"); } onInit();
For me the timed sending of commands is enough to get things running and sending texts. I just need to add in my GPS code and I'll have a tracker that can sms coords to twilio. My JS skills arent up to doing anything better.
-
• #19
Things working a bit nicer now!
var cmd=""; var issued_command=""; //Parse response v2 function parse(buffer){ var line = buffer.toString().substring(0, buffer.length - 1); //Remove anything remotely blank or echoey if(line.length < 2 || line.indexOf(">")===0 || issued_command===line){ return; } issued_command=""; //SMS Received if(line.indexOf("+CMTI")>0){ console.log("SMS Received"); } //Response Received console.log("Parsed: " + line); } //Serial on data v2 Serial4.on('data',function(data){ cmd += data; var id = cmd.indexOf("\n"); while (id>=0){ var line = cmd.substr(0,id); cmd=cmd.substr(id+1); parse(line); id=cmd.indexOf("\n"); } }); function Send_SMS(text) { Start_GSM(); setTimeout(function(){send(text);},10000); } //Start the GSM Module function Start_GSM() { Serial4.setup(9600); console.log("GSM Starting..."); pinMode(C0,"output"); digitalPulse(C0,1,2000); } function Stop_GSM(){ pinMode(C0,"output"); digitalPulse(C0,1,2000); } function send(input){ setTimeout(function(){send_command("AT+CMGS=\"XXXXXXXXXX\"\r");},2000); setTimeout(function(){send_command(input);},3000); setTimeout(function(){send_command("\x1A\r");},3500); setTimeout(function(){Stop_GSM();},10000); } function send_command(command){ //Set the command that we are sending to issue_command for later parse out issued_command=command; //Send the command to serial port connected to GSM Serial4.write(command); //Output what we sent to console console.log("Command Sent:" + command); } function onInit(){ Send_SMS("Heyyyy!"); } onInit();
Results in:
1v66 Copyright 2014 G.Williams >echo(0); GSM Starting... =undefined Command Sent:AT+CMGS="xxxxxx94740" Command Sent:Heyyyy! Command Sent: Parsed: +CMGS: 96 Parsed: OK Parsed: NORMAL POWER DOWN
Now I guess we can do lots with the Parsed responses and I have added in an example of a SMS received which the console will pick up on.
You can add in some error handling and also wait for an OK from an AT send on startup. I think I'll work on the latter next so that I can then kick off the actual SMS sending via callbacks once I'm sure the module is running.
-
• #20
Tried to add some parsing to build on your code.
Also added some functions like get_signal(), get_voltage(), get_network()
saying lights on or lights off in a message line will turn on LEDs on espruino board.
-
• #21
var cmd=""; var issued_command=""; Serial4.setup(9600,{rx:C11,tx:C10}); //Parse response v2 function parse(buffer){ var line = buffer.toString().substring(0, buffer.length - 1); //Remove anything remotely blank or echoey if(line.length < 2 || line.indexOf(">")===0 || issued_command===line){ return; } issued_command=""; //SMS Received if(line.indexOf("+")>-1){ var d = line.substr(line.indexOf("+")+1,line.indexOf(":")-1); switch(d) { case "CMTI": tnum = line.split(","); console.log("SMS received #", tnum[1]); setTimeout(function(){send_command("at+cmgr="+tnum[1]+"\r");},1000); break; //read incoming SMS case "CSQ": sgnl = line.slice(5); sgnl = sgnl.split(","); console.log("signal strength "+sgnl[0]); //console.log(line); break; //Read cell signal case "CMGL": //console.log("all text messages in memory "); break; //View all text message in memory case "CMGR": console.log("text message "+line); break; //read individual text message in memory case "COPS": ntwk = line.slice(5); ntwk = ntwk.split(","); console.log("network "+ ntwk[2]); break; //Read network carrier case "CBC": chg = line.split(","); console.log("battery charge " + chg[1] + "% + " + chg[2] + "mv's"); break; //read battery charge } } //Turn on LED's if command sent! else if (line.indexOf("Lights on")>-1) { console.log("do something"); LED.write(1); LED2.write(1); } //Turn off LED's if command sent. else if (line.indexOf("lights off")>-1) { LED.write(0); LED2.write(0); } else { //Response Received console.log("Parsed: " + line); }} //Serial on data v2 Serial4.on('data',function(data){ cmd += data; var id = cmd.indexOf("\n"); while (id>=0){ var line = cmd.substr(0,id); cmd=cmd.substr(id+1); parse(line); id=cmd.indexOf("\n"); } }); // get network carier function get_network() { setTimeout(function(){send_command("at+cops?\r");},200); } //get signal strength function get_signal() { setTimeout(function(){send_command("at+csq\r");},200); } //read all texts in memory function read_all_text() { setTimeout(function(){send_command("at+cmgl=\"all\"\r");},200); } //bet battery % charge and voltage function get_voltage() { setTimeout(function(){send_command("at+cbc\r");},200); } function Send_SMS(text) { Start_GSM(); setTimeout(function(){send(text);},1000); } //Start the GSM Module function Start_GSM() { console.log("GSM Starting..."); pinMode(C0,"output"); digitalPulse(C0,1,2000); setTimeout(function(){send_command("at+cmgf=1\r");},1000); } function Stop_GSM(){ pinMode(C0,"output"); digitalPulse(C0,1,2000); } function send(input){ setTimeout(function(){send_command("AT+CMGS=\"xxxxxxxxxx\"\r");},2000); setTimeout(function(){send_command(input);},3000); setTimeout(function(){send_command("\x1A\r");},3500); setTimeout(function(){Stop_GSM();},10000); } function send_command(command){ //Set the command that we are sending to issue_command for later parse out issued_command=command; //Send the command to serial port connected to GSM Serial4.write(command); //Output what we sent to console console.log("Command Sent:" + command); } function onInit(){ // send SMS when BTN pressed setWatch(function(e) { Send_SMS("Saying hello! "); }, BTN, { repeat:true, edge:'falling', debounce:20 }); //Send_SMS("Saying hello "); } onInit();
-
• #22
sorry about the messed up code format. If someone can tell me what I am doing wrong I will repost it
-
• #24
The </> code button sometimes refuses to work for me (same with the quote button).
It's 3 backticks: ```
And you need a blank line before the backticks, otherwise they don't seem to work right.
So after a few tweets back and forth with @Gordon regarding a GSM library I thought I would first make a small attempt at producing something which will send a SMS and then build from there.
Little did I know I have naff all javascript skills so began the learning process. After a few hours I have ended up with the following code:
https://gist.github.com/Hardware-Hacks/2fffb92cb3b277585cd9#file-gistfile1-js
I'm not going to overly explain what is going on as you can all read but the stop and start GSM functions are specific to the GSM2 Click board which is based on the M95 GSM chip. Everything else should hopefully be quite generic.
A few things I know need improving which I would love some help on:
Any help points/leg work much appreciated =]