It’s really quick and dirty, but it works. Open the following code in a text editor, edit the lines with the ‘define’ to match your serial device and the phone number you want to send your sms to (in full format, including the + and country code). Then save it as a file called send_sms.cpp
// --------------------------------------------------------------------------------
// Send SMS V0.1
// --------------------------------------------------------------------------------
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/times.h>
#include <fcntl.h>
#include <termios.h>
#include <string>
#include <bits/stdc++.h>
#include <algorithm>
#include <sys/file.h>
// --------------------------------------------------------------------------------
// Configuration
// --------------------------------------------------------------------------------
#define SIM800_DEVICE "/dev/ttyUSB0"
#define DESTINATION_NBR "+33673xxxxxx"
// --------------------------------------------------------------------------------
// Generic monotonic system clock, second granularity
// --------------------------------------------------------------------------------
unsigned long long GetClock(void)
{
static struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return( ts.tv_sec );
}
// --------------------------------------------------------------------------------
// Wait for SIM800 to reply to a command, timeout after 60 seconds
// --------------------------------------------------------------------------------
bool WaitForReply(int sfd, std::string *out = nullptr)
{
const unsigned long long timeout = 60;
char Buffer[201] = { 0 };
int p = 0;
unsigned long long t = GetClock();
int result = 0;
while( !result ) {
char c;
int count = read(sfd, &c, 1);
if( count ) {
if( c != '\r' ) {
Buffer[p++] = c;
Buffer[p] = 0;
if( strstr(Buffer, "OK\n") ) result = 1;
if( strstr(Buffer, "ERROR\n") ) result = 2;
if( p == 200 ) result = 2;
}
t = GetClock();
}
if( GetClock() - t > timeout ) result = 2;
}
if( out ) *out = Buffer;
return( result == 1 );
}
// --------------------------------------------------------------------------------
// Reply parsing
// --------------------------------------------------------------------------------
std::vector<std::string> ParseReply(const std::string &s)
{
std::stringstream ss(s);
std::string part;
std::vector<std::string> out;
while( std::getline(ss, part, '\n') ) out.push_back(part);
return( out );
}
std::string SearchReplyLine(const char *token, const std::vector<std::string> &out)
{
for( auto &i : out ) {
if( strstr(i.c_str(), token) ) return( i );
}
return( "" );
}
// --------------------------------------------------------------------------------
// Flush the UART read cache
// --------------------------------------------------------------------------------
void FlushAll(int sfd)
{
while( 1 ) {
char c;
int count = read(sfd, &c, 1);
if( !count ) break;
}
}
// --------------------------------------------------------------------------------
// SMS send command
// --------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
if( argc != 2 ) {
printf("Incorrect parameters (%d)\n", argc);
printf("Usage: send_sms <text to send>\n", argc);
return( 10 );
}
std::string smstext = argv[1];
// Turn %20 into spaces
while( smstext.size() ) {
size_t i = smstext.find("%20");
if( i == std::string::npos ) break;
smstext.erase(i, 3);
smstext.insert(i, " ");
}
// Open SIM800
int sfd = open(SIM800_DEVICE, O_RDWR | O_NOCTTY);
if( sfd != -1 ) {
// Lock access to the device, if another process is using it, then wait for it to unlock
int fr = flock(sfd, LOCK_EX);
if( fr != 0 ) {
printf("Lock did not succeed\n");
return( 11 );
}
// Configure UART
struct termios options;
tcgetattr(sfd, &options);
cfsetspeed(&options, B9600);
options.c_cflag &= ~CSTOPB;
options.c_cflag |= CLOCAL;
options.c_cflag |= CREAD;
cfmakeraw(&options);
options.c_cc[VTIME] = 5;
options.c_cc[VMIN] = 0;
options.c_lflag &= ~ECHO;
tcsetattr(sfd, TCSANOW, &options);
printf("OK, serial open\n");
} else {
printf("Can't open serial connection, error code %d\n", errno);
return( 1 );
}
int exitCode = 0;
try {
bool result;
// Clear the UART
FlushAll(sfd);
// Check if modem responds to AT commands
dprintf(sfd, "AT\n");
fsync(sfd);
result = WaitForReply(sfd);
if( !result ) throw(2);
// Reject all incoming calls
dprintf(sfd, "AT+GSMBUSY=1\n");
fsync(sfd);
result = WaitForReply(sfd);
if( !result ) throw(2);
// Check if registered to a network
dprintf(sfd, "AT+CREG?\n");
fsync(sfd);
std::string s;
result = WaitForReply(sfd, &s);
if( result ) {
std::vector<std::string> out = ParseReply(s);
s = SearchReplyLine("+CREG:", out);
if( s.empty() ) throw(3);
if( !s.size() > 0 || (s[s.size() - 1] != '5' && s[s.size() - 1] != '1')) throw(3);
printf("Registered to network: OK\n");
} else
throw(2);
// Set SMS format to text
dprintf(sfd, "AT+CMGF=1\n");
fsync(sfd);
result = WaitForReply(sfd);
if( !result ) throw(2);
printf("Sending <%s>\n", smstext.c_str());
// Send SMS
dprintf(sfd, "AT+CMGS=\"" DESTINATION_NBR "\"\n");
fsync(sfd);
usleep(250 * 1000);
dprintf(sfd, smstext.c_str());
dprintf(sfd, "\x1A");
fsync(sfd);
printf("SMS sent, waiting for ACK\n");
// Wait for network to ACK the SMS
result = WaitForReply(sfd, &s);
if( result ) {
std::vector<std::string> out = ParseReply(s);
s = SearchReplyLine("+CMGS:", out);
if( s.empty() ) throw(4);
s.erase(0, 7);
printf("ACK received, message ID is %s\n", s.c_str());
} else
throw(4);
// Clear the UART
FlushAll(sfd);
}
catch( int c ) {
// 2 = modem not responding to AT commands
// 3 = Not registered on network
// 4 = SMS not sent
printf("ERROR code %d\n", c);
exitCode = c;
}
close(sfd);
return( exitCode );
}
Next open a terminal, go to the folder where you saved the file and compile it with the following command:
g++ -o send_sms ./send_sms.cpp
This will only work on Linux and you will need the base development tools installed.
This will create an executable called send_sms. To test, call it from the terminal with a message to send, like this:
./send_sms "Hello there"
This will send the text Hello there as an sms to to the phone number you specified earlier. If that works you can then integrate it into HA as a shell command.