You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
6.0 KiB

/**
* @file TinyGsmSMS.tpp
* @author Volodymyr Shymanskyy
* @license LGPL-3.0
* @copyright Copyright (c) 2016 Volodymyr Shymanskyy
* @date Nov 2016
*/
#ifndef SRC_TINYGSMSMS_H_
#define SRC_TINYGSMSMS_H_
#include "TinyGsmCommon.h"
#define TINY_GSM_MODEM_HAS_SMS
template <class modemType>
class TinyGsmSMS {
public:
/*
* Messaging functions
*/
String sendUSSD(const String& code) {
return thisModem().sendUSSDImpl(code);
}
bool sendSMS(const String& number, const String& text) {
return thisModem().sendSMSImpl(number, text);
}
bool sendSMS_UTF16(const char* const number, const void* text, size_t len) {
return thisModem().sendSMS_UTF16Impl(number, text, len);
}
/*
* CRTP Helper
*/
protected:
inline const modemType& thisModem() const {
return static_cast<const modemType&>(*this);
}
inline modemType& thisModem() {
return static_cast<modemType&>(*this);
}
/*
* Messaging functions
*/
protected:
static inline String TinyGsmDecodeHex7bit(String& instr) {
String result;
byte reminder = 0;
int8_t bitstate = 7;
for (uint8_t i = 0; i < instr.length(); i += 2) {
char buf[4] = {
0,
};
buf[0] = instr[i];
buf[1] = instr[i + 1];
byte b = strtol(buf, NULL, 16);
byte bb = b << (7 - bitstate);
char c = (bb + reminder) & 0x7F;
result += c;
reminder = b >> bitstate;
bitstate--;
if (bitstate == 0) {
char cc = reminder;
result += cc;
reminder = 0;
bitstate = 7;
}
}
return result;
}
static inline String TinyGsmDecodeHex8bit(String& instr) {
String result;
for (uint16_t i = 0; i < instr.length(); i += 2) {
char buf[4] = {
0,
};
buf[0] = instr[i];
buf[1] = instr[i + 1];
char b = strtol(buf, NULL, 16);
result += b;
}
return result;
}
static inline String TinyGsmDecodeHex16bit(String& instr) {
String result;
for (uint16_t i = 0; i < instr.length(); i += 4) {
char buf[4] = {
0,
};
buf[0] = instr[i];
buf[1] = instr[i + 1];
char b = strtol(buf, NULL, 16);
if (b) { // If high byte is non-zero, we can't handle it ;(
#if defined(TINY_GSM_UNICODE_TO_HEX)
result += "\\x";
result += instr.substring(i, i + 4);
#else
result += "?";
#endif
} else {
buf[0] = instr[i + 2];
buf[1] = instr[i + 3];
b = strtol(buf, NULL, 16);
result += b;
}
}
return result;
}
String sendUSSDImpl(const String& code) {
// Set preferred message format to text mode
thisModem().sendAT(GF("+CMGF=1"));
thisModem().waitResponse();
// Set 8-bit hexadecimal alphabet (3GPP TS 23.038)
thisModem().sendAT(GF("+CSCS=\"HEX\""));
thisModem().waitResponse();
// Send the message
thisModem().sendAT(GF("+CUSD=1,\""), code, GF("\""));
if (thisModem().waitResponse() != 1) { return ""; }
if (thisModem().waitResponse(10000L, GF("+CUSD:")) != 1) { return ""; }
thisModem().stream.readStringUntil('"');
String hex = thisModem().stream.readStringUntil('"');
thisModem().stream.readStringUntil(',');
int8_t dcs = thisModem().streamGetIntBefore('\n');
if (dcs == 15) {
return TinyGsmDecodeHex8bit(hex);
} else if (dcs == 72) {
return TinyGsmDecodeHex16bit(hex);
} else {
return hex;
}
}
bool sendSMSImpl(const String& number, const String& text) {
// Set preferred message format to text mode
thisModem().sendAT(GF("+CMGF=1"));
thisModem().waitResponse();
// Set GSM 7 bit default alphabet (3GPP TS 23.038)
thisModem().sendAT(GF("+CSCS=\"GSM\""));
thisModem().waitResponse();
thisModem().sendAT(GF("+CMGS=\""), number, GF("\""));
if (thisModem().waitResponse(GF(">")) != 1) { return false; }
thisModem().stream.print(text); // Actually send the message
thisModem().stream.write(static_cast<char>(0x1A)); // Terminate the message
thisModem().stream.flush();
return thisModem().waitResponse(60000L) == 1;
}
// Common methods for UTF8/UTF16 SMS.
// Supported by: BG96, M95, MC60, SIM5360, SIM7000, SIM7600, SIM800
class UTF8Print : public Print {
public:
explicit UTF8Print(Print& p) : p(p) {}
size_t write(const uint8_t c) override {
if (prv < 0xC0) {
if (c < 0xC0) printHex(c);
prv = c;
} else {
uint16_t v = uint16_t(prv) << 8 | c;
v -= (v >> 8 == 0xD0) ? 0xCC80 : 0xCD40;
printHex(v);
prv = 0;
}
return 1;
}
private:
Print& p;
uint8_t prv = 0;
void printHex(const uint16_t v) {
uint8_t c = v >> 8;
if (c < 0x10) p.print('0');
p.print(c, HEX);
c = v & 0xFF;
if (c < 0x10) p.print('0');
p.print(c, HEX);
}
};
bool sendSMS_UTF8_begin(const char* const number) {
thisModem().sendAT(GF("+CMGF=1"));
thisModem().waitResponse();
thisModem().sendAT(GF("+CSCS=\"HEX\""));
thisModem().waitResponse();
thisModem().sendAT(GF("+CSMP=17,167,0,8"));
thisModem().waitResponse();
thisModem().sendAT(GF("+CMGS=\""), number, GF("\""));
return thisModem().waitResponse(GF(">")) == 1;
}
bool sendSMS_UTF8_end() {
thisModem().stream.write(static_cast<char>(0x1A));
thisModem().stream.flush();
return thisModem().waitResponse(60000L) == 1;
}
UTF8Print sendSMS_UTF8_stream() {
return UTF8Print(thisModem().stream);
}
bool sendSMS_UTF16Impl(const char* const number, const void* text,
size_t len) {
if (!sendSMS_UTF8_begin(number)) { return false; }
uint16_t* t =
const_cast<uint16_t*>(reinterpret_cast<const uint16_t*>(text));
for (size_t i = 0; i < len; i++) {
uint8_t c = t[i] >> 8;
if (c < 0x10) { thisModem().stream.print('0'); }
thisModem().stream.print(c, HEX);
c = t[i] & 0xFF;
if (c < 0x10) { thisModem().stream.print('0'); }
thisModem().stream.print(c, HEX);
}
return sendSMS_UTF8_end();
}
};
#endif // SRC_TINYGSMSMS_H_