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. 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: This software supports the following thermocouple types:
- B - B
- E - E
- J - J
@ -15,6 +14,7 @@ This software supports the following thermocouple types:
- T - T
Features not yet implemented but planned: Features not yet implemented but planned:
- More database logging options than SQLite3 - More database logging options than SQLite3
- Advanced filtering, incl. user-selectable filtering stages and sample sizes - Advanced filtering, incl. user-selectable filtering stages and sample sizes
- Several logging options at once, one local, one or more remote - 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 - Request dumps from filter stages for noise study
- ... and more! - ... and more!
Note: Most thermocouples that have a coloured and a white lead, usually have the coloured one as the positive. 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] [Channel_3]
type = 'T' type = 'T'
gain = 112.28821
[Channel_4] [Channel_4]
type = 'K' type = 'K'
gain = 2.8129883e2
[Channel_6] [Channel_6]
type = 'J' type = 'J'

10
main.go
View File

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

View File

@ -1,34 +1,91 @@
const vdd = document.getElementById('vdd'); const thermocoupleTypeTable = ["uV", "B", "E", "J", "K", "N", "R", "S", "T"];
const vref = document.getElementById('vref');
const vadj = document.getElementById('vadj');
const ts = document.getElementById('timestamp');
const table = document.getElementById('data');
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()}); async function fetchData(url) {
vdd.innerHTML = "V<sub>DD</sub>: " + parseFloat(data["ratio"]["pivdd"]); const response = await fetch(url);
vref.innerHTML = "V<sub>REF</sub>: " + parseFloat(data["ratio"]["vref"]); return response.json();
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++) { function clearTableBody(tableType) {
table.deleteRow(-1); const table = elements[tableType].table;
const tbody = table.querySelector("tbody");
if (tbody) {
tbody.innerHTML = "";
} else {
// If tbody doesn't exist, recreate it
const newTbody = document.createElement("tbody");
table.appendChild(newTbody);
} }
old_channel_count =0; }
data["channels"].forEach(e => { function updateTable(data, tableType, oldChannelCount, updateFunction) {
let unit = e["unit"]; clearTableBody(tableType);
let value = e["value"];
let channel = e["id"];
let row = table.insertRow(-1); oldChannelCount = 0;
row.insertCell(0).innerText = channel;
row.insertCell(1).innerText = value; data.channels.forEach((channel) => {
row.insertCell(2).innerText = unit; updateFunction(channel, tableType);
old_channel_count++; 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

@ -5,23 +5,88 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ .title }}</title> <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> </head>
<body> <body>
<h1>TH7</h1> <h1>TH7</h1>
<table id="data"> <table id="data">
<thead>
<tr> <tr>
<th>Channel</th> <th>Channel</th>
<th>Value</th> <th>Value</th>
<th>Unit</th>
</tr> </tr>
</thead>
</table> </table>
<p id="vdd">V<sub>DD</sub>:</p> <p id="vdd">V<sub>DD</sub>:</p>
<p id="vref">V<sub>REF</sub>:</p> <p id="vref">V<sub>REF</sub>:</p>
<p id="vadj">V<sub>ADJ</sub>:</p> <p id="vadj">V<sub>ADJ</sub>:</p>
<p id="timestamp">Last updated:</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> <script src="/assets/data-updater.js"></script>
</body> </body>
</html> </html>
{{ end }} {{ end }}

View File

@ -8,6 +8,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"th7/data/config"
"th7/data/core" "th7/data/core"
"th7/ports" "th7/ports"
) )
@ -15,6 +16,7 @@ import (
type GinAdapter struct { type GinAdapter struct {
router *gin.Engine router *gin.Engine
corePort ports.CorePort corePort ports.CorePort
cfg config.Config
port int 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 var adapter GinAdapter
adapter.corePort = corePort adapter.corePort = corePort
adapter.port = port adapter.port = cfg.Board.Port
adapter.cfg = cfg
//adapter.router = gin.Default() //adapter.router = gin.Default()
adapter.router = gin.New() 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("/channel/:id", adapter.ChannelByIDHandler)
adapter.router.GET("/time", adapter.TimestampHandler) adapter.router.GET("/time", adapter.TimestampHandler)
adapter.router.GET("/data", adapter.DataHandler) adapter.router.GET("/data", adapter.DataHandler)
adapter.router.GET("/config", adapter.ConfigHandler)
adapter.router.LoadHTMLGlob("./templates/*.tmpl") adapter.router.LoadHTMLGlob("./templates/*.tmpl")
adapter.router.Static("/assets", "./static") adapter.router.Static("/assets", "./static")