Trying to build a small GSM library

Posted on
Page
of 2
/ 2
Next
  • 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:

    var cmd="";
    var global_response=0;
    var answer="OK";
     
     
     
     
    //Function handles data coming in from Serial.
    Serial4.on('data',function (data) {
      if(cmd.indexOf(answer) > 0){
        console.log(cmd);
        cmd="";
        global_response=1;
      }
      else
      {
        cmd+=data;
      }
    });
     
     
    //Function handles sending AT commands to GSM device.
    function Send_Command(command,time,caller){
      var AT =  setInterval(function (f) { Serial4.write(command); }, time);
      var timey = setInterval(function(){
        if(global_response==1){
          clearInterval(AT);
          global_response=0;
          clearInterval(timey);
          caller();
        }
      },300);
    }
     
     
    //Start the GSM Module
    function Start_Gsm() {
      answer="OK";
      console.log("GSM Starting..");
      pinMode(C0,"output");
      digitalPulse(C0,1,2000);
      Send_Command("AT\r\n",500,function(){Set­_SMS_Mode();
                                           });
    }
     
    function Stop_GSM() {
      pinMode(C0,"output");
      digitalPulse(C0,1,2000);
       console.log("GSM Stopped...");
    }
     
    //Set the SMS Mode
    function Set_SMS_Mode() {
      answer="OK";
      console.log("Setting SMS Mode..");
      Send_Command("AT+CMGF=1\r\n",1000,functi­on(){Set_SMS_Number();});
    }
     
     
    //Set the SMS number to send text message to
    function Set_SMS_Number() {
      answer=">";
      console.log("Setting Phone Number.."); 
      Send_Command('AT+CMGS=\"INSERT A NUMBER HERE IN 44 for uk format\"\r',1000,function(){Send_SMS();}­);
    }
     
     
    //Send the text message
    function Send_SMS(){
      console.log("Send Message");
      answer="OK";
      Send_Command("testing\x1A\r",3000,functi­on(){Stop_GSM();});
    }
     
     
    //Set up the Serial port for the GSM
    Serial4.setup(9600,{rx:C11,tx:C10});
    Start_Gsm();
    

    https://gist.github.com/Hardware-Hacks/2­fffb92cb3b277585cd9#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:

    1. Puppies have probably died due to my poor use of callbacks. I'm not used to them but Gordon had suggested making some changes to utilise chaining.
    2. The Send Command function currently sets a timeout to wait for a response, there are probably much better ways to do this.
    3. The response I define within the Send Command function is given when I do the call, there may be cases where the response received is not the one expected. Once again probably much better ways to do this.
    4. Currently using console.log to view what is going on when I run this code, which assumes you are using USB. Obviously I have to remove this when running on the espruino from battery without USB to laptop which is a bit of a pain. Any better ways to show status? Maybe the Led blinks?

    Any help points/leg work much appreciated =]

  • 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.

  • 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,calle­r){
      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,f­unction(){
        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...");
          });
         });
       });
    });
    
    
  • 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.

  • Ahh - yes. I don't have GSM here to test it with though...

  • No problem I understand the principles anyway

  • 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.

  • I'm unsure on how the flora GSM board works @user7143 as the c0 high for 2 seconds is specific to the GSM2 click board I am using. It tells the board to turn on/off. This may not need to be the case in your instance (unsure but worth checking).

  • 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.

  • 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...

  • 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!

  • Serial4.println() allows the modem to be set up. The Serial.write does not seem to work

  • 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.

  • @user7143 Serial.write won't send a newline automatically, so you'll have to send it explicitly with \n or \r. Could that be your problem?

  • 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.

  • @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 :)

  • 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!

  • 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+C­MGS=\"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.

  • 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+C­MGS=\"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.

  • 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.

  • 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.ind­exOf(":")-1); 
           switch(d) {
    
             case "CMTI":
               tnum = line.split(",");
               console.log("SMS received #", tnum[1]);
               setTimeout(function(){send_command("at+c­mgr="+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+c­ops?\r");},200);
    
    }
      //get signal strength
    function get_signal() {
      setTimeout(function(){send_command("at+c­sq\r");},200);
      }
      //read all texts in memory
    function read_all_text() {
      setTimeout(function(){send_command("at+c­mgl=\"all\"\r");},200);
    
    }
      //bet battery % charge and voltage
    function get_voltage() {
      setTimeout(function(){send_command("at+c­bc\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+c­mgf=1\r");},1000);
    }
    function Stop_GSM(){
    pinMode(C0,"output");
    digitalPulse(C0,1,2000);
    }
    function send(input){
      setTimeout(function(){send_command("AT+C­MGS=\"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();
    
  • sorry about the messed up code format. If someone can tell me what I am doing wrong I will repost it

  • @user7143 I just fixed it for you. You need three backticks before and after the code. The simplest way is to just highlight the code you paste in and click the button that says </> code at the top of the edit window.

  • 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.

  • @user7143 looking good! Seems like we are getting somewhere now

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Trying to build a small GSM library

Posted by Avatar for Hardware_Hacks @Hardware_Hacks

Actions