Skip to content
This repository has been archived by the owner on Feb 16, 2024. It is now read-only.

Commit

Permalink
Added controller for the GSM module. Software power control has been …
Browse files Browse the repository at this point in the history
…tested. Voice calls and text SMS are awaiting testing and geolocation is to be implemented.

Signed-off-by: Eneko Cruz <[email protected]>
  • Loading branch information
cruzeneko committed Mar 15, 2015
1 parent a7217be commit 3ded729
Show file tree
Hide file tree
Showing 2 changed files with 220 additions and 0 deletions.
156 changes: 156 additions & 0 deletions gsm/GSMDevice.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#include "GSMDevice.hpp"

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

We are using Google C++ style, as you can see here: https://github.com/OpenStratos/server/blob/develop/contributing.md

So it would be #include "gsm/GSMDevice.h"


using namespace std;

GSMDevice::GSMDevice(){ //Instantiates a GSM device controlled using GPIO 4 (default, software pwr control) and /dev/ttyAMA0 for UART
GSMDevice(DEFAULT_GPIO,DEFAULT_UART);
}

//WARNING: May take up to six seconds
GSMDevice::GSMDevice(int gpio, string serial){ //Instantiates a GSM device controlled using a GPIO pin (given by GPIO parameter) and a UART port (given by serial){
gpionum = gpio;

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

Please, use this->gpionum for member functions and variables.

serialPort = serial;
InitSerial();
InitGSMModule();
delay(3000); //Allow some time for the GSM module to connect to the network.

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member
}

void GSMDevice::TogglePWR(){
//Toggles the power status of the GSM module. Turns it on if it is off and turns it off if it is on.
//WARNING: Takes 2500 miliseconds to complete

digitalWrite(gpionum, LOW); //Spec says that the adafruit FONA expects a 2000ms low pulse
delay(2000);
digitalWrite(gpionum, HIGH);
delay(500); //Give the module some time to turn on, ensure that when
//function returns the module is operational.
}

bool GSMDevice::Call(string num){ //Establishes a voice connection with a phone number. The function
char *number = new char[num.length() + 1]; //will return when the voice connection is established. Note that
strcpy(number, num.c_str()); //having a voice connection does not mean that both parties are
char send[20]; //ready to exchange data. In fact, the sender should allow for some
//time before transmitting so that the receiver can answer.

if(strlen(number)>(sizeof(send)/sizeof(char))-(3+1)){ //If the number does not fit into the buffer, reject it, as
if(OPENSTRATOS_GSM_DEBUG_){ //phone numbers (without C.C.) take 9 chars.
printf("[!] Number too long. Aborting call\n");
}
return false;
}
strncpy(send, "ATD", strlen("ATD")); //AT command preamble: ATD
strncpy(send+3, number, strlen(number)); //Number to dial
int length = strlen(send);
send[length] = (char)0x3b;//ascii for semicolon //Semicolon ending command
send[length+1] = (char)0x0;//terminate string with null byte
bool success = SerialSendCompareResponse(send, "OK"); //Modem will reply OK if a voice connection was established.
if(OPENSTRATOS_GSM_DEBUG_ && !success){
printf("[!] ATD+number+; did not reply OK\n");
}
return success;
}

bool GSMDevice::AbortCall(){ //Aborts a phone call. No effect if not in a call.
bool success = SerialSendCompareResponse("ATH","OK"); //Returns true if call aborted or if not in a call.
if(OPENSTRATOS_GSM_DEBUG_ && !success){ //Expecting the modem to return OK.
printf("[!] ATH did not reply OK\n");
}
return success;
}

bool GSMDevice::SendSMS(string message, string n){ //Sends a short message to a phone number
char *destnum = new char[n.length() + 1];
strcpy(destnum, n.c_str());

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

Why do we do this?

char *messagechar = new char[message.length() + 1];
strcpy(messagechar, message.c_str());

if (SerialSendCompareResponse("AT+CMGF=1", "OK")==false){ //Select SMS message format
if(OPENSTRATOS_GSM_DEBUG_){ //Abort if the modem cannot be set to text mode.
printf("[!] AT+CMFG=1 did not reply OK to SMS format selection\n");
}
return false;
}

char send[30] = "AT+CMGS=\""; //SMS command preamble: AT+CMGS="
strncpy(send+9 , destnum, 19); //SMS destination (actual phone number)
send[strlen(send)] = (char)0x22;//ascii for " (double quote) //SMS command termination: "

if (!SerialSendCompareResponse(send, "> ")){ //Input prompt should be open and expecting text input
if(OPENSTRATOS_GSM_DEBUG_){
printf("[!] Failed SMS attempt. SIM800L did not provide input prompt\n");
}
return false; //If there is not any input prompt, abort.
}

serialPrintf(fdesc,messagechar); //Send message
serialPrintf(fdesc,"\r\n"); //Return
FlushSerialInput(); //Module will reply with "+CMGS<length>". Not interested in that, so flush.
return true; //Message sent.
}

void GSMDevice::BroadcastGeolocation(string n){ //Tries to perform geolocation and sends coordinates to a phone number via SMS.
//TODO
}

bool GSMDevice::CheckGSMModule(){ //Returns true if module is up and running, false if it is not.
if(SerialSendCompareResponse("AT","OK")){ //Probe the SIM800L, see AT command reference for more info.
return true;
}
return false;
}

bool GSMDevice::InitSerial(){
char *cstr = new char[serialPort.length() + 1];
strcpy(cstr, serialPort.c_str());
fdesc = serialOpen(cstr, UART_BAUDRATE);
if(fdesc==-1){ //serialOpen returns a standard linux file descriptor. -1 means failed
;/*EXCEPTION: NO SERIAL PORT AVAILABLE*/
if(OPENSTRATOS_GSM_DEBUG_){
printf("[!] Serial port open error\n");
}
return false;
}
return true;
}

bool GSMDevice::InitGSMModule(){
if (wiringPiSetup() == -1){
/*EXCEPTION: UNABLE TO ACCESS GPIOS*/;

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

I don't like boolean returns, we should do exception handling and logging.

return false;
}
pinMode(gpionum, OUTPUT); //Set GPIO as digital output
digitalWrite(gpionum, HIGH); //Set to high so that GSM module stays in standby until TogglePWR is called
return true;
}

bool GSMDevice::SerialSendCompareResponse(char *send, char *expected){

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

Do we really need this? comparing strings is straightforward in C++ std::string == std::string

SerialSendRead(send); //Send a string via serial.
return (strcmp(serialin, expected) == 0); //Compare what got placed in serialin with the expected answer
}

uint16_t GSMDevice::SerialSendRead(char *send) {
FlushSerialInput();
if(OPENSTRATOS_GSM_DEBUG_){
printf("[i] Sending: %s", send);
}
serialPrintf(fdesc, send);
uint16_t readlength = SerialReadLine();
if(OPENSTRATOS_GSM_DEBUG_){
printf("[i] Received: %s\n", serialin);

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

Do not use printf() in C++, please, you can use std::cout and the << operator.

}
return readlength;
}

void GSMDevice::FlushSerialInput() {
serialFlush(fdesc);
}

uint16_t GSMDevice::SerialReadLine() {

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

Why aren't we using std::stringstream?

uint16_t i = 0;
while(serialDataAvail(fdesc)&&i<254) {
char c = serialGetchar(fdesc);
serialin[i++] = c;
}
serialin[i] = 0x0;
return i;
}
64 changes: 64 additions & 0 deletions gsm/GSMDevice.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#ifndef OPENSTRATOS_WIRINGPI_H_

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

This is not meant to be done here, you should only include the header file and the header itself has its own #define guards

#define OPENSTRATOS_WIRINGPI_H_
#include <wiringPi.h>
#endif

#ifndef OPENSTRATOS_WIRINGSERIAL_H
#define OPENSTRATOS_WIRINGSERIAL_H
#include <wiringSerial.h>
#endif

#ifndef OPENSTRATOS_GSM_INCLUDE_ALL_
#define OPENSTRATOS_GSM_INCLUDE_ALL_
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <stdint.h>
#include <algorithm>
#include <stdio.h>
#endif

#define OPENSTRATOS_GSM_DEBUG_ 0

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

This should be implemented in another way. We can use global flags, and we will be using loggers as soon as we implement them, but not here, please.

//0 = disable debug mode
//1 = enable debug mode

#ifndef OPENSTRATOS_GSM_GSMDEVICE_CLASS_

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

Please check http://google-styleguide.googlecode.com/svn/trunk/cppguide.html again so that we can maintain an uniform code style.

#define OPENSTRATOS_GSM_GSMDEVICE_CLASS_
using namespace std;

class GSMDevice{
public:
GSMDevice(); //Instantiates a GSM device controlled using GPIO 4 (default, software pwr control) and /dev/ttyAMA0 for UART
GSMDevice(int gpio, string serial); //Instantiates a GSM device controlled using a GPIO pin (given by GPIO parameter) and a UART port (given by serial);
void TogglePWR(); //Toggles the power status of the GSM module. Turns it on if it is off and turns it off if it is on.
bool Call(string number); //Calls a given phone number. Returns true if suceeded, false if failed.
bool AbortCall(); //Aborts a phone call. Returns true if suceeded, false if failed or not in a call.
bool SendSMS(string message, string n); //Sends a short message to a phone number. Returns true if suceeded, false if failed
void BroadcastGeolocation(string n); //Tries to perform geolocation and sends coordinates to a phone number via SMS.
bool CheckGSMModule(); //Returns true if module is up and running, false if it is not

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

Does this check power? If it does, shouldn't it be similar to the TogglePWR? If it doesn't we need to know the power state of the module before calling to TogglePWR, and, in fact, I think it would be better to have two methods: turn on and turn off.

private:
uint16_t SerialReadLine();
uint16_t SerialSendRead(char *);
void FlushSerialInput(); //Discards all data received from the serial port.
bool SerialSendCompareResponse(char*, char*); //Sends a string of data via serial port. Checks that the reply matches with another string.
bool InitGSMModule(); //Initializes the gsm module. Returns true if successful. False if failed.
bool InitSerial(); //Initializes the serial interface. Returns true if successful. False if failed.
int gpionum; //GPIO pin used for software power control
string serialPort; //Serial port identifier. Tipically /dev/ttyAMA0 in Raspbian.
int fdesc; //Standard unix file descriptor associated with the serial port (see serialPort)
char serialin[255]; //Buffer on which data received via the serial port is placed
};
#endif

#ifndef OPENSTRATOS_GSM_DEFAULT_PARAMETERS_
#define OPENSTRATOS_GSM_DEFAULT_PARAMETERS_
#define DEFAULT_UART "/dev/ttyAMA0"
#define DEFAULT_GPIO 4
#define UART_BAUDRATE 4800

This comment has been minimized.

Copy link
@Razican

Razican Mar 15, 2015

Member

We are using constants.h for these things.

#endif

#ifndef OPENSTRATOS_GSM_GPIO_VALUES_
#define OPENSTRATOS_GSM_GPIO_VALUES_
#define HIGH 1
#define LOW 0
#endif

0 comments on commit 3ded729

Please sign in to comment.