/* 10.02.2020 auf Grundlage:
https://lastminuteengineers.com/SIM900-gsm-shield-arduino-tutorial/
*/
#include <SoftwareSerial.h>
#include "Flanke.h"
#include <EEPROM.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_NeoPixel.h> //Leuchten
#ifdef __AVR__
#include <avr/power.h>
#endif
#define MESSUHR_N_AUFZEICHNUNGEN 250
#define MESSUHR_ML_PRO_UMDREHUNG 5 //konstante muss eingestellt werden vor laden
//Dallas Temperatursensor
int i_temp_oneWire = 2; //input tempfühler
OneWire oneWire(i_temp_oneWire);
DallasTemperature tempsens(&oneWire);
int oneWire_cnt = 0;
//Statusleuchten
int ampel_fuellung = 0;
int ampel_gsm_sig = 1;
int ampel_fehler = 2;
int o_ampel = 5;
Adafruit_NeoPixel ampel = Adafruit_NeoPixel(3, o_ampel, NEO_GRB + NEO_KHZ800); // (#NumPixel, PinOutput, ---)
//Create software serial object to communicate with SIM900
SoftwareSerial SIM900(10, 11); //SIM900 Tx & Rx is connected to Arduino #7 & #8
int o_sim900_on = 9;
//Text Message
String textmessIn = "";
String textmessWrite = "";
String signalqual = "";
//Daten GSM Modul
int eea_telnr = 10; //startadresse der Telefonnummer telnr der länge 20Bytes
int eel_telnr = 20; //stringlänge im EEPROM 20Bytes
String telnr = "+49123123456789"; //format "+49123123456789"
int i_signalqual = -1; //signalqualitat als zahl 0..31
//GSM-Modul:Schrittkette
int gsm_sk_schritt = 0;
int M_gsm_sk_schritt = 0;
int gsm_sk_dauer = 0;
int tries_creg = 0; //versuche der AT-CREG anfrage, bei zu vielen versuchen neues powerup
//Messuhr
int i_messuhr_R_ph = 0; //Photowiderstand am analogen Eingang A0
//Messuhr:Volumenberechnung
int eea_volumen = 40; //startadresse des double-werts volumen
int eel_volumen = 1; //länge des doublewertes = 32bit = 4Byte >> konvertiert zu 1/10 und byte >> bsp 400liter = 40
double volumen = 0.0; //doublewert volumen
byte bVolumen = 0; //bytewert volumen /10
//Messuhr:Zähler
bool messuhr_init = true;
long int messuhr_umdrehungen = 0;
int messuhr_n_fehler;
//Messuhr:Ableitung
long int messuhr_value[MESSUHR_N_AUFZEICHNUNGEN];
int messuhr_differential = 0;
int messuhr_max_auslenkung = 0;
//Messuhr:Durchschnitt
long int messuhr_summe = 0;
long int messuhr_durchschnitt = 0;
//Messuhr:Schrittkette
int messuhr_sk_schritt = 0;
int M_messuhr_sk_schritt = 0;
int messuhr_sk_dauer = 0;
//Timer
long unsigned int millisek_last;
unsigned int millisek = millis();
long unsigned int minuten = 0;
//Uhr aus GSM Zeit
struct gsm_uhr {
unsigned int YY;
unsigned int MM;
unsigned int DD;
unsigned int hh;
unsigned int mm;
unsigned int ss;
};
gsm_uhr uhr = { 2020, 1, 1, 0, 0, 0 };
//Daten Logging
struct log_24 {
int h[24];
int Temp_Kess[24];
int Temp_Waer[24];
int Temp_nMis[24];
int Temp_X[24];
int Vol[24];
};
//neuen Datalog vorbefüllen mit ungültigen werten
log_24 datalog{ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, {}, {}, {}, {}, {} };
//kritische Werte im Datenlogging erkannt.
bool vol80 = 1; //Volumen ist unter 80L gefallen
bool temp40 = 1; //Temperatur ist unter 40°C gefallen
//automatische Datenaufzeichnung und Rückmeldungserstellung
bool data_STATUSTAG = 1; //tägliche Statuserstellung jede 24*60 Minuten ab start
bool data_ERROR = 0; //information über neuen fehler
bool data_UHR = 0; //stündliche anforderung um Signalqualität auszulesen und Uhr zu synchonisieren
bool data_SEND = 0; //ein baustein hat eigenständig eine Sendeanforderung erstellt. im Schritt 100 wird zu senden gesprungen
bool data_SIGQ = 0; //manuelle Anforderung um Signalqualität
//mode und FM
bool FMsek = 0; //Merker zyklischer Aufruf timing
bool FMampel = 0; //Merker zyklischer Aufruf ampel
bool FMvol80 = 1; //1 weil ein Problem von Beginn an wäre ok
bool FMtemp40 = 1; //1 weil ein Problem von Beginn an wäre ok
bool FMerr_laser = 0;
bool FMerr_gsm_netz = 0;
bool FMerr_gsm_modul = 1; //1 weil ein Problem von Beginn an ist vorauszusetzen
bool FM_messuhr_01;
bool FM_gsm_sk; //Merker für Schrittkette GSM Modul
//Fehler und Fehlerauswertung
bool err_wrongtext = 0; //Störung falscher Text
bool err_laser = 0;
bool err_gsm_netz = 0;
bool err_gsm_modul = 1;
byte error = 0x00; //fehlermeldungen werden über error meldung ausgegeben
byte crit = 0x00; //kritische werte werden über status meldung ausgegeben
//**********************//
// Funktionsdeklaration //
//**********************//
void updateSerial();
/*
https://lastminuteengineers.com/, 2020
Standardfunktion zum auslesen des Softwareserial SIM900
angepasst Dominik Brugger, 27.10.2022
kein Übergabewert
Rück: Information wird in textmessIn abgelegt
*/
void sendSMS();
/*
nach https://lastminuteengineers.com/, 10.02.2020
sendet SMS an unten eingesetellte Nummer
angepasst Dominik Brugger, 27.10.2022
Inhalt der SMS ist der Übergabewert String textmessWrite
kein Rückgabewert
*/
void messgen();
/*
Dominik Brugger, 11.02.2020
Findet den Text der erhaltenen SMS heraus
kein Übergabewert
kein Rückgabewert
*/
void uhr_stellen(String uhrstring);
/*
Dominik Brugger, 05.11.2022
Speichert die Uhrzeit im Struct Uhr
Übergabewert Uhrstring, beginnend mit YY/MM/DD...
kein Rückgabewert
*/
void messgen_LAMPENTEST();
/*
Dominik Brugger, 28.11.2020
macht Lampentest für ca 9s generiert Meldung LAMPENTEST
wird aktiviert nach Empfang SMS "LAMPENTEST"
*/
void messgen_STATUS();
/*
Dominik Brugger, 10.02.2020
generiert Meldung STATUS
wird aktiviert nach Empfang SMS "STATUS"
Abschluss mit sendSMS()
*/
void messgen_TEL();
/*
Dominik Brugger, 11.02.2020
stellt Empfangstelefon um, generiert Meldung TEL
wird aktiviert nach Empfang SMS "TEL +4912309876543"
Abschluss mit Bestätigung sendSMS()
*/
void messgen_GETANKT();
/*
Dominik Brugger, 26.10.2022
stellt einen neuen Offset für Tankvolumen ein
wird aktiviert nach Empfang SMS "GETANKT XXX", beispiele: GETANKT 400, GETANKT 184, GETANKT 035
Abschluss mit Bestätigung sendSMS()
*/
void messgen_SIGNALQUAL();
/*
Dominik Brugger, 11.02.2020
generiert Meldung SIGNALQUALITÄT
wird aktiviert nach Empfang SMS "SIGNALQUAL"
Abschluss mit sendSMS()
*/
void messgen_ERROR();
/*
Dominik Brugger, 29.03.2020
generiert Meldung ERROR
wird aktiviert nach pos Flanke eines Errors "err_..."
Abschluss mit sendSMS()
*/
void messgen_TEMP();
/*
Dominik Brugger, 11.02.2020
generiert Meldung TEMP, mehrere Datenpunkte Temperaturen
wird aktiviert nach Empfang SMS "TEMP"
Abschluss mit sendSMS()
*/
void messgen_HELP();
/*
Dominik Brugger, 11.02.2020
generiert Meldung HELP
wird aktiviert nach Empfang SMS "HEP" oder SMS ohne erkennbare Anforderung
Abschluss mit sendSMS()
*/
int data_SIGNALQUAL();
/*
Dominik Brugger, 22.03.2020
generiert Wert für SIGNALQUALITÄT
schreibt in signalqual den Rückgabe-Text
rueck: integer Signalqualität
*/
void do_ampel();
/*
Dominik Brugger, 29.03.2020
zyklisch aufgerufener Baustein
erzeugt signale der statusampel und schickt sie raus.
input: kein
rueck: kein
*/
int tempmessung(int fall);
/*
Dominik Brugger, 18.10.2022, ersterstellung:19.04.2020
misst temperatur in °C
input 1 - 4 für die 4 Tempsensoren, 1 = Kessel, 2 = Wärmetauscher, 3 = nachMischer, 4 = Rücklauf
output Temperatur in °C, -100 falls Tempsensor nicht gefunden werden kann
*/
void do_timing();
/*
Dominik Brugger, 22.03.2020
zyklisch aufgerufener Baustein
wird jede 1s aufgerufen
Baut einen Minutentimer, der alle 490.000.000 Jahre überläuft
ohne rückgabewert
*/
void messuhr_hell();
/* Brugger Dominik 23.10.2022
* liest regelmäßig helligkeit ein und berechnet Ableitung
* input nicht notwendig, Funktionsaufruf genügt
* kein Rückgabewert
*/
void messuhr_sk();
/* Brugger Dominik 23.10.2022
* Warte auf neg. sinus des Helligkeitverlaufs
* Wartet auf fallende Helligkeit, Nulldurchgang mit ausreichender Helligkeitsminderung, steigende Helligkeit in gleicher Ableitung wie zu beginn
* input nicht notwendig, Funktionsaufruf genügt
* kein Rückgabewert
*/
void gsm_sk();
/* Brugger Dominik 27.10.2022
* Steuert den Bootupverlauf 10<S<29, Meldet sich im Netz an und prüft Signalqualität 30<S<39, Macht Uhrensync 40<S<49,
* Steuert lesen der SMS 100<S<109, Steuert Messgen 110<S<119, Steuert Versenden der Nachricht 120<S<129
* input nicht notwendig, Funktionsaufruf genügt
* kein Rückgabewert
*/
//********************//
// SETUP messuhr_initializing //
//********************//
void setup() {
//Begin serial communication with Arduino and Arduino IDE (Serial Monitor)
Serial.begin(9600);
//Begin serial communication with Arduino and SIM900
Serial1.begin(9600);
//Config Tempfühler
pinMode(i_temp_oneWire, INPUT_PULLUP); //interner Pullup funzt nicht, hat 20kOhm; oneWire benötigt 4k7Ohm
tempsens.begin();
oneWire_cnt = tempsens.getDS18Count();
Serial.print("Anz Temperatursensor: ");
Serial.println(oneWire_cnt);
//set up neopixel
pinMode(o_ampel, OUTPUT);
ampel.begin();
ampel.setPixelColor(2, ampel.Color(128, 32, 0)); //orange während bootup bis zum ersten funktionsaufruf do_ampel()
ampel.show();
//Set up Messuhr
pinMode(i_messuhr_R_ph, INPUT);
//lade volumen aus eeprom
Serial.print("gespeicherte Byte Fuellmenge: ");
Serial.println(bVolumen = EEPROM.read(eea_volumen));
volumen = ((double)(EEPROM.read(eea_volumen))) * 10 + 5;
//GSM Modul
pinMode(o_sim900_on, OUTPUT);
//lade telefonnummer aus eeprom
for (int i = 0; i < eel_telnr; i++) {
char readdata;
readdata = EEPROM.read(i + eea_telnr);
Serial.print(String(i) + " " + readdata + "\t");
telnr.setCharAt(i, readdata);
}
Serial.println("");
Serial.println("geladene Telefonnummer " + telnr);
}
//*******************//
// LOOP Main Program //
//*******************//
void loop() {
if (Flanke(TaktMB(), &FMsek)) {
do_timing(); //Timer
}
if (Flanke(TMB(17, 200), &FM_gsm_sk)) { // 17, 217, 417, 617, 817, 1017, 1217, 1417, 1617, 1817, 2017, 2217, ...
//alle 200ms schrittkette für GSM Modul starten,
Serial.print("SK GSM :S:");
Serial.print(gsm_sk_schritt);
Serial.print(" :D:");
Serial.println(gsm_sk_dauer);
gsm_sk();
}
if (Flanke(TMB(100, 500), &FMampel)) { // 100, 600, 1100, 1600, 2100, ...
do_ampel(); //Fehlerauswertung und Ampelanzeigen
}
//regelmäßige Abstände Helligkeiten einlesen und durchschnitt berechnen
if (Flanke(TMB(71, (15 * MESSUHR_ML_PRO_UMDREHUNG)), &FM_messuhr_01)) { // alle z.B. 25ms Helligkeit posten, 150ms == 10ml/U
Serial.print("SK Messuhr :S:");
Serial.print(messuhr_sk_schritt);
Serial.print(" :D:");
Serial.print(messuhr_sk_dauer);
messuhr_hell(); // alle xs Helligkeit posten, durchschnitt berechnen
messuhr_sk(); //Schrittkette zur Prüfung des Helligkeitsverlaufs
}
if (byte((volumen - 0.001) / 10) != bVolumen) {
bVolumen = byte((volumen - 0.001) / 10);
Serial.print("Neuer Volumenwert gespeichert: ");
Serial.print(bVolumen);
Serial.print(" x10 liter\n");
EEPROM.write(eea_volumen, bVolumen);
}
if (gsm_sk_schritt > 100) {
if (data_ERROR) {
messgen_ERROR();
} else if (data_STATUSTAG) {
messgen_STATUSTAG();
}
}
}
//**********//
// METHODEN //
//**********//
void updateSerial() {
while (Serial.available()) {
Serial1.write(Serial.read()); //Forward what Serial received to Software Serial Port
}
while (Serial1.available()) {
textmessIn = Serial1.readString(); //Forward what Software Serial received to Serial Port
}
textmessIn.toUpperCase();
if (M_gsm_sk_schritt != 103) {
Serial.println(textmessIn);
}
}
void sendSMS() {
data_SEND = 1;
}
void messgen() {
err_wrongtext = 0;
if (textmessIn.indexOf("STATUS") >= 0) {
messgen_STATUS();
} else if (textmessIn.indexOf("GETANKT") >= 0) {
messgen_GETANKT();
} else if (textmessIn.indexOf("TEL") >= 0) {
messgen_TEL();
} else if (textmessIn.indexOf("SIGNALQUAL") >= 0) {
messgen_SIGNALQUAL();
} else if (textmessIn.indexOf("TEMP") >= 0) {
messgen_TEMP();
} else if (textmessIn.indexOf("LOG") >= 0) {
messgen_LOG();
} else if (textmessIn.indexOf("LAMPENTEST") >= 0) {
messgen_LAMPENTEST();
} else if (textmessIn.indexOf("HELP") >= 0) {
messgen_HELP();
} else {
//sollte keiner der gegebenen Texte geschickt worden sein, einfach ignorieren und in Lesemodus zurück
//textmessIn = "";
err_wrongtext = 1;
}
if (textmessIn.indexOf("\",\"") >= 0) {
Serial.println("Neue Nachricht:");
Serial.print(textmessIn);
Serial.println("\nNeue Nachricht Ende");
uhr_stellen(textmessIn.substring(textmessIn.indexOf("\"\",\"") + 4));
}
}
void uhr_stellen(String uhrstring) {
Serial.print("Uhrstring: ");
uhrstring = uhrstring.substring(0, 17);
Serial.print(uhrstring);
if (uhrstring.substring(0, 2).toInt() > 0) {
uhr.YY = 2000 + (uhrstring.substring(0, 2).toInt());
uhr.MM = uhrstring.substring(3, 5).toInt();
uhr.DD = uhrstring.substring(6, 8).toInt();
uhr.hh = uhrstring.substring(9, 11).toInt();
uhr.mm = uhrstring.substring(12, 14).toInt();
uhr.ss = uhrstring.substring(15, 17).toInt();
Serial.print(" Uhr gestellt:");
Serial.print(uhr.hh);
Serial.print(":");
Serial.print(uhr.mm);
Serial.print(":");
Serial.print(uhr.ss);
} else {
Serial.print(" Uhr nicht gestellt!");
}
Serial.print("\n");
}
void messgen_STATUS() {
String smstext = "";
smstext = smstext + "Fuellmenge = " + String((int)volumen) + "l\n";
if (messuhr_umdrehungen < 1000) {
smstext = smstext + "Verbrauch = " + String(messuhr_umdrehungen) + " x" + String(MESSUHR_ML_PRO_UMDREHUNG) + "ml\n";
} else {
smstext = smstext + "Verbrauch = " + String((int)((double)messuhr_umdrehungen * (double)MESSUHR_ML_PRO_UMDREHUNG / 1000)) + "l\n";
}
smstext = smstext + String(tempmessung(1)) + "C" + " (T_Kessel)\n";
smstext = smstext + String(tempmessung(2)) + "C" + " (T_Waermetauscher)\n";
smstext = smstext + String(tempmessung(3)) + "C" + " (T_nachMischer)\n";
textmessWrite = smstext;
sendSMS();
}
void messgen_STATUSTAG() {
String smstext = "";
smstext = smstext + "Fuellmenge = " + String((int)volumen) + "l\n";
smstext = smstext + "Verbrauch = " + String((int)((double)messuhr_umdrehungen * (double)MESSUHR_ML_PRO_UMDREHUNG / 1000)) + "l\n";
smstext = smstext + "Temp = " + String(tempmessung(1)) + "C";
smstext = smstext + " > " + String(tempmessung(2)) + "C";
smstext = smstext + " > " + String(tempmessung(3)) + "C\n";
smstext = smstext + "Signal = " + (int)((double)i_signalqual * 3.24) + "%" + "\n\n";
smstext = smstext + "detailliert sende LOG oder TEMP\n";
smstext = smstext + "weiteres sende HELP";
textmessWrite = smstext;
sendSMS();
data_STATUSTAG = 0;
}
void messgen_TEL() {
String merktext;
merktext = textmessIn.substring(textmessIn.indexOf("\"") + 1, textmessIn.indexOf("\"") + 18);
//merktext = textmessIn.substring(9, 27);
Serial.println(merktext);
int pos_gaense = merktext.indexOf("\",\"");
merktext = merktext.substring(0, pos_gaense);
textmessWrite = "Telefonnummer erfolgreich auf " + merktext + " umgestellt.";
telnr = merktext;
for (int i = 0; i < eel_telnr; i++) {
char writedata = telnr.charAt(i);
if (i >= pos_gaense) {
char writedata = ' ';
}
Serial.print(String(i) + " " + writedata + "\t");
EEPROM.write(i + eea_telnr, writedata);
}
sendSMS();
}
void messgen_GETANKT() {
String merktext = textmessIn;
int pos_triggerword = merktext.indexOf("GETANKT");
merktext = merktext.substring(pos_triggerword + 8, pos_triggerword + 11);
volumen = ((double)merktext.toInt());
data_STATUSTAG = 1;
}
void messgen_SIGNALQUAL() {
String bewertung_SIGNAL;
if (M_gsm_sk_schritt == 110) {
data_SIGQ = 1;
}
if (i_signalqual > 0 && i_signalqual <= 8) bewertung_SIGNAL = "schlecht";
if (i_signalqual > 8 && i_signalqual <= 15) bewertung_SIGNAL = "akzeptabel";
if (i_signalqual > 15 && i_signalqual <= 25) bewertung_SIGNAL = "gut";
if (i_signalqual > 25 && i_signalqual <= 31) bewertung_SIGNAL = "ausgezeichnet";
textmessWrite = "Signalqualitaet von \n(schlecht= 0 - 31 =perfekt):\n\n" + signalqual + "\n\n" + bewertung_SIGNAL;
textmessWrite = textmessWrite + "\n\nSignal = " + (int)((double)i_signalqual * 3.24) + "%";
if (data_SIGQ) {
sendSMS();
}
}
int data_SIGNALQUAL() {
//signalqual abfragen
signalqual = textmessIn.substring(10, textmessIn.indexOf(","));
i_signalqual = String(signalqual.substring(signalqual.indexOf(":") + 2)).toInt();
Serial.println(i_signalqual);
return i_signalqual;
}
void messgen_TEMP() {
String smstext = "";
int stundenzeiger;
smstext = "Uhr: T_Kess > T_Waer > T_nMis";
for (int i = 0; i < 24; i += 3) {
stundenzeiger = (3 + i + uhr.hh - (uhr.hh % 3)) % 24;
if (datalog.h[stundenzeiger] >= 0) {
smstext = smstext + "\n";
if (datalog.h[stundenzeiger] < 10) smstext = smstext + "0";
smstext = smstext + datalog.h[stundenzeiger] + "h: ";
smstext = smstext + datalog.Temp_Kess[stundenzeiger] + "C";
smstext = smstext + " > ";
smstext = smstext + datalog.Temp_Waer[stundenzeiger] + "C";
smstext = smstext + " > ";
smstext = smstext + datalog.Temp_nMis[stundenzeiger] + "C";
smstext = smstext + "";
//smstext = smstext + ", ";
//smstext = smstext + datalog.Temp_X[stundenzeiger] + "C";
}
}
textmessWrite = smstext;
sendSMS();
}
void messgen_LOG() {
String smstext = "";
int stundenzeiger;
smstext = "Uhr: Vol";
for (int i = 0; i < 24; i += 3) {
stundenzeiger = (3 + i + uhr.hh - (uhr.hh % 3)) % 24;
if (datalog.h[stundenzeiger] >= 0) {
smstext = smstext + "\n";
if (datalog.h[stundenzeiger] < 10) smstext = smstext + "0";
smstext = smstext + datalog.h[stundenzeiger] + "h: ";
smstext = smstext + datalog.Vol[stundenzeiger] + "l";
}
}
textmessWrite = smstext;
sendSMS();
}
void messgen_LAMPENTEST() {
//ampel.Color muss in (g, r, b) immer 0 bis 255 angegeben werden
ampel.setPixelColor(ampel_fuellung, ampel.Color(255, 0, 0));
ampel.setPixelColor(ampel_gsm_sig, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_fehler, ampel.Color(0, 0, 0));
ampel.show();
delay(1000);
ampel.setPixelColor(ampel_fuellung, ampel.Color(0, 255, 0));
ampel.setPixelColor(ampel_gsm_sig, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_fehler, ampel.Color(0, 0, 0));
ampel.show();
delay(1000);
ampel.setPixelColor(ampel_fuellung, ampel.Color(0, 0, 255));
ampel.setPixelColor(ampel_gsm_sig, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_fehler, ampel.Color(0, 0, 0));
ampel.show();
delay(1000);
//ampel.Color muss in (g, r, b) immer 0 bis 255 angegeben werden
ampel.setPixelColor(ampel_fuellung, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_gsm_sig, ampel.Color(255, 0, 0));
ampel.setPixelColor(ampel_fehler, ampel.Color(0, 0, 0));
ampel.show();
delay(1000);
ampel.setPixelColor(ampel_fuellung, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_gsm_sig, ampel.Color(0, 255, 0));
ampel.setPixelColor(ampel_fehler, ampel.Color(0, 0, 0));
ampel.show();
delay(1000);
ampel.setPixelColor(ampel_fuellung, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_gsm_sig, ampel.Color(0, 0, 255));
ampel.setPixelColor(ampel_fehler, ampel.Color(0, 0, 0));
ampel.show();
delay(1000);
//ampel.Color muss in (g, r, b) immer 0 bis 255 angegeben werden
ampel.setPixelColor(ampel_fuellung, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_gsm_sig, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_fehler, ampel.Color(255, 0, 0));
ampel.show();
delay(1000);
ampel.setPixelColor(ampel_fuellung, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_gsm_sig, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_fehler, ampel.Color(0, 255, 0));
ampel.show();
delay(1000);
ampel.setPixelColor(ampel_fuellung, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_gsm_sig, ampel.Color(0, 0, 0));
ampel.setPixelColor(ampel_fehler, ampel.Color(0, 0, 255));
ampel.show();
delay(1000);
}
void messgen_ERROR() {
String smstext = "Neuer Fehler gekommen!!!\n\n";
if (err_laser) smstext = smstext + "Fehler an Messuhr! Angesteckt?\n";
if (err_gsm_netz) smstext = smstext + "Fehler am GSM-Modul, kein Netz!\n";
if (err_gsm_modul) smstext = smstext + "Fehler am GSM-Modul, nicht funktionsfähig!\n";
textmessWrite = smstext;
sendSMS();
data_ERROR = 0;
}
void messgen_HELP() {
Serial.println("help");
String smstext = "";
smstext = smstext + "STATUS Fuell&Temp\n";
smstext = smstext + "TEL Empf-tel aendern\n";
smstext = smstext + "TEMP Temp 24h\n";
smstext = smstext + "SIGNALQUAL Netzqual\n";
smstext = smstext + "LOG Fuell 24h\n";
smstext = smstext + "GETANKT XXX\n";
smstext = smstext + "LAMPENTEST";
textmessWrite = smstext;
sendSMS();
}
int tempmessung(int fall) {
int messung = 0;
tempsens.requestTemperatures();
if (fall <= oneWire_cnt) {
messung = (int)tempsens.getTempCByIndex(fall - 1);
} else {
messung = -100;
}
return messung;
}
void do_ampel() {
//generiere fehler
error = (err_laser << 0) | (err_gsm_netz << 1) | (err_gsm_modul << 2);
data_ERROR = Flanke(err_laser, &FMerr_laser) | Flanke(err_gsm_netz, &FMerr_gsm_netz) | Flanke(err_gsm_modul, &FMerr_gsm_modul);
crit = (temp40 << 0) | (vol80 << 1);
//anzeige fehler
int fehler_r = 128 * (error > 0);
if (error > 0) {
Serial.print("errorcode: ");
Serial.print(error, BIN);
Serial.print("\n");
}
if (crit > 0) {
Serial.print("critcode: ");
Serial.print(crit, BIN);
Serial.print("\n");
}
ampel.setPixelColor(ampel_fehler, ampel.Color(fehler_r, 0, 0));
//anzeige fuellung
int fuellung_r = 128 * (volumen < 180);
int fuellung_g = 128 * (volumen > 80);
int fuellung_b = 255 * (volumen > 350);
ampel.setPixelColor(ampel_fuellung, ampel.Color(fuellung_r, fuellung_g, fuellung_b));
//anzeige gsm
int gsm_sig_r = (64 * (i_signalqual < 8)) + (64 * (i_signalqual < 15));
int gsm_sig_g = 64 + (64 * (i_signalqual > 8));
int gsm_sig_b = (128 * (i_signalqual > 25));
ampel.setPixelColor(ampel_gsm_sig, ampel.Color(gsm_sig_r, gsm_sig_g, gsm_sig_b));
//raussenden an ampel
ampel.show();
}
void do_timing() {
if (millisek_last > millis() % (59542)) { //11 Minuten Abweichung bei 1440 Min/Tag
// Das passiert jede Minute
Serial.print("Minutencounter: ");
Serial.println(++minuten);
//uhr weiterlaufen lassen
uhr.mm++;
if (uhr.mm > 59) {
uhr.mm = 0;
uhr.hh++;
if (uhr.hh > 23) {
uhr.hh = 0;
uhr.DD++;
}
}
if (uhr.mm == 3 || minuten == 2) { //stündlich immer um xx:03 uhrensync
//Uhr einstellen
data_UHR = 1;
}
if (uhr.mm == 0 || minuten == 4) { //stündlich zur vollen Stunde
datalog.h[uhr.hh] = uhr.hh;
datalog.Temp_Kess[uhr.hh] = tempmessung(1);
datalog.Temp_Waer[uhr.hh] = tempmessung(2);
datalog.Temp_nMis[uhr.hh] = tempmessung(3);
datalog.Temp_X[uhr.hh] = tempmessung(4);
datalog.Vol[uhr.hh] = volumen;
Serial.println("Zeit \tT_Kess \tT_Waer \tT_nMis \tVol");
for (int i = 0; i < 24; i++) {
Serial.println(String(datalog.h[i]) + "h\t" + String(datalog.Temp_Kess[i]) + "C\t" + String(datalog.Temp_Waer[i]) + "C\t" + String(datalog.Temp_nMis[i]) + "C\t" + String(datalog.Vol[i]) + "l");
}
}
//nur wenn einer der Fehler neuerdings vorhanden...
data_STATUSTAG = (Flanke(vol80, &FMvol80) || Flanke(temp40, &FMtemp40));
//erkannter kritischer Bereich: Füllmenge zu klein, unter 80L
if (volumen < 80) vol80 = 1; //unter 80L fehler erzeugen
if (volumen > 180) vol80 = 0; //über 180L fehler ablöschen >> Schalthysterese 100L
//erkannter kritischer Bereich: Temperatur am Kessel zu klein, unter 40°C
int momentantemp = tempmessung(1);
if (momentantemp < 40) temp40 = 1; //unter 40°C fehler erzeugen
if (momentantemp > 49) temp40 = 0; //über 49°C fehler ablöschen >> Schalthysterese 9°C
//das passiert alle 24 Stunden
data_STATUSTAG |= ((minuten % (60 * 24)) == 0); //60*24 sind 60Min/Stunde *24Stunden/Tag
}
millisek_last = millis() % (59542);
}
void messuhr_hell() {
if (messuhr_init) {
//messuhr_initialisiere Fehlerauswertung
messuhr_n_fehler = 0;
//messuhr_initialisiere Durchschnitt
messuhr_value[0] = analogRead(i_messuhr_R_ph);
for (int i = 0; i < MESSUHR_N_AUFZEICHNUNGEN; i++) {
messuhr_value[i] = messuhr_value[0];
}
messuhr_summe = MESSUHR_N_AUFZEICHNUNGEN * messuhr_value[0];
}
Serial.print(", val: ");
Serial.println(messuhr_value[0]);
if (messuhr_n_fehler == 5) {
err_laser = 1;
}
if (messuhr_n_fehler == 0) {
err_laser = 0;
}
messuhr_summe -= messuhr_value[(MESSUHR_N_AUFZEICHNUNGEN - 1)];
for (int i = (MESSUHR_N_AUFZEICHNUNGEN - 1); i > 0; i--) {
messuhr_value[i] = messuhr_value[i - 1];
}
messuhr_value[0] = analogRead(i_messuhr_R_ph);
messuhr_differential = messuhr_value[0] - messuhr_value[1];
messuhr_summe += messuhr_value[0];
messuhr_durchschnitt = messuhr_summe / (MESSUHR_N_AUFZEICHNUNGEN);
if (messuhr_value[0] < 5) {
messuhr_n_fehler++;
Serial.print("Messuhr Value zum ");
Serial.print(messuhr_n_fehler);
Serial.println(". Mal zu klein.");
} else {
messuhr_n_fehler = 0;
}
messuhr_init = false; //messuhr_initalisierung ist abgeschlossen
}
void messuhr_sk() {
if (M_messuhr_sk_schritt != messuhr_sk_schritt) {
messuhr_sk_dauer = 0;
}
M_messuhr_sk_schritt = messuhr_sk_schritt;
switch (messuhr_sk_schritt) {
case 0:
if (messuhr_differential < -9 && messuhr_sk_dauer >= 25) {
messuhr_sk_schritt = 1;
Serial.print("Zeiger-Flanke erkannt: ");
Serial.print(messuhr_value[1]);
Serial.print(" > ");
Serial.print(messuhr_value[0]);
Serial.print("\n");
}
messuhr_max_auslenkung = 0;
break;
case 1:
if (messuhr_differential >= 0 && messuhr_sk_dauer >= 2 && (19 * messuhr_value[0]) < (20 * messuhr_durchschnitt)) {
messuhr_sk_schritt = 2;
}
if (abs(messuhr_differential) > messuhr_max_auslenkung) messuhr_max_auslenkung = abs(messuhr_differential);
break;
case 2:
if (messuhr_differential >= 5 && messuhr_sk_dauer > 0) {
messuhr_sk_schritt = 3;
}
break;
case 3:
if (messuhr_differential >= (5 * messuhr_max_auslenkung) / 10) {
messuhr_sk_schritt = 4;
}
break;
case 4:
messuhr_umdrehungen++;
volumen -= ((double)MESSUHR_ML_PRO_UMDREHUNG / 1000);
Serial.print("Z_U: ");
Serial.print(messuhr_umdrehungen);
Serial.print(", Vol: ");
Serial.print(volumen);
Serial.print("\n");
messuhr_sk_schritt = 0;
break;
default:
messuhr_sk_schritt = 0;
break;
}
messuhr_sk_dauer++;
if (messuhr_sk_dauer >= 75 && M_messuhr_sk_schritt != 0) {
//Schrittkette automatisch zurücksetzen, das kann nicht sein:
Serial.println("Schrittkette wird zurückgesetzt! Schritt=0");
messuhr_sk_schritt = 0;
}
}
void gsm_sk() {
if (M_gsm_sk_schritt != gsm_sk_schritt) {
gsm_sk_dauer = 0;
}
M_gsm_sk_schritt = gsm_sk_schritt;
switch (gsm_sk_schritt) {
case 0:
//messuhr_init
gsm_sk_schritt = 10;
break;
case 10:
//Boot GSM Shield
Serial.println("Booting GSM module");
digitalWrite(o_sim900_on, LOW);
//Nach 1000ms weiter
if (gsm_sk_dauer >= 5) {
gsm_sk_schritt = 12;
}
break;
case 12:
//Bootuptaste für ca. 2s drücken
ampel.setPixelColor(1, ampel.Color(128, 0, 0));
ampel.show();
digitalWrite(o_sim900_on, HIGH);
//Nach 1500ms weiter
if (gsm_sk_dauer >= 8) {
gsm_sk_schritt = 14;
}
break;
case 14:
//Taste wieder loslassen und etwas warten
digitalWrite(o_sim900_on, LOW);
//Nach 5000ms weiter
if (gsm_sk_dauer >= 15) {
gsm_sk_schritt = 20;
}
break;
case 20:
//Handshaking with Serial1
Serial1.println("AT");
gsm_sk_schritt = 21;
break;
case 21:
//wartet auf antwort von modul, diese Antwort wird nicht geprüft, daher möglicherweise nicht nötig (27.10.)
updateSerial();
if ((textmessIn.indexOf("RDY") > 0)) {
gsm_sk_schritt = 22;
} else if ((textmessIn.indexOf("NORMAL POWER DOWN") > 0)) {
gsm_sk_schritt = 10;
} else {
gsm_sk_schritt = 22;
}
break;
case 22:
//Read SIM information to confirm whether the SIM is plugged
Serial1.println("AT+CCID");
gsm_sk_schritt = 23;
break;
case 23:
//wartet auf antwort von modul
updateSerial();
Serial.print(textmessIn);
Serial.println(" SIM card ID");
gsm_sk_schritt = 30;
break;
case 30:
//Check whether it has registered in the network
if (tries_creg > 60) {
tries_creg = 0;
gsm_sk_schritt = 10;
} else {
Serial1.println("AT+CREG?");
gsm_sk_schritt = 31;
}
break;
case 31:
//wartet auf antwort von modul, wenn verbindung besteht gehe weiter zu signalprüfung, sonst wieder anfragen ob bereits registriert
updateSerial();
if (tries_creg == 0) {
Serial.print("Versuche Netzverbindung herzustellen: ");
}
if ((textmessIn.indexOf("1,1") > 0) || (textmessIn.indexOf("1,5") > 0) || (textmessIn.indexOf("0,1") > 0) || (textmessIn.indexOf("0,5") > 0)) {
Serial.println("\nNetzverbindung besteht!");
err_gsm_netz = 0;
gsm_sk_schritt = 32;
} else {
tries_creg++;
gsm_sk_schritt = 30;
}
break;
case 32:
//Signal quality test, value range is 0-31 , 31 is the best
Serial1.println("AT+CSQ");
gsm_sk_schritt = 33;
break;
case 33:
//wartet auf antwort von modul, Die Signalqualität wird gespeichert
updateSerial();
data_SIGNALQUAL();
gsm_sk_schritt = 34;
break;
case 34:
//erzeuge Text
if (data_SIGQ) messgen_SIGNALQUAL();
data_SIGQ = 0;
gsm_sk_schritt = 40;
break;
case 40:
// Uhrzeit aus RTC abfragen
Serial1.println("AT+CCLK?");
gsm_sk_schritt = 41;
break;
case 41:
//wartet auf antwort von modul, Die Signalqualität wird gespeichert
updateSerial();
err_gsm_modul = 0; //gsmmodul hat einwandfrei gestartet
gsm_sk_schritt = 42;
break;
case 42:
// Uhrzeit aus GSM Netz abfragen
Serial1.println("AT+QLTS");
gsm_sk_schritt = 43;
break;
case 43:
//wartet auf antwort von modul, Die Signalqualität wird gespeichert
updateSerial();
uhr_stellen(textmessIn.substring(textmessIn.indexOf("\"") + 1));
data_UHR = 0;
gsm_sk_schritt = 100;
break;
case 100:
//LESE MODUS, sollte uhrensync anstehen, zunächst in diesen ablauf gehen, schrittkette kommt von selbst wieder in Schritt 100
if (((data_SEND != 1) && (data_SIGQ != 1) && data_UHR != 1)) {
// Configuring TEXT mode
Serial1.println("AT+CMGF=1");
gsm_sk_schritt = 102;
textmessWrite = "";
} else if (data_SEND) {
//ein Baustein hat eine Sendeanforderung gestellt, springe zu versand
gsm_sk_schritt = 120;
} else if (data_SIGQ) {
//jump to Uhrensync und Signalabfrage
gsm_sk_schritt = 32;
} else if (data_UHR) {
//jump to Uhrensync und Signalabfrage
gsm_sk_schritt = 32;
} else {
Serial.println("keine Entscheidung?! Programmierfehler");
}
textmessIn = "";
break;
case 102:
// Decides how newly arrived SMS messages should be handled
Serial1.println("AT+CNMI=1,2,0,0,0");
gsm_sk_schritt = 103;
break;
case 103:
//wartet auf antwort von modul, diese Antwort wird nicht geprüft, daher möglicherweise nicht nötig (27.10.)
updateSerial();
gsm_sk_schritt = 110;
break;
case 110:
// Organisiert den korrekten Text zum zurücksenden, falls Text ungültig zurück in lesemodus
//updateSerial();
//Gültiger Text wurde eingegeben, generierte Meldung absenden
gsm_sk_schritt = 120;
messgen();
if (err_wrongtext || !(data_SEND)) {
//Ungültiger Text oder kein Rückgabetext erforderlich, zurück zu Lesemodus
err_wrongtext = 0;
gsm_sk_schritt = 100;
}
break;
case 120:
//ANTWORTEN MODUS
data_SEND = 0;
// Configuring TEXT mode
Serial1.println("AT+CMGF=1");
gsm_sk_schritt = 121;
break;
case 121:
//wartet auf antwort von modul, diese Antwort wird nicht geprüft, daher möglicherweise nicht nötig (27.10.)
updateSerial();
gsm_sk_schritt = 122;
break;
case 122:
//change ZZ with country code and xxxxxxxxxxx with phone number to sms
Serial1.println("AT+CMGS=\"" + String(telnr) + "\"");
gsm_sk_schritt = 123;
break;
case 123:
//wartet auf antwort von modul, diese Antwort wird nicht geprüft, daher möglicherweise nicht nötig (27.10.)
updateSerial();
gsm_sk_schritt = 124;
break;
case 124:
//text content
Serial.println("::::");
Serial.println(textmessWrite);
Serial.println("::::");
Serial1.print(textmessWrite);
gsm_sk_schritt = 125;
break;
case 125:
//wartet auf antwort von modul, diese Antwort wird nicht geprüft, daher möglicherweise nicht nötig (27.10.)
updateSerial();
gsm_sk_schritt = 126;
break;
case 126:
//abschlusssequenz
Serial1.write(26);
gsm_sk_schritt = 100;
break;
default:
gsm_sk_schritt = 0;
break;
}
gsm_sk_dauer++;
if (gsm_sk_dauer > 100 && gsm_sk_schritt != 0) {
//Schrittkette automatisch zurücksetzen, das kann nicht sein:
Serial.println("Schrittkette wird zurückgesetzt! Schritt=0");
gsm_sk_schritt = 0;
}
}