179 lines
5.1 KiB
Go
179 lines
5.1 KiB
Go
// backlight isolates which frame turns on the FCIM backlight. Press Enter to
|
|
// step through conditions; each one sends a single candidate (or one piece of
|
|
// 0x3E8) on top of the safe base that is known not to light it:
|
|
// 0x50C keep-alive on every 0x55D, the BCD clock on 0x3F2, and the label text.
|
|
//
|
|
// The FDIM shows the active condition (e.g. "COND 3/12" / "3E8 BASELINE").
|
|
// The FCIM may hold the backlight on for a while after a frame stops; if it
|
|
// stays lit, Ctrl-C, wait for it to go dark, and restart with -start N.
|
|
//
|
|
// Usage:
|
|
//
|
|
// go run ./cmd/backlight
|
|
// go run ./cmd/backlight -start 5
|
|
// go run ./cmd/backlight -only 5 # hold condition 5 until Ctrl-C
|
|
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"flag"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/brutella/can"
|
|
|
|
"fdim"
|
|
)
|
|
|
|
type frame struct {
|
|
id uint32
|
|
n uint8
|
|
data [8]uint8
|
|
}
|
|
|
|
var (
|
|
init3F1 = frame{0x3F1, 2, [8]uint8{0xF5, 0x90}}
|
|
base3E8 = frame{0x3E8, 8, [8]uint8{0x07, 0x00, 0x10, 0x04, 0, 0, 0, 0}}
|
|
base3EF = frame{0x3EF, 8, [8]uint8{0x32, 0x32, 0x32, 0x32, 0x03, 0x00, 0x00, 0x20}}
|
|
keepWake = frame{0x50C, 3, [8]uint8{0x0C, 0x02, 0x00}}
|
|
)
|
|
|
|
func e8(b0, b1, b2, b3 uint8) frame {
|
|
return frame{0x3E8, 8, [8]uint8{b0, b1, b2, b3, 0, 0, 0, 0}}
|
|
}
|
|
|
|
// condition: once is sent when the condition starts, repeat every -refresh.
|
|
type condition struct {
|
|
label string // FDIM row 2, max 20 chars
|
|
desc string
|
|
once []frame
|
|
repeat []frame
|
|
}
|
|
|
|
var conditions = []condition{
|
|
{label: "CONTROL", desc: "base only (keep-alive, clock, text) - expect OFF"},
|
|
{label: "3F1 ONCE", desc: "0x3F1 F5 90 sent once", once: []frame{init3F1}},
|
|
{label: "3F1 REPEAT", desc: "0x3F1 F5 90 every refresh", repeat: []frame{init3F1}},
|
|
{label: "3EF BASELINE", desc: "0x3EF 32 32 32 32 03 00 00 20 every refresh", repeat: []frame{base3EF}},
|
|
{label: "3E8 BASELINE", desc: "0x3E8 07 00 10 04 00 00 00 00 every refresh", repeat: []frame{base3E8}},
|
|
{label: "3E8 ALL ZERO", desc: "0x3E8 00 00 00 00 00 00 00 00", repeat: []frame{e8(0, 0, 0, 0)}},
|
|
{label: "3E8 B0=07 SOURCE", desc: "0x3E8 07 00 00 00 ... (source AUX only)", repeat: []frame{e8(0x07, 0, 0, 0)}},
|
|
{label: "3E8 B2=10 VOLUME", desc: "0x3E8 00 00 10 00 ... (volume only)", repeat: []frame{e8(0, 0, 0x10, 0)}},
|
|
{label: "3E8 B3=04 MODE", desc: "0x3E8 00 00 00 04 ... (byte3 bit 0x04 only)", repeat: []frame{e8(0, 0, 0, 0x04)}},
|
|
{label: "3E8 NO B3", desc: "0x3E8 07 00 10 00 ... (baseline with byte3 cleared)", repeat: []frame{e8(0x07, 0, 0x10, 0)}},
|
|
{label: "ALL THREE", desc: "3F1 once + 3E8 + 3EF baselines (what fdimprobe sends) - expect ON",
|
|
once: []frame{init3F1}, repeat: []frame{base3E8, base3EF}},
|
|
{label: "CONTROL AGAIN", desc: "base only - does the backlight go back off?"},
|
|
}
|
|
|
|
func main() {
|
|
iface := flag.String("if", "can0", "CAN interface")
|
|
start := flag.Int("start", 1, "condition number to start at")
|
|
only := flag.Int("only", 0, "run just this condition number and hold it until Ctrl-C")
|
|
refresh := flag.Duration("refresh", 500*time.Millisecond, "resend interval")
|
|
flag.Parse()
|
|
|
|
if *only != 0 {
|
|
*start = *only
|
|
}
|
|
if *start < 1 || *start > len(conditions) {
|
|
fmt.Fprintf(os.Stderr, "-start/-only must be 1..%d\n", len(conditions))
|
|
os.Exit(2)
|
|
}
|
|
|
|
bus, err := can.NewBusForInterfaceWithName(*iface)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
bus.SubscribeFunc(func(f can.Frame) {
|
|
if f.ID == 0x55D {
|
|
send(bus, keepWake)
|
|
}
|
|
})
|
|
go func() {
|
|
if err := bus.ConnectAndPublish(); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
sig := make(chan os.Signal, 1)
|
|
signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
|
|
go func() {
|
|
<-sig
|
|
bus.Disconnect()
|
|
os.Exit(0)
|
|
}()
|
|
|
|
var (
|
|
mu sync.Mutex
|
|
cur = *start - 1
|
|
)
|
|
|
|
send(bus, keepWake)
|
|
go func() {
|
|
for {
|
|
f := fdim.ClockFrame(time.Now(), false)
|
|
send(bus, frame{f.ID, f.Length, f.Data})
|
|
time.Sleep(time.Second)
|
|
}
|
|
}()
|
|
go func() {
|
|
for {
|
|
mu.Lock()
|
|
i := cur
|
|
mu.Unlock()
|
|
c := conditions[i]
|
|
for _, f := range c.repeat {
|
|
send(bus, f)
|
|
}
|
|
sendText(bus, fmt.Sprintf("COND %d/%d", i+1, len(conditions)), c.label)
|
|
time.Sleep(*refresh)
|
|
}
|
|
}()
|
|
|
|
if *only != 0 {
|
|
c := conditions[*only-1]
|
|
for _, f := range c.once {
|
|
send(bus, f)
|
|
}
|
|
fmt.Printf("[%d/%d] %s\n %s\n holding, Ctrl-C to quit\n", *only, len(conditions), c.label, c.desc)
|
|
select {}
|
|
}
|
|
|
|
fmt.Println("Watch the FCIM backlight. Enter = next condition, Ctrl-C = quit.")
|
|
in := bufio.NewReader(os.Stdin)
|
|
for i := *start - 1; i < len(conditions); i++ {
|
|
c := conditions[i]
|
|
mu.Lock()
|
|
cur = i
|
|
mu.Unlock()
|
|
for _, f := range c.once {
|
|
send(bus, f)
|
|
}
|
|
fmt.Printf("[%d/%d] %s\n %s\n Enter for next ", i+1, len(conditions), c.label, c.desc)
|
|
in.ReadString('\n')
|
|
}
|
|
fmt.Println("done")
|
|
}
|
|
|
|
func sendText(bus *can.Bus, line1, line2 string) {
|
|
body, err := fdim.EncodeISOTP(0x337, fdim.BuildTwoLine(line1, line2))
|
|
if err != nil {
|
|
log.Println("encode:", err)
|
|
return
|
|
}
|
|
for _, f := range append([]fdim.Frame{fdim.PairB336, fdim.PairB324}, body...) {
|
|
send(bus, frame{f.ID, f.Length, f.Data})
|
|
}
|
|
}
|
|
|
|
func send(bus *can.Bus, f frame) {
|
|
bus.Publish(can.Frame{ID: f.id, Length: f.n, Data: f.data})
|
|
time.Sleep(5 * time.Millisecond)
|
|
}
|