├── README.md ├── .gitattributes ├── WaterMeter_Inductive └── WaterMeter_Inductive.ino ├── WaterMeterV3 └── WaterMeterV3.ino ├── WaterMeterV2 └── WaterMeterV2.ino └── WaterMeter └── WaterMeter.ino /README.md: -------------------------------------------------------------------------------- 1 | # AI-on-the-Edge 2 | 3 | Video: https://youtu.be/d_u8c3bu-zg 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /WaterMeter_Inductive/WaterMeter_Inductive.ino: -------------------------------------------------------------------------------- 1 | /* Water Meter based on the PubSub example 2 | Inductive Sensor (https://s.click.aliexpress.com/e/_AkOSxV ) 3 | 4 | Copyright: Andreas Spiess, 21.2.2021 5 | 6 | MIT license 7 | */ 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #define BUILTIN_LED 2 18 | 19 | #define OUTTOPIC "water" 20 | #define IRPIN 33 21 | 22 | //#define DEBUG 23 | 24 | // Update these with values suitable for your network. 25 | 26 | const char* ssid = mySSID; 27 | const char* password = myPASSWORD; 28 | const char* mqtt_server = "192.168.0.203"; 29 | 30 | WiFiClient espClient; 31 | PubSubClient client(espClient); 32 | unsigned long lastMsg = 0; 33 | #define MSG_BUFFER_SIZE (200) 34 | char msg[MSG_BUFFER_SIZE]; 35 | byte dialStat, lastStat; 36 | 37 | void setup_wifi() { 38 | 39 | delay(10); 40 | // We start by connecting to a WiFi network 41 | Serial.println(); 42 | Serial.print("Connecting to "); 43 | Serial.println(ssid); 44 | 45 | WiFi.mode(WIFI_STA); 46 | WiFi.begin(ssid, password); 47 | 48 | while (WiFi.status() != WL_CONNECTED) { 49 | delay(500); 50 | Serial.print("."); 51 | } 52 | 53 | randomSeed(micros()); 54 | 55 | Serial.println(""); 56 | Serial.println("WiFi connected"); 57 | Serial.println("IP address: "); 58 | Serial.println(WiFi.localIP()); 59 | digitalWrite(BUILTIN_LED, LOW); 60 | } 61 | 62 | void callback(char* topic, byte* payload, unsigned int length) { 63 | Serial.print("Message arrived ["); 64 | Serial.print(topic); 65 | Serial.print("] "); 66 | for (int i = 0; i < length; i++) { 67 | Serial.print((char)payload[i]); 68 | } 69 | Serial.println(); 70 | 71 | // Switch on the LED if an 1 was received as first character 72 | if ((char)payload[0] == '1') { 73 | // but actually the LED is on; this is because 74 | // it is active low on the ESP-01) 75 | } else { 76 | } 77 | 78 | } 79 | 80 | void reconnect() { 81 | // Loop until we're reconnected 82 | while (!client.connected()) { 83 | Serial.print("Attempting MQTT connection..."); 84 | // Create a random client ID 85 | String clientId = "ESP8266Client-"; 86 | clientId += String(random(0xffff), HEX); 87 | // Attempt to connect 88 | if (client.connect(clientId.c_str())) { 89 | Serial.println("connected"); 90 | // Once connected, publish an announcement... 91 | client.publish(OUTTOPIC"/debug", "hello world"); 92 | // ... and resubscribe 93 | client.subscribe("inTTopic"); 94 | } else { 95 | Serial.print("failed, rc="); 96 | Serial.print(client.state()); 97 | Serial.println(" try again in 5 seconds"); 98 | // Wait 5 seconds before retrying 99 | delay(5000); 100 | } 101 | } 102 | } 103 | 104 | void setup() { 105 | Serial.begin(115200); 106 | pinMode(IRPIN,INPUT); 107 | setup_wifi(); 108 | Serial.println("WiFi ok"); 109 | ArduinoOTA.setHostname("WaterMeter"); 110 | ArduinoOTA 111 | .onStart([]() { 112 | String type; 113 | if (ArduinoOTA.getCommand() == U_FLASH) 114 | type = "sketch"; 115 | else // U_SPIFFS 116 | type = "filesystem"; 117 | 118 | // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() 119 | Serial.println("Start updating " + type); 120 | }) 121 | .onEnd([]() { 122 | Serial.println("\nEnd"); 123 | }) 124 | .onProgress([](unsigned int progress, unsigned int total) { 125 | Serial.printf("Progress: %u%%\r", (progress / (total / 100))); 126 | }) 127 | .onError([](ota_error_t error) { 128 | Serial.printf("Error[%u]: ", error); 129 | if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); 130 | else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); 131 | else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); 132 | else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); 133 | else if (error == OTA_END_ERROR) Serial.println("End Failed"); 134 | }); 135 | 136 | ArduinoOTA.begin(); 137 | 138 | Serial.println("Ready"); 139 | Serial.print("IP address: "); 140 | Serial.println(WiFi.localIP()); 141 | client.setServer(mqtt_server, 1883); 142 | client.setCallback(callback); 143 | } 144 | 145 | void loop() { 146 | ArduinoOTA.handle(); 147 | if (!client.connected()) { 148 | reconnect(); 149 | } 150 | client.loop(); 151 | dialStat = digitalRead(IRPIN); 152 | Serial.println(dialStat); 153 | if (dialStat != lastStat) { 154 | lastStat = dialStat; 155 | snprintf (msg, MSG_BUFFER_SIZE, "Tick"); 156 | client.publish(OUTTOPIC"/value", msg); 157 | Serial.print("Water "); 158 | Serial.println(msg); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /WaterMeterV3/WaterMeterV3.ino: -------------------------------------------------------------------------------- 1 | /* Water Meter based on the PubSub example 2 | 3 | IR sensor 4 | 5 | Copyright: Andreas Spiess, 19.2.2021 6 | 7 | MIT license 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define BUILTIN_LED 2 20 | 21 | #define OUTTOPIC "water" 22 | #define IRPIN 33 23 | 24 | #define DEBUG 25 | 26 | // Update these with values suitable for your network. 27 | 28 | const char* ssid = mySSID; 29 | const char* password = myPASSWORD; 30 | const char* mqtt_server = "192.168.0.203"; 31 | RTC_DATA_ATTR int wifiTrials, mqttTrials; 32 | RTC_DATA_ATTR int bootCount; 33 | 34 | int waterTicks, lastTicks; 35 | int irLevel, lastLevel, diff, lastDiff; 36 | int irMin = 1200; 37 | int irMiddle; 38 | int irMax = 2000; 39 | unsigned long entry, lastaliveMessage; 40 | int machineStat, lastMaschStat; 41 | int consumption; 42 | 43 | WiFiClient espClient; 44 | PubSubClient client(espClient); 45 | 46 | #define MSG_BUFFER_SIZE (200) 47 | char msg[MSG_BUFFER_SIZE]; 48 | char msg1[MSG_BUFFER_SIZE]; 49 | 50 | void setup_wifi() { 51 | delay(10); 52 | // We start by connecting to a WiFi network 53 | Serial.println(); 54 | Serial.print("Connecting to "); 55 | Serial.println(ssid); 56 | 57 | WiFi.mode(WIFI_STA); 58 | WiFi.begin(ssid, password); 59 | wifiTrials = 0; 60 | while (WiFi.status() != WL_CONNECTED && wifiTrials < 50) { 61 | delay(500); 62 | wifiTrials++; 63 | Serial.print("."); 64 | } 65 | 66 | if (wifiTrials >= 100) ESP.restart(); 67 | 68 | randomSeed(micros()); 69 | 70 | Serial.println(""); 71 | Serial.println("WiFi connected"); 72 | Serial.println("IP address: "); 73 | Serial.println(WiFi.localIP()); 74 | digitalWrite(BUILTIN_LED, LOW); 75 | } 76 | 77 | void callback(char* topic, byte* payload, unsigned int length) { 78 | Serial.print("Message arrived ["); 79 | Serial.print(topic); 80 | Serial.print("] "); 81 | for (int i = 0; i < length; i++) { 82 | Serial.print((char)payload[i]); 83 | } 84 | Serial.println(); 85 | 86 | // Switch on the LED if an 1 was received as first character 87 | if ((char)payload[0] == '1') { 88 | // but actually the LED is on; this is because 89 | // it is active low on the ESP-01) 90 | } else { 91 | } 92 | 93 | } 94 | 95 | void reconnect() { 96 | // Loop until we're reconnected 97 | mqttTrials = 0; 98 | while (!client.connected() && (mqttTrials < 5)) { 99 | Serial.print("Attempting MQTT connection..."); 100 | Serial.print("Attempting MQTT connection..."); 101 | // Create a random client ID 102 | String clientId = "ESP8266Client-"; 103 | clientId += String(random(0xffff), HEX); 104 | // Attempt to connect 105 | if (client.connect(clientId.c_str())) { 106 | Serial.println("connected"); 107 | // ... and resubscribe 108 | client.subscribe("inTTopic"); 109 | } else { 110 | Serial.print("failed, rc="); 111 | Serial.print(client.state()); 112 | Serial.println(" try again in 5 seconds"); 113 | Serial.print("RSSI: "); 114 | Serial.println(WiFi.RSSI()); 115 | // Wait 5 seconds before retrying 116 | delay(5000); 117 | } 118 | mqttTrials++; 119 | Serial.print("mqttTrials: "); 120 | Serial.println(mqttTrials); 121 | publishDebugMessage('Y'); 122 | if (mqttTrials >= 5) ESP.restart(); 123 | } 124 | } 125 | 126 | void incrementTicks() { 127 | consumption = 1; 128 | StaticJsonDocument<200> doc; 129 | lastaliveMessage = millis(); 130 | doc["messageType"] = "C"; 131 | doc["consumption"] = consumption; 132 | serializeJsonPretty(doc, Serial); 133 | for (int i = 0; i < MSG_BUFFER_SIZE; i++)msg[i] = 0; 134 | serializeJsonPretty(doc, msg); 135 | client.publish(OUTTOPIC"/value", msg); 136 | lastaliveMessage = millis(); 137 | } 138 | 139 | void publishDebugMessage(char messageType) { 140 | StaticJsonDocument<200> doc; 141 | doc["messageType"] = String(messageType); 142 | doc["min"] = irMin; 143 | doc["middle"] = irMiddle; 144 | doc["max"] = irMax; 145 | doc["level"] = irLevel; 146 | doc["diff"] = diff; 147 | doc["RSSI"] = WiFi.RSSI(); 148 | doc["boot"] = bootCount; 149 | doc["mqttTrials"] = mqttTrials; 150 | doc["wifiTrials"] = wifiTrials; 151 | doc["Stat"] = machineStat; 152 | serializeJsonPretty(doc, Serial); 153 | for (int i = 0; i < MSG_BUFFER_SIZE; i++)msg[i] = 0; 154 | serializeJsonPretty(doc, msg); 155 | client.publish(OUTTOPIC"/debug", msg); 156 | } 157 | 158 | int readIR() { 159 | irLevel = analogRead(IRPIN); 160 | if (irLevel < 900) irLevel = 900; 161 | if (irLevel > 5000) irLevel = 5000; 162 | return irLevel; 163 | } 164 | 165 | void setup() { 166 | pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output 167 | Serial.begin(115200); 168 | bootCount++; 169 | Serial.println("Boot number: " + String(bootCount)); 170 | 171 | digitalWrite(BUILTIN_LED, HIGH); 172 | setup_wifi(); 173 | ArduinoOTA.setHostname("WaterMeter"); 174 | ArduinoOTA 175 | .onStart([]() { 176 | String type; 177 | if (ArduinoOTA.getCommand() == U_FLASH) 178 | type = "sketch"; 179 | else // U_SPIFFS 180 | type = "filesystem"; 181 | 182 | // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() 183 | Serial.println("Start updating " + type); 184 | }) 185 | .onEnd([]() { 186 | Serial.println("\nEnd"); 187 | }) 188 | .onProgress([](unsigned int progress, unsigned int total) { 189 | Serial.printf("Progress: %u%%\r", (progress / (total / 100))); 190 | }) 191 | .onError([](ota_error_t error) { 192 | Serial.printf("Error[%u]: ", error); 193 | if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); 194 | else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); 195 | else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); 196 | else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); 197 | else if (error == OTA_END_ERROR) Serial.println("End Failed"); 198 | }); 199 | 200 | ArduinoOTA.begin(); 201 | 202 | Serial.println("Ready"); 203 | Serial.print("IP address: "); 204 | Serial.println(WiFi.localIP()); 205 | client.setServer(mqtt_server, 1883); 206 | client.setCallback(callback); 207 | entry = millis(); 208 | lastaliveMessage = millis(); 209 | irLevel = readIR(); 210 | delay(1000); 211 | } 212 | 213 | void loop() { 214 | ArduinoOTA.handle(); 215 | if (!client.connected()) { 216 | reconnect(); 217 | } 218 | client.loop(); 219 | 220 | irLevel = readIR(); 221 | if (irLevel > irMax) irMax = irLevel; // maximum signal 222 | if (irLevel < irMin) irMin = irLevel; // minimum signal 223 | irMiddle = (irMax + irMin) / 2; // calculate middle of the signal 224 | diff = irLevel - lastLevel; // first derivative 225 | if (millis() - entry > 500) { 226 | entry = millis(); 227 | lastLevel = irLevel; 228 | publishDebugMessage('D'); 229 | 230 | switch (machineStat) { 231 | case 0: 232 | if ((diff < 0) && (irLevel > irMiddle)) { 233 | machineStat = 1; 234 | } 235 | digitalWrite(BUILTIN_LED, LOW); 236 | break; 237 | case 1: 238 | if ((diff < 0) && (irLevel < irMiddle)) { 239 | irMax = irMax - 100; // for longterm adjustment 240 | machineStat = 2; 241 | } 242 | digitalWrite(BUILTIN_LED, HIGH); 243 | break; 244 | case 2: 245 | if ((diff > 0) && (irLevel < irMiddle)) machineStat = 3; 246 | digitalWrite(BUILTIN_LED, LOW); 247 | break; 248 | case 3: 249 | if (diff > 0 && (irLevel > irMiddle)) { 250 | irMin = irMin + 100; // long-term adjustment 251 | incrementTicks(); 252 | machineStat = 0; 253 | } 254 | digitalWrite(BUILTIN_LED, HIGH); 255 | break; 256 | default: 257 | break; 258 | } 259 | } 260 | #ifdef DEBUG 261 | if (machineStat != lastMaschStat) { 262 | publishDebugMessage('D'); 263 | lastaliveMessage = millis(); 264 | lastMaschStat = machineStat; 265 | } 266 | #endif 267 | 268 | if (millis() > (lastaliveMessage + 1 * 60 * 1000)) { 269 | publishDebugMessage('A'); 270 | lastaliveMessage = millis(); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /WaterMeterV2/WaterMeterV2.ino: -------------------------------------------------------------------------------- 1 | /* Water Meter based on the PubSub example 2 | 3 | IR sensor 4 | 5 | Copyright: Andreas Spiess, 19.2.2021 6 | 7 | MIT license 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define BUILTIN_LED 2 20 | 21 | #define OUTTOPIC "water" 22 | #define IRPIN 33 23 | 24 | #define DEBUG 25 | 26 | // Update these with values suitable for your network. 27 | 28 | const char* ssid = mySSID; 29 | const char* password = myPASSWORD; 30 | const char* mqtt_server = "192.168.0.203"; 31 | RTC_DATA_ATTR int wifiTrials, mqttTrials; 32 | RTC_DATA_ATTR int bootCount; 33 | 34 | int waterTicks, lastTicks; 35 | int irLevel, lastLevel, diff, lastDiff; 36 | int irMin = 1200; 37 | int irMiddle; 38 | int irMax = 2000; 39 | unsigned long entry, lastaliveMessage; 40 | int machineStat, lastMaschStat; 41 | int consumption; 42 | 43 | WiFiClient espClient; 44 | PubSubClient client(espClient); 45 | 46 | #define MSG_BUFFER_SIZE (200) 47 | char msg[MSG_BUFFER_SIZE]; 48 | char msg1[MSG_BUFFER_SIZE]; 49 | 50 | void setup_wifi() { 51 | 52 | delay(10); 53 | // We start by connecting to a WiFi network 54 | Serial.println(); 55 | Serial.print("Connecting to "); 56 | Serial.println(ssid); 57 | 58 | WiFi.mode(WIFI_STA); 59 | WiFi.begin(ssid, password); 60 | wifiTrials = 0; 61 | while (WiFi.status() != WL_CONNECTED && wifiTrials < 50) { 62 | delay(500); 63 | wifiTrials++; 64 | Serial.print("."); 65 | } 66 | 67 | if (wifiTrials >= 100) ESP.restart(); 68 | 69 | randomSeed(micros()); 70 | 71 | Serial.println(""); 72 | Serial.println("WiFi connected"); 73 | Serial.println("IP address: "); 74 | Serial.println(WiFi.localIP()); 75 | digitalWrite(BUILTIN_LED, LOW); 76 | } 77 | 78 | void callback(char* topic, byte* payload, unsigned int length) { 79 | Serial.print("Message arrived ["); 80 | Serial.print(topic); 81 | Serial.print("] "); 82 | for (int i = 0; i < length; i++) { 83 | Serial.print((char)payload[i]); 84 | } 85 | Serial.println(); 86 | 87 | // Switch on the LED if an 1 was received as first character 88 | if ((char)payload[0] == '1') { 89 | // but actually the LED is on; this is because 90 | // it is active low on the ESP-01) 91 | } else { 92 | } 93 | 94 | } 95 | 96 | void reconnect() { 97 | // Loop until we're reconnected 98 | mqttTrials = 0; 99 | while (!client.connected() && (mqttTrials < 5)) { 100 | Serial.print("Attempting MQTT connection..."); 101 | // Create a random client ID 102 | String clientId = "ESP8266Client-"; 103 | clientId += String(random(0xffff), HEX); 104 | // Attempt to connect 105 | if (client.connect(clientId.c_str())) { 106 | Serial.println("connected"); 107 | // ... and resubscribe 108 | client.subscribe("inTTopic"); 109 | } else { 110 | Serial.print("failed, rc="); 111 | Serial.print(client.state()); 112 | Serial.println(" try again in 5 seconds"); 113 | Serial.print("RSSI: "); 114 | Serial.println(WiFi.RSSI()); 115 | // Wait 5 seconds before retrying 116 | delay(5000); 117 | } 118 | mqttTrials++; 119 | Serial.print("mqttTrials: "); 120 | Serial.println(mqttTrials); 121 | } 122 | publishDebugMessage('X'); 123 | if (mqttTrials >= 5) ESP.restart(); 124 | } 125 | 126 | void incrementTicks() { 127 | consumption = 1; 128 | StaticJsonDocument<200> doc; 129 | lastaliveMessage = millis(); 130 | doc["messageType"] = "C"; 131 | doc["consumption"] = consumption; 132 | serializeJsonPretty(doc, Serial); 133 | for (int i = 0; i < MSG_BUFFER_SIZE; i++)msg[i] = 0; 134 | serializeJsonPretty(doc, msg); 135 | client.publish(OUTTOPIC"/value", msg); 136 | lastaliveMessage = millis(); 137 | } 138 | 139 | void publishDebugMessage(char messageType) { 140 | StaticJsonDocument<200> doc; 141 | doc["messageType"] = String(messageType); 142 | doc["min"] = irMin; 143 | doc["middle"] = irMiddle; 144 | doc["max"] = irMax; 145 | doc["level"] = irLevel; 146 | doc["diff"] = diff; 147 | doc["RSSI"] = WiFi.RSSI(); 148 | doc["boot"] = bootCount; 149 | doc["mqttTrials"] = mqttTrials; 150 | doc["wifiTrials"] = wifiTrials; 151 | doc["Stat"] = machineStat; 152 | serializeJsonPretty(doc, Serial); 153 | for (int i = 0; i < MSG_BUFFER_SIZE; i++)msg[i] = 0; 154 | serializeJsonPretty(doc, msg); 155 | client.publish(OUTTOPIC"/debug", msg); 156 | } 157 | 158 | int readIR() { 159 | irLevel = analogRead(IRPIN); 160 | if (irLevel < 900) irLevel = 900; 161 | if (irLevel > 5000) irLevel = 5000; 162 | return irLevel; 163 | } 164 | 165 | void setup() { 166 | pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output 167 | Serial.begin(115200); 168 | delay(1000); 169 | bootCount++; 170 | Serial.println("Boot number: " + String(bootCount)); 171 | 172 | digitalWrite(BUILTIN_LED, HIGH); 173 | setup_wifi(); 174 | ArduinoOTA.setHostname("WaterMeter"); 175 | ArduinoOTA 176 | .onStart([]() { 177 | String type; 178 | if (ArduinoOTA.getCommand() == U_FLASH) 179 | type = "sketch"; 180 | else // U_SPIFFS 181 | type = "filesystem"; 182 | 183 | // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() 184 | Serial.println("Start updating " + type); 185 | }) 186 | .onEnd([]() { 187 | Serial.println("\nEnd"); 188 | }) 189 | .onProgress([](unsigned int progress, unsigned int total) { 190 | Serial.printf("Progress: %u%%\r", (progress / (total / 100))); 191 | }) 192 | .onError([](ota_error_t error) { 193 | Serial.printf("Error[%u]: ", error); 194 | if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); 195 | else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); 196 | else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); 197 | else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); 198 | else if (error == OTA_END_ERROR) Serial.println("End Failed"); 199 | }); 200 | 201 | ArduinoOTA.begin(); 202 | 203 | Serial.println("Ready"); 204 | Serial.print("IP address: "); 205 | Serial.println(WiFi.localIP()); 206 | client.setServer(mqtt_server, 1883); 207 | client.setCallback(callback); 208 | entry = millis(); 209 | lastaliveMessage = millis(); 210 | irLevel = readIR(); 211 | } 212 | 213 | void loop() { 214 | ArduinoOTA.handle(); 215 | if (!client.connected()) { 216 | reconnect(); 217 | } 218 | client.loop(); 219 | 220 | irLevel = readIR(); 221 | if (irLevel > irMax) irMax = irLevel; // maximum signal 222 | if (irLevel < irMin) irMin = irLevel; // minimum signal 223 | irMiddle = (irMax + irMin) / 2; // calculate middle of the signal 224 | diff = irLevel - lastLevel; // first derivative 225 | if (millis() - entry > 500) { 226 | entry = millis(); 227 | lastLevel = irLevel; 228 | 229 | #ifdef DEBUG 230 | snprintf (msg1, MSG_BUFFER_SIZE, "Alive Message: Min %ld, Middle %ld, Max %ld, Level %ld, diff %ld, RSSI %ld, Boot %ld, Stat %ld, ", irMin, irMiddle, irMax, irLevel, diff, WiFi.RSSI(), bootCount, machineStat); 231 | Serial.println(msg1); 232 | #endif 233 | 234 | switch (machineStat) { 235 | case 0: 236 | if ((diff < 0) && (irLevel > irMiddle)) { 237 | machineStat = 1; 238 | } 239 | digitalWrite(BUILTIN_LED, LOW); 240 | break; 241 | case 1: 242 | if ((diff < 0) && (irLevel < irMiddle)) { 243 | irMax = irMax - 100; // for longterm adjustment 244 | machineStat = 2; 245 | } 246 | digitalWrite(BUILTIN_LED, HIGH); 247 | break; 248 | case 2: 249 | if ((diff > 0) && (irLevel < irMiddle)) machineStat = 3; 250 | digitalWrite(BUILTIN_LED, LOW); 251 | break; 252 | case 3: 253 | if (diff > 0 && (irLevel > irMiddle)) { 254 | irMin = irMin + 100; // long-term adjustment 255 | incrementTicks(); 256 | machineStat = 0; 257 | } 258 | digitalWrite(BUILTIN_LED, HIGH); 259 | break; 260 | default: 261 | break; 262 | } 263 | } 264 | #ifdef DEBUG 265 | if (machineStat != lastMaschStat) { 266 | publishDebugMessage('D'); 267 | lastaliveMessage = millis(); 268 | lastMaschStat = machineStat; 269 | } 270 | #endif 271 | 272 | if (millis() > (lastaliveMessage + 1 * 60 * 1000)) { 273 | publishDebugMessage('A'); 274 | lastaliveMessage = millis(); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /WaterMeter/WaterMeter.ino: -------------------------------------------------------------------------------- 1 | /* Water Meter based on the PubSub example 2 | 3 | IR sensor 4 | 5 | Copyright: Andreas Spiess, 19.2.2021 6 | 7 | MIT license 8 | */ 9 | 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define BUILTIN_LED 2 20 | 21 | #define OUTTOPIC "water" 22 | #define IRPIN 33 23 | 24 | //#define DEBUG 25 | 26 | // Update these with values suitable for your network. 27 | 28 | const char* ssid = mySSID; 29 | const char* password = myPASSWORD; 30 | const char* mqtt_server = "192.168.0.203"; 31 | 32 | int waterTicks, lastTicks; 33 | int irLevel, lastLevel, diff, lastDiff; 34 | int irMin = 9999; 35 | int irMiddle; 36 | int irMax = 0; 37 | unsigned long entry = millis(); 38 | int machineStat = 0; 39 | unsigned long currentMeasurement, lastMeasurement; 40 | float consumption; 41 | 42 | int addr = 0; 43 | #define EEPROM_SIZE 10 44 | 45 | struct eepromLayout 46 | { 47 | int magicNumber; 48 | unsigned long value; 49 | } eepromContent; 50 | 51 | WiFiClient espClient; 52 | PubSubClient client(espClient); 53 | unsigned long lastMsg = 0; 54 | #define MSG_BUFFER_SIZE (200) 55 | char msg[MSG_BUFFER_SIZE]; 56 | int value = 0; 57 | 58 | void setup_wifi() { 59 | 60 | delay(10); 61 | // We start by connecting to a WiFi network 62 | Serial.println(); 63 | Serial.print("Connecting to "); 64 | Serial.println(ssid); 65 | 66 | WiFi.mode(WIFI_STA); 67 | WiFi.begin(ssid, password); 68 | 69 | while (WiFi.status() != WL_CONNECTED) { 70 | delay(500); 71 | Serial.print("."); 72 | } 73 | 74 | randomSeed(micros()); 75 | 76 | Serial.println(""); 77 | Serial.println("WiFi connected"); 78 | Serial.println("IP address: "); 79 | Serial.println(WiFi.localIP()); 80 | digitalWrite(BUILTIN_LED, LOW); 81 | } 82 | 83 | void callback(char* topic, byte* payload, unsigned int length) { 84 | Serial.print("Message arrived ["); 85 | Serial.print(topic); 86 | Serial.print("] "); 87 | for (int i = 0; i < length; i++) { 88 | Serial.print((char)payload[i]); 89 | } 90 | Serial.println(); 91 | 92 | // Switch on the LED if an 1 was received as first character 93 | if ((char)payload[0] == '1') { 94 | // but actually the LED is on; this is because 95 | // it is active low on the ESP-01) 96 | } else { 97 | } 98 | 99 | } 100 | 101 | void reconnect() { 102 | // Loop until we're reconnected 103 | while (!client.connected()) { 104 | Serial.print("Attempting MQTT connection..."); 105 | // Create a random client ID 106 | String clientId = "ESP8266Client-"; 107 | clientId += String(random(0xffff), HEX); 108 | // Attempt to connect 109 | if (client.connect(clientId.c_str())) { 110 | Serial.println("connected"); 111 | // Once connected, publish an announcement... 112 | client.publish(OUTTOPIC"/debug", "hello world"); 113 | // ... and resubscribe 114 | client.subscribe("inTTopic"); 115 | } else { 116 | Serial.print("failed, rc="); 117 | Serial.print(client.state()); 118 | Serial.println(" try again in 5 seconds"); 119 | // Wait 5 seconds before retrying 120 | delay(5000); 121 | } 122 | } 123 | } 124 | 125 | void incrementTicks() { 126 | currentMeasurement = millis(); 127 | Serial.println("Increment"); 128 | digitalWrite(BUILTIN_LED, HIGH); // Turn the LED on (Note that LOW is the voltage level 129 | delay(500); 130 | digitalWrite(BUILTIN_LED, LOW); // Turn the LED off by making the voltage HIGH 131 | waterTicks++; 132 | if (waterTicks % 2 == 0) { 133 | if (currentMeasurement - lastMeasurement > 0) consumption = 2000.0 / (currentMeasurement - lastMeasurement); // two ticks divided by the time between the two ticks; protect against division by zero 134 | else consumption = 0.0; 135 | Serial.println(consumption); 136 | 137 | snprintf (msg, MSG_BUFFER_SIZE, "{\"consumption\":%lf, \"total\":%ld}", consumption, waterTicks); 138 | Serial.print("Water "); 139 | Serial.println(msg); 140 | client.publish(OUTTOPIC"/value", msg); 141 | lastMeasurement = currentMeasurement; 142 | } 143 | 144 | if (((waterTicks % 1000) == 0)) { 145 | snprintf (msg, MSG_BUFFER_SIZE, "Stored %ld", waterTicks); 146 | Serial.print("Store in eeprom "); 147 | Serial.println(msg); 148 | client.publish(OUTTOPIC"/debug", msg); 149 | 150 | eepromContent.value = waterTicks; 151 | EEPROM.put(0, eepromContent); 152 | EEPROM.commit(); 153 | } 154 | } 155 | 156 | void setup() { 157 | pinMode(BUILTIN_LED, OUTPUT); // Initialize the BUILTIN_LED pin as an output 158 | Serial.begin(115200); 159 | delay(1000); 160 | 161 | digitalWrite(BUILTIN_LED, HIGH); 162 | setup_wifi(); 163 | ArduinoOTA.setHostname("WaterMeter"); 164 | ArduinoOTA 165 | .onStart([]() { 166 | String type; 167 | if (ArduinoOTA.getCommand() == U_FLASH) 168 | type = "sketch"; 169 | else // U_SPIFFS 170 | type = "filesystem"; 171 | 172 | // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end() 173 | Serial.println("Start updating " + type); 174 | }) 175 | .onEnd([]() { 176 | Serial.println("\nEnd"); 177 | }) 178 | .onProgress([](unsigned int progress, unsigned int total) { 179 | Serial.printf("Progress: %u%%\r", (progress / (total / 100))); 180 | }) 181 | .onError([](ota_error_t error) { 182 | Serial.printf("Error[%u]: ", error); 183 | if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); 184 | else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); 185 | else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); 186 | else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); 187 | else if (error == OTA_END_ERROR) Serial.println("End Failed"); 188 | }); 189 | 190 | ArduinoOTA.begin(); 191 | 192 | Serial.println("Ready"); 193 | Serial.print("IP address: "); 194 | Serial.println(WiFi.localIP()); 195 | client.setServer(mqtt_server, 1883); 196 | client.setCallback(callback); 197 | 198 | if (!EEPROM.begin(EEPROM_SIZE)) 199 | { 200 | Serial.println("failed to initialise EEPROM"); 201 | delay(1000); 202 | ESP.restart(); 203 | } 204 | EEPROM.get(0, eepromContent); 205 | if (eepromContent.magicNumber != 0x55) { 206 | Serial.println("EEprom Corrupted"); 207 | eepromContent.magicNumber = 0x55; 208 | eepromContent.value = 1591001; 209 | EEPROM.put(0, eepromContent); 210 | EEPROM.commit(); 211 | } 212 | else waterTicks = eepromContent.value; 213 | Serial.print("Stored consumption "); 214 | Serial.println(waterTicks); 215 | currentMeasurement = millis(); 216 | lastMeasurement = currentMeasurement; 217 | } 218 | 219 | void loop() { 220 | ArduinoOTA.handle(); 221 | if (!client.connected()) { 222 | reconnect(); 223 | } 224 | client.loop(); 225 | 226 | if (millis() - entry > 500) { 227 | entry = millis(); 228 | irLevel = analogRead(IRPIN); 229 | 230 | if (irLevel > irMax) irMax = irLevel; // maximum signal 231 | if (irLevel < irMin) irMin = irLevel; // minimum signal 232 | irMiddle = (irMax + irMin) / 2; // calculate middle of the signal 233 | diff = irLevel - lastLevel; // first derivative (averaged) 234 | 235 | #ifdef DEBUG 236 | Serial.print(irMin); 237 | Serial.print(" "); 238 | Serial.print(irMiddle); 239 | Serial.print(" "); 240 | Serial.print(irMax); 241 | Serial.print(" "); 242 | Serial.print(diff); 243 | Serial.print(" "); 244 | Serial.print(irLevel); 245 | Serial.print(" "); 246 | Serial.println(machineStat); 247 | #endif 248 | 249 | switch (machineStat) { 250 | case 0: 251 | if ((diff < 2) && (irLevel > (irMiddle + 100))) { 252 | incrementTicks(); 253 | machineStat = 1; 254 | } 255 | digitalWrite(BUILTIN_LED, LOW); 256 | break; 257 | case 1: 258 | if ((diff > 10) && irLevel < irMiddle - 100) machineStat = 2; 259 | digitalWrite(BUILTIN_LED, HIGH); 260 | break; 261 | case 2: 262 | if ((diff < 2) && (irLevel < (irMiddle - 100))) machineStat = 3; 263 | digitalWrite(BUILTIN_LED, LOW); 264 | break; 265 | case 3: 266 | if (diff > 10 && irLevel > irMiddle + 100) machineStat = 0; 267 | digitalWrite(BUILTIN_LED, HIGH); 268 | break; 269 | } 270 | 271 | #ifdef DEBUG 272 | snprintf (msg, MSG_BUFFER_SIZE, "Min %ld, Middle %ld, Max %ld, Level %ld, diff %ld, Stat %ld, Ticks %ld", irMin, irMiddle, irMax, irLevel, diff, machineStat, waterTicks); 273 | client.publish(OUTTOPIC"/debug", msg); 274 | #endif 275 | lastLevel = irLevel; 276 | } 277 | } 278 | --------------------------------------------------------------------------------