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

  1. /**
  2. * @file TinyGsmSMS.tpp
  3. * @author Volodymyr Shymanskyy
  4. * @license LGPL-3.0
  5. * @copyright Copyright (c) 2016 Volodymyr Shymanskyy
  6. * @date Nov 2016
  7. */
  8. #ifndef SRC_TINYGSMSMS_H_
  9. #define SRC_TINYGSMSMS_H_
  10. #include "TinyGsmCommon.h"
  11. #define TINY_GSM_MODEM_HAS_SMS
  12. template <class modemType>
  13. class TinyGsmSMS {
  14. public:
  15. /*
  16. * Messaging functions
  17. */
  18. String sendUSSD(const String& code) {
  19. return thisModem().sendUSSDImpl(code);
  20. }
  21. bool sendSMS(const String& number, const String& text) {
  22. return thisModem().sendSMSImpl(number, text);
  23. }
  24. bool sendSMS_UTF16(const char* const number, const void* text, size_t len) {
  25. return thisModem().sendSMS_UTF16Impl(number, text, len);
  26. }
  27. /*
  28. * CRTP Helper
  29. */
  30. protected:
  31. inline const modemType& thisModem() const {
  32. return static_cast<const modemType&>(*this);
  33. }
  34. inline modemType& thisModem() {
  35. return static_cast<modemType&>(*this);
  36. }
  37. /*
  38. * Messaging functions
  39. */
  40. protected:
  41. static inline String TinyGsmDecodeHex7bit(String& instr) {
  42. String result;
  43. byte reminder = 0;
  44. int8_t bitstate = 7;
  45. for (uint8_t i = 0; i < instr.length(); i += 2) {
  46. char buf[4] = {
  47. 0,
  48. };
  49. buf[0] = instr[i];
  50. buf[1] = instr[i + 1];
  51. byte b = strtol(buf, NULL, 16);
  52. byte bb = b << (7 - bitstate);
  53. char c = (bb + reminder) & 0x7F;
  54. result += c;
  55. reminder = b >> bitstate;
  56. bitstate--;
  57. if (bitstate == 0) {
  58. char cc = reminder;
  59. result += cc;
  60. reminder = 0;
  61. bitstate = 7;
  62. }
  63. }
  64. return result;
  65. }
  66. static inline String TinyGsmDecodeHex8bit(String& instr) {
  67. String result;
  68. for (uint16_t i = 0; i < instr.length(); i += 2) {
  69. char buf[4] = {
  70. 0,
  71. };
  72. buf[0] = instr[i];
  73. buf[1] = instr[i + 1];
  74. char b = strtol(buf, NULL, 16);
  75. result += b;
  76. }
  77. return result;
  78. }
  79. static inline String TinyGsmDecodeHex16bit(String& instr) {
  80. String result;
  81. for (uint16_t i = 0; i < instr.length(); i += 4) {
  82. char buf[4] = {
  83. 0,
  84. };
  85. buf[0] = instr[i];
  86. buf[1] = instr[i + 1];
  87. char b = strtol(buf, NULL, 16);
  88. if (b) { // If high byte is non-zero, we can't handle it ;(
  89. #if defined(TINY_GSM_UNICODE_TO_HEX)
  90. result += "\\x";
  91. result += instr.substring(i, i + 4);
  92. #else
  93. result += "?";
  94. #endif
  95. } else {
  96. buf[0] = instr[i + 2];
  97. buf[1] = instr[i + 3];
  98. b = strtol(buf, NULL, 16);
  99. result += b;
  100. }
  101. }
  102. return result;
  103. }
  104. String sendUSSDImpl(const String& code) {
  105. // Set preferred message format to text mode
  106. thisModem().sendAT(GF("+CMGF=1"));
  107. thisModem().waitResponse();
  108. // Set 8-bit hexadecimal alphabet (3GPP TS 23.038)
  109. thisModem().sendAT(GF("+CSCS=\"HEX\""));
  110. thisModem().waitResponse();
  111. // Send the message
  112. thisModem().sendAT(GF("+CUSD=1,\""), code, GF("\""));
  113. if (thisModem().waitResponse() != 1) { return ""; }
  114. if (thisModem().waitResponse(10000L, GF("+CUSD:")) != 1) { return ""; }
  115. thisModem().stream.readStringUntil('"');
  116. String hex = thisModem().stream.readStringUntil('"');
  117. thisModem().stream.readStringUntil(',');
  118. int8_t dcs = thisModem().streamGetIntBefore('\n');
  119. if (dcs == 15) {
  120. return TinyGsmDecodeHex8bit(hex);
  121. } else if (dcs == 72) {
  122. return TinyGsmDecodeHex16bit(hex);
  123. } else {
  124. return hex;
  125. }
  126. }
  127. bool sendSMSImpl(const String& number, const String& text) {
  128. // Set preferred message format to text mode
  129. thisModem().sendAT(GF("+CMGF=1"));
  130. thisModem().waitResponse();
  131. // Set GSM 7 bit default alphabet (3GPP TS 23.038)
  132. thisModem().sendAT(GF("+CSCS=\"GSM\""));
  133. thisModem().waitResponse();
  134. thisModem().sendAT(GF("+CMGS=\""), number, GF("\""));
  135. if (thisModem().waitResponse(GF(">")) != 1) { return false; }
  136. thisModem().stream.print(text); // Actually send the message
  137. thisModem().stream.write(static_cast<char>(0x1A)); // Terminate the message
  138. thisModem().stream.flush();
  139. return thisModem().waitResponse(60000L) == 1;
  140. }
  141. // Common methods for UTF8/UTF16 SMS.
  142. // Supported by: BG96, M95, MC60, SIM5360, SIM7000, SIM7600, SIM800
  143. class UTF8Print : public Print {
  144. public:
  145. explicit UTF8Print(Print& p) : p(p) {}
  146. size_t write(const uint8_t c) override {
  147. if (prv < 0xC0) {
  148. if (c < 0xC0) printHex(c);
  149. prv = c;
  150. } else {
  151. uint16_t v = uint16_t(prv) << 8 | c;
  152. v -= (v >> 8 == 0xD0) ? 0xCC80 : 0xCD40;
  153. printHex(v);
  154. prv = 0;
  155. }
  156. return 1;
  157. }
  158. private:
  159. Print& p;
  160. uint8_t prv = 0;
  161. void printHex(const uint16_t v) {
  162. uint8_t c = v >> 8;
  163. if (c < 0x10) p.print('0');
  164. p.print(c, HEX);
  165. c = v & 0xFF;
  166. if (c < 0x10) p.print('0');
  167. p.print(c, HEX);
  168. }
  169. };
  170. bool sendSMS_UTF8_begin(const char* const number) {
  171. thisModem().sendAT(GF("+CMGF=1"));
  172. thisModem().waitResponse();
  173. thisModem().sendAT(GF("+CSCS=\"HEX\""));
  174. thisModem().waitResponse();
  175. thisModem().sendAT(GF("+CSMP=17,167,0,8"));
  176. thisModem().waitResponse();
  177. thisModem().sendAT(GF("+CMGS=\""), number, GF("\""));
  178. return thisModem().waitResponse(GF(">")) == 1;
  179. }
  180. bool sendSMS_UTF8_end() {
  181. thisModem().stream.write(static_cast<char>(0x1A));
  182. thisModem().stream.flush();
  183. return thisModem().waitResponse(60000L) == 1;
  184. }
  185. UTF8Print sendSMS_UTF8_stream() {
  186. return UTF8Print(thisModem().stream);
  187. }
  188. bool sendSMS_UTF16Impl(const char* const number, const void* text,
  189. size_t len) {
  190. if (!sendSMS_UTF8_begin(number)) { return false; }
  191. uint16_t* t =
  192. const_cast<uint16_t*>(reinterpret_cast<const uint16_t*>(text));
  193. for (size_t i = 0; i < len; i++) {
  194. uint8_t c = t[i] >> 8;
  195. if (c < 0x10) { thisModem().stream.print('0'); }
  196. thisModem().stream.print(c, HEX);
  197. c = t[i] & 0xFF;
  198. if (c < 0x10) { thisModem().stream.print('0'); }
  199. thisModem().stream.print(c, HEX);
  200. }
  201. return sendSMS_UTF8_end();
  202. }
  203. };
  204. #endif // SRC_TINYGSMSMS_H_