/**
|
|
* @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_
|