diff --git a/otp1527/otp1527.go b/otp1527/otp1527.go new file mode 100644 index 000000000..846a5680b --- /dev/null +++ b/otp1527/otp1527.go @@ -0,0 +1,128 @@ +// Package otp1527 implements a driver to decode signal transmitted over 433MHz +// by various key fob, leak detectors, etc., which are based on Silvan OTP +// Encoder chips HS1527, EV1527, RT1527, or FP1527. +// See http://sc-tech.cn/en/hs1527.pdf for details. +// This package works with microcontrollers that support GPIO PinToggle interrupt. +// +// Example: +// +// package main +// +// import ( +// "fmt" +// "machine" +// +// "tinygo.org/x/drivers/otp1527" +// ) +// +// func main() { +// d := otp1527.NewDecoder(machine.Pin(3), 24, 0) +// for { +// select { +// case v := <-d.Out(): +// println("RECV:", fmt.Sprintf("0x%02x", v)) +// } +// } +// } +// + +package otp1527 + +import "machine" + +type OTP1527 struct { + pin machine.Pin + ch chan uint32 + time int64 + preambleClk int64 + timeHigh int64 + data uint32 + bits int + last bool +} + +// NewDecoder create driver to decode stream generated by HS1527 compatible source +// and deliver decoded data into output buffered channel of channelSize size. +// When channelSize set to -1, the size default to 100. +func NewDecoder(p machine.Pin, channelSize int) *OTP1527 { + if channelSize < 0 { + channelSize = 100 + } + d := OTP1527{pin: p, ch: make(chan uint32, channelSize)} + d.pin.Configure(machine.PinConfig{Mode: machine.PinInputPullup}) + if err := d.pin.SetInterrupt(machine.PinToggle, func(p machine.Pin) { + d.interrupt() + }); err != nil { + println("error:", err.Error()) + } + return &d +} + +// Out return output channel for decoded data. +func (d *OTP1527) Out() <-chan uint32 { + return d.ch +} + +func (d *OTP1527) interrupt() { + now := machine.GetNano() + // filter signal + highCnt := 0 + for n := 0; n < 65; n++ { + v := d.pin.Get() + if v { + highCnt++ + } else { + highCnt-- + } + } + pin := highCnt > 0 + // decode + if d.last != pin { + interval := now - d.time + if pin == true { + d.timeHigh = interval + } else { + if d.timeHigh > 0 { + switch { + case interval/d.timeHigh > 20: + // this is end of preamble + d.preambleClk = interval / 31 + d.resetData() + + case (interval+d.timeHigh) < d.preambleClk*5 && interval/d.timeHigh > 0: + // this is 0 + d.data = d.data << 1 + d.bits++ + // check for last data + if d.bits == 24 { + d.ch <- d.data + d.resetData() + } + + case (interval+d.timeHigh) < d.preambleClk*5 && interval/d.timeHigh == 0: + // this is 1 + d.data = d.data<<1 | 1 + d.bits++ + // check for last data + if d.bits == 24 { + d.ch <- d.data + d.resetData() + } + + default: + d.resetData() + } + } else { + d.resetData() + } + } + d.time = now + d.last = pin + } +} + +//go:inline +func (d *OTP1527) resetData() { + d.data = 0 + d.bits = 0 +}