├── .DS_Store
├── .gitignore
├── .vscode
└── extensions.json
├── LICENSE
├── README.md
├── boards
└── lilygo-t-display-s3.json
├── images
├── IMG_0277.jpg
└── IMG_0279.jpeg
├── include
├── README
└── pin_config.h
├── lib
└── README
├── metrics-collector
├── collector.py
├── metrics-collector.service
└── requirments.txt
├── platformio.ini
├── src
└── main.cpp
└── test
└── README
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/denmark111/esp32ServerMon/ca7569a4f1c2fdb1addeb29a05f087dd17753c5b/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .pio
2 | .vscode/.browse.c_cpp.db*
3 | .vscode/c_cpp_properties.json
4 | .vscode/launch.json
5 | .vscode/ipch
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | "platformio.platformio-ide"
6 | ],
7 | "unwantedRecommendations": [
8 | "ms-vscode.cpptools-extension-pack"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Moon, S.H.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # esp32ServerMon
2 | Offline server monitoring tool
3 | Any fixes or improvements are always welcome!!
4 |
5 | ## Working examples
6 |
7 |
8 |
9 | ## Available metrics on screen
10 | - Current time
11 | - Wifi address
12 | - CPU usage in %
13 | - CPU usage history graph
14 | - CPU1/2 Temperature
15 | - Memory usage in %
16 | - Load Average (15m)
17 | - Disk R/W IOPS (Cycles between read and write with a button)
18 | - Disk health (green dots in top right corner of disk grid)
19 | - Overall health **(Currently not implemented)**
20 |
21 | ## Tested environment
22 | 1. Hardware
23 | - Lilygo T-Display S3 (Non-touch version)
24 | - Dell R740xd
25 | 2. Software
26 | - PlatformIO w/ VSCode
27 | - ProxmoxVE 8
28 | - Python3.11
29 |
30 | ## Usage
31 | #### First, upload the firmware to lilygo t-display s3
32 | 1. Install PlatformIO vscode plugin (install vscode first if not installed)
33 | 2. Clone this repo
34 | 3. Open repo directory in vscode
35 | - platformio should setup environment automatically
36 | - if not, check if platformio.ini file is visiable in vscode explorer.
37 | 4. Connect lilygo t-display s3 via USB
38 | 5. Start compile & upload
39 | - If you successfully installed PlatformIO, right arrow button should be visiable on the bottom left corner
40 | 6. Reset lilygo t-display s3 and check if default UI is loaded.
41 |
42 | #### Next, Setup collector in Proxmox
43 | 1. Make sure python3.11 is installed
44 | - python3.11 is installed by default in Proxmox 8
45 | 2. Copy collector script
46 | ``` bash
47 | cd esp32servermon
48 | cp -r metrics-collector /opt/
49 | ```
50 | 3. Install python requirments
51 | ``` python
52 | cd /opt/metrics-collector
53 | python3 -m venv venv
54 | venv/bin/pip install -r requirments.txt
55 | ```
56 | 4. Copy systemd service file
57 | 5. Config collector script
58 | - Change DEVICE_NAME
59 | - Change BAUD_RATE **(Only if necessary)**
60 | - Change CHECK_INTERVAL **(Only if necessary)**
61 | ``` python
62 | DEVICE_NAME = "/dev/ttyACM0"
63 | BAUD_RATE = 115200
64 | CHECK_INTERVAL = 30
65 | ```
66 | 6. Run collector
67 | ``` bash
68 | systemctl daemon-reload
69 | systemctl start metrics-collector
70 | ```
71 |
72 | #### Finally, check if all metrics are visiable in lilygo t-display s3
73 | 1. Press right button to check if diskIO cycles between read and write.
74 | 2. Check everything is working.
75 |
76 |
--------------------------------------------------------------------------------
/boards/lilygo-t-display-s3.json:
--------------------------------------------------------------------------------
1 | {
2 | "build": {
3 | "arduino": {
4 | "ldscript": "esp32s3_out.ld",
5 | "memory_type": "qio_opi",
6 | "partitions": "default_16MB.csv"
7 | },
8 | "core": "esp32",
9 | "extra_flags": [
10 | "-DBOARD_HAS_PSRAM"
11 | ],
12 | "f_cpu": "240000000L",
13 | "f_flash": "80000000L",
14 | "flash_mode": "qio",
15 | "hwids": [
16 | [
17 | "0X303A",
18 | "0x1001"
19 | ]
20 | ],
21 | "mcu": "esp32s3",
22 | "variant": "esp32s3"
23 | },
24 | "connectivity": [
25 | "wifi",
26 | "bluetooth"
27 | ],
28 | "debug": {
29 | "openocd_target": "esp32s3-builtin.cfg"
30 | },
31 | "frameworks": [
32 | "arduino",
33 | "espidf"
34 | ],
35 | "name": "T-DisplayS3",
36 | "upload": {
37 | "flash_size": "16MB",
38 | "maximum_ram_size": 327680,
39 | "maximum_size": 16777216,
40 | "require_upload_port": true,
41 | "speed": 921600
42 | },
43 | "url": "https://www.lilygo.cc/products/t-display-s3",
44 | "vendor": "LILYGO"
45 | }
--------------------------------------------------------------------------------
/images/IMG_0277.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/denmark111/esp32ServerMon/ca7569a4f1c2fdb1addeb29a05f087dd17753c5b/images/IMG_0277.jpg
--------------------------------------------------------------------------------
/images/IMG_0279.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/denmark111/esp32ServerMon/ca7569a4f1c2fdb1addeb29a05f087dd17753c5b/images/IMG_0279.jpeg
--------------------------------------------------------------------------------
/include/README:
--------------------------------------------------------------------------------
1 |
2 | This directory is intended for project header files.
3 |
4 | A header file is a file containing C declarations and macro definitions
5 | to be shared between several project source files. You request the use of a
6 | header file in your project source file (C, C++, etc) located in `src` folder
7 | by including it, with the C preprocessing directive `#include'.
8 |
9 | ```src/main.c
10 |
11 | #include "header.h"
12 |
13 | int main (void)
14 | {
15 | ...
16 | }
17 | ```
18 |
19 | Including a header file produces the same results as copying the header file
20 | into each source file that needs it. Such copying would be time-consuming
21 | and error-prone. With a header file, the related declarations appear
22 | in only one place. If they need to be changed, they can be changed in one
23 | place, and programs that include the header file will automatically use the
24 | new version when next recompiled. The header file eliminates the labor of
25 | finding and changing all the copies as well as the risk that a failure to
26 | find one copy will result in inconsistencies within a program.
27 |
28 | In C, the usual convention is to give header files names that end with `.h'.
29 | It is most portable to use only letters, digits, dashes, and underscores in
30 | header file names, and at most one dot.
31 |
32 | Read more about using header files in official GCC documentation:
33 |
34 | * Include Syntax
35 | * Include Operation
36 | * Once-Only Headers
37 | * Computed Includes
38 |
39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
40 |
--------------------------------------------------------------------------------
/include/pin_config.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 |
4 | /*ESP32S3*/
5 | #define PIN_LCD_BL 38
6 |
7 | #define PIN_LCD_D0 39
8 | #define PIN_LCD_D1 40
9 | #define PIN_LCD_D2 41
10 | #define PIN_LCD_D3 42
11 | #define PIN_LCD_D4 45
12 | #define PIN_LCD_D5 46
13 | #define PIN_LCD_D6 47
14 | #define PIN_LCD_D7 48
15 |
16 | #define PIN_POWER_ON 15
17 |
18 | #define PIN_LCD_RES 5
19 | #define PIN_LCD_CS 6
20 | #define PIN_LCD_DC 7
21 | #define PIN_LCD_WR 8
22 | #define PIN_LCD_RD 9
23 |
24 | #define PIN_BUTTON_1 0
25 | #define PIN_BUTTON_2 14
26 | #define PIN_BAT_VOLT 4
27 |
28 | #define PIN_IIC_SCL 17
29 | #define PIN_IIC_SDA 18
30 |
31 | #define PIN_TOUCH_INT 16
32 | #define PIN_TOUCH_RES 21
--------------------------------------------------------------------------------
/lib/README:
--------------------------------------------------------------------------------
1 |
2 | This directory is intended for project specific (private) libraries.
3 | PlatformIO will compile them to static libraries and link into executable file.
4 |
5 | The source code of each library should be placed in an own separate directory
6 | ("lib/your_library_name/[here are source files]").
7 |
8 | For example, see a structure of the following two libraries `Foo` and `Bar`:
9 |
10 | |--lib
11 | | |
12 | | |--Bar
13 | | | |--docs
14 | | | |--examples
15 | | | |--src
16 | | | |- Bar.c
17 | | | |- Bar.h
18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
19 | | |
20 | | |--Foo
21 | | | |- Foo.c
22 | | | |- Foo.h
23 | | |
24 | | |- README --> THIS FILE
25 | |
26 | |- platformio.ini
27 | |--src
28 | |- main.c
29 |
30 | and a contents of `src/main.c`:
31 | ```
32 | #include
33 | #include
34 |
35 | int main (void)
36 | {
37 | ...
38 | }
39 |
40 | ```
41 |
42 | PlatformIO Library Dependency Finder will find automatically dependent
43 | libraries scanning project source files.
44 |
45 | More information about PlatformIO Library Dependency Finder
46 | - https://docs.platformio.org/page/librarymanager/ldf.html
47 |
--------------------------------------------------------------------------------
/metrics-collector/collector.py:
--------------------------------------------------------------------------------
1 | import psutil
2 | import serial
3 | from subprocess import PIPE, run
4 | import time
5 |
6 |
7 | # USB device name, your device name could be different
8 | DEVICE_NAME = "/dev/ttyACM0"
9 | BAUD_RATE = 115200
10 | # Interval between metrics sent to esp32
11 | CHECK_INTERVAL = 30
12 |
13 |
14 | def sendSerialData(data, device, baudrate):
15 | dev = serial.Serial(
16 | port=device,
17 | baudrate=baudrate,
18 | timeout=1
19 | )
20 |
21 | dev.write(bytes(f"[{data}]", "utf-8"))
22 |
23 |
24 | def checkHealth():
25 | # TODO
26 | result = 200
27 |
28 | return f"OVERALL:{result}"
29 |
30 |
31 | def getCpuMetrics():
32 | cpuPercent = psutil.cpu_percent(interval=1)
33 | loadAvg = psutil.getloadavg()[2]
34 |
35 | return f"CPU:{cpuPercent},LAVG:{loadAvg}"
36 |
37 |
38 | def getCpuTempMetrics():
39 | temp = psutil.sensors_temperatures()
40 | cpuTempRes = {}
41 | for elem in temp['coretemp']:
42 | if elem.label == "Package id 0":
43 | cpuTempRes[0] = elem.current
44 | if elem.label == "Package id 1":
45 | cpuTempRes[1] = elem.current
46 |
47 | output = "CPUTEMP:"
48 | for cpuid, cputemp in cpuTempRes.items():
49 | output += f"{cputemp}|"
50 |
51 | return output
52 |
53 |
54 | def getDiskMetrics():
55 | slotNumIndex = 5
56 | queryDiskCmd = "find /dev/disk/by-path -type l -ls | grep -e scsi -e ata | grep -v sr | grep -v usb | grep -v part"
57 | diskInfo = run(queryDiskCmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True).stdout.splitlines()
58 |
59 | identDisk = {}
60 | for di in diskInfo:
61 | if di is not None:
62 | if di.split(":")[slotNumIndex] is not None:
63 | identDisk[di.split("/")[-1]] = di.split(":")[slotNumIndex]
64 |
65 | diskUsage = {}
66 | pDiskStat = psutil.disk_io_counters(perdisk=True)
67 | time.sleep(1)
68 | diskStat = psutil.disk_io_counters(perdisk=True)
69 | for devName, slotNum in identDisk.items():
70 |
71 | querySmartCmd = f"smartctl --attributes --log=selftest --quietmode=errorsonly /dev/{devName}"
72 | smartOutput = run(querySmartCmd, stdout=PIPE, stderr=PIPE, universal_newlines=True, shell=True).stdout.splitlines()
73 | if smartOutput:
74 | smtResult = 2
75 | else:
76 | smtResult = 1
77 |
78 | pRead = pDiskStat[devName].read_count
79 | pWrite = pDiskStat[devName].write_count
80 | cRead = diskStat[devName].read_count
81 | cWrite = diskStat[devName].write_count
82 | diskUsage[slotNum] = (cRead - pRead, cWrite - pWrite, smtResult)
83 |
84 | output = "DISKIO:"
85 | for n, s in diskUsage.items():
86 | output += f"{n}-{s[0]}-{s[1]}-{s[2]}-|"
87 |
88 | return output
89 |
90 |
91 | def getMemoryMetrics():
92 | memoryUsage = psutil.virtual_memory().percent
93 |
94 | return f"MEM:{memoryUsage}"
95 |
96 |
97 | def getPowerUsage():
98 | pass
99 |
100 |
101 | def main():
102 | payload = f",{getCpuMetrics()},{getMemoryMetrics()},{getDiskMetrics()},{getCpuTempMetrics()},{checkHealth()}\n"
103 |
104 | ## Uncomment to debug
105 | #print(getCpuMetrics())
106 | #print(getMemoryMetrics())
107 | #print(getDiskMetrics())
108 | #print(getCpuTempMetrics())
109 | #print(payload)
110 |
111 | sendSerialData(payload, DEVICE_NAME, BAUD_RATE)
112 |
113 |
114 | if __name__ in "__main__":
115 | while(True):
116 | main()
117 | time.sleep(CHECK_INTERVAL)
--------------------------------------------------------------------------------
/metrics-collector/metrics-collector.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=ESP32 Metrics Collector
3 | After=network.target
4 |
5 | [Service]
6 | Type=idle
7 | Restart=on-failure
8 | User=root
9 | ExecStart=/opt/metrics-collector/venv/bin/python3 /opt/metrics-collector/collector.py
10 |
11 | [Install]
12 | WantedBy=multi-user.target
--------------------------------------------------------------------------------
/metrics-collector/requirments.txt:
--------------------------------------------------------------------------------
1 | psutil==6.0.0
2 | pyserial==3.5
--------------------------------------------------------------------------------
/platformio.ini:
--------------------------------------------------------------------------------
1 | ; PlatformIO Project Configuration File
2 | ;
3 | ; Build options: build flags, source filter
4 | ; Upload options: custom upload port, speed and extra flags
5 | ; Library options: dependencies, extra library storages
6 | ; Advanced options: extra scripting
7 | ;
8 | ; Please visit documentation for the other options and examples
9 | ; https://docs.platformio.org/page/projectconf.html
10 |
11 | [platformio]
12 | src_dir = ./src
13 | boards_dir = ./boards
14 |
15 | [env]
16 | platform = espressif32
17 | board = lilygo-t-display-s3
18 | framework = arduino
19 | debug_tool = esp-builtin
20 | upload_protocol = esptool
21 | build_flags =
22 | -DLV_LVGL_H_INCLUDE_SIMPLE
23 | -DARDUINO_USB_CDC_ON_BOOT=1
24 | -DDISABLE_ALL_LIBRARY_WARNINGS
25 | -DARDUINO_USB_MODE=1
26 | -DTOUCH_MODULES_CST_MUTUAL
27 |
28 | [env:lilygo-t-display-s3]
29 | build_flags =
30 | ${env.build_flags}
31 | -D USER_SETUP_LOADED=1
32 | -include $PROJECT_LIBDEPS_DIR/$PIOENV/TFT_eSPI/User_Setups/Setup206_LilyGo_T_Display_S3.h
33 | lib_deps =
34 | bodmer/TFT_eSPI@^2.5.43
35 | mathertel/OneButton@^2.5.0
36 | lib_ignore =
37 | GFX Library for Arduino
38 | arduino-nofrendo
39 | Adafruit MPR121
40 | DabbleESP32
41 | PCF8575 library
42 | PCA95x5
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #include // Graphics and font library for ST7735 driver chip
2 | #include
3 | #include
4 | #include
5 |
6 |
7 | #include "pin_config.h"
8 | #include "time.h"
9 |
10 |
11 | #define SCREEN_WIDTH 320
12 | #define SCREEN_HEIGHT 170
13 | #define PIN_INPUT_BUTTON 14
14 |
15 |
16 | TaskHandle_t drawGuiUpdate;
17 |
18 |
19 | TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.h
20 | TFT_eSprite sprite = TFT_eSprite(&tft);
21 |
22 | const char* ssid = "WIFI SSID";
23 | const char* password = "WIFI PASSWORD";
24 | const char* ntpServer = "time.google.com";
25 | const long gmtOffset = 3600;
26 | const int daylightOffset = 0;
27 | const int wifiTimeout = 10000;
28 |
29 | const int fontSize = 15;
30 |
31 | // Splash screen config
32 | String productName = "ServerMon";
33 | String productVer = "v0.1";
34 | String productType = "For Dell R740xd";
35 |
36 | // Graph data
37 | const int cpuUsagePoints = 100;
38 | int cpuUsage[cpuUsagePoints] = {0, };
39 |
40 | // Metrics gui config
41 | const int cpu1TempPosX = 6;
42 | const int cpu1TempPosY = 43;
43 | const int cpu2TempPosX = 6;
44 | const int cpu2TempPosY = 58;
45 | const int cpuUsePosX = 6;
46 | const int cpuUsePosY = 73;
47 | const int statusPosX = 160;
48 | const int statusPosY = 43;
49 | const int memoryPosX = 160;
50 | const int memoryPosY = 58;
51 | const int loadAvgPosX = 160;
52 | const int loadAvgPosY = 73;
53 | const int metricsValueSpacing = 104;
54 |
55 | // Disk gui config
56 | const int diskTextSize = 8;
57 | const int diskPosX = 0;
58 | const int diskPosY = 110;
59 | const int totalDiskCount = 12;
60 | const int diskRow = 3;
61 | const int cellSpacing = 2;
62 | const int cellWidth = 40;
63 | const int cellHeight = 20;
64 | // 1 = ReadIO, 2 = WriteIO
65 | int diskUsageRW = 1;
66 |
67 | // Cpu graph gui config
68 | const int graphPosX = 160;
69 | const int graphPosY = 110;
70 | const int graphHeight = 60;
71 | const int graphWidth = 160;
72 | const int legendSize = 22;
73 |
74 | // Status modelbox config
75 | const int mbPosX = 220;
76 | const int mbPosY = 4;
77 | const String mbName = "Dell R740xd";
78 |
79 | char attributeMarker = ':';
80 | char valueMarker = ',';
81 | char valueSpace = '|';
82 | String buff = "";
83 | String property = "";
84 | String value = "";
85 |
86 | int ntpUpdateTimer = 0;
87 | int graphUpdateTimer = 0;
88 |
89 | int overallStatus = -1;
90 | float cpuPercentage = -1;
91 | float loadAvg15 = -1;
92 | float memPercentage = -1;
93 | String diskUsage = "";
94 | String cpuTemp = "";
95 | typedef struct cpuTemp_ {
96 | int cpuNum = 0;
97 | float temperature = -1;
98 | } cpuTemp_st;
99 | typedef struct diskUsage_ {
100 | int diskRead = -1;
101 | int diskWrite = -1;
102 | int diskStatus = -1;
103 | } diskUsage_st;
104 |
105 | // Button config
106 | OneButton button(PIN_INPUT_BUTTON, true);
107 |
108 |
109 | void drawGraphDots(int offsetX, int offsetY, int width, int height) {
110 | // Draw graph dots
111 | int dotPerRow = 5;
112 | int dotPerCol = 3;
113 | int dotSpacingX = width / (dotPerRow + 1);
114 | int dotSpacingY = height / (dotPerCol + 1);
115 | int posX = offsetX + (dotSpacingX / 2);
116 | int posY = offsetY + dotSpacingY;
117 | for (int i=0; i= cpuUsagePoints) {
145 | cpuUsage[i] = 0;
146 | }
147 | else {
148 | cpuUsage[i] = cpuUsage[i+1];
149 | }
150 | }
151 | if (data > 100) {
152 | cpuUsage[cpuUsagePoints - 1] = 100;
153 | }
154 | else if (data < 0) {
155 | cpuUsage[cpuUsagePoints - 1] = 0;
156 | }
157 | else {
158 | cpuUsage[cpuUsagePoints - 1] = data;
159 | }
160 |
161 | float interval = width / (float) cpuUsagePoints;
162 | float prevPosX, nextPosX;
163 | int prevPosY, nextPosY;
164 | prevPosX = offsetX;
165 | prevPosY = offsetY - cpuUsage[0];
166 | for(int j=0; j 45) {
294 | color = TFT_YELLOW;
295 | }
296 | if (data > 55) {
297 | color = TFT_ORANGE;
298 | }
299 | if (data > 60) {
300 | color = TFT_RED;
301 | }
302 | sprite.fillCircle(indicatorPosX, offsetY, 3, color);
303 | break;
304 |
305 | case 2:
306 | if (data <= 70) {
307 | color = TFT_GREEN;
308 | }
309 | if (data > 70) {
310 | color = TFT_YELLOW;
311 | }
312 | if (data > 80) {
313 | color = TFT_ORANGE;
314 | }
315 | if (data > 90) {
316 | color = TFT_RED;
317 | }
318 | sprite.fillCircle(indicatorPosX, offsetY, 3, color);
319 | break;
320 |
321 | case 3:
322 | if (data <= 70) {
323 | color = TFT_GREEN;
324 | }
325 | if (data > 70) {
326 | color = TFT_YELLOW;
327 | }
328 | if (data > 80) {
329 | color = TFT_ORANGE;
330 | }
331 | if (data > 90) {
332 | color = TFT_RED;
333 | }
334 | sprite.fillCircle(indicatorPosX, offsetY, 3, color);
335 | break;
336 |
337 | case 4:
338 | if (data <= 12) {
339 | color = TFT_GREEN;
340 | }
341 | if (data > 14) {
342 | color = TFT_YELLOW;
343 | }
344 | if (data > 17) {
345 | color = TFT_ORANGE;
346 | }
347 | if (data > 20) {
348 | color = TFT_RED;
349 | }
350 | sprite.fillCircle(indicatorPosX, offsetY, 3, color);
351 | break;
352 |
353 | case 5:
354 | if (data == 200) {
355 | textBuff = " OK ";
356 | color = TFT_GREEN;
357 | }
358 | else if (data == 400) {
359 | textBuff = " FAULT ";
360 | color = TFT_RED;
361 | }
362 | else {
363 | textBuff = " ";
364 | color = TFT_PURPLE;
365 | }
366 | sprite.fillRect(offsetX - 5, offsetY - (fontSize / 2), textBuff.length() * fontWidth + 10, fontSize - 2, TFT_BLACK);
367 | sprite.drawString(textBuff, offsetX, offsetY);
368 | sprite.drawRect(offsetX - 5, offsetY - (fontSize / 2), textBuff.length() * fontWidth + 10, fontSize - 2, color);
369 | break;
370 |
371 | default:
372 | break;
373 | }
374 | }
375 |
376 |
377 | void drawNtpTime() {
378 | char textBuff[9];
379 | struct tm timeinfo;
380 |
381 | char hour[3];
382 | char minute[3];
383 | char second[3];
384 |
385 | if(getLocalTime(& timeinfo)) {
386 | strftime(hour, sizeof(hour), "%H", & timeinfo);
387 | strftime(minute, sizeof(minute), "%M", & timeinfo);
388 | strftime(second, sizeof(second), "%S", & timeinfo);
389 | }
390 |
391 | sprintf(textBuff, "%s:%s:%s", String(hour), String(minute), String(second));
392 | sprite.drawString(textBuff, 120, 6);
393 | }
394 |
395 |
396 | void drawInitScreen() {
397 | String textBuff = " ";
398 |
399 | tft.setTextSize(fontSize);
400 |
401 | // Config current time
402 | configTzTime("KST-9", ntpServer);
403 |
404 | // Draw NTP TIme
405 | textBuff = "Time(Asia/Seoul) :";
406 | sprite.drawString(textBuff, 6, 6);
407 |
408 | // Draw modelbox
409 | sprite.drawString(mbName, mbPosX + 8, mbPosY + 10);
410 | sprite.drawRoundRect(mbPosX, mbPosY, 80, 20, 4, TFT_DARKGREY);
411 |
412 | // Draw current IP
413 | textBuff = "ESP32 IP(WiFi) : ";
414 | char ipChar[16];
415 | IPAddress ip = WiFi.localIP();
416 | sprintf(ipChar, "%u.%u.%u.%u", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24) & 0xFF);
417 | String ipStr(ipChar); //convert char array to string here.
418 | sprite.drawString(textBuff + ipStr, 6, 21);
419 |
420 | // Draw ping status
421 | // textBuff = "Ping : ";
422 | // sprite.drawString(textBuff, 100, 30);
423 |
424 | // Draw current CPU Temperature
425 | textBuff = "CPU1 Temp(C) : ";
426 | sprite.drawString(textBuff, cpu1TempPosX, cpu1TempPosY);
427 | drawMetricText(1, -1, cpu1TempPosX + metricsValueSpacing, cpu1TempPosY);
428 | textBuff = "CPU2 Temp(C) : ";
429 | sprite.drawString(textBuff, cpu2TempPosX, cpu2TempPosY);
430 | drawMetricText(1, -1, cpu2TempPosX + metricsValueSpacing, cpu2TempPosY);
431 |
432 | // Draw total CPU/RAM usage in percentage
433 | textBuff = "CPU Usage(%) : ";
434 | sprite.drawString(textBuff, cpuUsePosX, cpuUsePosY);
435 | drawMetricText(2, -1, cpuUsePosX + metricsValueSpacing, cpuUsePosY);
436 | textBuff = "RAM Usage(%) : ";
437 | sprite.drawString(textBuff, memoryPosX, memoryPosY);
438 | drawMetricText(3, -1, memoryPosX + metricsValueSpacing, memoryPosY);
439 |
440 | // Draw Load Average
441 | textBuff = "Load Avg 15m : ";
442 | sprite.drawString(textBuff, loadAvgPosX, loadAvgPosY);
443 | drawMetricText(4, -1, loadAvgPosX + metricsValueSpacing, loadAvgPosY);
444 |
445 | // Draw Overall Status
446 | textBuff = "Overall Status : ";
447 | sprite.drawString(textBuff, statusPosX, statusPosY);
448 | drawMetricText(5, -1, statusPosX + metricsValueSpacing, statusPosY);
449 |
450 | drawDiskGrid(diskPosX, diskPosY);
451 | drawGraph(graphPosX, graphPosY);
452 | // drawDiskStatusIndicator(0, 0);
453 |
454 | sprite.pushSprite(0, 0);
455 | }
456 |
457 |
458 | void parseCpuTemp(String data, cpuTemp_st *output) {
459 | int r=0,t=0;
460 |
461 | for(int i=0;i 1)
466 | {
467 | output[t].cpuNum = t;
468 | output[t].temperature = data.substring(r,i).toFloat();
469 | t++;
470 | }
471 | r = (i+1);
472 | }
473 | }
474 | }
475 |
476 |
477 | void parseDiskUsage(String data, diskUsage_st *output) {
478 | String temp[totalDiskCount];
479 |
480 | int idx = 0;
481 | int startIdx = 0;
482 | for(int i=0; i graphUpdateTimer + 10000) {
532 | // Draw actual graph
533 | drawAndSaveCpuGraph(cpuPercentage, graphPosX, graphPosY + graphHeight, graphWidth, graphHeight, true);
534 | graphUpdateTimer = millis();
535 | }
536 |
537 | cpuTemp_st cpuTempParsed[2];
538 | parseCpuTemp(cpuTemp, cpuTempParsed);
539 |
540 | drawMetricText(1, cpuTempParsed[0].temperature, cpu1TempPosX + metricsValueSpacing, cpu1TempPosY);
541 | drawMetricText(1, cpuTempParsed[1].temperature, cpu2TempPosX + metricsValueSpacing, cpu2TempPosY);
542 | drawMetricText(2, cpuPercentage, cpuUsePosX + metricsValueSpacing, cpuUsePosY);
543 | drawMetricText(3, memPercentage, memoryPosX + metricsValueSpacing, memoryPosY);
544 | drawMetricText(4, loadAvg15, loadAvgPosX + metricsValueSpacing, loadAvgPosY);
545 | drawMetricText(5, overallStatus, statusPosX + metricsValueSpacing, statusPosY);
546 |
547 | delay(50);
548 | }
549 | }
550 |
551 |
552 | void changeDiskIoStat() {
553 | if (diskUsageRW == 1) {
554 | diskUsageRW = 2;
555 | }
556 | else {
557 | diskUsageRW = 1;
558 | }
559 | drawDiskGrid(diskPosX, diskPosY);
560 | }
561 |
562 |
563 | void setup(void) {
564 | int timeoutCounter = 0;
565 | bool isWifiConnnected = false;
566 | // put your setup code here, to run once:
567 | Serial.begin(115200);
568 |
569 | pinMode(PIN_POWER_ON, OUTPUT);
570 | digitalWrite(PIN_POWER_ON, HIGH);
571 |
572 | tft.init();
573 | tft.setRotation(3);
574 | tft.setSwapBytes(true);
575 |
576 | sprite.createSprite(320, 170);
577 | sprite.setTextColor(TFT_WHITE,TFT_BLACK);
578 | sprite.setTextDatum(ML_DATUM);
579 |
580 | // Splash screen
581 | // sprite.fillScreen(TFT_BLACK);
582 | // sprite.setTextDatum(MC_DATUM);
583 | // sprite.drawString(productName + " " + productVer, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 3);
584 | // sprite.drawString(productType, SCREEN_WIDTH / 2, (SCREEN_HEIGHT / 3) + 20);
585 | // sprite.pushSprite(0, 0);
586 | // delay(2000);
587 |
588 | WiFi.begin(ssid, password);
589 | while (WiFi.status() != WL_CONNECTED) {
590 | Serial.println("Connecting to WiFi...");
591 | delay(500);
592 | timeoutCounter += 500;
593 | if (timeoutCounter > wifiTimeout) {
594 | Serial.println("WiFi timeout reached");
595 | break;
596 | }
597 | isWifiConnnected = true;
598 | }
599 | if (isWifiConnnected) {
600 | Serial.println("WiFi Connected");
601 | }
602 | else {
603 | Serial.println("WiFi Failed");
604 | }
605 |
606 | // tft.fillScreen(TFT_BLACK);
607 | // sprite.setTextDatum(ML_DATUM);
608 | // sprite.pushSprite(0, 0);
609 | // delay(500);
610 |
611 | button.attachClick(changeDiskIoStat);
612 |
613 | drawInitScreen();
614 | // drawPVGraph("Test");
615 |
616 | xTaskCreatePinnedToCore(
617 | drawGuiUpdateTask,
618 | "drawGuiUpdate",
619 | 10000,
620 | NULL,
621 | 1,
622 | &drawGuiUpdate,
623 | 0
624 | );
625 |
626 | Serial.println("Initialization Done");
627 | }
628 |
629 | void loop() {
630 | // put your main code here, to run repeatedly:
631 |
632 | /* Expected input format from Serial */
633 | /*
634 | Format(comma seperated) : cpu:,memory:,network:,disk:
635 | Value description :
636 | cpu -> float number within the range of 0.0 ~ 100.0
637 | memory ->
638 | network ->
639 | disk ->
640 | */
641 |
642 | if (millis() > ntpUpdateTimer + 1000) {
643 | drawNtpTime();
644 | ntpUpdateTimer = millis();
645 | }
646 |
647 | button.tick();
648 |
649 | while (Serial.available()) {
650 | char input = Serial.read();
651 |
652 | if (input == ':') {
653 | property = buff;
654 | buff = "";
655 | }
656 | else if (input == valueMarker || input == '\n') {
657 | value = buff;
658 | buff = "";
659 |
660 | Serial.println("=========================");
661 | Serial.println("property: " + property + ", value: " + value);
662 | Serial.println("=========================");
663 |
664 | if (property == "CPU") {
665 | cpuPercentage = value.toFloat();
666 | }
667 | else if (property == "LAVG") {
668 | loadAvg15 = value.toFloat();
669 | }
670 | else if (property == "MEM") {
671 | memPercentage = value.toFloat();
672 | }
673 | else if (property == "DISKIO") {
674 | diskUsage = value;
675 | }
676 | else if (property == "CPUTEMP") {
677 | cpuTemp = value;
678 | }
679 | else if (property == "OVERALL") {
680 | overallStatus = value.toInt();
681 | }
682 | else {
683 |
684 | }
685 | }
686 | else {
687 | buff += input;
688 | }
689 | }
690 | }
691 |
692 | // TFT Pin check
693 | #if PIN_LCD_WR != TFT_WR || \
694 | PIN_LCD_RD != TFT_RD || \
695 | PIN_LCD_CS != TFT_CS || \
696 | PIN_LCD_DC != TFT_DC || \
697 | PIN_LCD_RES != TFT_RST || \
698 | PIN_LCD_D0 != TFT_D0 || \
699 | PIN_LCD_D1 != TFT_D1 || \
700 | PIN_LCD_D2 != TFT_D2 || \
701 | PIN_LCD_D3 != TFT_D3 || \
702 | PIN_LCD_D4 != TFT_D4 || \
703 | PIN_LCD_D5 != TFT_D5 || \
704 | PIN_LCD_D6 != TFT_D6 || \
705 | PIN_LCD_D7 != TFT_D7 || \
706 | PIN_LCD_BL != TFT_BL || \
707 | TFT_BACKLIGHT_ON != HIGH || \
708 | 170 != TFT_WIDTH || \
709 | 320 != TFT_HEIGHT
710 | #error "Error! Please make sure is selected in "
711 | #endif
712 |
713 | #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,0,0)
714 | #error "The current version is not supported for the time being, please use a version below Arduino ESP32 3.0"
715 | #endif
--------------------------------------------------------------------------------
/test/README:
--------------------------------------------------------------------------------
1 |
2 | This directory is intended for PlatformIO Test Runner and project tests.
3 |
4 | Unit Testing is a software testing method by which individual units of
5 | source code, sets of one or more MCU program modules together with associated
6 | control data, usage procedures, and operating procedures, are tested to
7 | determine whether they are fit for use. Unit testing finds problems early
8 | in the development cycle.
9 |
10 | More information about PlatformIO Unit Testing:
11 | - https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
12 |
--------------------------------------------------------------------------------