├── .gitignore ├── .vscode └── arduino.json ├── 1U-case.stl ├── 1U_rack_mount_for_Olimex-ESP32-POE.stl ├── DS18B20 Sensor Address.c ├── README.md ├── img ├── Cold_Case.jpg ├── IMG_0295.JPG ├── IMG_0819.JPG ├── IMG_1958.JPG ├── IMG_2920.JPG ├── IMG_3179.JPG ├── IMG_3180.JPG ├── IMG_5406.JPG ├── IMG_5555.JPG ├── IMG_6995.JPG ├── Integromat.png ├── The_Wire.jpg ├── breadboard.png ├── f360.png ├── graph.png ├── imgs └── slack_alert.png ├── ntp.cpp ├── ntp.h ├── temp-sensor_Esp32_main.ino └── temp-sensor_OlimexESP32Ethernet.ino /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /.vscode/arduino.json: -------------------------------------------------------------------------------- 1 | { 2 | "port": "/dev/cu.usbserial-1410", 3 | "board": "esp32:esp32:esp32-poe", 4 | "configuration": "FlashFreq=80,UploadSpeed=115200", 5 | "sketch": "temp-sensor.ino", 6 | "output": "build/" 7 | } -------------------------------------------------------------------------------- /1U-case.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/1U-case.stl -------------------------------------------------------------------------------- /1U_rack_mount_for_Olimex-ESP32-POE.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/1U_rack_mount_for_Olimex-ESP32-POE.stl -------------------------------------------------------------------------------- /DS18B20 Sensor Address.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // Based on the OneWire library example 4 | 5 | OneWire ds(15); //data wire connected to GPIO15 6 | 7 | void setup(void) { 8 | Serial.begin(9600); 9 | } 10 | 11 | void loop(void) { 12 | byte i; 13 | byte addr[8]; 14 | 15 | if (!ds.search(addr)) { 16 | Serial.println(" No more addresses."); 17 | Serial.println(); 18 | ds.reset_search(); 19 | delay(250); 20 | return; 21 | } 22 | Serial.print(" ROM ="); 23 | for (i = 0; i < 8; i++) { 24 | Serial.write(' '); 25 | Serial.print(addr[i], HEX); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # A Data Center Temperature Monitor 3 | A temperature monitor based on and ESP32 and DS18B20 sensors, that can be mounted in a server rack. 4 | 5 | 6 | ![Temperaturemonitor](img/IMG_5406.JPG "temperature monitor") 7 | 8 | ## The project 9 | 10 | I know, yet another temperature monitor ... 11 | This is my first complete project that involves CAD design, 3D printing, electronics prototyping, C code (I am not a developer ;) and webhooks. 12 | 13 | Goals of this project : 14 | - Getting temperatures from the 3 sensors (AC, back of the server racks and ambient) 15 | - Log the temperatures and build a graph to monitor trends overtime. 16 | - Send live alerts when a temperature level is reached. 17 | 18 | ## Connecting the sensors to the controller 19 | 20 | I initially prototyped the temperature monitor using the typical breadboard : 21 | 22 | ![breadboard](img/IMG_2920.JPG "breadboard") 23 | 24 | ![breadboard2](img/breadboard.png "breadboard2") 25 | 26 | I spliced the sensors to Cat5 network cables and ended cables with mini-XLR connectors (these are pretty cool as they have 3 pins and a locking lever). 27 | The DS18B20 is accessed through a one wire bus, it means that they are all connected to the same wire and you can add many sensors as long as you power them accordingly. 28 | 29 | 30 | ![The Wire](img/The_Wire.jpg "The Wire") 31 | *I used a old epson ribbon printer cartridge as a case for 3 female plugs. These are connected to the main wire going to the controller. I could easily go with many other sensors plugging in one of these plugs ...* 32 | 33 | 34 | Each sensor has an address and you can use the code from the `DS18B20 Sensor Address.c` sketch to figure them out. Upload the sketch to the esp32, plug one of the sensor, switch on, read the value in the serial monitor, switch off, plug another sensor, ... 35 | 36 | Label your sensors in case you want to move them or reuse them. 37 | 38 | 39 | 40 | ## The Case 41 | 42 | The first interation of the case revealed that wifi does not do well when the antenna of the controller is placed between 2 server blades (and the access to the usb plug was really bad) : 43 | 44 | ![first case](img/IMG_1958.JPG "first case") 45 | 46 | ![f360](img/f360.png "f360") 47 | 48 | I messed up some dimentions and had to go back to f360 to fix the problems : 49 | 50 | ![3D printing is awesome](img/IMG_0819.JPG "3D printing is awesome") 51 | *(3D printing is awesome)* 52 | 53 | Final case : 54 | 55 | ![Cold Case](img/Cold_Case.jpg "Cold Case") 56 | 57 | Updated case for the Olimex ESP32-POE : 58 | 59 | ![ESP32-POE-Case](img/IMG_5555.JPG "ESP32-POE-Case") 60 | 61 | ## The Code 62 | 63 | The code is pretty straightforward, I used a lot of existing bits and stiched everything together. It probably lacks some error checkings while posting. 64 | The SSL part of the webhook gave me some headhaches (Thanks you Alex for your help !). 65 | Make sure you include all the required librairies ! 66 | 67 | ## Posting the temperature values 68 | 69 | We are using Inegromat for several other projects and it is really cool to use. 70 | Values are read by the controller and then posted to Integromat via a WebHook. Integromat then stores the values in a Google sheet (we may move this to a logstash/kibana). 71 | 72 | ![graph](img/graph.png "graph") 73 | 74 | ![Integromat](img/Integromat.png "Integromat") 75 | 76 | If one of tha values goes beyond a set theshold, an alert is send to a Slack Channel. 77 | 78 | ![slack_alert](img/slack_alert.png "slack_alert") 79 | 80 | ## Final considerations 81 | Wifi ... Well ... is ... Wifi ... Connexion was not reliable and the project was updated to work on an Olimex ESP32-POE https://github.com/OLIMEX/ESP32-POE. 82 | I uploaded the new code and case file. 83 | 84 | 85 | -------------------------------------------------------------------------------- /img/Cold_Case.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/Cold_Case.jpg -------------------------------------------------------------------------------- /img/IMG_0295.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/IMG_0295.JPG -------------------------------------------------------------------------------- /img/IMG_0819.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/IMG_0819.JPG -------------------------------------------------------------------------------- /img/IMG_1958.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/IMG_1958.JPG -------------------------------------------------------------------------------- /img/IMG_2920.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/IMG_2920.JPG -------------------------------------------------------------------------------- /img/IMG_3179.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/IMG_3179.JPG -------------------------------------------------------------------------------- /img/IMG_3180.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/IMG_3180.JPG -------------------------------------------------------------------------------- /img/IMG_5406.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/IMG_5406.JPG -------------------------------------------------------------------------------- /img/IMG_5555.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/IMG_5555.JPG -------------------------------------------------------------------------------- /img/IMG_6995.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/IMG_6995.JPG -------------------------------------------------------------------------------- /img/Integromat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/Integromat.png -------------------------------------------------------------------------------- /img/The_Wire.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/The_Wire.jpg -------------------------------------------------------------------------------- /img/breadboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/breadboard.png -------------------------------------------------------------------------------- /img/f360.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/f360.png -------------------------------------------------------------------------------- /img/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/graph.png -------------------------------------------------------------------------------- /img/imgs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /img/slack_alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hixair/Data-center-temperature-monitor/167ee1b1b293c3d7e54ed07cac47ab500d3755ce/img/slack_alert.png -------------------------------------------------------------------------------- /ntp.cpp: -------------------------------------------------------------------------------- 1 | #include "ntp.h" 2 | #include 3 | #include 4 | #include 5 | 6 | WiFiUDP Udp; 7 | const char *serverName; 8 | unsigned int localUDPPort = 8888; // local port to listen for UDP packets 9 | const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message 10 | byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets 11 | 12 | // send an NTP request to the time server at the given address 13 | void sendNTPpacket(IPAddress &address) 14 | { 15 | // set all bytes in the buffer to 0 16 | memset(packetBuffer, 0, NTP_PACKET_SIZE); 17 | // Initialize values needed to form NTP request 18 | // (see URL above for details on the packets) 19 | packetBuffer[0] = 0b11100011; // LI, Version, Mode 20 | packetBuffer[1] = 0; // Stratum, or type of clock 21 | packetBuffer[2] = 6; // Polling Interval 22 | packetBuffer[3] = 0xEC; // Peer Clock Precision 23 | // 8 bytes of zero for Root Delay & Root Dispersion 24 | packetBuffer[12] = 49; 25 | packetBuffer[13] = 0x4E; 26 | packetBuffer[14] = 49; 27 | packetBuffer[15] = 52; 28 | // all NTP fields have been given values, now 29 | // you can send a packet requesting a timestamp: 30 | Udp.beginPacket(address, 123); //NTP requests are to port 123 31 | Udp.write(packetBuffer, NTP_PACKET_SIZE); 32 | Udp.endPacket(); 33 | } 34 | 35 | time_t getNtpTime() 36 | { 37 | IPAddress ntpServerIP; // NTP server's ip address 38 | 39 | while (Udp.parsePacket() > 0) ; // discard any previously received packets 40 | Serial.println("Sending NTP request..."); 41 | // get a random server from the pool 42 | WiFi.hostByName(serverName, ntpServerIP); 43 | Serial.print(serverName); 44 | Serial.print(": "); 45 | Serial.println(ntpServerIP); 46 | sendNTPpacket(ntpServerIP); 47 | uint32_t beginWait = millis(); 48 | while (millis() - beginWait < 1500) { 49 | int size = Udp.parsePacket(); 50 | if (size >= NTP_PACKET_SIZE) { 51 | Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer 52 | 53 | uint8_t ntpVersion = (packetBuffer[0] & 0x38) >> 3; 54 | 55 | if (ntpVersion != 4) { 56 | Serial.print("Received NTP packet with invalid version ("); Serial.print(ntpVersion); Serial.println(" instead of 4), ignoring!"); 57 | return 0; 58 | } 59 | 60 | // convert four bytes starting at location 40 to a long integer 61 | unsigned long secsSince1900; 62 | secsSince1900 = (unsigned long)packetBuffer[40] << 24; 63 | secsSince1900 |= (unsigned long)packetBuffer[41] << 16; 64 | secsSince1900 |= (unsigned long)packetBuffer[42] << 8; 65 | secsSince1900 |= (unsigned long)packetBuffer[43]; 66 | 67 | if (secsSince1900 == 0) { 68 | Serial.println("Received NTP packet with null time, ignoring!"); 69 | return 0; 70 | } 71 | 72 | Serial.println("Received valid NTP packet, syncing time."); 73 | 74 | return secsSince1900 - 2208988800UL; 75 | } 76 | } 77 | Serial.println("No NTP response!"); 78 | return 0; // return 0 if unable to get the time 79 | } 80 | 81 | void setupNTP(const char *ntpServer, int syncInterval) { 82 | serverName = ntpServer; 83 | Udp.begin(localUDPPort); 84 | setSyncProvider(getNtpTime); 85 | setSyncInterval(syncInterval); 86 | } 87 | -------------------------------------------------------------------------------- /ntp.h: -------------------------------------------------------------------------------- 1 | #ifndef MLP_NTP_H 2 | #define MLP_NTP_H 3 | 4 | void setupNTP(const char *ntpServer, int syncInterval); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /temp-sensor_Esp32_main.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // WiFi settings 8 | #define WIFI_SSID "YOURSSID" 9 | #define WIFI_PASSWORD "YOURSSIRDPASSWORD" 10 | 11 | // Delay between temperature checks (in minutes) 12 | #define CHECK_INTERVAL 15 13 | 14 | // Setup a oneWire instance to communicate with a OneWire device on GPIO 15 15 | OneWire oneWire(15); 16 | 17 | // Pass our oneWire reference to Dallas Temperature sensor 18 | DallasTemperature sensors(&oneWire); 19 | 20 | DeviceAddress sensor_rack = { 0x28, 0xF0, 0x66, 0xAE, 0x08, 0x00, 0x00, 0x78 }; 21 | DeviceAddress sensor_ac = { 0x28, 0xFF, 0x9B, 0x1A, 0xA2, 0x16, 0x03, 0xB1 }; 22 | DeviceAddress sensor_ambient = { 0x28, 0xFF, 0x01, 0xE4, 0xA2, 0x16, 0x03, 0xDF }; 23 | 24 | const char *ntpServer = "pool.ntp.org"; 25 | 26 | static const char integromat_root_ca[] PROGMEM = R"EOF( 27 | -----BEGIN CERTIFICATE----- 28 | MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB 29 | hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G 30 | A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV 31 | BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 32 | MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT 33 | EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR 34 | Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh 35 | dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR 36 | 6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X 37 | pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC 38 | 9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV 39 | /erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf 40 | Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z 41 | +pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w 42 | qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah 43 | SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC 44 | u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf 45 | Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq 46 | crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E 47 | FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB 48 | /wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl 49 | wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM 50 | 4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV 51 | 2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna 52 | FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ 53 | CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK 54 | boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke 55 | jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL 56 | S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb 57 | QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl 58 | 0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB 59 | NVOFBkpdn627G190 60 | -----END CERTIFICATE----- 61 | )EOF"; 62 | 63 | void setup(void){ 64 | Serial.begin(115200); 65 | sensors.begin(); 66 | 67 | Serial.print("Connecting to "); 68 | Serial.print(WIFI_SSID); 69 | 70 | WiFi.begin(WIFI_SSID, WIFI_PASSWORD); 71 | while (WiFi.status() != WL_CONNECTED) { 72 | delay(500); 73 | Serial.print("."); 74 | } 75 | 76 | Serial.println(); 77 | Serial.print("Connected to the WiFi network"); 78 | Serial.print(", IP address: "); 79 | Serial.println(WiFi.localIP()); 80 | 81 | configTime(0, 0, ntpServer); 82 | } 83 | 84 | void loop(void){ 85 | Serial.print("Reading temperatures... "); 86 | sensors.requestTemperatures(); 87 | Serial.println("OK"); 88 | 89 | Serial.print("Rack temperature: "); 90 | Serial.print(sensors.getTempC(sensor_rack)); 91 | Serial.println("°C"); 92 | 93 | Serial.print("AC temperature: "); 94 | Serial.print(sensors.getTempC(sensor_ac)); 95 | Serial.println("°C"); 96 | 97 | Serial.print("Ambient temperature: "); 98 | Serial.print(sensors.getTempC(sensor_ambient)); 99 | Serial.println("°C"); 100 | 101 | struct tm timeinfo; 102 | if (!getLocalTime(&timeinfo)){ 103 | Serial.println("Failed to get time."); 104 | return; 105 | } 106 | 107 | char payload[200]; 108 | snprintf(payload, sizeof(payload), "{\"date\":\"%04d-%02d-%02d %02d:%02d:%02d\",\"rack_temp\":%f,\"ac_temp\":%f,\"ambient_temp\":%f}", 109 | timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, 110 | sensors.getTempC(sensor_rack), sensors.getTempC(sensor_ac), sensors.getTempC(sensor_ambient)); 111 | 112 | HTTPClient http; 113 | http.begin("https://hook.integromat.com/HookID", integromat_root_ca); 114 | http.addHeader("Content-Type", "application/json"); 115 | http.POST(payload); 116 | 117 | delay(CHECK_INTERVAL * 60 * 1000); 118 | } 119 | -------------------------------------------------------------------------------- /temp-sensor_OlimexESP32Ethernet.ino: -------------------------------------------------------------------------------- 1 | #define ETH_CLK_MODE ETH_CLOCK_GPIO17_OUT 2 | #define ETH_PHY_POWER 12 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "ntp.h" 12 | 13 | // Delay between temperature checks (in minutes) 14 | #define CHECK_INTERVAL 5 15 | 16 | #define NTP_SERVER "pool.ntp.org" 17 | #define NTP_SYNC_INTERVAL 60 18 | 19 | // Setup a oneWire instance to communicate with a OneWire device on GPIO 15 20 | OneWire oneWire(0); 21 | 22 | // Pass our oneWire reference to Dallas Temperature sensor 23 | DallasTemperature sensors(&oneWire); 24 | 25 | DeviceAddress sensor_rack = { 0x28, 0xF0, 0x66, 0xAE, 0x08, 0x00, 0x00, 0x78 }; 26 | DeviceAddress sensor_ac = { 0x28, 0xFF, 0x9B, 0x1A, 0xA2, 0x16, 0x03, 0xB1 }; 27 | DeviceAddress sensor_ambient = { 0x28, 0xFF, 0x01, 0xE4, 0xA2, 0x16, 0x03, 0xDF }; 28 | 29 | bool eth_connected = false; 30 | 31 | static const char integromat_root_ca[] PROGMEM = R"EOF( 32 | -----BEGIN CERTIFICATE----- 33 | MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB 34 | hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G 35 | A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV 36 | BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 37 | MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT 38 | EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR 39 | Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh 40 | dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR 41 | 6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X 42 | pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC 43 | 9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV 44 | /erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf 45 | Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z 46 | +pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w 47 | qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah 48 | SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC 49 | u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf 50 | Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq 51 | crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E 52 | FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB 53 | /wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl 54 | wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM 55 | 4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV 56 | 2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna 57 | FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ 58 | CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK 59 | boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke 60 | jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL 61 | S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb 62 | QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl 63 | 0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB 64 | NVOFBkpdn627G190 65 | -----END CERTIFICATE----- 66 | )EOF"; 67 | 68 | void WiFiEvent(WiFiEvent_t event) 69 | { 70 | switch (event) { 71 | case SYSTEM_EVENT_ETH_START: 72 | Serial.println("ETH Started"); 73 | //set eth hostname here 74 | ETH.setHostname("esp32-ethernet"); 75 | break; 76 | case SYSTEM_EVENT_ETH_CONNECTED: 77 | Serial.println("ETH Connected"); 78 | break; 79 | case SYSTEM_EVENT_ETH_GOT_IP: 80 | Serial.print("ETH MAC: "); 81 | Serial.print(ETH.macAddress()); 82 | Serial.print(", IPv4: "); 83 | Serial.print(ETH.localIP()); 84 | if (ETH.fullDuplex()) { 85 | Serial.print(", FULL_DUPLEX"); 86 | } 87 | Serial.print(", "); 88 | Serial.print(ETH.linkSpeed()); 89 | Serial.println("Mbps"); 90 | eth_connected = true; 91 | setupNTP(NTP_SERVER, NTP_SYNC_INTERVAL); 92 | break; 93 | case SYSTEM_EVENT_ETH_DISCONNECTED: 94 | Serial.println("ETH Disconnected"); 95 | eth_connected = false; 96 | break; 97 | case SYSTEM_EVENT_ETH_STOP: 98 | Serial.println("ETH Stopped"); 99 | eth_connected = false; 100 | break; 101 | default: 102 | break; 103 | } 104 | } 105 | 106 | 107 | 108 | 109 | void setup(void){ 110 | Serial.begin(115200); 111 | Serial.println("Init..."); 112 | 113 | sensors.begin(); 114 | 115 | WiFi.onEvent(WiFiEvent); 116 | ETH.begin(); 117 | 118 | discoverOneWireDevices(); 119 | 120 | Serial.println("Waiting for ethernet..."); 121 | } 122 | 123 | void loop(void){ 124 | if (!eth_connected) { 125 | return; 126 | } 127 | 128 | Serial.print("Reading temperatures... "); 129 | sensors.requestTemperatures(); 130 | Serial.println("OK"); 131 | 132 | Serial.print("Rack temperature: "); 133 | Serial.print(sensors.getTempC(sensor_rack)); 134 | Serial.println("°C"); 135 | 136 | Serial.print("AC temperature: "); 137 | Serial.print(sensors.getTempC(sensor_ac)); 138 | Serial.println("°C"); 139 | 140 | Serial.print("Ambient temperature: "); 141 | Serial.print(sensors.getTempC(sensor_ambient)); 142 | Serial.println("°C"); 143 | 144 | char payload[200]; 145 | snprintf(payload, sizeof(payload), "{\"date\":\"%04d-%02d-%02d %02d:%02d:%02d\",\"rack_temp\":%f,\"ac_temp\":%f,\"ambient_temp\":%f}", 146 | year(), month(), day(), hour(), minute(), second(), 147 | sensors.getTempC(sensor_rack), sensors.getTempC(sensor_ac), sensors.getTempC(sensor_ambient)); 148 | 149 | HTTPClient http; 150 | http.begin("https://hook.integromat.com/0hm2cmrmy4fus0t2tc8lxnxegu2f5yq5", integromat_root_ca); 151 | http.addHeader("Content-Type", "application/json"); 152 | http.POST(payload); 153 | 154 | delay(CHECK_INTERVAL * 60 * 1000); 155 | } 156 | 157 | void discoverOneWireDevices(void) { 158 | byte i; 159 | byte present = 0; 160 | byte data[12]; 161 | byte addr[8]; 162 | 163 | Serial.print("Looking for 1-Wire devices...\n\r"); 164 | while(oneWire.search(addr)) { 165 | Serial.print("\n\rFound \'1-Wire\' device with address:\n\r"); 166 | for( i = 0; i < 8; i++) { 167 | Serial.print("0x"); 168 | if (addr[i] < 16) { 169 | Serial.print('0'); 170 | } 171 | Serial.print(addr[i], HEX); 172 | if (i < 7) { 173 | Serial.print(", "); 174 | } 175 | } 176 | if ( OneWire::crc8( addr, 7) != addr[7]) { 177 | Serial.print("CRC is not valid!\n"); 178 | return; 179 | } 180 | } 181 | Serial.print("\n\r\n\rThat's it.\r\n"); 182 | oneWire.reset_search(); 183 | return; 184 | } 185 | --------------------------------------------------------------------------------