Arduino与LCD1602结合Python打造高效桌面电脑性能监视器

文章目录

  • 简介
  • PC部分
  • 安装库
  • 定义协议
  • 代码
  • Arduino部分
  • 界面
  • 星期显示
  • 代码
  • 运行效果
  • 简介

    想做一个电脑性能监视器放在桌面上,遂在网上找合适的方案,但是基本都是基于AIDA64的LCD项目的,这样虽然方便定制但是因为协议都是厂家做好的所以下位机代码不是很好写。所以就想着能不能用Python自己写一个小程序来替代AIDA64,然后自己定义一个协议给下位机接收。于是就有了这个小项目。

    PC部分

    首先确认你安装了Python ,在命令行中输入

    python --version
    

    如果返回类似这样,那么你的电脑就已经有Python运行环境了

    Python 3.11.6
    

    如果没有请参照这篇文章:
    https://blog.csdn.net/qq_41604569/article/details/123400016
    我使用了以下两个库来获取硬件信息:

  • psutil:一个跨平台库,可获取 CPU、内存、磁盘、网络等系统资源的使用情况。
  • GPUtil:用于获取 GPU 信息的库,支持 NVIDIA 和 AMD 显卡。
  • 因为获取硬件温度需要下载额外软件所以暂时没写,上面两个库可以调取CPU、GPU、磁盘、网络等的详细参数,也够了。
    我的代码就写了四项参数,其他的就自己去看库文档吧。 :)

    安装库

    首先需要安装这两个库,在命令行中输入以下命令以安装:

    pip install psutil
    pip install gputil
    

    定义协议

    协议很简单,就是直接把相应的数据取整后通过串口发过去,每一项以“@”结尾方便分割字符串。
    后面因为加了年月日显示所以就在年月日后面加了“Z”以便区分;实际使用的时候取一个不常用的字母或者符号就行。

    输出样例:

    2025-03-17 ZCPU:0@GPU:2@RAM:66@GRAM:20@
    

    对应的数据拆分即为:

  • 日期:2025-03-17
  • CPU使用率:0%
  • GPU使用率:2%
  • RAM使用率:66%
  • 显存使用率:20%
  • 代码

    PCMonitor.py

    import serial
    import time
    import psutil
    from datetime import datetime
    
    # 初始化串口通信
    ser = serial.Serial('COM2', 115200, timeout=1)  # 请将 'COM3' 替换为您的 Arduino 所连接的实际端口
    time.sleep(2)  # 等待 Arduino 重置
    
    def get_current_time():
        # 获取当前时间并格式化为字符串
        now = datetime.now()
        current_time = now.strftime("%Y-%m-%d ")
        return current_time
    
    def get_system_stats():
        # 获取 CPU 使用率,并取整
        cpu_usage = int(psutil.cpu_percent(interval=1))
    
        # 获取内存使用率,并取整
        memory = psutil.virtual_memory()
        ram_usage = int(memory.percent)
    
        # 获取 GPU 使用率和显存使用情况,并取整
        try:
            import GPUtil
            gpus = GPUtil.getGPUs()
            if gpus:
                gpu = gpus[0]
                gpu_usage = int(gpu.load * 100)
                gpu_memory_used = int(gpu.memoryUsed)
                gpu_memory_total = int(gpu.memoryTotal)
            else:
                gpu_usage = 0
                gpu_memory_used = 0
        except ImportError:
            gpu_usage = 0
            gpu_memory_used = 0
    
        return cpu_usage, gpu_usage, ram_usage, gpu_memory_used, gpu_memory_total
    
    def format_data(cpu_usage, gpu_usage, ram_usage, gpu_memory_used, gpu_memory_total):
        gpu_memory_useage = int(gpu_memory_used / gpu_memory_total * 100)
        # 格式化数据字符串,确保所有值为整数
        data = f"CPU:{cpu_usage}@GPU:{gpu_usage}@RAM:{ram_usage}@GRAM:{gpu_memory_useage}@\n"
        return data
    
    try:
        times = get_current_time() + "Z"
        while True:
            # 获取系统统计数据
            cpu_usage, gpu_usage, ram_usage, gpu_memory_used, gpu_memory_total = get_system_stats()
    
            # 格式化数据
            data = times + format_data(cpu_usage, gpu_usage, ram_usage,  gpu_memory_used, gpu_memory_total)
            print(data)
            # 发送数据到 Arduino
            ser.write(data.encode())
    
            # 等待一段时间后再次发送
            time.sleep(1)
    
    except KeyboardInterrupt:
        print("程序已终止")
    
    finally:
        # 关闭串口通信
        ser.close()
    
    

    其中的串口号和波特率自己改成下位机对应的参数即可。

    在命令行中输入以下命令即可开始运行:

    cd ./path(代码保存的地址)
    python PCMonitor.py
    

    运行效果:

    Arduino部分

    PC端代码写好了之后,Arduino这边要实现的功能就很简单了。
    我是用的4线的LCD1602,走的I2C协议所以直接用的第三方库;如果是用的普通LCD的需要修改代码,不能直接用。
    显示屏长这样,非常的省针脚:

    显示屏接线(以Arduino Uno为例):

  • GND > GND
  • VCC > 5V
  • SCL > SCL 或者 A5
  • SDA > SDA 或者 A4
  • 界面

    主要写了两种界面,一种是纯显示数据的版本,可以同时显示4个数据;另一种是增加了日期和星期的显示,只能同时显示2个数据。
    (简单打了个盒子装起来,比较丑还请见谅)
    在未运行程序的时候,显示屏会显示待机界面:

    纯数据界面如图所示:

    显示日期界面如图所示:

    星期显示

    星期的显示比较暴力,直接使用蔡勒公式算出星期数。
    其实可以不用switch判断直接用数组,但是我懒得改了(真的);
    算法代码如下:

    //使用Zeller算法计算星期数
    void getWeekday(int y, int m, int d) 
    {
        if (m < 3) 
        {  // 1 月 & 2 月视为上一年的 13 月 & 14 月
            m += 12;
            y -= 1;
        }
        
        int J = y / 100;  // 世纪数
        int ye = y - 100 * J;
        int weekday = int(J / 4) - 2 * J + ye + int(ye / 4) + (26 *(m + 1) / 10) + d - 1;
        
        // Zeller 公式返回
        int week = (weekday % 7 + 7) % 7;
    
        switch(week)
        {
          case 0:
            Weeks = "Sun";
            break;
          case 1:
            Weeks = "Mon";
            break;
          case 2:
            Weeks = "Tue";
            break;
          case 3:
            Weeks = "Wed";
            break;
          case 4:
            Weeks = "Thr";
            break;
          case 5:
            Weeks = "Fri";
            break;
          case 6:
            Weeks = "Sat";
            break;
        }
    }
    

    代码

    在运行代码前,需要安装“LiquidCrystal_I2C”这个库;不装程序可能编译失败或无法执行功能。

    PCMonitor.ino

    #include <Wire.h>
    #include <LiquidCrystal_I2C.h>
    #include <String.h>
    
    // 定义串口接收缓冲区大小
    #define BUFFER 128
    
    // 初始化 I2C LCD,地址 0x27,20 列 4 行
    LiquidCrystal_I2C lcd(0x27, 20, 4);
    
    // 用于存储串口接收的数据
    String Rec_Data = "";
    
    // 用于存储 CPU、GPU、RAM、显存的使用率
    String cpuu;
    String gpuu;
    String ramu;
    String gramu;
    String cput;
    String gput;
    
    String year, month, day;
    int y,m,d;
    
    String Weeks;
    String Date;
    
    // 函数声明
    void SerialRec();  // 读取串口数据
    void SerialExp();  // 解析串口数据
    void ShowDetails(String D1, String D2, String D3, String D4); // 显示信息到 LCD
    void ShowTime_Details(String date, String D1, String D2); //显示时间和两个信息
    void getWeekday(int y, int m, int d); //计算星期
    
    void setup() 
    {
      Serial.begin(115200); // 初始化串口通信
      lcd.init();           // 初始化 LCD
      lcd.backlight();      // 打开 LCD 背光
      lcd.setCursor(0, 0);
      lcd.print("PC Monitor V1.5");  //待机信息
      lcd.setCursor(0, 1);
      lcd.print("StandBy...");
      delay(100);
    }
    
    void loop() 
    {
      SerialRec();          // 读取串口数据
      delay(100);
      // ShowDetails(cpuu, gpuu, ramu, gramu); // 更新 LCD 显示
      getWeekday(y, m, d);
      ShowTime_Details(Date, cpuu, ramu);
      delay(100);
    }
    
    // 读取串口数据
    void SerialRec()
    {
      if(Serial.available() > 0) // 检查是否有数据可读
      {
        lcd.clear();
        while(Serial.available() > 0) // 读取完整的输入数据
        {
          char data = Serial.read(); // 读取一个字符
          Rec_Data += data; // 拼接到字符串
          if(data == 'Z')
          {
            Date = Rec_Data.substring(0, 10);
            year = Rec_Data.substring(0, 4);
            month = Rec_Data.substring(5, 7);
            day = Rec_Data.substring(8, 10);
    
            y = year.toInt();
            m = month.toInt();
            d = day.toInt();
          }
          else if(data == '\n') // 如果遇到换行符,表示数据接收完毕
          {
            SerialExp(); // 解析数据
            delay(100);
            Rec_Data = ""; // 清空缓冲区
          }
    
          if(Rec_Data.length() >= BUFFER) // 防止缓冲区溢出
          {
            Rec_Data = "";
          }
        }
      }
    }
    
    // 解析串口接收的数据
    void SerialExp()
    {
      int cpuUseage = Rec_Data.indexOf("CPU:");
      int gpuUseage = Rec_Data.indexOf("GPU:");
      int ramUseage = Rec_Data.indexOf("RAM:");
      int gramUseage = Rec_Data.indexOf("GRAM:");
      // int cpuTemp = Rec_Data.indexOf("CPUT:");
      // int gpuTemp = Rec_Data.indexOf("GPUT:");
    
      // 提取 CPU、GPU、RAM 和显存的使用率,并添加百分号
      cpuu = Rec_Data.substring(cpuUseage, Rec_Data.indexOf('@', cpuUseage)) + "%";
      gpuu = Rec_Data.substring(gpuUseage, Rec_Data.indexOf('@', gpuUseage)) + "%";
      ramu = Rec_Data.substring(ramUseage, Rec_Data.indexOf('@', ramUseage)) + "%";
      gramu = Rec_Data.substring(gramUseage, Rec_Data.indexOf('@', gramUseage)) + "%";
      
      // cput = Rec_Data.substring(cpuTemp, Rec_Data.indexOf('@', cpuTemp)) + "C";
      // gput = Rec_Data.substring(gpuTemp, Rec_Data.indexOf('@', gpuTemp)) + "C";
    }
    
    // 在 LCD 上显示 CPU、GPU、RAM 和显存使用情况
    void ShowDetails(String D1, String D2, String D3, String D4)
    {
      lcd.setCursor(0, 0);
      lcd.print(D1);  // 显示 CPU 使用率
      lcd.setCursor(0, 1);
      lcd.print(D2);  // 显示 GPU 使用率
      lcd.setCursor(8, 0);
      lcd.print(D3);  // 显示 RAM 使用率
      lcd.setCursor(8, 1);
      lcd.print(D4);  // 显示 显存 使用率
      delay(10);
    }
    
    void ShowTime_Details(String date, String D1, String D2)
    {
      lcd.setCursor(0, 0);
      lcd.print(date);
      lcd.setCursor(13,0);
      lcd.print(Weeks);
      lcd.setCursor(0, 1);
      lcd.print(D1);
      lcd.setCursor(8, 1);
      lcd.print(D2);
    }
    
    //使用Zeller算法计算星期数
    void getWeekday(int y, int m, int d) 
    {
        if (m < 3) 
        {  // 1 月 & 2 月视为上一年的 13 月 & 14 月
            m += 12;
            y -= 1;
        }
        
        int J = y / 100;  // 世纪数
        int ye = y - 100 * J;
        int weekday = int(J / 4) - 2 * J + ye + int(ye / 4) + (26 *(m + 1) / 10) + d - 1;
        
        // Zeller 公式返回
        int week = (weekday % 7 + 7) % 7;
    
        switch(week)
        {
          case 0:
            Weeks = "Sun";
            break;
          case 1:
            Weeks = "Mon";
            break;
          case 2:
            Weeks = "Tue";
            break;
          case 3:
            Weeks = "Wed";
            break;
          case 4:
            Weeks = "Thr";
            break;
          case 5:
            Weeks = "Fri";
            break;
          case 6:
            Weeks = "Sat";
            break;
        }
    }
    
    

    运行效果

    在上电后应当显示待机界面,并且在PC端运行Python程序后自动重置,然后开始显示指定的信息。
    信息更新的频率可以自己设定,但是不建议低于0.1s每次,因为显示屏的刷新率可能跟不上……

    本项目仅经过本人自己电脑环境验证,不一定100%跑通。
    如有不足之处欢迎各位批评指正~

    作者:Misakazunami

    物联沃分享整理
    物联沃-IOTWORD物联网 » Arduino与LCD1602结合Python打造高效桌面电脑性能监视器

    发表回复