This commit is contained in:
William Clark 2023-11-22 11:35:15 +00:00
parent fc516343ed
commit ed5c03ffec
7 changed files with 184 additions and 49 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 63 KiB

View File

@ -2,9 +2,8 @@
Seven-channel multiple thermocouple reader and logger HAT for the Raspberry Pi.
![Alt](/.gitea/webview0.png "Web view demo")
This software supports the following thermocouple types:
- B
- E
- J
@ -15,6 +14,7 @@ This software supports the following thermocouple types:
- T
Features not yet implemented but planned:
- More database logging options than SQLite3
- Advanced filtering, incl. user-selectable filtering stages and sample sizes
- Several logging options at once, one local, one or more remote
@ -24,5 +24,6 @@ Features not yet implemented but planned:
- Request dumps from filter stages for noise study
- ... and more!
Note: Most thermocouples that have a coloured and a white lead, usually have the coloured one as the positive.
![Alt](/.gitea/webview0.png "Web view demo")

View File

@ -25,9 +25,11 @@ filter.samples = 20
[Channel_3]
type = 'T'
gain = 112.28821
[Channel_4]
type = 'K'
gain = 2.8129883e2
[Channel_6]
type = 'J'

10
main.go
View File

@ -28,29 +28,29 @@ func main() {
syscall.SIGHUP,
syscall.SIGQUIT)
config, err := config.Load()
cfg, err := config.Load()
if err != nil {
log.Fatalf("Error loading config: %v", err)
}
pcbPort, err = pcb.NewAdapter(config)
pcbPort, err = pcb.NewAdapter(cfg)
if err != nil {
log.Fatalf("Fatal error: %v\n", err)
}
defer pcbPort.Deinit()
corePort, err = core.NewAdapter(pcbPort, config)
corePort, err = core.NewAdapter(pcbPort, cfg)
if err != nil {
log.Fatalf("Error starting TH7 Adapter: %v\n", err)
}
dbPort, err = db.NewAdapter(corePort, config)
dbPort, err = db.NewAdapter(corePort, cfg)
if err != nil {
log.Fatalf("Fatal error: %v\n", err)
}
defer dbPort.Close()
webPort = web.NewGinAdapter(corePort, config.Board.Port)
webPort = web.NewGinAdapter(corePort, cfg)
go webPort.Run()
sig := <-kill

View File

@ -1,34 +1,91 @@
const vdd = document.getElementById('vdd');
const vref = document.getElementById('vref');
const vadj = document.getElementById('vadj');
const ts = document.getElementById('timestamp');
const table = document.getElementById('data');
const thermocoupleTypeTable = ["uV", "B", "E", "J", "K", "N", "R", "S", "T"];
let old_channel_count=0;
const elements = {
read: {
vdd: document.getElementById("vdd"),
vref: document.getElementById("vref"),
vadj: document.getElementById("vadj"),
ts: document.getElementById("timestamp"),
table: document.getElementById("data"),
},
config: {
table: document.getElementById("thermocouple"),
},
};
async function update () {
let readOldChannelCount = 0;
let configOldChannelCount = 0;
data = await fetch("/data").then((data)=>{return data.json()});
vdd.innerHTML = "V<sub>DD</sub>: " + parseFloat(data["ratio"]["pivdd"]);
vref.innerHTML = "V<sub>REF</sub>: " + parseFloat(data["ratio"]["vref"]);
vadj.innerHTML = "V<sub>ADJ</sub>: " + parseFloat(data["ratio"]["vadj"]);
ts.innerHTML = "Last updated: " + data["time"];
for (let i=0; i<old_channel_count; i++) {
table.deleteRow(-1);
async function fetchData(url) {
const response = await fetch(url);
return response.json();
}
old_channel_count =0;
data["channels"].forEach(e => {
let unit = e["unit"];
let value = e["value"];
let channel = e["id"];
function clearTableBody(tableType) {
const table = elements[tableType].table;
const tbody = table.querySelector("tbody");
let row = table.insertRow(-1);
row.insertCell(0).innerText = channel;
row.insertCell(1).innerText = value;
row.insertCell(2).innerText = unit;
old_channel_count++;
if (tbody) {
tbody.innerHTML = "";
} else {
// If tbody doesn't exist, recreate it
const newTbody = document.createElement("tbody");
table.appendChild(newTbody);
}
}
function updateTable(data, tableType, oldChannelCount, updateFunction) {
clearTableBody(tableType);
oldChannelCount = 0;
data.channels.forEach((channel) => {
updateFunction(channel, tableType);
oldChannelCount++;
});
}
update();
setInterval(update, 1000);
function updateReadTable(channel, tableType) {
const { value, id } = channel;
const row = elements[tableType].table.querySelector("tbody").insertRow(-1);
row.insertCell(0).innerText = id;
row.insertCell(1).innerText = value;
}
function updateConfigTable(channel, tableType) {
const { id, thermocouple, gain, offset, filter } = channel;
const row = elements[tableType].table.querySelector("tbody").insertRow(-1);
row.insertCell(0).innerText = id;
row.insertCell(1).innerText = thermocoupleTypeTable[thermocouple];
row.insertCell(2).innerText = gain;
row.insertCell(3).innerText = offset;
row.insertCell(4).innerText = filter.sample_size;
row.insertCell(5).innerText = filter.type;
}
function updateOthers(data) {
const { ratio, time } = data;
elements.read.ts.innerText = time;
elements.read.vref.innerHTML = `V<sub>REF</sub>: ${ratio.vref}`;
elements.read.vadj.innerHTML = `V<sub>ADJ</sub>: ${ratio.vadj}`;
elements.read.vdd.innerHTML = `V<sub>DD</sub>: ${ratio.pivdd}`;
}
async function updateRead() {
const data = await fetchData("/data");
updateTable(data, "read", readOldChannelCount, updateReadTable);
updateOthers(data);
}
async function updateConfig() {
const data = await fetchData("/config");
updateTable(data.config, "config", configOldChannelCount, updateConfigTable);
}
updateRead();
updateConfig();
setInterval(updateRead, 1000);
setInterval(updateConfig, 5000);

View File

@ -6,22 +6,87 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.title}}</title>
<style>
body {
font-family: 'Arial', sans-serif;
margin: 20px;
background-color: #f2f2f2;
}
h1 {
text-align: center;
color: #333;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
th, td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #4CAF50;
color: #fff;
}
#vdd, #vref, #vadj, #timestamp {
margin-top: 20px;
color: #333;
}
#thermocouple {
margin-top: 20px;
background-color: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#thermocouple td {
min-width: 6rem;
text-align: center;
}
</style>
</head>
<body>
<h1>TH7</h1>
<table id="data">
<thead>
<tr>
<th>Channel</th>
<th>Value</th>
<th>Unit</th>
</tr>
</thead>
</table>
<p id="vdd">V<sub>DD</sub>:</p>
<p id="vref">V<sub>REF</sub>:</p>
<p id="vadj">V<sub>ADJ</sub>:</p>
<p id="timestamp">Last updated:</p>
<h2>Thermocouple config</h2>
<table id="thermocouple">
<thead>
<tr>
<th>Channel</th>
<th>Type</th>
<th>Gain</th>
<th>Offset</th>
<th>Filter size</th>
<th>Filter type</th>
</tr>
</thead>
</table>
<script src="/assets/data-updater.js"></script>
</body>
</html>
{{ end }}

View File

@ -8,6 +8,7 @@ import (
"github.com/gin-gonic/gin"
"th7/data/config"
"th7/data/core"
"th7/ports"
)
@ -15,6 +16,7 @@ import (
type GinAdapter struct {
router *gin.Engine
corePort ports.CorePort
cfg config.Config
port int
}
@ -89,11 +91,18 @@ func (g *GinAdapter) IndexHandler(c *gin.Context) {
})
}
func NewGinAdapter(corePort ports.CorePort, port int) *GinAdapter {
func (g *GinAdapter) ConfigHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"config": g.cfg,
})
}
func NewGinAdapter(corePort ports.CorePort, cfg config.Config) *GinAdapter {
var adapter GinAdapter
adapter.corePort = corePort
adapter.port = port
adapter.port = cfg.Board.Port
adapter.cfg = cfg
//adapter.router = gin.Default()
adapter.router = gin.New()
@ -104,6 +113,7 @@ func NewGinAdapter(corePort ports.CorePort, port int) *GinAdapter {
adapter.router.GET("/channel/:id", adapter.ChannelByIDHandler)
adapter.router.GET("/time", adapter.TimestampHandler)
adapter.router.GET("/data", adapter.DataHandler)
adapter.router.GET("/config", adapter.ConfigHandler)
adapter.router.LoadHTMLGlob("./templates/*.tmpl")
adapter.router.Static("/assets", "./static")