Одним из моих любимых хабов всегда был DIY; Я и сам не прочь сделать что-то своими руками.
Но поскольку я больше программист, а не электронщик, то «прототипы», которые я делаю, всегда непрезентабельны.
Это устройство не является исключением.
Код также не аккуратен, потому что.
это скорее доказательство концепции, чем коммерческое решение.
Тем не менее, думаю, этот пост будет полезен, и даже найдутся те, кто повторит эту поделку.
Вдохновленный публикацией об индикаторах Vu, показывающих загрузку процессора и использование оперативной памяти, я решил сделать свою собственную версию.
Без миниатюрных вольтметров, но с экраном 16х2 на базе контроллера, который подкупил своей ценой и простотой протокола.
hd44780 Я решил организовать на нем визуализацию.
Как «МК» выбирал стартовую площадку MSP430G2 , которые я купил кучу, когда они стоили 4,30 доллара.
Ничто не мешает вам реализовать всё это на любом ардуино, нужно лишь изменить названия пинов.
Схема очень простая (взята из интернета):
Также можно подключить подсветку и регулировку контрастности, но думаю разобраться в этом не составит труда даже начинающим электронщикам (судя по себе).
Программная часть состоит из двух частей, первая — скетч для Energia/Arduino. Все очень просто и понятно.
Эскиз
Логика проста, ждем два байта и выводим их значения в виде строки символов с кодом 255. Предыдущие значения я затираю пробелами, потому что при lcd.clear() все мигает. Если данные не приходят более 3 секунд - ОТКЛЮЧЕНО.#include <LiquidCrystal.h> byte cpuByte; byte ramByte; unsigned long lastUpdateTime; unsigned long disconntime; int difer = 0; 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(); lcd.print("FPanel V1.2"); } 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);} for (int i=cpuByte; i < 16; i++) { lcd.write(32);} lcd.setCursor(0, 1); lcd.print("RAM "); lcd.setCursor(4, 1); for (int i=0; i < ramByte; i++) { lcd.write(255);} for (int i=ramByte; i < 16; i++) { lcd.write(32);} lastUpdateTime = millis(); } if (millis()-lastUpdateTime > 3000) { lcd.setCursor(0, 0); lcd.print(" CONNECTING "); lcd.setCursor(0, 1); difer = (millis()-disconntime-3000) / 250; if (difer > 16) {disconntime = millis()-3000;} for (int i=0; i < difer; i++) {lcd.write(255);} for (int i=difer; i < 16; i++) {lcd.write(32);} delay(10); } delay(50); }
Программирую в основном для платформ 1Сх, а среди компилируемых языков мне ближе всего Delphi/Pascal. Вторую часть проекта я решил написать на Delphi версии XE2. Поскольку данное устройство планируется для работы на серверных операционных системах семейства Windows, программное обеспечение реализовано как сервис.
Исходник в Delphi 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);
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_service.exe необходимо запустить с параметром /install с правами администратора.
После этого нужно создать в реестре строковый ключ ComNum, со значением номера COM-порта, на котором «висит» наша панель запуска по пути HKEY_LOCAL_MACHINE\SOFTWARE\FPanel, в моем случае значение ComNum = «12».
После этого просто запустите службу FPService через оснастку «Службы».
В следующий раз она начнет сама.
Служба автоматически переподключается к панели запуска и никак не мешает работе других программ.
Видео работы: Архив проекта Delphi + Ready.exe Спасибо за внимание.
UPD: После 5 дней тестирования никаких проблем обнаружено не было.
UPD: Обновил скетч, улучшил алгоритм рендеринга, плюс добавил анимацию ожидания соединения.
UPD: Обновил ссылку на архив проекта.
Теги: #MSP430 #delphi #diy #delphi
-
Финальная Версия Вивальди 1.5.
19 Oct, 24 -
Исследование Кортежей В C# 7
19 Oct, 24 -
Реляционный Подход
19 Oct, 24 -
Жк-Мониторы, Проблема Выбора
19 Oct, 24 -
3 Легендарных Харизматика
19 Oct, 24