Вдохновившись постом о стрелочных Vu-метрах, показывающих загрузку CPU и использование RAM, решил сделать свой вариант. Не имея миниатюрных вольтметров, но имея покоривший своей ценой и простотой протокола 16х2 экран на базе контроллера hd44780 решил организовать визуализацию на нем. Как МК был выбран ланчпад MSP430G2, коих я купил жменьку, когда они были по $4.30. Ничего не мешает реализовать это все на любой arduino, нужно лишь поменять названия пинов.
Схема очень проста(взята из интернета):
Можно еще подключить подсветку и регулировку контраста, но думаю не составит труда разобраться с этим даже начинающим электронщикам(сужу по себе).
Программная часть состоит из двух частей, первая — скетч для Energia/Arduino. Все очень просто и понятно.
#include <LiquidCrystal.h> byte cpuByte; byte ramByte; unsigned long lastUpdateTime; LiquidCrystal lcd(P2_3, P2_4, P1_5, P2_0, P2_1, P2_2); // У меня подключение не такое как в схеме выше void setup() { Serial.begin(9600); lcd.begin(16, 2); lcd.clear(); } void loop() { if (Serial.available() == 2) { cpuByte = Serial.read(); ramByte = Serial.read(); lcd.setCursor(0, 0); lcd.print("CPU "); lcd.setCursor(4, 0); for (int i=0; i < cpuByte; i++) { lcd.write(255);} lcd.setCursor(0, 1); lcd.print("RAM "); lcd.setCursor(4, 1); for (int i=0; i < ramByte; i++) { lcd.write(255);} lastUpdateTime = millis(); } if (millis()-lastUpdateTime > 3000) { lcd.setCursor(0, 0); lcd.print(" DISCONNECTED "); lcd.setCursor(0, 1); lcd.print(" "); } delay(50); }
Логика проста, ждем два байта и выводим их значения в виде строки из символов с кодом 255. Прошлые значения затираю пробелами, ибо с lcd.clear() все мигает. Если более 3х секунд данные не приходят — DISCONNECTED.
Программирую я в основном под платформы 1Cх, а из компилируемых ЯП близок мне Delphi/Pascal больше всего. На Delphi версии XE2 и решил я писать вторую часть проекта. Так как планируется работа данного девайса на серверных ОС семейства Windows, ПО реализовано как служба.
unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs, Registry; type TFPService = class(TService) procedure ServiceExecute(Sender: TService); procedure ServiceCreate(Sender: TObject); private { Private declarations } public function GetServiceController: TServiceController; override; { Public declarations } end; var FPService: TFPService; const SystemBasicInformation = 0; SystemPerformanceInformation = 2; SystemTimeInformation = 3; type TPDWord = ^DWORD; TSystem_Basic_Information = packed record dwUnknown1: DWORD; uKeMaximumIncrement: ULONG; uPageSize: ULONG; uMmNumberOfPhysicalPages: ULONG; uMmLowestPhysicalPage: ULONG; uMmHighestPhysicalPage: ULONG; uAllocationGranularity: ULONG; pLowestUserAddress: Pointer; pMmHighestUserAddress: Pointer; uKeActiveProcessors: ULONG; bKeNumberProcessors: byte; bUnknown2: byte; wUnknown3: word; end; type TSystem_Time_Information = packed record liKeBootTime: LARGE_INTEGER; liKeSystemTime: LARGE_INTEGER; liExpTimeZoneBias: LARGE_INTEGER; uCurrentTimeZoneId: ULONG; dwReserved: DWORD; end; type TSystem_Performance_Information = packed record liIdleTime: LARGE_INTEGER; {LARGE_INTEGER} dwSpare: array[0..750] of DWORD; end; var NtQuerySystemInformation: function(infoClass: DWORD; buffer: Pointer; bufSize: DWORD; returnSize: TPDword): DWORD; stdcall = nil; SysBaseInfo: TSystem_Basic_Information; SysPerfInfo: TSystem_Performance_Information; SysTimeInfo: TSystem_Time_Information; status: Longint; {long} liOldIdleTime, liOldSystemTime: LARGE_INTEGER; dbSystemTime, dbIdleTime, dbIdleTimePercent: Double; hCom: THandle; DCB:TDCB; Errors, Bytes : Cardinal; TheStruct:TCOMSTAT; Timeouts: TCommTimeOuts; ComNum:string; implementation {$R *.DFM} procedure ServiceController(CtrlCode: DWord); stdcall; begin FPService.Controller(CtrlCode); end; function TFPService.GetServiceController: TServiceController; begin Result := ServiceController; end; Procedure getComNum; var Reg: TRegistry; begin Reg := TRegistry.Create(KEY_READ or KEY_WOW64_64KEY); try Reg.RootKey := HKEY_LOCAL_MACHINE; if Reg.OpenKey('\SOFTWARE\FPanel', false) then begin ComNum := Reg.ReadString('ComNum'); Reg.CloseKey; end; finally Reg.Free; end; end; Procedure InitCPUUsage; begin if @NtQuerySystemInformation = nil then NtQuerySystemInformation := GetProcAddress(GetModuleHandle('ntdll.dll'), 'NtQuerySystemInformation'); status := NtQuerySystemInformation(SystemBasicInformation, @SysBaseInfo, SizeOf(SysBaseInfo), nil); if status <> 0 then exit; end; function CPUUsed: integer; function Li2Double(x: LARGE_INTEGER): Double; begin Result := x.HighPart * 4.294967296E9 + x.LowPart end; begin result := 0; status := NtQuerySystemInformation(SystemTimeInformation, @SysTimeInfo, SizeOf(SysTimeInfo), nil); if status <> 0 then Exit; status := NtQuerySystemInformation(SystemPerformanceInformation, @SysPerfInfo, SizeOf(SysPerfInfo), nil); if status <> 0 then Exit; dbIdleTime := Li2Double(SysPerfInfo.liIdleTime) - Li2Double(liOldIdleTime); dbSystemTime := Li2Double(SysTimeInfo.liKeSystemTime) - Li2Double(liOldSystemTime); dbIdleTimePercent := dbIdleTime / dbSystemTime * 100; liOldIdleTime := SysPerfInfo.liIdleTime; liOldSystemTime := SysTimeInfo.liKeSystemTime; if (dbIdleTimePercent / SysBaseInfo.bKeNumberProcessors) < 0 then result := 0 else result := round(abs(100-(dbIdleTimePercent / SysBaseInfo.bKeNumberProcessors))); end; function RAMUsed: byte; var RamStats: TMemoryStatus; begin GlobalMemoryStatus(RamStats); result := ramStats.dwMemoryLoad; end; procedure con2com; var ComFN:string; begin CloseHandle(hCom); ComFN := '\\.\COM' + comNum; hCom := CreateFile(PWidechar(ComFN), GENERIC_WRITE or GENERIC_READ, 0, nil, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, 0); //Change COM1 to the comport you are using... if hCom = INVALID_HANDLE_VALUE then Exit; SetupComm(hCom,100,100); GetCommState(hCom,DCB); with DCB do begin BaudRate:=9600; ByteSize:=8; Parity:=NoParity; StopBits:=OneStopBit; end; SetCommState(hCom,DCB); GetCommTimeouts(hCom,Timeouts); with TimeOuts do begin ReadIntervalTimeout := 1; ReadTotalTimeoutMultiplier := 0; ReadTotalTimeoutConstant := 1; WriteTotalTimeoutMultiplier := 2; WriteTotalTimeoutConstant := 2; end; SetCommTimeouts(hCom,Timeouts); end; procedure TFPService.ServiceExecute(Sender: TService); var NumberWritten:LongWord; twoBytes:array[0..1] of byte; begin while not Terminated do begin twoBytes[0] := round(CPUUsed / 8.33); twoBytes[1] := round(RAMUsed / 8.33); if WriteFile(hCom, twoBytes, 2, NumberWritten, nil) = False then con2com; Sleep(250); ServiceThread.ProcessRequests(False); end; end; procedure TFPService.ServiceCreate(Sender: TObject); begin initCPUUsage; getComNum; con2com; end; end.
Для установки службы файл FP_servicel.ехе нужно запустить с параметром /install с правами администратора. После этого нужно создать строковый ключ ComNum в реестре, со значением номера COM-порта, на котором «висит» наш ланчпад по пути HKEY_LOCAL_MACHINE\SOFTWARE\FPanel, в моем случае значение ComNum = «12». После этого достаточно запустить службу FPService через оснастку «Службы». В следующий раз она будет стартовать сама.
Видео работы:
Архив Delphi проекта+готовый.ехе
Спасибо за внимание.
ссылка на оригинал статьи http://habrahabr.ru/post/206392/
Добавить комментарий