mirror of
https://github.com/Luzifer/espsms.git
synced 2024-12-20 18:01:22 +00:00
Initial version
This commit is contained in:
commit
ed4caab6cf
9 changed files with 445 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.env
|
||||||
|
.pio
|
39
include/README
Normal file
39
include/README
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
lib/README
Normal file
46
lib/README
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
27
platformio.ini
Normal file
27
platformio.ini
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:esp-wrover-kit]
|
||||||
|
platform = espressif32
|
||||||
|
board = esp-wrover-kit
|
||||||
|
framework = arduino
|
||||||
|
|
||||||
|
build_flags =
|
||||||
|
-DMQTT_CLIENT_ID="\"${sysenv.MQTT_CLIENT_ID}\""
|
||||||
|
-DMQTT_HOST="\"${sysenv.MQTT_HOST}\""
|
||||||
|
-DMQTT_PASS="\"${sysenv.MQTT_PASS}\""
|
||||||
|
-DMQTT_USER="\"${sysenv.MQTT_USER}\""
|
||||||
|
-DWIFI_NAME="\"${sysenv.WIFI_NAME}\""
|
||||||
|
-DWIFI_PASS="\"${sysenv.WIFI_PASS}\""
|
||||||
|
|
||||||
|
lib_deps =
|
||||||
|
ArduinoJson
|
||||||
|
PubSub=https://github.com/knolleary/pubsubclient.git
|
||||||
|
Timer=https://github.com/brunocalou/Timer.git
|
229
src/main.cpp
Normal file
229
src/main.cpp
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "main.hpp"
|
||||||
|
#include "modem.hpp"
|
||||||
|
#include "mqtt.hpp"
|
||||||
|
#include "timer.h"
|
||||||
|
#include "timerManager.h"
|
||||||
|
|
||||||
|
// Debug output: Mirror all Modem output to Serial
|
||||||
|
#define MODEM_DEBUG
|
||||||
|
|
||||||
|
// Set serial for AT commands (to SIM800 module)
|
||||||
|
#define SerialAT Serial1
|
||||||
|
|
||||||
|
void checkNet() {
|
||||||
|
String csq = sendCommand("AT+CSQ", true);
|
||||||
|
quality = csq.substring(6, csq.indexOf(",", 6)).toInt();
|
||||||
|
Serial.println("INF: Quality (0-31): " + String(quality, DEC));
|
||||||
|
|
||||||
|
String creg = sendCommand("AT+CREG?", true);
|
||||||
|
regInfo = creg.substring(7);
|
||||||
|
Serial.println("INF: Registration: " + regInfo);
|
||||||
|
|
||||||
|
if (!sendTelemetry(quality, regInfo)) {
|
||||||
|
Serial.println("ERR: Unable to submit telemetry");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool expectString(String expect) { return readLine() == expect; }
|
||||||
|
|
||||||
|
void initModem() {
|
||||||
|
// Set SerialAT reset, enable, power pins
|
||||||
|
pinMode(MODEM_PWKEY, OUTPUT);
|
||||||
|
pinMode(MODEM_RST, OUTPUT);
|
||||||
|
pinMode(MODEM_POWER_ON, OUTPUT);
|
||||||
|
digitalWrite(MODEM_PWKEY, LOW);
|
||||||
|
digitalWrite(MODEM_RST, HIGH);
|
||||||
|
digitalWrite(MODEM_POWER_ON, HIGH);
|
||||||
|
|
||||||
|
// Set GSM module baud rate and UART pins
|
||||||
|
Serial.println("Initializing modem in 4s...");
|
||||||
|
SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
|
||||||
|
|
||||||
|
delay(4000);
|
||||||
|
|
||||||
|
if (!sendBoolCommand("AT")) {
|
||||||
|
Serial.println("Modem initializing not successful");
|
||||||
|
return initModem();
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Waiting for PIN ready...");
|
||||||
|
|
||||||
|
while (!expectString("+CPIN: READY")) {
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Card ready, sending config...");
|
||||||
|
|
||||||
|
if (!sendBoolCommand("AT+CMEE=2")) {
|
||||||
|
Serial.println("ERR: Unable to set error logging to verbose (AT+CMEE=2)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendBoolCommand("AT+CMGF=1")) {
|
||||||
|
Serial.println("ERR: Unable to set text mode (AT+CMGF=1)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendBoolCommand("AT+CSCS=\"8859-1\"")) {
|
||||||
|
Serial.println("ERR: Unable to set charset (AT+CSCS=\"8859-1\")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendBoolCommand("AT+CNMI=1,2,0,0,0")) {
|
||||||
|
Serial.println("ERR: Unable to subscribe for new SMS (AT+CNMI=1,2,0,0,0)");
|
||||||
|
}
|
||||||
|
|
||||||
|
netCheck.setInterval(30000);
|
||||||
|
netCheck.setCallback(checkNet);
|
||||||
|
|
||||||
|
TimerManager::instance().start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void initWiFi() {
|
||||||
|
Serial.println("Connecting to WiFi...");
|
||||||
|
WiFi.setHostname("espsms");
|
||||||
|
WiFi.begin(WIFI_NAME, WIFI_PASS);
|
||||||
|
|
||||||
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
|
delay(250);
|
||||||
|
Serial.print(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
while (commandRunning) {
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WiFi.status() != WL_CONNECTED || mqtt.state() != 0) {
|
||||||
|
digitalWrite(MODEM_POWER_ON, LOW);
|
||||||
|
esp_restart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String line = readLine(1000);
|
||||||
|
|
||||||
|
// Some error was received
|
||||||
|
if (line.startsWith("+CME ERROR")) {
|
||||||
|
Serial.println("ERR: " + line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DBG: Received +CMT: "+49157***","","20/07/19,12:57:45+08"
|
||||||
|
// DBG: Received Sehr lange SMS um zu demonstrieren wie sich der Kram verh<72>lt wenn sowohl Sonderzeichen als auch viel Text in der Nachricht stecken
|
||||||
|
if (line.startsWith("+CMT: ")) {
|
||||||
|
String message = readLine();
|
||||||
|
|
||||||
|
int pos = 6;
|
||||||
|
int npos = line.indexOf(",", pos);
|
||||||
|
String senderNo = line.substring(pos, npos);
|
||||||
|
|
||||||
|
pos = npos + 1;
|
||||||
|
npos = line.indexOf(",", pos);
|
||||||
|
String senderName = line.substring(pos, npos);
|
||||||
|
|
||||||
|
pos = npos + 1;
|
||||||
|
String date = line.substring(pos);
|
||||||
|
|
||||||
|
if (!sendMessage(trimQuotes(senderNo), trimQuotes(senderName), trimQuotes(date), message)) {
|
||||||
|
Serial.println("ERR: Unable to submit message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TimerManager::instance().update();
|
||||||
|
mqtt.loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
String readLine() {
|
||||||
|
return readLine(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
String readLine(int timeout) {
|
||||||
|
String response = "";
|
||||||
|
int start = millis();
|
||||||
|
|
||||||
|
while(true) {
|
||||||
|
while (!SerialAT.available()) {
|
||||||
|
delay(10);
|
||||||
|
|
||||||
|
if (timeout > 0 && millis() > start + timeout) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte ch = SerialAT.read();
|
||||||
|
|
||||||
|
// Drop invalid chars and CRs
|
||||||
|
if (ch == 0 || ch == 13 || ch == 255) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we have a LF and a string, return it!
|
||||||
|
if (ch == 10 && response != "") {
|
||||||
|
break;
|
||||||
|
} else if (ch == 10 && response == "") {
|
||||||
|
// Silently drop CR and therefore line
|
||||||
|
} else {
|
||||||
|
response += (char)ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MODEM_DEBUG
|
||||||
|
Serial.println("DBG: Received " + response);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendBoolCommand(String command) {
|
||||||
|
return sendCommand(command, false) == "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
String sendCommand(String command, bool readOK) {
|
||||||
|
while (commandRunning) {
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
commandRunning = true;
|
||||||
|
SerialAT.println(command);
|
||||||
|
if (!expectString(command)) {
|
||||||
|
return "ERR Command does not match";
|
||||||
|
}
|
||||||
|
String resp = readLine();
|
||||||
|
if (readOK) {
|
||||||
|
readLine();
|
||||||
|
}
|
||||||
|
commandRunning = false;
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
initWiFi();
|
||||||
|
while(!initMQTT()) {
|
||||||
|
Serial.println("ERR: Unable to connect to MQTT broker");
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
initModem();
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimQuotes(String in) {
|
||||||
|
while(in.startsWith("\"")) {
|
||||||
|
in = in.substring(1);
|
||||||
|
}
|
||||||
|
while(in.endsWith("\"")) {
|
||||||
|
in = in.substring(0, in.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSerial() {
|
||||||
|
delay(500);
|
||||||
|
while (Serial.available()) {
|
||||||
|
SerialAT.write(Serial.read());//Forward what Serial received to Software Serial Port
|
||||||
|
}
|
||||||
|
while(SerialAT.available()) {
|
||||||
|
Serial.write(SerialAT.read());//Forward what Software Serial received to Serial Port
|
||||||
|
}
|
||||||
|
}
|
22
src/main.hpp
Normal file
22
src/main.hpp
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#ifndef H_MAIN
|
||||||
|
#define H_MAIN
|
||||||
|
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
bool commandRunning;
|
||||||
|
Timer netCheck;
|
||||||
|
int quality;
|
||||||
|
String regInfo;
|
||||||
|
|
||||||
|
void checkNet();
|
||||||
|
bool expectString(String expect);
|
||||||
|
void initModem();
|
||||||
|
void initWiFi();
|
||||||
|
String readLine();
|
||||||
|
String readLine(int timeout);
|
||||||
|
bool sendBoolCommand(String command);
|
||||||
|
String sendCommand(String command, bool readOK);
|
||||||
|
String trimQuotes(String in);
|
||||||
|
void updateSerial();
|
||||||
|
|
||||||
|
#endif
|
8
src/modem.hpp
Normal file
8
src/modem.hpp
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// TTGO T-Call pins
|
||||||
|
#define MODEM_RST 5
|
||||||
|
#define MODEM_PWKEY 4
|
||||||
|
#define MODEM_POWER_ON 23
|
||||||
|
#define MODEM_TX 27
|
||||||
|
#define MODEM_RX 26
|
||||||
|
#define I2C_SDA 21
|
||||||
|
#define I2C_SCL 22
|
55
src/mqtt.cpp
Normal file
55
src/mqtt.cpp
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
#include "mqtt.hpp"
|
||||||
|
|
||||||
|
WiFiClient wifi;
|
||||||
|
PubSubClient mqtt;
|
||||||
|
|
||||||
|
bool initMQTT() {
|
||||||
|
Serial.println("Connecting to MQTT...");
|
||||||
|
|
||||||
|
mqtt.setClient(wifi);
|
||||||
|
mqtt.setServer(MQTT_HOST, 1883);
|
||||||
|
mqtt.setBufferSize(1024);
|
||||||
|
|
||||||
|
return mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendMessage(String from, String fromName, String date, String message) {
|
||||||
|
Serial.println("DBG: Sending message to MQTT...");
|
||||||
|
|
||||||
|
if (mqtt.state() != 0) {
|
||||||
|
Serial.println("ERR: Invalid MQTT state " + String(mqtt.state(), DEC));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticJsonDocument<400> doc;
|
||||||
|
doc["from"] = from;
|
||||||
|
doc["fromName"] = fromName;
|
||||||
|
doc["date"] = date;
|
||||||
|
doc["message"] = message;
|
||||||
|
|
||||||
|
char buffer[400];
|
||||||
|
serializeJson(doc, buffer);
|
||||||
|
|
||||||
|
return mqtt.publish(MQTT_TOPIC_MESSAGE, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendTelemetry(int quality, String regInfo) {
|
||||||
|
Serial.println("DBG: Sending telemetry to MQTT...");
|
||||||
|
|
||||||
|
if (mqtt.state() != 0) {
|
||||||
|
Serial.println("ERR: Invalid MQTT state " + String(mqtt.state(), DEC));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
StaticJsonDocument<100> doc;
|
||||||
|
doc["quality"] = quality;
|
||||||
|
doc["reg"] = regInfo;
|
||||||
|
|
||||||
|
char buffer[100];
|
||||||
|
serializeJson(doc, buffer);
|
||||||
|
|
||||||
|
return mqtt.publish(MQTT_TOPIC_TELE, buffer);
|
||||||
|
}
|
||||||
|
|
17
src/mqtt.hpp
Normal file
17
src/mqtt.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef H_MQTT
|
||||||
|
#define H_MQTT
|
||||||
|
|
||||||
|
#define MQTT_TOPIC_TELE "espsms/tele"
|
||||||
|
#define MQTT_TOPIC_MESSAGE "espsms/message"
|
||||||
|
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
#include <WiFi.h>
|
||||||
|
|
||||||
|
extern WiFiClient wifi;
|
||||||
|
extern PubSubClient mqtt;
|
||||||
|
|
||||||
|
bool initMQTT();
|
||||||
|
bool sendMessage(String from, String fromName, String date, String message);
|
||||||
|
bool sendTelemetry(int quality, String regInfo);
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue