diff --git a/.gitea/webview0.png b/.gitea/webview0.png index 4c77b3c..d9f4f89 100644 Binary files a/.gitea/webview0.png and b/.gitea/webview0.png differ diff --git a/README.md b/README.md index 8bfc7ca..1bd9418 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,9 @@ # TH7 -Seven-channel multiple thermocouple reader and logger HAT for the Raspberry Pi. - -![Alt](/.gitea/webview0.png "Web view demo") +Seven-channel multiple thermocouple reader and logger HAT for the Raspberry Pi. 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") diff --git a/config.toml b/config.toml index 5a7dfde..f757ed4 100644 --- a/config.toml +++ b/config.toml @@ -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' diff --git a/main.go b/main.go index c1b09a3..2985dc7 100644 --- a/main.go +++ b/main.go @@ -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 diff --git a/static/data-updater.js b/static/data-updater.js index fac9d57..0ed6b1e 100644 --- a/static/data-updater.js +++ b/static/data-updater.js @@ -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 = "VDD: " + parseFloat(data["ratio"]["pivdd"]); - vref.innerHTML = "VREF: " + parseFloat(data["ratio"]["vref"]); - vadj.innerHTML = "VADJ: " + parseFloat(data["ratio"]["vadj"]); - ts.innerHTML = "Last updated: " + data["time"]; - for (let i=0; i { - let unit = e["unit"]; - let value = e["value"]; - let channel = e["id"]; - - let row = table.insertRow(-1); - row.insertCell(0).innerText = channel; - row.insertCell(1).innerText = value; - row.insertCell(2).innerText = unit; - old_channel_count++; - }); +async function fetchData(url) { + const response = await fetch(url); + return response.json(); } -update(); -setInterval(update, 1000); \ No newline at end of file + +function clearTableBody(tableType) { + 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); + } +} + +function updateTable(data, tableType, oldChannelCount, updateFunction) { + clearTableBody(tableType); + + oldChannelCount = 0; + + data.channels.forEach((channel) => { + updateFunction(channel, tableType); + oldChannelCount++; + }); +} + +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 = `VREF: ${ratio.vref}`; + elements.read.vadj.innerHTML = `VADJ: ${ratio.vadj}`; + elements.read.vdd.innerHTML = `VDD: ${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); diff --git a/templates/index.tmpl b/templates/index.tmpl index e1ab139..8525c09 100644 --- a/templates/index.tmpl +++ b/templates/index.tmpl @@ -5,23 +5,88 @@ - {{ .title }} + {{.title}} +

TH7

+ - - - - - + + + + + +
ChannelValueUnit
ChannelValue
+

VDD:

VREF:

VADJ:

Last updated:

+ +

Thermocouple config

+ + + + + + + + + + + + +
ChannelTypeGainOffsetFilter sizeFilter type
+ - -{{ end }} \ No newline at end of file +{{ end }} diff --git a/web/gin.go b/web/gin.go index 401d182..fd408e4 100644 --- a/web/gin.go +++ b/web/gin.go @@ -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")