Compare commits

..

2 Commits

Author SHA1 Message Date
5bea19be36 flake: Add pytec runnables to devShell
Makes it possible to directly run `plot` and `autotune` in the shell.
2024-11-04 16:56:29 +08:00
a7aa78a608 flake: Introduce pytec to PYTHONPATH in devShell
For easier testing of pytec client code in the shell.
2024-11-04 16:26:51 +08:00
3 changed files with 34 additions and 100 deletions

View File

@ -2,22 +2,14 @@
description = "Firmware for the Sinara 8451 Thermostat";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
inputs.rust-overlay = {
inputs.rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
outputs =
{
self,
nixpkgs,
rust-overlay,
}:
outputs = { self, nixpkgs, rust-overlay }:
let
pkgs = import nixpkgs {
system = "x86_64-linux";
overlays = [ (import rust-overlay) ];
};
pkgs = import nixpkgs { system = "x86_64-linux"; overlays = [ (import rust-overlay) ]; };
rust = pkgs.rust-bin.stable."1.66.0".default.override {
extensions = [ "rust-src" ];
@ -33,7 +25,7 @@
version = "0.0.0";
src = self;
cargoLock = {
cargoLock = {
lockFile = ./Cargo.lock;
outputHashes = {
"stm32-eth-0.2.0" = "sha256-48RpZgagUqgVeKm7GXdk3Oo0v19ScF9Uby0nTFlve2o=";
@ -58,17 +50,6 @@
auditable = false;
};
pytec = pkgs.python3Packages.buildPythonPackage {
pname = "pytec";
version = "0.0.0";
src = "${self}/pytec";
propagatedBuildInputs = with pkgs.python3Packages; [
numpy
matplotlib
];
};
pytec-dev-wrappers = pkgs.runCommandNoCC "pytec-dev-wrappers" { } ''
mkdir -p $out/bin
for program in ${self}/pytec/*.py; do
@ -81,10 +62,9 @@
fi
done
'';
in
{
in {
packages.x86_64-linux = {
inherit thermostat pytec;
inherit thermostat;
default = thermostat;
};
@ -94,25 +74,16 @@
devShells.x86_64-linux.default = pkgs.mkShellNoCC {
name = "thermostat-dev-shell";
packages =
with pkgs;
[
rust
llvm
openocd
dfu-util
rlwrap
pytec-dev-wrappers
]
++ (with python3Packages; [
numpy
matplotlib
packages = with pkgs; [
rust llvm
openocd dfu-util rlwrap
pytec-dev-wrappers
] ++ (with python3Packages; [
numpy matplotlib
]);
shellHook = ''
export PYTHONPATH=`git rev-parse --show-toplevel`/pytec:$PYTHONPATH
export PYTHONPATH=`pwd`/pytec:$PYTHONPATH
'';
};
formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixfmt-rfc-style;
};
}
}

View File

@ -1,4 +1,3 @@
import time
from pytec.client import Client
tec = Client() #(host="localhost", port=6667)
@ -8,6 +7,5 @@ print(tec.get_pid())
print(tec.get_output())
print(tec.get_postfilter())
print(tec.get_b_parameter())
while True:
print(tec.get_report())
time.sleep(0.05)
for data in tec.report_mode():
print(data)

View File

@ -1,7 +1,7 @@
import socket
import json
import logging
import time
class CommandError(Exception):
pass
@ -12,10 +12,6 @@ class Client:
self._lines = [""]
self._check_zero_limits()
def disconnect(self):
self._socket.shutdown(socket.SHUT_RDWR)
self._socket.close()
def _check_zero_limits(self):
output_report = self.get_output()
for output_channel in output_report:
@ -114,18 +110,18 @@ class Client:
"""
return self._get_conf("postfilter")
def get_report(self):
"""Obtain one-time report on measurement values
def report_mode(self):
"""Start reporting measurement values
Example of yielded data::
{'channel': 0,
'time': 2302524,
'interval': 0.12
'adc': 0.6199188965423515,
'sens': 6138.519310282602,
'temperature': 36.87032392655527,
'pid_engaged': True,
'i_set': 2.0635816680889123,
'vref': 1.494,
'dac_value': 2.527790834044456,
'dac_feedback': 2.523,
'i_tec': 2.331,
@ -133,19 +129,16 @@ class Client:
'tec_u_meas': 2.5340000000000003,
'pid_output': 2.067581958092247}
"""
return self._get_conf("report")
def get_ipv4(self):
"""Get the IPv4 settings of the Thermostat"""
return self._command("ipv4")
def get_fan(self):
"""Get Thermostat current fan settings"""
return self._command("fan")
def get_hwrev(self):
"""Get Thermostat hardware revision"""
return self._command("hwrev")
while True:
self._socket.sendall("report\n".encode('utf-8'))
line = self._read_line()
if not line:
break
try:
yield json.loads(line)
except json.decoder.JSONDecodeError:
pass
time.sleep(0.05)
def set_param(self, topic, channel, field="", value=""):
"""Set configuration parameters
@ -170,38 +163,10 @@ class Client:
self.set_param("pid", channel, "target", value=target)
self.set_param("output", channel, "pid")
def save_config(self, channel=""):
def save_config(self):
"""Save current configuration to EEPROM"""
self._command("save", channel)
if channel != "":
self._read_line() # read the extra {}
self._command("save")
def load_config(self, channel=""):
def load_config(self):
"""Load current configuration from EEPROM"""
self._command("load", channel)
if channel != "":
self._read_line() # read the extra {}
def reset(self):
"""Reset the device"""
self._socket.sendall("reset".encode("utf-8"))
self.disconnect() # resetting ends the TCP session, disconnect anyway
def enter_dfu_mode(self):
"""Reset device and enters USB device firmware update (DFU) mode"""
self._socket.sendall("dfu".encode("utf-8"))
self.disconnect() # resetting ends the TCP session, disconnect anyway
def set_ipv4(self, address, netmask, gateway=""):
"""Configure IPv4 address, netmask length, and optional default gateway"""
self._command("ipv4", f"{address}/{netmask}", gateway)
def set_fan(self, power=None):
"""Set fan power with values from 1 to 100. If omitted, set according to fcurve"""
if power is None:
power = "auto"
self._command("fan", power)
def set_fcurve(self, a=1.0, b=0.0, c=0.0):
"""Set fan controller curve coefficients"""
self._command("fcurve", a, b, c)
self._command("load")