Skip to content

Commit

Permalink
Implemented basic SIM900 GSM firmware to test with.
Browse files Browse the repository at this point in the history
  • Loading branch information
kallaspriit committed Mar 29, 2016
1 parent 4b544b0 commit ba52555
Show file tree
Hide file tree
Showing 4 changed files with 390 additions and 0 deletions.
85 changes: 85 additions & 0 deletions firmware/SIM900/Commander.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include "Commander.h"

Commander::Commander(Stream *serial, char startToken, char endToken, char parameterDelimiter, char escapeChar) :
serial(serial),
startToken(startToken),
endToken(endToken),
parameterDelimiter(parameterDelimiter),
escapeChar(escapeChar),
lastChar('0'),
parameterCount(0),
command("")
{

}

boolean Commander::gotCommand() {
boolean gotCommand = false;

while (serial->available() > 0) {
char character = serial->read();

if (character == startToken && lastChar != escapeChar) {
buffer = "";
} else if (character == endToken && lastChar != escapeChar) {
parseCommand(buffer);

buffer = "";
gotCommand = true;

lastChar = character;

break;
} else {
if (character != escapeChar) {
buffer += character;
}

lastChar = character;
}
}

return gotCommand;
}

void Commander::parseCommand(String buffer) {
parameterCount = 0;
command = "";

int lastDelimiterPos = 0;
int delimitersFound = 0;

while (true) {
// make sure we don't exceed the parameter limit
if (parameterCount >= COMMANDER_MAX_PARAMETER_COUNT - 1) {
break;
}

int delimiterPos = buffer.indexOf(parameterDelimiter, lastDelimiterPos + 1);

if (delimiterPos == -1) {
// there are no parameters so the whole command is a name
if (delimitersFound == 0) {
command = buffer;
}

break;
}

delimitersFound++;

// first one is the name and parameters follow
if (delimitersFound == 1) {
command = buffer.substring(0, delimiterPos);
} else {
parameters[parameterCount++] = buffer.substring(lastDelimiterPos + 1, delimiterPos);
}

lastDelimiterPos = delimiterPos;
}

// extract last parameter
if (delimitersFound > 0) {
parameters[parameterCount++] = buffer.substring(lastDelimiterPos + 1, buffer.length());
}
}
30 changes: 30 additions & 0 deletions firmware/SIM900/Commander.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#ifndef COMMANDER_H
#define COMMANDER_H

#include "Arduino.h"

#define COMMANDER_MAX_PARAMETER_COUNT 10

class Commander {

public:
Commander(Stream *serial, char startToken = '<', char endToken = '>', char parameterDelimiter = ':', char escapeChar = '\\');

boolean gotCommand();
void parseCommand(String buffer);

String command;
String parameters[COMMANDER_MAX_PARAMETER_COUNT];
int parameterCount;

private:
Stream *serial;
char startToken;
char endToken;
char parameterDelimiter;
char escapeChar;
char lastChar;
String buffer;
};

#endif
192 changes: 192 additions & 0 deletions firmware/SIM900/SIM900.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#include "Commander.h"

// application config
const int GSM_BAUDRATE = 19200;
const int COM_BAUDRATE = 115200;
const unsigned long BTN_DEBOUNCE_PERIOD_MS = 300;

// pin config
const int GSM_PWRKEY_PIN = 9;

// serials
Serial_ *local = &Serial;
HardwareSerial *remote = &Serial1;

// runtime
unsigned long lastUpdateTime = 0;
unsigned long lastButtonPressTime = 0;
boolean isGsmModulePoweredOn = false;

Commander commander(local);

void setup() {
setupPinModes();
setupSerials();
}

void loop() {
unsigned long currentTime = millis();
unsigned long dt = currentTime - lastUpdateTime;
updateSerials(currentTime, dt);
updateCommander(currentTime, dt);

lastUpdateTime = currentTime;
}

void setupPinModes() {
pinMode(GSM_PWRKEY_PIN, OUTPUT);
}

void setupSerials() {
local->begin(COM_BAUDRATE);
remote->begin(GSM_BAUDRATE);
}

void updateSerials(unsigned long currentTime, unsigned long dt) {
/*while (local->available() > 0) {
char character = local->read();
remote->print(character);
}*/

while (remote->available() > 0) {
char character = remote->read();

local->print(character);
}
}

void updateCommander(unsigned long currentTime, unsigned long dt) {
while (commander.gotCommand()) {
handleCommand(commander.command, commander.parameters, commander.parameterCount);
}
}

void toggleGsmPower() {
digitalWrite(GSM_PWRKEY_PIN, LOW);
delay(1000);
digitalWrite(GSM_PWRKEY_PIN, HIGH);
delay(2000);
digitalWrite(GSM_PWRKEY_PIN, LOW);
delay(3000);

local->println("done!");
}

void sendTextMessage(String number, String message) {
remote->print("AT+CMGF=1\r");

delay(100);
remote->print("AT + CMGS = \"");
remote->print(number);
remote->println("\"");

delay(100);
remote->println(message);

delay(100);
remote->println((char)26);// the ASCII code of the ctrl+z is 26

delay(100);
remote->println();
}

void startCall(String number) {
remote->print("ATD + ");
remote->print(number);
remote->println(";");

delay(100);
remote->println();
}

void endCall() {
remote->println("ATH");
}

void handleCommand(String command, String parameters[], int parameterCount) {
if (command == "on" && parameterCount == 0) {
handleOnCommand();
} else if (command == "off" && parameterCount == 0) {
handleOffCommand();
} else if (command == "cmd" && parameterCount == 1) {
handleCmdCommand(parameters);
} else if (command == "sms" && parameterCount == 2) {
handleSmsCommand(parameters);
} else if (command == "call" && parameterCount == 1) {
handleCallCommand(parameters);
} else {
local->print("Unhandled command '");
local->print(command);
local->print("' with ");
local->print(parameterCount);
local->println(" parameters: ");

for (int i = 0; i < parameterCount; i++) {
local->print(" > ");
local->print(i);
local->print(": ");
local->println(parameters[i]);
}
}
}

void handleOnCommand() {
if (isGsmModulePoweredOn) {
local->println("Module is already turned on, ignoring request");

return;
}

local->println("Turning GSM module on");

toggleGsmPower();
}

void handleOffCommand() {
if (!isGsmModulePoweredOn) {
local->println("Module is already turned off, ignoring request");

return;
}

local->println("Turning GSM module off");

toggleGsmPower();
}

void handleCmdCommand(String parameters[]) {
String cmd = parameters[0];

local->print("Forwarding AT command '");
local->print(cmd);
local->println("'");

remote->print(cmd);
remote->print('\r');
}

void handleSmsCommand(String parameters[]) {
String number = parameters[0];
String message = parameters[1];

local->print("Sending SMS message '");
local->print(message);
local->print("' to number '");
local->print(number);
local->println("'");

sendTextMessage(number, message);
}

void handleCallCommand(String parameters[]) {
String number = parameters[0];

local->print("Calling number '");
local->print(number);
local->println("'");

startCall(number);
delay(20000);
endCall();
}
83 changes: 83 additions & 0 deletions lib/logger/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import path from 'path';
import bunyan from 'bunyan';
import { logger as config } from '../../config';

/* examples
log('info "%d"', 123, { 'a': 1 });
log({ user: 'server' }, 'info "%d"', 123, { 'a': 1 });
log({ user: { firstName: 'Priit', lastName: 'Kallas' } }, 'user logged in');
log.warn('warning', 123, { 'a': 1 });
log.error('error', 123, { 'a': 1 });
log.error(new Error('Test error'), 'something went wrong...');
*/

// log levels
const levels = [
'trace', // Logging from external libraries used by your app or very detailed application logging.
'debug', // Anything else, i.e. too verbose to be included in "info" level.
'info', // Detail on regular operation.
'warn', // A note on something that should probably be looked at by an operator eventually.
'error', // Fatal for a particular request, but the service/app continues servicing other requests.
'fatal' // The service/app is going to stop or become unusable now.
];

// default logger
const defaultLog = bunyan.createLogger({
name: 'app',
...config
});

// default info logger
const log = (...args) => {
defaultLog.info(...args);
};

// resolves full component filename to a relative component name
function resolveComponentName(filename) {
const componentFilename = path.resolve(filename);
const currentFilename = path.resolve(__filename);
const rootFilename = path.resolve(currentFilename, '../../../');
const relativeFilename = componentFilename.substr(rootFilename.length + 1);
const dirname = path.dirname(relativeFilename);
const extname = path.extname(relativeFilename);
const basename = path.basename(relativeFilename, extname);
const dirComponents = dirname.split(path.sep).filter(
(dirComponent) => dirComponent.length > 0 && dirComponent !== '.'
);

return [...dirComponents, basename].join('/');
}

// get a component logger
log.get = (component, parameters = {}) => {
const componentLog = defaultLog.child({
component: resolveComponentName(component),
// streams: defaultLog.streams,
...parameters
});

/*
const componentLog = bunyan.createLogger({
name: component,
...defaultParameters,
...parameters
});
*/

const defaultFn = (...args) => {
componentLog.info(...args);
};

return Object.assign(
defaultFn,
levels.reduce((extenders, level) => {
extenders[level] = (...args) => {
componentLog[level](...args);
};

return extenders;
}, {})
);
};

export default log;

0 comments on commit ba52555

Please sign in to comment.