package main import ( "encoding/hex" "flag" "image" "log" "os" "time" "pm3.dev/cvend" "pm3.dev/fb" ) type cvendState struct { ready bool hasCard bool } type displayState struct { now time.Time cvend cvendState } func main() { flag.Parse() log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime | log.Lmicroseconds) log.Println(os.Args[0], "start") displayStateCh := make(chan displayState) go displayTask(displayStateCh) displayState := displayState{ now: time.Now(), } displayStateCh <- displayState cvendStateCh := make(chan cvendState) go cvendTask(cvendStateCh) tickerCh := wallTicker(time.Minute).C for { select { case now := <-tickerCh: displayState.now = now case cvendState := <-cvendStateCh: displayState.cvend = cvendState } displayStateCh <- displayState } } // wallTicker returns a ticker that fires whenever time.Now().Truncate(d) changes func wallTicker(d time.Duration) *time.Ticker { first := time.Until(time.Now().Add(d).Truncate(d)) ticker := time.NewTicker(first) go func() { time.Sleep(first) ticker.Reset(d) }() return ticker } func displayTask(in <-chan displayState) { fb, err := fb.Open() if err != nil { log.Fatalf("open fb: %v", err) } defer fb.Close() for state := range in { start := time.Now() img := image.NewRGBA(fb.Bounds()) drawDisplay(img, state) fb.WriteRGBA(img) dur := time.Now().Sub(start) log.Printf("display update %s %+v", dur, state) } } func cvendTask(out chan<- cvendState) { var state cvendState cv, err := cvend.Open() if err != nil { log.Fatalf("cvend Open: %s", err) } status, err := cv.AwaitStatus() if err != nil { log.Fatalf("cvend AwaitStatus: %s", err) } log.Printf("cvend status: %q", string(status)) reply, err := cv.ProxCardFunction(7, true) if err != nil { log.Fatalf("cvend ProxCardFunction: %s", err) } log.Printf("enabled DESFire %s", hex.EncodeToString(reply)) state.ready = true out <- state go func() { // send periodic status requests to keep cvend from going to sleep for { time.Sleep(30 * time.Second) if err := cv.SendIPP(0x04, nil); err != nil { log.Printf("failed to send Status: %s", err) } } }() for { cardRaw, err := cv.AwaitCard() if err != nil { log.Println("error awaiting card: %s", err) continue } card := cardRaw.(*cvend.DESFireCard) // todo other types log.Println("got card %+v", card) state.hasCard = true out <- state card.AwaitRemoved() log.Println("card removed") state.hasCard = false out <- state } }