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"
|
||||
nb = "1"
|
||||
embedded-hal = "0.2.3"
|
||||
embedded-msgpack = "0.2"
|
||||
sh1106 = "0.4"
|
||||
embedded-graphics = "0.7"
|
||||
|
||||
[dependencies.arduino-hal]
|
||||
git = "https://github.com/rahix/avr-hal"
|
||||
|
|
|
@ -1,22 +1,38 @@
|
|||
#!/usr/bin/env python
|
||||
from time import sleep
|
||||
import sys
|
||||
import struct
|
||||
import serial
|
||||
from time import sleep
|
||||
|
||||
import serial
|
||||
import psutil
|
||||
|
||||
def avg_usage_to_serial(dev:str):
|
||||
port = serial.Serial(dev, baudrate=57600)
|
||||
def cpu_load_serial_driver(device:str, retry_interval:float=5.0):
|
||||
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:
|
||||
# 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
|
||||
port.write(struct.pack("BBBB", *load))
|
||||
port.flush()
|
||||
load = [ int(((x/100) **2) * 255) for x in psutil.cpu_percent(0.05, percpu=True) ] # mypy whines but percpu returns a list
|
||||
try:
|
||||
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__":
|
||||
import sys
|
||||
if len(sys.argv) < 2:
|
||||
print("[!] No device specified")
|
||||
else:
|
||||
avg_usage_to_serial(sys.argv[1])
|
||||
exit(-1)
|
||||
|
||||
cpu_load_serial_driver(sys.argv[1])
|
||||
|
||||
|
|
141
src/main.rs
141
src/main.rs
|
@ -1,91 +1,96 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use packet::PacketId;
|
||||
use panic_halt as _;
|
||||
|
||||
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?
|
||||
struct 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>,
|
||||
counter: u8,
|
||||
}
|
||||
mod packet;
|
||||
mod utils;
|
||||
|
||||
impl FourLedDisplay {
|
||||
fn new(
|
||||
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);
|
||||
}
|
||||
}
|
||||
use crate::packet::PacketBuilder;
|
||||
use crate::utils::FourLedDisplay;
|
||||
|
||||
#[arduino_hal::entry]
|
||||
fn main() -> ! {
|
||||
// init board peripherals
|
||||
let dp = arduino_hal::Peripherals::take().unwrap();
|
||||
let timer1 = Timer1Pwm::new(dp.TC1, Prescaler::Prescale8);
|
||||
let timer2 = Timer2Pwm::new(dp.TC2, Prescaler::Prescale8);
|
||||
let timer1 = Timer1Pwm::new(dp.TC1, Prescaler::Direct);
|
||||
let timer2 = Timer2Pwm::new(dp.TC2, Prescaler::Direct);
|
||||
let pins = arduino_hal::pins!(dp);
|
||||
let led1 = pins.d3.into_output().into_pwm(&timer2);
|
||||
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 mut led_load = pins.d6.into_output();
|
||||
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);
|
||||
|
||||
// prepare display struct
|
||||
let mut display = FourLedDisplay::new(led1, led2, led3, led4, button).init();
|
||||
led_load.set_high();
|
||||
|
||||
// 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
|
||||
if display.should_reset() {
|
||||
display.reset();
|
||||
} else {
|
||||
match serial.read() {
|
||||
Ok(value) => display.update(value),
|
||||
Err(_) => {},
|
||||
}
|
||||
if button.is_low() { // If reset button is pressed, don't process serial bus
|
||||
cpu_leds.set_all(0);
|
||||
led_load.set_high();
|
||||
continue;
|
||||
}
|
||||
|
||||
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