#include <FS.h> //must be first
#include "SPIFFS.h"
#include "esp32/ulp.h"
#include "esp_deep_sleep.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "driver/adc.h"
#include <WiFiManager.h>
#include <ArduinoJson.h>
#include <PubSubClient.h>
#define ULP_DATA_OFFSET 100 //muss größer als ULP Programmlänge sein -> auch in Wifimanager anpassen
#define SARADC1 0 //ADC1
#pragma region "Parameter handling"
char c_brokeraddress[100] = "192.168.2.100";
char c_brokerport[5] = "1883";
char c_brokeruser[50] = "none";
char c_brokerpassword[50] = "none";
char c_topic[200] = "sensor/";
char c_clientid[100] = "ESP8266Client-XXXX";
char c_interval[10] = "250000";
char c_led_on[10] = "3000";
char c_max_freerun[10] = "240";
char c_rpkwh[10] = "75.0";
char c_hysterese[10] = "20";
char c_threshold[10] = "512";
WiFiManagerParameter* custom_brokeraddress;
WiFiManagerParameter* custom_brokerport;
WiFiManagerParameter* custom_brokeruser;
WiFiManagerParameter* custom_brokerpassword;
WiFiManagerParameter* custom_topic;
WiFiManagerParameter* custom_clientid;
WiFiManagerParameter* custom_interval;
WiFiManagerParameter* custom_led_on;
WiFiManagerParameter* custom_max_freerun;
WiFiManagerParameter* custom_rpkwh;
WiFiManagerParameter* custom_hysterese;
WiFiManagerParameter* custom_threshold;
WiFiManagerParameter* custom_text;
bool paramShouldSave = false;
void saveConfigCallback () {
paramShouldSave = true;
}
void add_parameter(WiFiManager &w) {
custom_brokeraddress = new WiFiManagerParameter("brokeraddress", "Broker-Address", c_brokeraddress, 100);
custom_brokerport = new WiFiManagerParameter("brokerport", "Broker-Port", c_brokerport, 5);
custom_brokeruser = new WiFiManagerParameter("brokeruser", "Broker-User", c_brokeruser, 50);
custom_brokerpassword = new WiFiManagerParameter("brokerpassword", "Broker-Password", c_brokerpassword, 50);
custom_topic = new WiFiManagerParameter("topic", "Topic", c_topic, 200);
custom_clientid = new WiFiManagerParameter("clientid", "Client-ID", c_clientid, 100);
custom_interval = new WiFiManagerParameter("interval", "Interval (us)", c_interval, 10);
custom_led_on = new WiFiManagerParameter("led_on", "LED on time (ulp-cycles)", c_led_on, 10);
custom_max_freerun = new WiFiManagerParameter("max_freerun", "Max freerun (s)", c_max_freerun, 10);
custom_rpkwh = new WiFiManagerParameter("rpkwh", "Rotations per kWh", c_rpkwh, 10);
custom_hysterese = new WiFiManagerParameter("hysterese", "Hysterese", c_hysterese, 10);
custom_threshold = new WiFiManagerParameter("threshold", "Threshold (-val = falling edge)", c_threshold, 10);
custom_text = new WiFiManagerParameter("actual value: <iframe frameborder=\"0\" height=\"20\" id=\"frame\" src=\"/other\"></iframe> <script>setInterval(function(){ document.getElementById('frame').contentWindow.location.reload(); },500); </script>");
w.addParameter(custom_brokeraddress);
w.addParameter(custom_brokerport);
w.addParameter(custom_brokeruser);
w.addParameter(custom_brokerpassword);
w.addParameter(custom_topic);
w.addParameter(custom_clientid);
w.addParameter(custom_interval);
w.addParameter(custom_led_on);
w.addParameter(custom_max_freerun);
w.addParameter(custom_rpkwh);
w.addParameter(custom_hysterese);
w.addParameter(custom_threshold);
w.addParameter(custom_text);
}
void read_parameter() {
//read Variables from Flash
if (SPIFFS.begin(true)) {
if (SPIFFS.exists("/config.json")) {
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
size_t size = configFile.size();
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer(2048);
JsonObject& json = jsonBuffer.parseObject(buf.get());
if (json.success()) {
strcpy(c_brokeraddress, json["brokeraddress"]);
strcpy(c_brokerport, json["brokerport"]);
strcpy(c_brokeruser, json["brokeruser"]);
strcpy(c_brokerpassword, json["brokerpassword"]);
strcpy(c_topic, json["topic"]);
strcpy(c_clientid, json["clientid"]);
strcpy(c_interval, json["interval"]);
strcpy(c_led_on, json["led_on"]);
strcpy(c_max_freerun, json["max_freerun"]);
strcpy(c_rpkwh, json["rpkwh"]);
strcpy(c_hysterese, json["hysterese"]);
strcpy(c_threshold, json["threshold"]);
}
}
}
}
}
void write_parameter() {
if(paramShouldSave) {
DynamicJsonBuffer jsonBuffer(2048);
JsonObject& json = jsonBuffer.createObject();
json["brokeraddress"] = (*custom_brokeraddress).getValue();
json["brokerport"] = (*custom_brokerport).getValue();
json["brokeruser"] = (*custom_brokeruser).getValue();
json["brokerpassword"] = (*custom_brokerpassword).getValue();
json["topic"] = (*custom_topic).getValue();
json["clientid"] = (*custom_clientid).getValue();
json["interval"] = (*custom_interval).getValue();
json["led_on"] = (*custom_led_on).getValue();
json["max_freerun"] = (*custom_max_freerun).getValue();
json["rpkwh"] = (*custom_rpkwh).getValue();
json["hysterese"] = (*custom_hysterese).getValue();
json["threshold"] = (*custom_threshold).getValue();
File configFile = SPIFFS.open("/config.json", "w");
json.printTo(configFile);
configFile.close();
delay(10);
}
}
#pragma endregion
/*
+---------------------------------------------------+
| +-----+ +-----------+ |
| +-+ | | LED | |
| | | | | grün +-+ |
| 1MOhm| | | +-----+ | | |
| +-+ | + + +-> | | 50Ohm|
| +-----------+ | +-+ +--> +-+ |
| | Photo | | +---+ | |
| | Diode +-----+ | | | |
| | +-> + + | | | |
| | +--> +-+ +-----+ | |
| | +---+ | | |
+---------------------------------------------------+
| | | |
| | | |
+ + + +
IO34 3V3 GND IO25
*/
void init_ulp() {
RTC_SLOW_MEM[ULP_DATA_OFFSET + 0] = 0;
//RTC-Memory:
//0: count
//1: adc
//2: signal
//3: signal_last
//4: adc_bat
ulp_insn_t program[] = {
//R3-> Immer Speicher-Addresse!!!
I_MOVI(R3, ULP_DATA_OFFSET),
//Wert laden und inkrementieren
I_LD(R0, R3, 0),
I_ADDI(R0, R0, 1),
I_ST(R0, R3, 0),
//test
//I_MOVI(R0, abs(String(c_threshold).toInt())),
//I_ST(R0, R3, 4),
//LED an
I_WR_REG_BIT(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + RTCIO_GPIO25_CHANNEL, 1), // LED an
I_DELAY(String(c_led_on).toInt()), //30000 ~ 3,x ms -> 3000 ~ 3xx uS
//ADC (Bat) lesen
I_ADC(R0, SARADC1, ADC1_CHANNEL_5),
I_ST(R0, R3, 4),
//ADC lesen
I_ADC(R0, SARADC1, ADC1_CHANNEL_6),
I_WR_REG_BIT(RTC_GPIO_OUT_REG, RTC_GPIO_OUT_DATA_S + RTCIO_GPIO25_CHANNEL, 0), // LED aus
I_ST(R0, R3, 1),
//Signal mit Hysterese - Achtung: benötigt passenden R0 (ADC von LED)
M_BGE(1, abs(String(c_threshold).toInt())+(String(c_hysterese).toInt()/2)),
M_BL(2, abs(String(c_threshold).toInt())-(String(c_hysterese).toInt()/2)),
M_BX(3),
M_LABEL(1), //größer
I_MOVI(R0, 1),
I_ST(R0, R3, 2),
M_BX(3),
M_LABEL(2), //kleiner
I_MOVI(R0, 0),
I_ST(R0, R3, 2),
M_BX(3),
M_LABEL(3), //ende
//Flankenerkennung
I_LD(R1, R3, 2),
I_LD(R2, R3, 3),
I_SUBR(R0, R1, R2),
M_BXZ(11), //ueberspringe wenn keine Aenderung
I_SUBI(R0, R1, (((String(c_threshold).toInt())>0) ? 1 : 0)), //pos/neg Flanke
M_BXZ(10), //wenn richtige flanke
M_BX(11), //sonst überspringe
M_LABEL(10), //richtige flanke
I_WAKE(), //wecken
M_LABEL(11), //ende
//Signal_last update
I_LD(R0, R3, 2),
I_ST(R0, R3, 3),
I_HALT()
};
size_t load_addr = 0;
size_t size = sizeof(program)/sizeof(ulp_insn_t);
adc1_config_channel_atten(ADC1_CHANNEL_5, ADC_ATTEN_DB_11); //GPIO33
adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11); //GPIO34
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_ulp_enable();
rtc_gpio_init(GPIO_NUM_25);
rtc_gpio_set_direction(GPIO_NUM_25, RTC_GPIO_MODE_OUTPUT_ONLY);
rtc_gpio_set_level(GPIO_NUM_25, 0);
ulp_set_wakeup_period(0, String(c_interval).toInt());
ulp_process_macros_and_load(load_addr, program, &size);
ulp_run(load_addr);
//energie sparen durch disconnect der Pins
rtc_gpio_isolate(GPIO_NUM_12);
rtc_gpio_isolate(GPIO_NUM_15);
// ? //esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_OFF);
}
void setup() {
Serial.begin(115200);
esp_sleep_wakeup_cause_t wakeup_reason;
wakeup_reason = esp_sleep_get_wakeup_cause();
read_parameter();
Serial.println("rst-cause " + (String)wakeup_reason);
if(wakeup_reason != ESP_DEEP_SLEEP_WAKEUP_ULP) // ESP_DEEP_SLEEP_WAKEUP_ULP (5) -> ULP-Wake
init_ulp();
int count = RTC_SLOW_MEM[ULP_DATA_OFFSET + 0] & 0xffff; //Maskieren, da nur 16-bit
RTC_SLOW_MEM[ULP_DATA_OFFSET + 0] = 0;
strncpy(c_clientid,((String)"ESP8266Client-" + String(random(0xffff), HEX)).c_str(),strlen(c_clientid));
WiFiManager wifimgr;
if(wakeup_reason != ESP_DEEP_SLEEP_WAKEUP_ULP) wifimgr.resetSettings();
wifimgr.setSaveConfigCallback(saveConfigCallback);
add_parameter(wifimgr);
wifimgr.autoConnect("WIFI-CONFIG");
write_parameter();
if(wakeup_reason != ESP_DEEP_SLEEP_WAKEUP_ULP) init_ulp();
if(wakeup_reason == ESP_DEEP_SLEEP_WAKEUP_ULP) {
int adc = RTC_SLOW_MEM[ULP_DATA_OFFSET + 1] & 0xffff; //Maskieren, da nur 16-bit
int signal = RTC_SLOW_MEM[ULP_DATA_OFFSET + 2] & 0xffff;
int signal_last = RTC_SLOW_MEM[ULP_DATA_OFFSET + 3] & 0xffff;
int adc_bat = RTC_SLOW_MEM[ULP_DATA_OFFSET + 4] & 0xffff;
//int signal_test = RTC_SLOW_MEM[ULP_DATA_OFFSET + 4] & 0xffff;
//Serial.println(signal_test);
//int led_delay = (int)((double)String(c_led_on).toInt()*(1000000.0/(double)RTC_FAST_CLK)); aktuell unberücksichtigt...
int timediff = ((count-1)*String(c_interval).toInt())/1000; //in ms
double kwh = 1000.0/String(c_rpkwh).toFloat()/((float)timediff/3600000.0); //3.6e+6ms = 1h
Serial.println("values " + (String)count + " " + (String)adc/* + " - " + (String)analogRead(34)*/ + " " + (String)signal + " " + (String)timediff + " " + (String)kwh);
//
// Send Values
//
char* user = c_brokeruser;
char* password = c_brokerpassword;
if((String)user=="none") user = NULL;
if((String)password=="none") password = NULL;
WiFiClient espClient;
espClient.setNoDelay(true);
PubSubClient client(espClient);
client.setServer(c_brokeraddress, String(c_brokerport).toInt());
if (client.connect(c_clientid, user, password)) {
client.publish((c_topic + (String)"kwh").c_str(), String(kwh).c_str());
client.publish((c_topic + (String)"timediff").c_str(), String((double)timediff).c_str());
client.publish((c_topic + (String)"powerlevel").c_str(), String(adc_bat).c_str());
client.disconnect();
}
//rtc_gpio_set_level(GPIO_NUM_25, 1);
//delay(500);
//rtc_gpio_set_level(GPIO_NUM_25, 0);
//Serial.println(rtc_gpio_is_valid_gpio(GPIO_NUM_25));
}
Serial.flush();
delay(20);
esp_deep_sleep_enable_ulp_wakeup();
esp_deep_sleep_start();
}
void loop() {
delay(1);
}