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