Machine ID:
%s
Copy and save the machine ID because you will need it to control the device.
", machineId); 378 | WiFiManagerParameter custom_text_machine_id(htmlMachineId); 379 | 380 | //WiFiManager 381 | //Local intialization. Once its business is done, there is no need to keep it around 382 | WiFiManager wifiManager; 383 | 384 | //set config save notify callback 385 | wifiManager.setSaveConfigCallback(saveConfigCallback); 386 | 387 | //add all your parameters here 388 | wifiManager.addParameter(&custom_mqtt_server); 389 | wifiManager.addParameter(&custom_mqtt_port); 390 | wifiManager.addParameter(&custom_workgroup); 391 | wifiManager.addParameter(&custom_mqtt_user); 392 | wifiManager.addParameter(&custom_mqtt_pass); 393 | wifiManager.addParameter(&custom_temperature_scale); 394 | #ifdef HOME_ASSISTANT_DISCOVERY 395 | wifiManager.addParameter(&custom_mqtt_ha_name); 396 | #endif 397 | #ifdef OTA_UPGRADES 398 | wifiManager.addParameter(&custom_ota_server); 399 | #endif 400 | wifiManager.addParameter(&custom_text_machine_id); 401 | 402 | //reset settings - for testing 403 | //wifiManager.resetSettings(); 404 | 405 | //set minimu quality of signal so it ignores AP's under that quality 406 | //defaults to 8% 407 | //wifiManager.setMinimumSignalQuality(); 408 | 409 | //sets timeout until configuration portal gets turned off 410 | //useful to make it all retry or go to sleep 411 | //in seconds 412 | wifiManager.setTimeout(300); 413 | 414 | digitalWrite(pinAlarm, HIGH); 415 | drawDisplay("Gas Detector", "Connecting...", WiFi.SSID().c_str()); 416 | 417 | //fetches ssid and pass and tries to connect 418 | //if it does not connect it starts an access point 419 | //and goes into a blocking loop awaiting configuration 420 | wifiManager.setAPCallback(apWiFiCallback); 421 | // Append the last 5 character of the machine id to the access point name 422 | String apId(machineId); 423 | apId = apId.substring(apId.length() - 5); 424 | String accessPointName = "ANAVI Gas Detector " + apId; 425 | if (!wifiManager.autoConnect(accessPointName.c_str(), "")) 426 | { 427 | digitalWrite(pinAlarm, LOW); 428 | Serial.println("failed to connect and hit timeout"); 429 | delay(3000); 430 | //reset and try again, or maybe put it to deep sleep 431 | ESP.reset(); 432 | delay(5000); 433 | } 434 | 435 | //if you get here you have connected to the WiFi 436 | Serial.println("connected...yeey :)"); 437 | digitalWrite(pinAlarm, LOW); 438 | 439 | //read updated parameters 440 | strcpy(mqtt_server, custom_mqtt_server.getValue()); 441 | strcpy(mqtt_port, custom_mqtt_port.getValue()); 442 | strcpy(workgroup, custom_workgroup.getValue()); 443 | strcpy(username, custom_mqtt_user.getValue()); 444 | strcpy(password, custom_mqtt_pass.getValue()); 445 | strcpy(temp_scale, custom_temperature_scale.getValue()); 446 | #ifdef HOME_ASSISTANT_DISCOVERY 447 | strcpy(ha_name, custom_mqtt_ha_name.getValue()); 448 | #endif 449 | #ifdef OTA_UPGRADES 450 | strcpy(ota_server, custom_ota_server.getValue()); 451 | #endif 452 | 453 | //save the custom parameters to FS 454 | if (shouldSaveConfig) 455 | { 456 | saveConfig(); 457 | } 458 | 459 | Serial.println("local ip"); 460 | Serial.println(WiFi.localIP()); 461 | drawDisplay("Connected!", "Local IP:", WiFi.localIP().toString().c_str()); 462 | delay(2000); 463 | 464 | // Sensors 465 | htu.begin(); 466 | bmp.begin(); 467 | 468 | // MQTT 469 | Serial.print("MQTT Server: "); 470 | Serial.println(mqtt_server); 471 | Serial.print("MQTT Port: "); 472 | Serial.println(mqtt_port); 473 | // Print MQTT Username 474 | Serial.print("MQTT Username: "); 475 | Serial.println(username); 476 | // Hide password from the log and show * instead 477 | char hiddenpass[20] = ""; 478 | for (size_t charP=0; charP < strlen(password); charP++) 479 | { 480 | hiddenpass[charP] = '*'; 481 | } 482 | hiddenpass[strlen(password)] = '\0'; 483 | Serial.print("MQTT Password: "); 484 | Serial.println(hiddenpass); 485 | Serial.print("Saved temperature scale: "); 486 | Serial.println(temp_scale); 487 | configTempCelsius = ( (0 == strlen(temp_scale)) || String(temp_scale).equalsIgnoreCase("celsius")); 488 | Serial.print("Temperature scale: "); 489 | if (true == configTempCelsius) 490 | { 491 | Serial.println("Celsius"); 492 | } 493 | else 494 | { 495 | Serial.println("Fahrenheit"); 496 | } 497 | #ifdef HOME_ASSISTANT_DISCOVERY 498 | Serial.print("Home Assistant sensor name: "); 499 | Serial.println(ha_name); 500 | #endif 501 | #ifdef OTA_UPGRADES 502 | if (ota_server[0] != '\0') 503 | { 504 | Serial.print("OTA server: "); 505 | Serial.println(ota_server); 506 | } 507 | else 508 | { 509 | # ifndef OTA_SERVER 510 | Serial.println("No OTA server"); 511 | # endif 512 | } 513 | 514 | # ifdef OTA_SERVER 515 | Serial.print("Hardcoded OTA server: "); 516 | Serial.println(OTA_SERVER); 517 | # endif 518 | 519 | #endif 520 | 521 | const int mqttPort = atoi(mqtt_port); 522 | mqttClient.setServer(mqtt_server, mqttPort); 523 | mqttClient.setCallback(mqttCallback); 524 | 525 | mqttReconnect(); 526 | 527 | Serial.println(""); 528 | Serial.println("-----"); 529 | Serial.print("Machine ID: "); 530 | Serial.println(machineId); 531 | Serial.println("-----"); 532 | Serial.println(""); 533 | 534 | setupADPS9960(); 535 | } 536 | 537 | void setupADPS9960() 538 | { 539 | if(apds.begin()) 540 | { 541 | //gesture mode will be entered once proximity mode senses something close 542 | apds.enableProximity(true); 543 | apds.enableGesture(true); 544 | } 545 | } 546 | 547 | void waitForFactoryReset() 548 | { 549 | Serial.println("Press button within 2 seconds for factory reset..."); 550 | for (int iter = 0; iter < 20; iter++) 551 | { 552 | digitalWrite(pinAlarm, HIGH); 553 | delay(50); 554 | if (false == digitalRead(pinButton)) 555 | { 556 | factoryReset(); 557 | return; 558 | } 559 | digitalWrite(pinAlarm, LOW); 560 | delay(50); 561 | if (false == digitalRead(pinButton)) 562 | { 563 | factoryReset(); 564 | return; 565 | } 566 | } 567 | } 568 | 569 | void factoryReset() 570 | { 571 | if (false == digitalRead(pinButton)) 572 | { 573 | Serial.println("Hold the button to reset to factory defaults..."); 574 | bool cancel = false; 575 | for (int iter=0; iter<30; iter++) 576 | { 577 | digitalWrite(pinAlarm, HIGH); 578 | delay(100); 579 | if (true == digitalRead(pinButton)) 580 | { 581 | cancel = true; 582 | break; 583 | } 584 | digitalWrite(pinAlarm, LOW); 585 | delay(100); 586 | if (true == digitalRead(pinButton)) 587 | { 588 | cancel = true; 589 | break; 590 | } 591 | } 592 | if (false == digitalRead(pinButton) && !cancel) 593 | { 594 | digitalWrite(pinAlarm, HIGH); 595 | Serial.println("Disconnecting..."); 596 | WiFi.disconnect(); 597 | 598 | // NOTE: the boot mode:(1,7) problem is known and only happens at the first restart after serial flashing. 599 | 600 | Serial.println("Restarting..."); 601 | // Clean the file system with configurations 602 | SPIFFS.format(); 603 | // Restart the board 604 | ESP.restart(); 605 | } 606 | else 607 | { 608 | // Cancel reset to factory defaults 609 | Serial.println("Reset to factory defaults cancelled."); 610 | digitalWrite(pinAlarm, LOW); 611 | } 612 | } 613 | } 614 | 615 | #ifdef OTA_UPGRADES 616 | void do_ota_upgrade(char *text) 617 | { 618 | DynamicJsonDocument json(1024); 619 | auto error = deserializeJson(json, text); 620 | if (error) 621 | { 622 | Serial.println("No success decoding JSON.\n"); 623 | } 624 | else if (!json.containsKey("server")) 625 | { 626 | Serial.println("JSON is missing server\n"); 627 | } 628 | else if (!json.containsKey("file")) 629 | { 630 | Serial.println("JSON is missing file\n"); 631 | } 632 | else 633 | { 634 | int port = 0; 635 | if (json.containsKey("port")) 636 | { 637 | port = json["port"]; 638 | Serial.print("Port configured to "); 639 | Serial.println(port); 640 | } 641 | 642 | if (0 >= port || 65535 < port) 643 | { 644 | port = 80; 645 | } 646 | 647 | String server = json["server"]; 648 | String file = json["file"]; 649 | 650 | bool ok = false; 651 | if (ota_server[0] != '\0' && !strcmp(server.c_str(), ota_server)) 652 | ok = true; 653 | 654 | # ifdef OTA_SERVER 655 | if (!strcmp(server.c_str(), OTA_SERVER)) 656 | ok = true; 657 | # endif 658 | 659 | if (!ok) 660 | { 661 | Serial.println("Wrong OTA server. Refusing to upgrade."); 662 | return; 663 | } 664 | 665 | Serial.print("Attempting to upgrade from "); 666 | Serial.print(server); 667 | Serial.print(":"); 668 | Serial.print(port); 669 | Serial.println(file); 670 | ESPhttpUpdate.setLedPin(pinAlarm, HIGH); 671 | WiFiClient update_client; 672 | t_httpUpdate_return ret = ESPhttpUpdate.update(update_client, 673 | server, port, file); 674 | switch (ret) 675 | { 676 | case HTTP_UPDATE_FAILED: 677 | Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); 678 | break; 679 | 680 | case HTTP_UPDATE_NO_UPDATES: 681 | Serial.println("HTTP_UPDATE_NO_UPDATES"); 682 | break; 683 | 684 | case HTTP_UPDATE_OK: 685 | Serial.println("HTTP_UPDATE_OK"); 686 | break; 687 | } 688 | } 689 | } 690 | #endif 691 | 692 | void processMessageScale(const char* text) 693 | { 694 | StaticJsonDocument<200> data; 695 | deserializeJson(data, text); 696 | // Set temperature to Celsius or Fahrenheit and redraw screen 697 | Serial.print("Changing the temperature scale to: "); 698 | if (data.containsKey("scale") && (0 == strcmp(data["scale"], "celsius")) ) 699 | { 700 | Serial.println("Celsius"); 701 | configTempCelsius = true; 702 | strcpy(temp_scale, "celsius"); 703 | } 704 | else 705 | { 706 | Serial.println("Fahrenheit"); 707 | configTempCelsius = false; 708 | strcpy(temp_scale, "fahrenheit"); 709 | } 710 | need_redraw = true; 711 | // Save configurations to file 712 | saveConfig(); 713 | } 714 | 715 | void mqttCallback(char* topic, byte* payload, unsigned int length) 716 | { 717 | // Convert received bytes to a string 718 | char text[length + 1]; 719 | snprintf(text, length + 1, "%s", payload); 720 | 721 | Serial.print("Message arrived ["); 722 | Serial.print(topic); 723 | Serial.print("] "); 724 | Serial.println(text); 725 | 726 | if (strcmp(topic, line1_topic) == 0) 727 | { 728 | snprintf(mqtt_line1, sizeof(mqtt_line1), "%s", text); 729 | need_redraw = true; 730 | } 731 | 732 | if (strcmp(topic, line2_topic) == 0) 733 | { 734 | snprintf(mqtt_line2, sizeof(mqtt_line2), "%s", text); 735 | need_redraw = true; 736 | } 737 | 738 | if (strcmp(topic, line3_topic) == 0) 739 | { 740 | snprintf(mqtt_line3, sizeof(mqtt_line3), "%s", text); 741 | need_redraw = true; 742 | } 743 | 744 | if (strcmp(topic, cmnd_temp_format) == 0) 745 | { 746 | processMessageScale(text); 747 | } 748 | 749 | #ifdef OTA_UPGRADES 750 | if (strcmp(topic, cmnd_update_topic) == 0) 751 | { 752 | Serial.println("OTA request seen.\n"); 753 | do_ota_upgrade(text); 754 | // Any OTA upgrade will stop the mqtt client, so if the 755 | // upgrade failed and we get here publishState() will fail. 756 | // Just return here, and we will reconnect from within the 757 | // loop(). 758 | return; 759 | } 760 | #endif 761 | 762 | publishState(); 763 | } 764 | 765 | void calculateMachineId() 766 | { 767 | MD5Builder md5; 768 | md5.begin(); 769 | char chipId[25]; 770 | sprintf(chipId,"%d",ESP.getChipId()); 771 | md5.add(chipId); 772 | md5.calculate(); 773 | md5.toString().toCharArray(machineId, sizeof(machineId)); 774 | } 775 | 776 | void mqttReconnect() 777 | { 778 | char clientId[18 + sizeof(machineId)]; 779 | snprintf(clientId, sizeof(clientId), "anavi-gas-detector-%s", machineId); 780 | 781 | // Loop until we're reconnected 782 | for (int attempt = 0; attempt < 3; ++attempt) 783 | { 784 | Serial.print("Attempting MQTT connection..."); 785 | // Attempt to connect 786 | if (true == mqttClient.connect(clientId, username, password)) 787 | { 788 | Serial.println("connected"); 789 | 790 | // Subscribe to MQTT topics 791 | mqttClient.subscribe(line1_topic); 792 | mqttClient.subscribe(line2_topic); 793 | mqttClient.subscribe(line3_topic); 794 | mqttClient.subscribe(cmnd_temp_coefficient_topic); 795 | mqttClient.subscribe(cmnd_temp_format); 796 | #ifdef OTA_UPGRADES 797 | mqttClient.subscribe(cmnd_update_topic); 798 | #endif 799 | publishState(); 800 | break; 801 | 802 | } 803 | else 804 | { 805 | Serial.print("failed, rc="); 806 | Serial.print(mqttClient.state()); 807 | Serial.println(" try again in 5 seconds"); 808 | // Wait 5 seconds before retrying 809 | delay(5000); 810 | } 811 | } 812 | } 813 | 814 | #ifdef HOME_ASSISTANT_DISCOVERY 815 | bool publishSensorDiscovery(const char *config_key, 816 | const char *device_class, 817 | const char *name_suffix, 818 | const char *state_topic, 819 | const char *unit, 820 | const char *value_template, 821 | bool binary = false) 822 | { 823 | DynamicJsonDocument json(1024); 824 | 825 | String sensorType = "sensor"; 826 | if (true == binary) 827 | { 828 | sensorType = "binary_sensor"; 829 | } 830 | else 831 | { 832 | // Unit of measurement is supported only by non-binary sensors 833 | json["unit_of_measurement"] = unit; 834 | json["value_template"] = value_template; 835 | } 836 | static char topic[48 + sizeof(machineId)]; 837 | snprintf(topic, sizeof(topic), 838 | "homeassistant/%s/%s/%s/config", sensorType.c_str(), machineId, config_key); 839 | 840 | if (0 < strlen(device_class)) 841 | { 842 | json["device_class"] = device_class; 843 | } 844 | json["name"] = String(ha_name) + " " + name_suffix; 845 | json["unique_id"] = String("anavi-") + machineId + "-" + config_key; 846 | json["state_topic"] = String(workgroup) + "/" + machineId + "/" + state_topic; 847 | 848 | json["device"]["identifiers"] = machineId; 849 | json["device"]["manufacturer"] = "ANAVI Technology"; 850 | json["device"]["model"] = "ANAVI Gas Detector"; 851 | json["device"]["name"] = ha_name; 852 | json["device"]["sw_version"] = ESP.getSketchMD5(); 853 | 854 | JsonArray connections = json["device"].createNestedArray("connections").createNestedArray(); 855 | connections.add("mac"); 856 | connections.add(WiFi.macAddress()); 857 | 858 | Serial.print("Home Assistant discovery topic: "); 859 | Serial.println(topic); 860 | 861 | int payload_len = measureJson(json); 862 | if (!mqttClient.beginPublish(topic, payload_len, true)) 863 | { 864 | Serial.println("beginPublish failed!\n"); 865 | return false; 866 | } 867 | 868 | if (serializeJson(json, mqttClient) != payload_len) 869 | { 870 | Serial.println("writing payload: wrong size!\n"); 871 | return false; 872 | } 873 | 874 | if (!mqttClient.endPublish()) 875 | { 876 | Serial.println("endPublish failed!\n"); 877 | return false; 878 | } 879 | 880 | return true; 881 | } 882 | #endif 883 | 884 | void publishState() 885 | { 886 | static char payload[300]; 887 | static char topic[80]; 888 | 889 | #ifdef HOME_ASSISTANT_DISCOVERY 890 | 891 | String homeAssistantTempScale = (true == configTempCelsius) ? "°C" : "°F"; 892 | 893 | publishSensorDiscovery("DangerousGas", 894 | "gas", 895 | "Dangerous Gas", 896 | "DangerousGas", 897 | "", 898 | "", 899 | true); 900 | 901 | publishSensorDiscovery("AirConductivity", 902 | "", 903 | "Air Conductivity", 904 | "AirConductivity", 905 | "%", 906 | "{{ value_json.Conductivity | round(2) }}"); 907 | 908 | 909 | if (isSensorAvailable(sensorHTU21D)) 910 | { 911 | publishSensorDiscovery("temp", 912 | "temperature", 913 | "Temperature", 914 | "temperature", 915 | homeAssistantTempScale.c_str(), 916 | "{{ value_json.temperature | round(1) }}"); 917 | 918 | publishSensorDiscovery("humidity", 919 | "humidity", 920 | "Humidity", 921 | "humidity", 922 | "%", 923 | "{{ value_json.humidity | round(0) }}"); 924 | } 925 | 926 | if (isSensorAvailable(sensorBMP180)) 927 | { 928 | publishSensorDiscovery("BMPtemperature", 929 | "temperature", 930 | "BMP Temperature", 931 | "BMPtemperature", 932 | homeAssistantTempScale.c_str(), 933 | "{{ value_json.BMPtemperature | round(1) }}"); 934 | 935 | publishSensorDiscovery("BMPpressure", 936 | "pressure", 937 | "Pressure", 938 | "BMPpressure", 939 | "hPa", 940 | "{{ value_json.BMPpressure | round(0) }}"); 941 | 942 | publishSensorDiscovery("BMPaltitude", 943 | "", 944 | "Altitude", 945 | "BMPaltitude", 946 | "m", 947 | "{{ value_json.BMPaltitude | round(0) }}"); 948 | } 949 | 950 | if (isSensorAvailable(sensorBH1750)) 951 | { 952 | publishSensorDiscovery("light", 953 | "illuminance", 954 | "Light", 955 | "light", 956 | "Lux", 957 | "{{ value_json.light }}"); 958 | } 959 | 960 | #endif 961 | } 962 | 963 | void publishSensorData(const char* subTopic, const char* key, const float value) 964 | { 965 | StaticJsonDocument<100> json; 966 | json[key] = value; 967 | char payload[100]; 968 | serializeJson(json, payload); 969 | char topic[200]; 970 | sprintf(topic,"%s/%s/%s", workgroup, machineId, subTopic); 971 | mqttClient.publish(topic, payload, true); 972 | } 973 | 974 | void publishSensorData(const char* subTopic, const char* key, const String& value) 975 | { 976 | StaticJsonDocument<100> json; 977 | json[key] = value; 978 | char payload[100]; 979 | serializeJson(json, payload); 980 | char topic[200]; 981 | sprintf(topic,"%s/%s/%s", workgroup, machineId, subTopic); 982 | mqttClient.publish(topic, payload, true); 983 | } 984 | 985 | void publishSensorDataPlain(const char* subTopic, const String& payload) 986 | { 987 | char topic[200]; 988 | sprintf(topic,"%s/%s/%s", workgroup, machineId, subTopic); 989 | mqttClient.publish(topic, payload.c_str(), true); 990 | } 991 | 992 | bool isSensorAvailable(int sensorAddress) 993 | { 994 | // Check if I2C sensor is present 995 | Wire.beginTransmission(sensorAddress); 996 | return 0 == Wire.endTransmission(); 997 | } 998 | 999 | void handleHTU21D() 1000 | { 1001 | // Check if temperature has changed 1002 | const float tempTemperature = htu.readTemperature(); 1003 | if (0.3 <= abs(tempTemperature - sensorTemperature)) 1004 | { 1005 | // Print new temprature value 1006 | sensorTemperature = tempTemperature; 1007 | Serial.print("Temperature: "); 1008 | Serial.println(formatTemperature(sensorTemperature)); 1009 | 1010 | // Publish new temperature value through MQTT 1011 | publishSensorData("temperature", "temperature", convertTemperature(sensorTemperature)); 1012 | 1013 | // Temperature and humidity are shown on the display 1014 | // so the text has to be refreshed 1015 | need_redraw = true; 1016 | } 1017 | 1018 | // Check if humidity has changed 1019 | const float tempHumidity = htu.readHumidity(); 1020 | if (1 <= abs(tempHumidity - sensorHumidity)) 1021 | { 1022 | // Print new humidity value 1023 | sensorHumidity = tempHumidity; 1024 | Serial.print("Humidity: "); 1025 | Serial.print(sensorHumidity); 1026 | Serial.println("%"); 1027 | 1028 | // Publish new humidity value through MQTT 1029 | publishSensorData("humidity", "humidity", sensorHumidity); 1030 | 1031 | // Temperature and humidity are shown on the display 1032 | // so the text has to be refreshed 1033 | need_redraw = true; 1034 | } 1035 | } 1036 | 1037 | void sensorWriteData(int i2cAddress, uint8_t data) 1038 | { 1039 | Wire.beginTransmission(i2cAddress); 1040 | Wire.write(data); 1041 | Wire.endTransmission(); 1042 | } 1043 | 1044 | void handleBH1750() 1045 | { 1046 | //Wire.begin(); 1047 | // Power on sensor 1048 | sensorWriteData(sensorBH1750, 0x01); 1049 | // Set mode continuously high resolution mode 1050 | sensorWriteData(sensorBH1750, 0x10); 1051 | 1052 | uint16_t tempAmbientLight; 1053 | 1054 | Wire.requestFrom(sensorBH1750, 2); 1055 | tempAmbientLight = Wire.read(); 1056 | tempAmbientLight <<= 8; 1057 | tempAmbientLight |= Wire.read(); 1058 | // s. page 7 of datasheet for calculation 1059 | tempAmbientLight = tempAmbientLight/1.2; 1060 | 1061 | if (1 <= abs(tempAmbientLight - sensorAmbientLight)) 1062 | { 1063 | // Print new brightness value 1064 | sensorAmbientLight = tempAmbientLight; 1065 | Serial.print("Light: "); 1066 | Serial.print(tempAmbientLight); 1067 | Serial.println("Lux"); 1068 | 1069 | // Publish new brightness value through MQTT 1070 | publishSensorData("light", "light", sensorAmbientLight); 1071 | } 1072 | } 1073 | 1074 | void detectGesture() 1075 | { 1076 | //read a gesture from the device 1077 | const uint8_t gestureCode = apds.readGesture(); 1078 | // Skip if gesture has not been detected 1079 | if (0 == gestureCode) 1080 | { 1081 | return; 1082 | } 1083 | String gesture = ""; 1084 | switch(gestureCode) 1085 | { 1086 | case APDS9960_DOWN: 1087 | gesture = "down"; 1088 | break; 1089 | case APDS9960_UP: 1090 | gesture = "up"; 1091 | break; 1092 | case APDS9960_LEFT: 1093 | gesture = "left"; 1094 | break; 1095 | case APDS9960_RIGHT: 1096 | gesture = "right"; 1097 | break; 1098 | } 1099 | Serial.print("Gesture: "); 1100 | Serial.println(gesture); 1101 | // Publish the detected gesture through MQTT 1102 | publishSensorData("gesture", "gesture", gesture); 1103 | } 1104 | 1105 | void handleBMP() 1106 | { 1107 | sensors_event_t event; 1108 | bmp.getEvent(&event); 1109 | if (!event.pressure) 1110 | { 1111 | // BMP180 sensor error 1112 | return; 1113 | } 1114 | Serial.print("BMP180 Pressure: "); 1115 | Serial.print(event.pressure); 1116 | Serial.println(" hPa"); 1117 | float temperature; 1118 | bmp.getTemperature(&temperature); 1119 | Serial.print("BMP180 Temperature: "); 1120 | Serial.println(formatTemperature(temperature)); 1121 | // For accurate results replace SENSORS_PRESSURE_SEALEVELHPA with the current SLP 1122 | float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA; 1123 | float altitude; 1124 | altitude = bmp.pressureToAltitude(seaLevelPressure, event.pressure, temperature); 1125 | Serial.print("BMP180 Altitude: "); 1126 | Serial.print(altitude); 1127 | Serial.println(" m"); 1128 | 1129 | // Do we need to draw again data on the mini OLED display? 1130 | // Yes if HTU21D sensor is not attached and there is a significant temperature change 1131 | if ( (false == isTempSensorAttached) && (0.3 <= abs(temperature - sensorBmpTemperature)) ) 1132 | { 1133 | sensorBmpTemperature = temperature; 1134 | need_redraw = true; 1135 | } 1136 | 1137 | // Publish new pressure values through MQTT 1138 | publishSensorData("BMPpressure", "BMPpressure", event.pressure); 1139 | publishSensorData("BMPtemperature", "BMPtemperature", convertTemperature(temperature)); 1140 | publishSensorData("BMPaltitude", "BMPaltitude", altitude); 1141 | } 1142 | 1143 | void handleSensors() 1144 | { 1145 | isTempSensorAttached = false; 1146 | isPressureSensorAttached = false; 1147 | if (isSensorAvailable(sensorHTU21D)) 1148 | { 1149 | isTempSensorAttached = true; 1150 | handleHTU21D(); 1151 | } 1152 | if (isSensorAvailable(sensorBH1750)) 1153 | { 1154 | handleBH1750(); 1155 | } 1156 | if (isSensorAvailable(sensorBMP180)) 1157 | { 1158 | isPressureSensorAttached = true; 1159 | handleBMP(); 1160 | } 1161 | } 1162 | 1163 | void detectGas() 1164 | { 1165 | int gas = analogRead(pinAdc); 1166 | // Calculate conductivity in pecents 1167 | // The gas concetration depends on the coductivity 1168 | // If the analog MQ sensor detects more gases 1169 | // the conductivity will be higher 1170 | int conductivity = round(((float)gas/1023)*100); 1171 | 1172 | String quality = "Good"; 1173 | String gasState = "off"; 1174 | if (gas <= 190) 1175 | { 1176 | setColor(LOW, LOW, HIGH); 1177 | gasState = "OFF"; 1178 | } 1179 | else if (gas <= 300) 1180 | { 1181 | quality="Moderate"; 1182 | setColor(LOW, HIGH, LOW); 1183 | gasState = "OFF"; 1184 | } 1185 | else 1186 | { 1187 | quality="Poor"; 1188 | setColor(HIGH, LOW, LOW); 1189 | gasState = "ON"; 1190 | } 1191 | 1192 | // Do not print or draw on the display if there is no 1193 | // change of the state from the MQ gas sensor 1194 | // or if the change of the value for ADC is minimal 1195 | if ( (5 > abs(gas - prevGas)) || ((prevConductivity == conductivity) && (prevQuality == quality)) ) 1196 | { 1197 | return; 1198 | } 1199 | 1200 | // Update the detected gas values 1201 | prevConductivity = conductivity; 1202 | prevQuality = quality; 1203 | prevGas = gas; 1204 | 1205 | sensor_line2 = "Quality: " + quality; 1206 | sensor_line3 = "Conductivity: "; 1207 | sensor_line3 += conductivity; 1208 | sensor_line3 += "%"; 1209 | 1210 | // Publish new pressure values through MQTT 1211 | publishSensorData("AirQuality", "Quality", quality); 1212 | publishSensorDataPlain("DangerousGas", gasState); 1213 | publishSensorData("AirConductivity", "Conductivity", conductivity); 1214 | 1215 | // Print values in the serial output 1216 | Serial.print("Gas value: "); 1217 | Serial.println(gas); 1218 | Serial.println(sensor_line1); 1219 | Serial.println(sensor_line2); 1220 | Serial.println(sensor_line3); 1221 | 1222 | need_redraw = true; 1223 | } 1224 | 1225 | float convertCelsiusToFahrenheit(float temperature) 1226 | { 1227 | return (temperature * 9/5 + 32); 1228 | } 1229 | 1230 | float convertTemperature(float temperature) 1231 | { 1232 | return (true == configTempCelsius) ? temperature : convertCelsiusToFahrenheit(temperature); 1233 | } 1234 | 1235 | String formatTemperature(float temperature) 1236 | { 1237 | String unit = (true == configTempCelsius) ? "°C" : "°F"; 1238 | return String(convertTemperature(temperature), 1) + unit; 1239 | } 1240 | 1241 | void loop() 1242 | { 1243 | // put your main code here, to run repeatedly: 1244 | mqttClient.loop(); 1245 | 1246 | // Reconnect if there is an issue with the MQTT connection 1247 | const unsigned long mqttConnectionMillis = millis(); 1248 | if ( (false == mqttClient.connected()) && (mqttConnectionInterval <= (mqttConnectionMillis - mqttConnectionPreviousMillis)) ) 1249 | { 1250 | mqttConnectionPreviousMillis = mqttConnectionMillis; 1251 | mqttReconnect(); 1252 | } 1253 | 1254 | // Handle gestures at a shorter interval 1255 | if (isSensorAvailable(APDS9960_ADDRESS)) 1256 | { 1257 | detectGesture(); 1258 | } 1259 | 1260 | const unsigned long currentMillis = millis(); 1261 | // First check the I2C sensors and WiFi signal 1262 | if (sensorInterval <= (currentMillis - sensorPreviousMillis)) 1263 | { 1264 | sensorPreviousMillis = currentMillis; 1265 | handleSensors(); 1266 | 1267 | long rssiValue = WiFi.RSSI(); 1268 | String rssi = String(rssiValue) + " dBm"; 1269 | Serial.println(rssi); 1270 | 1271 | publishSensorData("wifi/ssid", "ssid", WiFi.SSID()); 1272 | publishSensorData("wifi/bssid", "bssid", WiFi.BSSIDstr()); 1273 | publishSensorData("wifi/rssi", "rssi", rssiValue); 1274 | publishSensorData("wifi/ip", "ip", WiFi.localIP().toString()); 1275 | publishSensorData("sketch", "sketch", ESP.getSketchMD5()); 1276 | 1277 | #ifdef PUBLISH_CHIP_ID 1278 | char chipid[9]; 1279 | snprintf(chipid, sizeof(chipid), "%08x", ESP.getChipId()); 1280 | publishSensorData("chipid", "chipid", chipid); 1281 | #endif 1282 | } 1283 | // Update the values from the analog MQ gas sensor 1284 | if (gasInterval <= (currentMillis - sensorPreviousMillis)) 1285 | { 1286 | gasPreviousMillis = currentMillis; 1287 | detectGas(); 1288 | } 1289 | 1290 | if (true == need_redraw) 1291 | { 1292 | sensor_line1 = "Air "; 1293 | if (true == isTempSensorAttached) 1294 | { 1295 | sensor_line1 += formatTemperature(sensorTemperature) + " "; 1296 | sensor_line1 += (int)round(sensorHumidity); 1297 | sensor_line1 += "%"; 1298 | } 1299 | else if (true == isPressureSensorAttached) 1300 | { 1301 | // show only temperature if HTU21D sensor is not attached but 1302 | // BMP180/280 sensor is attached 1303 | sensor_line1 += formatTemperature(sensorBmpTemperature); 1304 | } 1305 | drawDisplay(mqtt_line1[0] ? mqtt_line1 : sensor_line1.c_str(), 1306 | mqtt_line2[0] ? mqtt_line2 : sensor_line2.c_str(), 1307 | mqtt_line3[0] ? mqtt_line3 : sensor_line3.c_str()); 1308 | need_redraw = false; 1309 | } 1310 | 1311 | // Press and hold the button to reset to factory defaults 1312 | factoryReset(); 1313 | } 1314 | --------------------------------------------------------------------------------