mirror of
https://git.alemi.dev/pc-monitor.git
synced 2024-11-22 07:24:49 +01:00
feat: added display code, basic packet handling
added a super basic packet handling mechanism with a tiny buffer, allowing for easier expansion later on. Added display code, waiting for actual display to test, improved the cpu load script
This commit is contained in:
parent
e400459168
commit
def620665e
5 changed files with 246 additions and 79 deletions
|
@ -14,7 +14,8 @@ panic-halt = "0.2.0"
|
||||||
ufmt = "0.1.0"
|
ufmt = "0.1.0"
|
||||||
nb = "1"
|
nb = "1"
|
||||||
embedded-hal = "0.2.3"
|
embedded-hal = "0.2.3"
|
||||||
embedded-msgpack = "0.2"
|
sh1106 = "0.4"
|
||||||
|
embedded-graphics = "0.7"
|
||||||
|
|
||||||
[dependencies.arduino-hal]
|
[dependencies.arduino-hal]
|
||||||
git = "https://github.com/rahix/avr-hal"
|
git = "https://github.com/rahix/avr-hal"
|
||||||
|
|
|
@ -1,22 +1,38 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
from time import sleep
|
import sys
|
||||||
import struct
|
import struct
|
||||||
import serial
|
from time import sleep
|
||||||
|
|
||||||
|
import serial
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
def avg_usage_to_serial(dev:str):
|
def cpu_load_serial_driver(device:str, retry_interval:float=5.0):
|
||||||
port = serial.Serial(dev, baudrate=57600)
|
while True:
|
||||||
|
try:
|
||||||
|
port = serial.Serial(device, baudrate=57600)
|
||||||
|
avg_usage_to_serial(port)
|
||||||
|
except serial.SerialException as e:
|
||||||
|
print(f"[!] Could not connect to device: {str(e)}", file=sys.stderr)
|
||||||
|
sleep(retry_interval)
|
||||||
|
|
||||||
|
def avg_usage_to_serial(port:serial.Serial):
|
||||||
|
port.write(struct.pack("BB", 0, 0))
|
||||||
|
port.flush()
|
||||||
while True:
|
while True:
|
||||||
# Map float [0:100] to int [0:255], square it to put more values in the lower end, where led is more sensible
|
# Map float [0:100] to int [0:255], square it to put more values in the lower end, where led is more sensible
|
||||||
load = [ int(((x/100) **2) * 255) for x in psutil.cpu_percent(0.1, percpu=True) ] # mypy whines but percpu returns a list
|
load = [ int(((x/100) **2) * 255) for x in psutil.cpu_percent(0.05, percpu=True) ] # mypy whines but percpu returns a list
|
||||||
port.write(struct.pack("BBBB", *load))
|
try:
|
||||||
port.flush()
|
port.write(struct.pack("BBBBBB", 1, 4, *load))
|
||||||
|
port.flush()
|
||||||
|
except serial.SerialException as e:
|
||||||
|
print(f"[!] Failed writing payload to device: {str(e)}", file=sys.stderr)
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
|
||||||
if len(sys.argv) < 2:
|
if len(sys.argv) < 2:
|
||||||
print("[!] No device specified")
|
print("[!] No device specified")
|
||||||
else:
|
exit(-1)
|
||||||
avg_usage_to_serial(sys.argv[1])
|
|
||||||
|
cpu_load_serial_driver(sys.argv[1])
|
||||||
|
|
||||||
|
|
141
src/main.rs
141
src/main.rs
|
@ -1,91 +1,96 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
|
use packet::PacketId;
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
|
|
||||||
use embedded_hal::serial::Read;
|
use embedded_hal::serial::Read;
|
||||||
|
|
||||||
use arduino_hal::{simple_pwm::*, port::{Pin, mode::{PwmOutput, PullUp, Input}}, hal::port::{PD3, PB1, PB2, PB3, PD2}};
|
use arduino_hal::simple_pwm::{IntoPwmPin, Prescaler, Timer1Pwm, Timer2Pwm};
|
||||||
|
use sh1106::{prelude::GraphicsMode, Builder};
|
||||||
|
|
||||||
// TODO can I make it a generic "Pin" and use a slice?
|
mod packet;
|
||||||
struct FourLedDisplay {
|
mod utils;
|
||||||
led1: Pin<PwmOutput<Timer2Pwm>, PD3>,
|
|
||||||
led2: Pin<PwmOutput<Timer1Pwm>, PB1>,
|
|
||||||
led3: Pin<PwmOutput<Timer1Pwm>, PB2>,
|
|
||||||
led4: Pin<PwmOutput<Timer2Pwm>, PB3>,
|
|
||||||
button: Pin<Input<PullUp>, PD2>,
|
|
||||||
counter: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FourLedDisplay {
|
use crate::packet::PacketBuilder;
|
||||||
fn new(
|
use crate::utils::FourLedDisplay;
|
||||||
led1: Pin<PwmOutput<Timer2Pwm>, PD3>,
|
|
||||||
led2: Pin<PwmOutput<Timer1Pwm>, PB1>,
|
|
||||||
led3: Pin<PwmOutput<Timer1Pwm>, PB2>,
|
|
||||||
led4: Pin<PwmOutput<Timer2Pwm>, PB3>,
|
|
||||||
button: Pin<Input<PullUp>, PD2>,
|
|
||||||
) -> Self {
|
|
||||||
FourLedDisplay{
|
|
||||||
led1, led2, led3, led4, button,
|
|
||||||
counter: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, value: u8) {
|
|
||||||
match self.counter {
|
|
||||||
0 => self.led1.set_duty(value),
|
|
||||||
1 => self.led2.set_duty(value),
|
|
||||||
2 => self.led3.set_duty(value),
|
|
||||||
3 => self.led4.set_duty(value),
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
self.counter = (self.counter + 1) % 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(mut self) -> Self {
|
|
||||||
self.led1.enable();
|
|
||||||
self.led2.enable();
|
|
||||||
self.led3.enable();
|
|
||||||
self.led4.enable();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_reset(&self) -> bool { self.button.is_low() }
|
|
||||||
|
|
||||||
fn reset(&mut self) {
|
|
||||||
self.counter = 0;
|
|
||||||
self.led1.set_duty(0);
|
|
||||||
self.led2.set_duty(0);
|
|
||||||
self.led3.set_duty(0);
|
|
||||||
self.led4.set_duty(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[arduino_hal::entry]
|
#[arduino_hal::entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
// init board peripherals
|
// init board peripherals
|
||||||
let dp = arduino_hal::Peripherals::take().unwrap();
|
let dp = arduino_hal::Peripherals::take().unwrap();
|
||||||
let timer1 = Timer1Pwm::new(dp.TC1, Prescaler::Prescale8);
|
let timer1 = Timer1Pwm::new(dp.TC1, Prescaler::Direct);
|
||||||
let timer2 = Timer2Pwm::new(dp.TC2, Prescaler::Prescale8);
|
let timer2 = Timer2Pwm::new(dp.TC2, Prescaler::Direct);
|
||||||
let pins = arduino_hal::pins!(dp);
|
let pins = arduino_hal::pins!(dp);
|
||||||
let led1 = pins.d3.into_output().into_pwm(&timer2);
|
let mut led_load = pins.d6.into_output();
|
||||||
let led2 = pins.d9.into_output().into_pwm(&timer1);
|
|
||||||
let led3 = pins.d10.into_output().into_pwm(&timer1);
|
|
||||||
let led4 = pins.d11.into_output().into_pwm(&timer2);
|
|
||||||
let button = pins.d2.into_pull_up_input();
|
let button = pins.d2.into_pull_up_input();
|
||||||
|
let mut cpu_leds = FourLedDisplay::new(
|
||||||
|
pins.d3.into_output().into_pwm(&timer2),
|
||||||
|
pins.d9.into_output().into_pwm(&timer1),
|
||||||
|
pins.d10.into_output().into_pwm(&timer1),
|
||||||
|
pins.d11.into_output().into_pwm(&timer2),
|
||||||
|
);
|
||||||
|
let i2c = arduino_hal::i2c::I2c::new(
|
||||||
|
dp.TWI, pins.a4.into_pull_up_input(), pins.a5.into_pull_up_input(), 100000
|
||||||
|
);
|
||||||
let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
|
let mut serial = arduino_hal::default_serial!(dp, pins, 57600);
|
||||||
|
|
||||||
// prepare display struct
|
led_load.set_high();
|
||||||
let mut display = FourLedDisplay::new(led1, led2, led3, led4, button).init();
|
|
||||||
|
|
||||||
|
// prepare display
|
||||||
|
let mut display: GraphicsMode<_> = Builder::new().with_size(sh1106::prelude::DisplaySize::Display128x64).connect_i2c(i2c).into();
|
||||||
|
cpu_leds.set(1, 255);
|
||||||
|
|
||||||
|
display.init().unwrap();
|
||||||
|
cpu_leds.set(2, 255);
|
||||||
|
|
||||||
|
let mut flip : u8 = 0;
|
||||||
|
for x in 0..128 {
|
||||||
|
for y in 0..64 {
|
||||||
|
cpu_leds.set(3, if flip == 0 { 0 } else { 255 });
|
||||||
|
display.set_pixel(x, y, flip);
|
||||||
|
flip = !flip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cpu_leds.set(3, 255);
|
||||||
|
|
||||||
|
display.flush().unwrap();
|
||||||
|
cpu_leds.set(4, 255);
|
||||||
|
|
||||||
|
led_load.set_low();
|
||||||
|
|
||||||
|
let mut pkt_builder = PacketBuilder::new();
|
||||||
|
|
||||||
|
arduino_hal::delay_ms(25);
|
||||||
|
cpu_leds.set_all(0);
|
||||||
loop { // main loop
|
loop { // main loop
|
||||||
if display.should_reset() {
|
if button.is_low() { // If reset button is pressed, don't process serial bus
|
||||||
display.reset();
|
cpu_leds.set_all(0);
|
||||||
} else {
|
led_load.set_high();
|
||||||
match serial.read() {
|
continue;
|
||||||
Ok(value) => display.update(value),
|
}
|
||||||
Err(_) => {},
|
|
||||||
}
|
match serial.read() { // if there's a byte available
|
||||||
|
Ok(value) => {
|
||||||
|
led_load.set_high();
|
||||||
|
if let Some(pkt) = pkt_builder.update(value) { // update packet builder
|
||||||
|
match pkt.id { // if a packet is ready, match against its id
|
||||||
|
PacketId::Reset => {
|
||||||
|
cpu_leds.set_all(0);
|
||||||
|
},
|
||||||
|
PacketId::SetLedsPacket => {
|
||||||
|
if let Some(payload) = pkt.payload && payload.len() == 4 {
|
||||||
|
cpu_leds.set_many(payload[0], payload[1], payload[2], payload[3]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {}, // TODO log it?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
led_load.set_low();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
92
src/packet.rs
Normal file
92
src/packet.rs
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
const PACKET_BUFFER : usize = 32;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum PacketId {
|
||||||
|
Invalid = 0xFF,
|
||||||
|
Reset = 0x00,
|
||||||
|
SetLedsPacket = 0x01,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for PacketId {
|
||||||
|
fn from(x: u8) -> Self {
|
||||||
|
match x {
|
||||||
|
0 => PacketId::Reset,
|
||||||
|
1 => PacketId::SetLedsPacket,
|
||||||
|
_ => PacketId::Invalid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Packet<'a> {
|
||||||
|
pub id: PacketId,
|
||||||
|
pub payload: Option<&'a[u8]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PacketBuilderStep {
|
||||||
|
ID,
|
||||||
|
SIZE,
|
||||||
|
PAYLOAD,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PacketBuilder {
|
||||||
|
step: PacketBuilderStep,
|
||||||
|
id: PacketId,
|
||||||
|
size: u8,
|
||||||
|
index: usize,
|
||||||
|
buffer: [u8; PACKET_BUFFER],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
PacketBuilder {
|
||||||
|
step: PacketBuilderStep::ID,
|
||||||
|
id: PacketId::Invalid,
|
||||||
|
size: 0,
|
||||||
|
index: 0,
|
||||||
|
buffer: [0;PACKET_BUFFER],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, byte:u8) -> Option<Packet> {
|
||||||
|
let mut ret = None;
|
||||||
|
match self.step {
|
||||||
|
PacketBuilderStep::ID => {
|
||||||
|
let id = PacketId::from(byte);
|
||||||
|
match id {
|
||||||
|
PacketId::SetLedsPacket | PacketId::Reset => {
|
||||||
|
self.id = id;
|
||||||
|
self.step = PacketBuilderStep::SIZE;
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
// TODO log it somehow?
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PacketBuilderStep::SIZE => {
|
||||||
|
if byte as usize > PACKET_BUFFER {
|
||||||
|
self.step = PacketBuilderStep::ID;
|
||||||
|
// TODO log it somehow?
|
||||||
|
} else if byte == 0 {
|
||||||
|
// packet without payload
|
||||||
|
ret = Some(Packet{id: self.id, payload: None}); // jank zero size slice
|
||||||
|
self.step = PacketBuilderStep::ID;
|
||||||
|
} else {
|
||||||
|
self.size = byte;
|
||||||
|
self.index = 0;
|
||||||
|
self.step = PacketBuilderStep::PAYLOAD;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PacketBuilderStep::PAYLOAD => {
|
||||||
|
self.buffer[self.index] = byte;
|
||||||
|
self.index += 1;
|
||||||
|
if self.index >= self.size as usize {
|
||||||
|
ret = Some(Packet{id: self.id, payload: Some(&self.buffer[0..self.size as usize])});
|
||||||
|
self.step = PacketBuilderStep::ID;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
53
src/utils.rs
Normal file
53
src/utils.rs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
use arduino_hal::{simple_pwm::*, port::{Pin, mode::PwmOutput}, hal::port::{PD3, PB1, PB2, PB3}};
|
||||||
|
|
||||||
|
// TODO can I make it a generic "Pin" and use a slice?
|
||||||
|
pub struct FourLedDisplay {
|
||||||
|
led1: Pin<PwmOutput<Timer2Pwm>, PD3>,
|
||||||
|
led2: Pin<PwmOutput<Timer1Pwm>, PB1>,
|
||||||
|
led3: Pin<PwmOutput<Timer1Pwm>, PB2>,
|
||||||
|
led4: Pin<PwmOutput<Timer2Pwm>, PB3>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FourLedDisplay {
|
||||||
|
pub fn new(
|
||||||
|
mut led1: Pin<PwmOutput<Timer2Pwm>, PD3>,
|
||||||
|
mut led2: Pin<PwmOutput<Timer1Pwm>, PB1>,
|
||||||
|
mut led3: Pin<PwmOutput<Timer1Pwm>, PB2>,
|
||||||
|
mut led4: Pin<PwmOutput<Timer2Pwm>, PB3>,
|
||||||
|
) -> Self {
|
||||||
|
led1.enable();
|
||||||
|
led2.enable();
|
||||||
|
led3.enable();
|
||||||
|
led4.enable();
|
||||||
|
FourLedDisplay{
|
||||||
|
led1, led2, led3, led4,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, index:u8, value:u8) -> &mut Self {
|
||||||
|
match index {
|
||||||
|
1 => self.led1.set_duty(value),
|
||||||
|
2 => self.led2.set_duty(value),
|
||||||
|
3 => self.led3.set_duty(value),
|
||||||
|
4 => self.led4.set_duty(value),
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_all(&mut self, value:u8) -> &mut Self {
|
||||||
|
self.led1.set_duty(value);
|
||||||
|
self.led2.set_duty(value);
|
||||||
|
self.led3.set_duty(value);
|
||||||
|
self.led4.set_duty(value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_many(&mut self, first:u8, second:u8, third:u8, fourth:u8) -> &mut Self {
|
||||||
|
self.led1.set_duty(first);
|
||||||
|
self.led2.set_duty(second);
|
||||||
|
self.led3.set_duty(third);
|
||||||
|
self.led4.set_duty(fourth);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue