From 80db262c5e87599a3eeb197ef7c881592b6e6aa4 Mon Sep 17 00:00:00 2001 From: akif999 Date: Mon, 16 Jan 2023 10:03:53 +0900 Subject: [PATCH] Add support for E220-900T22S(JP) Lora module --- Makefile | 4 + README.md | 1 + e220/config.go | 380 +++++++++++++++ e220/e220.go | 349 ++++++++++++++ e220/e220_test.go | 739 ++++++++++++++++++++++++++++++ e220/params.go | 90 ++++ examples/e220/conf/main.go | 63 +++ examples/e220/passthrough/main.go | 46 ++ examples/e220/rx/main.go | 51 +++ examples/e220/tx/main.go | 56 +++ 10 files changed, 1779 insertions(+) create mode 100644 e220/config.go create mode 100644 e220/e220.go create mode 100644 e220/e220_test.go create mode 100644 e220/params.go create mode 100644 examples/e220/conf/main.go create mode 100644 examples/e220/passthrough/main.go create mode 100644 examples/e220/rx/main.go create mode 100644 examples/e220/tx/main.go diff --git a/Makefile b/Makefile index df60b5df1..2407c5267 100644 --- a/Makefile +++ b/Makefile @@ -253,6 +253,10 @@ endif @md5sum ./build/test.hex tinygo build -size short -o ./build/test.uf2 -target=pico ./examples/as560x/main.go @md5sum ./build/test.uf2 + tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/e220/tx/main.go + @md5sum ./build/test.hex + tinygo build -size short -o ./build/test.hex -target=feather-m4 ./examples/e220/rx/main.go + @md5sum ./build/test.hex # rwildcard is a recursive version of $(wildcard) diff --git a/README.md b/README.md index 571b252ed..7e13837ef 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,7 @@ The following 90 devices are supported. | [XPT2046 touch controller](http://grobotronics.com/images/datasheets/xpt2046-datasheet.pdf) | GPIO | | [Semtech SX126x Lora](https://www.semtech.com/products/wireless-rf/lora-connect/sx1261) | SPI | | [Semtech SX127x Lora](https://www.semtech.com/products/wireless-rf/lora-connect/sx1276) | SPI | +| [E220-900T22S(JP) Lora](https://clealink.jp/top/document/iot/data_sheet.pdf) | UART | | [SSD1289 TFT color display](http://aitendo3.sakura.ne.jp/aitendo_data/product_img/lcd/tft2/M032C1289TP/3.2-SSD1289.pdf) | GPIO | ## Contributing diff --git a/e220/config.go b/e220/config.go new file mode 100644 index 000000000..d233f3732 --- /dev/null +++ b/e220/config.go @@ -0,0 +1,380 @@ +// Package e220 implements a driver for the e220 LoRa module. +package e220 + +import "fmt" + +// Config represents E220's configuration parameters +type Config struct { + ModuleAddr uint16 + UartSerialPortRate uint8 + AirDataRate uint8 + SubPacket uint8 + RssiAmbient uint8 + TransmitPower uint8 + Channel uint8 + RssiByte uint8 + TransmitMethod uint8 + WorCycleSetting uint8 + EncryptionKey uint16 + Version uint8 + errors []error +} + +func (c *Config) paramsToBytes( + bytes *[]byte, +) error { + // byte [8] is read only + if len(*bytes) < E220RegisterLength-1 { + return fmt.Errorf("length of bytes must be greater than or equal to %d: got=%d", E220RegisterLength-1, (*bytes)) + } + (*bytes)[0] = byte((c.ModuleAddr & 0xFF00) >> 8) + (*bytes)[1] = byte((c.ModuleAddr & 0x00FF) >> 0) + (*bytes)[2] = byte(((c.UartSerialPortRate & 0x07) << 5) | (c.AirDataRate & 0x1F)) + reserved := byte(0b000) + (*bytes)[3] = byte(((c.SubPacket & 0x03) << 6) | ((c.RssiAmbient & 0x01) << 5) | ((reserved & 0x07) << 2) | (c.TransmitPower & 0x03)) + (*bytes)[4] = byte(c.Channel) + reserved = byte(0b000) + (*bytes)[5] = byte(((c.RssiByte & 0x01) << 7) | ((c.TransmitMethod & 0x01) << 6) | ((reserved & 0x07) << 3) | (c.WorCycleSetting & 0x07)) + (*bytes)[6] = byte((c.EncryptionKey & 0xFF00) >> 8) + (*bytes)[7] = byte((c.EncryptionKey & 0x00FF) >> 0) + + return nil +} + +func (c *Config) bytesToParams(bytes []byte) error { + if len(bytes) < E220RegisterLength { + return fmt.Errorf("length of bytes must be greater than or equal to %d: got=%d", E220RegisterLength, (bytes)) + } + c.ModuleAddr = uint16((uint16(bytes[0]) << 8) | (uint16(bytes[1]) << 0)) + c.UartSerialPortRate = uint8((bytes[2] & 0xE0) >> 5) + c.AirDataRate = uint8((bytes[2] & 0x1F) >> 0) + c.SubPacket = uint8((bytes[3] & 0xC0) >> 6) + c.RssiAmbient = uint8((bytes[3] & 0x20) >> 5) + c.TransmitPower = uint8((bytes[3] & 0x03) >> 0) + c.Channel = bytes[4] + c.RssiByte = uint8((bytes[5] & 0x80) >> 7) + c.TransmitMethod = uint8((bytes[5] & 0x40) >> 6) + c.WorCycleSetting = uint8((bytes[5] & 0x07) >> 0) + c.EncryptionKey = uint16((uint16(bytes[6]) << 8) | (uint16(bytes[7]) << 0)) + c.Version = bytes[8] + + return nil +} + +// Validate validates configuration parameters +// Detected errors can be retrieved with Errors(). +func (c *Config) Validate() { + params := []struct { + name string + value uint8 + limit uint8 + }{ + // ModuleAddr does not have limitation + { + "UartSerialPortRate", c.UartSerialPortRate, UartSerialPortRate115200Bps, + }, + { + "AirDataRate", c.AirDataRate, AirDataRate2148Bps, + }, + { + "SubPacket32Bytes", c.SubPacket, SubPacket32Bytes, + }, + { + "RSSIAmbient", c.RssiAmbient, RSSIAmbientEnable, + }, + { + "TransmitPower", c.TransmitPower, TransmitPower0Dbm, + }, + // Channel does not have limitation + { + "RSSIByte", c.RssiByte, RSSIByteEnable, + }, + { + "TransmitMethod", c.TransmitMethod, TransmitMethodFixed, + }, + { + "WorCycleSetting", c.WorCycleSetting, WorCycleSetting4000ms, + }, + // EncryptionKey does not have limitation + // Version does not have limitation + } + + c.errors = make([]error, 0, 8) + for _, p := range params { + if err := assertLtOrEq(p.name, p.value, p.limit); err != nil { + c.errors = append(c.errors, err) + } + } + + var bandWidth uint16 + switch c.AirDataRate { + case AirDataRate15625Bps: + bandWidth = 125 + case AirDataRate9375Bps: + bandWidth = 125 + case AirDataRate5469Bps: + bandWidth = 125 + case AirDataRate3125Bps: + bandWidth = 125 + case AirDataRate1758Bps: + bandWidth = 125 + case AirDataRate31250Bps: + bandWidth = 250 + case AirDataRate18750Bps: + bandWidth = 250 + case AirDataRate10938Bps: + bandWidth = 250 + case AirDataRate6250Bps: + bandWidth = 250 + case AirDataRate3516Bps: + bandWidth = 250 + case AirDataRate1953Bps: + bandWidth = 250 + case AirDataRate62500Bps: + bandWidth = 500 + case AirDataRate37500Bps: + bandWidth = 500 + case AirDataRate21875Bps: + bandWidth = 500 + case AirDataRate12500Bps: + bandWidth = 500 + case AirDataRate7031Bps: + bandWidth = 500 + case AirDataRate3906Bps: + bandWidth = 500 + case AirDataRate2148Bps: + bandWidth = 500 + default: + // It does not come here because the same check is actually performed in the previous process. + c.errors = append(c.errors, fmt.Errorf("invalid AirDataRate value: %d", c.AirDataRate)) + } + + switch bandWidth { + case 125: + if c.Channel > 37 { + c.errors = append( + c.errors, + fmt.Errorf("if band width is %dKHz, c.Channel must be less than or equal to %d, got=%d", 125, 37, c.Channel), + ) + } + case 250: + if c.Channel > 36 { + c.errors = append( + c.errors, + fmt.Errorf("if band width is %dKHz, c.Channel must be less than or equal to %d, got=%d", 250, 36, c.Channel), + ) + } + case 500: + if c.Channel > 30 { + c.errors = append( + c.errors, + fmt.Errorf("if band width is %dKHz, c.Channel must be less than or equal to %d, got=%d", 500, 30, c.Channel), + ) + } + default: + c.errors = append(c.errors, fmt.Errorf("invalid band width value: %d", bandWidth)) + } +} + +func assertLtOrEq(name string, val, limit uint8) error { + if val > limit { + return fmt.Errorf("%s must be less than or equal to %d, got=%d", name, limit, val) + } + return nil +} + +// Errors returns errors of configurations parameters +// Must call Validate() before calling this. +func (c *Config) Errors() []error { + return c.errors +} + +func (c *Config) String() string { + moduleAddr := fmt.Sprintf("ModuleAddr0x%04X", c.ModuleAddr) + channel := fmt.Sprintf("Channel%02d", c.Channel) + uartSerialPortRate := c.uartSerialPortRateString() + airDataRate := c.airDataRateString() + subPacket := c.subPacketString() + rssiAmbient := c.rssiAmbientString() + transmitPower := c.transmitPowerString() + rssiByte := c.rssiByteString() + transmitMethod := c.transmitMethodString() + worCycleSetting := c.worCycleSettingString() + encryptionKey := fmt.Sprintf("EncryptionKey0x%04X", c.ModuleAddr) + version := fmt.Sprintf("Version0x%02X", c.Version) + + return fmt.Sprintf( + "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s", + moduleAddr, channel, uartSerialPortRate, airDataRate, + subPacket, rssiAmbient, transmitPower, rssiByte, + transmitMethod, worCycleSetting, encryptionKey, version, + ) +} + +func (c *Config) uartSerialPortRateString() string { + var output string + switch c.UartSerialPortRate { + case UartSerialPortRate1200Bps: + output = "UartSerialPortRate1200Bps" + case UartSerialPortRate2400Bps: + output = "UartSerialPortRate2400Bps" + case UartSerialPortRate4800Bps: + output = "UartSerialPortRate4800Bps" + case UartSerialPortRate9600Bps: + output = "UartSerialPortRate9600Bps" + case UartSerialPortRate19200Bps: + output = "UartSerialPortRate19200Bps" + case UartSerialPortRate38400Bps: + output = "UartSerialPortRate38400Bps" + case UartSerialPortRate57600Bps: + output = "UartSerialPortRate57600Bps" + case UartSerialPortRate115200Bps: + output = "UartSerialPortRate115200Bps" + default: + output = "UartSerialPortRate: invalid parameter" + } + return output +} + +func (c *Config) airDataRateString() string { + var output string + switch c.AirDataRate { + case AirDataRate15625Bps: + output = "AirDataRate15625Bps" + case AirDataRate9375Bps: + output = "AirDataRate9375Bps" + case AirDataRate5469Bps: + output = "AirDataRate5469Bps" + case AirDataRate3125Bps: + output = "AirDataRate3125Bps" + case AirDataRate1758Bps: + output = "AirDataRate1758Bps" + case AirDataRate31250Bps: + output = "AirDataRate31250Bps" + case AirDataRate18750Bps: + output = "AirDataRate18750Bps" + case AirDataRate10938Bps: + output = "AirDataRate10938Bps" + case AirDataRate6250Bps: + output = "AirDataRate6250Bps" + case AirDataRate3516Bps: + output = "AirDataRate3516Bps" + case AirDataRate1953Bps: + output = "AirDataRate1953Bps" + case AirDataRate62500Bps: + output = "AirDataRate62500Bps" + case AirDataRate37500Bps: + output = "AirDataRate37500Bps" + case AirDataRate21875Bps: + output = "AirDataRate21875Bps" + case AirDataRate12500Bps: + output = "AirDataRate12500Bps" + case AirDataRate7031Bps: + output = "AirDataRate7031Bps" + case AirDataRate3906Bps: + output = "AirDataRate3906Bps" + case AirDataRate2148Bps: + output = "AirDataRate2148Bps" + default: + output = "AirDataRate: invalid parameter" + } + return output +} + +func (c *Config) subPacketString() string { + var output string + switch c.SubPacket { + case SubPacket200Bytes: + output = "SubPacket200Bytes" + case SubPacket128Bytes: + output = "SubPacket128Bytes" + case SubPacket64Bytes: + output = "SubPacket64Bytes" + case SubPacket32Bytes: + output = "SubPacket32Bytes" + default: + output = "SubPacket: invalid parameter" + } + return output +} + +func (c *Config) rssiAmbientString() string { + var output string + switch c.RssiAmbient { + case RSSIAmbientDisable: + output = "RSSIAmbientDisable" + case RSSIAmbientEnable: + output = "RSSIAmbientEnable" + default: + output = "RSSIAmbient: invalid parameter" + } + return output +} + +func (c *Config) transmitPowerString() string { + var output string + switch c.TransmitPower { + case TransmitPowerUnavailable: + output = "TransmitPowerUnavailable" + case TransmitPower13Dbm: + output = "TransmitPower13Dbm" + case TransmitPower7Dbm: + output = "TransmitPower7Dbm" + case TransmitPower0Dbm: + output = "TransmitPower0Dbm" + default: + output = "TransmitPower: invalid parameter" + } + return output +} + +func (c *Config) rssiByteString() string { + var output string + switch c.RssiByte { + case RSSIByteDisable: + output = "RSSIByteDisable" + case RSSIByteEnable: + output = "RSSIByteEnable" + default: + output = "RSSIByte: invalid parameter" + } + return output +} + +func (c *Config) transmitMethodString() string { + var output string + switch c.TransmitMethod { + case TransmitMethodTransparent: + output = "TransmitMethodTransparent" + case TransmitMethodFixed: + output = "TransmitMethodFixed" + default: + output = "TransmitMethod: invalid parameter" + } + return output +} + +func (c *Config) worCycleSettingString() string { + var output string + switch c.WorCycleSetting { + case WorCycleSetting500ms: + output = "WorCycleSetting500ms" + case WorCycleSetting1000ms: + output = "WorCycleSetting1000ms" + case WorCycleSetting1500ms: + output = "WorCycleSetting1500ms" + case WorCycleSetting2000ms: + output = "WorCycleSetting2000ms" + case WorCycleSetting2500ms: + output = "WorCycleSetting2500ms" + case WorCycleSetting3000ms: + output = "WorCycleSetting3000ms" + case WorCycleSetting3500ms: + output = "WorCycleSetting3500ms" + case WorCycleSetting4000ms: + output = "WorCycleSetting4000ms" + default: + output = "WorCycleSetting: invalid parameter" + } + return output +} diff --git a/e220/e220.go b/e220/e220.go new file mode 100644 index 000000000..3fcc9fda2 --- /dev/null +++ b/e220/e220.go @@ -0,0 +1,349 @@ +//go:build tinygo + +// Package e220 implements a driver for the e220 LoRa module. +// +// Datasheet(E220-900T22S JP): https://dragon-torch.tech/wp-content/uploads/2022/08/data_sheet.pdf +// Datasheet(E220-900T2S): https://www.ebyte.com/en/downpdf.aspx?id=1211 +// Datasheet(LLCC68): https://semtech.my.salesforce.com/sfc/p/#E0000000JelG/a/2R000000HTJR/Tem0gUxGfOZ2Qn3bUzmV2zKNQRYJ3bpobPfOQ7B.erE +// +// LoraWAN 1.1 Specification: https://hz137b.p3cdn1.secureserver.net/wp-content/uploads/2020/11/lorawantm_specification_-v1.1.pdf?time=1673563697 +package e220 + +import ( + "bytes" + "context" + "fmt" + "machine" + "runtime" + "strings" + "time" +) + +// targetUARTbaud represents the UART baud rate of the target board +const ( + TargetUARTBaud1200kbps = 1200 + TargetUARTBaud2400kbps = 2400 + TargetUARTBaud4800kbps = 4800 + TargetUARTBaud9600kbps = 9600 + TargetUARTBaud19200kbps = 19200 + TargetUARTBaud38400kbps = 38400 + TargetUARTBaud57600kbps = 57600 + TargetUARTBaud115200kbps = 115200 +) + +// Mode represents the operating mode of the E220 +const ( + Mode0 uint8 = iota + Mode1 + Mode2 + Mode3 +) + +// Device wraps E220 LoRa module +type Device struct { + uart *machine.UART + m0 machine.Pin + m1 machine.Pin + tx machine.Pin + rx machine.Pin + aux machine.Pin + targetUARTBaudRate uint32 + + parameters []byte + payload []byte + + txAddr uint16 + txCh uint8 + txMethod uint8 +} + +const ( + writingRegisterSize = 8 + bufferSize = 400 +) + +// New returns a new E220 driver. Pass UART object and each used machine.Pin +func New( + uart *machine.UART, + m0, m1, tx, rx, aux machine.Pin, + targetUARTBaudRate uint32, +) *Device { + d := &Device{ + uart: uart, + m0: m0, + m1: m1, + tx: tx, + rx: rx, + aux: aux, + targetUARTBaudRate: targetUARTBaudRate, + parameters: make([]byte, writingRegisterSize), + payload: make([]byte, bufferSize), + // Set to factory setting + txAddr: 0xFFFF, + txCh: 15, + txMethod: TxMethodTransparent, + } + return d +} + +// Configure sets up the device for communication +func (d *Device) Configure(startupE220Mode uint8) error { + d.uart.Configure(machine.UARTConfig{BaudRate: d.targetUARTBaudRate, TX: d.tx, RX: d.rx}) + d.m0.Configure(machine.PinConfig{Mode: machine.PinOutput}) + d.m1.Configure(machine.PinConfig{Mode: machine.PinOutput}) + d.aux.Configure(machine.PinConfig{Mode: machine.PinInput}) + + err := d.SetMode(startupE220Mode) + if err != nil { + return err + } + + // AUX Pin becomes Low Level for about 30ms after the E220 is started. + // therefore, wait until transmission/reception becomes possible. + time.Sleep(50 * time.Millisecond) + + return nil +} + +// SetMode sets operation mode of E220 +func (d *Device) SetMode(mode uint8) error { + switch mode { + case Mode0: + d.m1.Low() + d.m0.Low() + case Mode1: + d.m1.Low() + d.m0.High() + case Mode2: + d.m1.High() + d.m0.Low() + case Mode3: + d.m1.High() + d.m0.High() + default: + return fmt.Errorf("Invalid mode: %d", mode) + } + + return nil +} + +const ( + writeCmd = 0xC0 + readCmd = 0xC1 +) + +// ReadRegister reads and returns length register values from startAddr +func (d *Device) ReadRegister(startAddr uint8, length uint8) ([]byte, error) { + response, err := d.writeRegister(readCmd, startAddr, length, []byte{}) + if err != nil { + return nil, err + } + // response must start at 0xC1 and be the length requested + if (len(response) != int(length+3)) || (response[0] != 0xC1) { + return nil, fmt.Errorf("unexpected response: %X", response) + } + + return response[3:], nil +} + +// WriteRegister writes the register value from startAddr to the value of params +func (d *Device) WriteRegister(startAddr uint8, params []byte) error { + response, err := d.writeRegister(writeCmd, startAddr, uint8(len(params)), params) + if err != nil { + return err + } + // response must start with C1 and the subsequent values must be the same as the requested values + if (response[0] != 0xC1) || ((response[1]) != startAddr) || + (int(response[2]) != len(params)) || !bytes.Equal(response[3:], params) { + return fmt.Errorf("unexpected response: want=%X%X got=%X", + []byte{0xC1, startAddr, byte(len(params))}, params, response) + } + + return nil +} + +func (d *Device) writeRegister(cmd, startAddr, length uint8, params []byte) ([]byte, error) { + d.payload[0] = cmd + d.payload[1] = startAddr + d.payload[2] = length + if len(params) > writingRegisterSize { + return nil, fmt.Errorf("params must be greater than or equal to %d: got=%d", writingRegisterSize, len(params)) + } + copy(d.payload[3:], params) + writeLength := uint8(3) + if cmd == writeCmd { + writeLength += length + } + // Wait for device is ready + ctxWrite, cancelWrite := context.WithTimeout(context.Background(), 5000*time.Millisecond) + defer cancelWrite() + for !d.IsReady() { + select { + case <-ctxWrite.Done(): + return nil, fmt.Errorf("writing register timed out") + default: + } + runtime.Gosched() + } + _, err := d.uart.Write(d.payload[:writeLength]) + if err != nil { + return nil, err + } + + ctxRead, cancelRead := context.WithTimeout(context.Background(), 5000*time.Millisecond) + defer cancelRead() + readIndex := 0 + for { + if d.uart.Buffered() > 0 { + d.payload[readIndex], err = d.uart.ReadByte() + if err != nil { + return nil, err + } + readIndex++ + if readIndex == 3+int(length) { + break + } + } + select { + case <-ctxRead.Done(): + return nil, fmt.Errorf("waiting for response from E220 timed out") + default: + } + runtime.Gosched() + } + // Wait for a while because the next register access + // cannot be received immediately after register access + time.Sleep(50 * time.Millisecond) + + return d.payload[:readIndex], nil +} + +// WriteConfig writes configuration values to E220 +func (d *Device) WriteConfig(cfg Config) error { + + cfg.Validate() + errors := cfg.Errors() + if len(errors) > 0 { + s := make([]string, 0, 8) + for _, e := range errors { + s = append(s, e.Error()) + } + return fmt.Errorf("configuration parameter validation failed:\n%s", strings.Join(s, "\n")) + } + err := cfg.paramsToBytes(&d.parameters) + if err != nil { + return err + } + + return d.WriteRegister(0x00, d.parameters) +} + +// ReadConfig reads configuration values from E220 +func (d *Device) ReadConfig() (*Config, error) { + // "+1" is read only register (addr = 0x08) + registerBytes, err := d.ReadRegister(0x00, writingRegisterSize+1) + if err != nil { + return nil, err + } + config := Config{} + err = config.bytesToParams(registerBytes) + if err != nil { + return nil, err + } + + return &config, nil +} + +// IsReady returns whether the device is ready for new commands +// This API returns false in the following cases. +// - Self checking +// - TX/RX buffer is not empty +// - In mode3 or sleep mode +// - 1 ms has not passed since the operation mode was changed from mode3 (or sleep mode) to another mode +func (d *Device) IsReady() bool { + return d.aux.Get() +} + +// TxMethod represents transmit method of E220 +// This value should be set to the same value that the user set for the E220. +const ( + TxMethodTransparent = iota + TxMethodFixed +) + +// SetTxInfo sets information such as the destination address +func (d *Device) SetTxInfo(addr uint16, ch uint8, txMethod uint8) error { + d.txAddr = addr + d.txCh = ch + if (txMethod != TxMethodTransparent) && (txMethod != TxMethodFixed) { + return fmt.Errorf("invalid Transmit Method: %d", d.txMethod) + } + d.txMethod = txMethod + + return nil +} + +// Write sends msg to the device corresponding to addr and ch +func (d *Device) Write(p []byte) (n int, err error) { + var offset int + if d.txMethod == TxMethodTransparent { + offset = 0 + } else if d.txMethod == TxMethodFixed { + d.payload[0] = byte((d.txAddr & 0xFF00) >> 8) + d.payload[1] = byte((d.txAddr & 0x00FF) >> 0) + d.payload[2] = d.txCh + offset = 3 + } else { + return 0, fmt.Errorf("invalid transmit method: %d", d.txMethod) + } + + if len(p) > (bufferSize - offset) { + return 0, fmt.Errorf("p must be %dbytes or less: got=%d", bufferSize-offset, len(p)) + } + copy(d.payload[offset:], p) + + // Wait for device is ready + ctx, cancel := context.WithTimeout(context.Background(), 5000*time.Millisecond) + defer cancel() + for !d.IsReady() { + select { + case <-ctx.Done(): + return 0, fmt.Errorf("transmitting timed out") + default: + } + runtime.Gosched() + } + return d.uart.Write(d.payload[:offset+len(p)]) +} + +// Buffered returns the number of bytes of data received from E220 +func (d *Device) Buffered() int { + return d.uart.Buffered() +} + +// ReadByte reads the value received from E220 +func (d *Device) ReadByte() (byte, error) { + data, err := d.uart.ReadByte() + if err != nil { + return 0, err + } + return data, nil +} + +// Read reads the value received from E220 +func (d *Device) Read(p []byte) (n int, err error) { + if len(p) == 0 { + return 0, nil + } + + size := d.uart.Buffered() + for size == 0 { + runtime.Gosched() + size = d.uart.Buffered() + } + + if size > len(p) { + size = len(p) + } + return d.uart.Read(p[:size]) +} diff --git a/e220/e220_test.go b/e220/e220_test.go new file mode 100644 index 000000000..eb8aab434 --- /dev/null +++ b/e220/e220_test.go @@ -0,0 +1,739 @@ +package e220 + +import ( + "bytes" + "fmt" + "reflect" + "testing" +) + +func TestParamsToBytes(t *testing.T) { + tests := []struct { + config Config + expected []byte + }{ + // min + { + Config{ + 0x0000, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0000, + 0x00, + nil, + }, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + }, + // arbitary + { + Config{ + 0x55AA, + 0x03, + 0x0A, + 0x02, + 0x00, + 0x01, + 0x14, + 0x00, + 0x01, + 0x03, + 0xAA55, + 0x00, + nil, + }, + []byte{0x55, 0xAA, 0x6A, 0x81, 0x14, 0x43, 0xAA, 0x55}, + }, + // max + { + Config{ + 0xFFFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xFFFF, + 0x00, + nil, + }, + []byte{0xFF, 0xFF, 0xFF, 0xE3, 0xFF, 0xC7, 0xFF, 0xFF}, + }, + } + + for _, tt := range tests { + got := make([]byte, 8) + tt.config.paramsToBytes( + &got, + ) + if !bytes.Equal(got, tt.expected) { + t.Errorf("bytes are not equall: want=%02X got=%02X", tt.expected, got) + } + } +} + +func TestBytesToParams(t *testing.T) { + tests := []struct { + expected Config + configBytes []byte + }{ + // min + { + Config{ + 0x0000, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x0000, + 0x00, + nil, + }, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + }, + // arbitary + { + Config{ + 0x55AA, + 0x03, + 0x0A, + 0x02, + 0x00, + 0x01, + 0x14, + 0x00, + 0x01, + 0x03, + 0xAA55, + 0xAA, + nil, + }, + []byte{0x55, 0xAA, 0x6A, 0x81, 0x14, 0x43, 0xAA, 0x55, 0xAA}, + }, + // max + { + Config{ + 0xFFFF, + 0x07, + 0x1F, + 0x03, + 0x01, + 0x03, + 0xFF, + 0x01, + 0x01, + 0x07, + 0xFFFF, + 0xFF, + nil, + }, + []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, + }, + } + + for _, tt := range tests { + got := Config{} + got.bytesToParams( + tt.configBytes, + ) + if !reflect.DeepEqual(got, tt.expected) { + t.Errorf("objects are not equall: want=%02X got=%02X", tt.expected, got) + } + } +} + +func TestConfig_Validate(t *testing.T) { + type fields struct { + ModuleAddr uint16 + UartSerialPortRate uint8 + AirDataRate uint8 + SubPacket uint8 + RssiAmbient uint8 + TransmitPower uint8 + Channel uint8 + RssiByte uint8 + TransmitMethod uint8 + WorCycleSetting uint8 + EncryptionKey uint16 + Version uint8 + errors []error + } + tests := []struct { + name string + fields fields + want []error + }{ + { + "all fields are valid(minimum value)", + fields{ + 0x0000, + UartSerialPortRate1200Bps, + AirDataRate15625Bps, + SubPacket200Bytes, + RSSIAmbientDisable, + TransmitPowerUnavailable, + 0, + RSSIByteDisable, + TransmitMethodFixed, + WorCycleSetting500ms, + 0x0000, + 0x00, + nil, + }, + []error{}, + }, + { + "all fields are valid(maximum value)", + fields{ + 0xFFFF, + UartSerialPortRate115200Bps, + AirDataRate2148Bps, + SubPacket32Bytes, + RSSIAmbientEnable, + TransmitPower0Dbm, + 30, // limitaion by band-width configration + RSSIByteEnable, + TransmitMethodFixed, + WorCycleSetting4000ms, + 0xFFFF, + 0xFF, + nil, + }, + []error{}, + }, + { + "some fields are invalid", + fields{ + 0xFFFF, + UartSerialPortRate115200Bps + 1, + AirDataRate2148Bps, + SubPacket32Bytes, + RSSIAmbientEnable, + TransmitPower0Dbm + 1, + 30, // limitaion by band-width configuration + RSSIByteEnable, + TransmitMethodFixed, + WorCycleSetting4000ms + 1, + 0xFFFF, + 0xFF, + nil, + }, + []error{ + fmt.Errorf("%s must be less than or equal to %d, got=%d", "UartSerialPortRate", UartSerialPortRate115200Bps, UartSerialPortRate115200Bps+1), + fmt.Errorf("%s must be less than or equal to %d, got=%d", "TransmitPower", TransmitPower0Dbm, TransmitPower0Dbm+1), + fmt.Errorf("%s must be less than or equal to %d, got=%d", "WorCycleSetting", WorCycleSetting4000ms, WorCycleSetting4000ms+1), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + ModuleAddr: tt.fields.ModuleAddr, + UartSerialPortRate: tt.fields.UartSerialPortRate, + AirDataRate: tt.fields.AirDataRate, + SubPacket: tt.fields.SubPacket, + RssiAmbient: tt.fields.RssiAmbient, + TransmitPower: tt.fields.TransmitPower, + Channel: tt.fields.Channel, + RssiByte: tt.fields.RssiByte, + TransmitMethod: tt.fields.TransmitMethod, + WorCycleSetting: tt.fields.WorCycleSetting, + EncryptionKey: tt.fields.EncryptionKey, + Version: tt.fields.Version, + errors: tt.fields.errors, + } + c.Validate() + got := c.Errors() + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("errors are not equall: want=%q got=%q", tt.want, got) + } + }) + } +} + +func TestConfig_uartSerialPortRateString(t *testing.T) { + type fields struct { + ModuleAddr uint16 + UartSerialPortRate uint8 + AirDataRate uint8 + SubPacket uint8 + RssiAmbient uint8 + TransmitPower uint8 + Channel uint8 + RssiByte uint8 + TransmitMethod uint8 + WorCycleSetting uint8 + EncryptionKey uint16 + Version uint8 + } + tests := []struct { + name string + fields fields + want string + }{ + { + "valid minimum value", + fields{UartSerialPortRate: UartSerialPortRate1200Bps}, + "UartSerialPortRate1200Bps", + }, + { + "valid max value", + fields{UartSerialPortRate: UartSerialPortRate115200Bps}, + "UartSerialPortRate115200Bps", + }, + { + "invalid value", + fields{UartSerialPortRate: UartSerialPortRate115200Bps + 1}, + "UartSerialPortRate: invalid parameter", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + ModuleAddr: tt.fields.ModuleAddr, + UartSerialPortRate: tt.fields.UartSerialPortRate, + AirDataRate: tt.fields.AirDataRate, + SubPacket: tt.fields.SubPacket, + RssiAmbient: tt.fields.RssiAmbient, + TransmitPower: tt.fields.TransmitPower, + Channel: tt.fields.Channel, + RssiByte: tt.fields.RssiByte, + TransmitMethod: tt.fields.TransmitMethod, + WorCycleSetting: tt.fields.WorCycleSetting, + EncryptionKey: tt.fields.EncryptionKey, + Version: tt.fields.Version, + } + if got := c.uartSerialPortRateString(); got != tt.want { + t.Errorf("Config.uartSerialPortRateString() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestConfig_airDataRateString(t *testing.T) { + type fields struct { + ModuleAddr uint16 + UartSerialPortRate uint8 + AirDataRate uint8 + SubPacket uint8 + RssiAmbient uint8 + TransmitPower uint8 + Channel uint8 + RssiByte uint8 + TransmitMethod uint8 + WorCycleSetting uint8 + EncryptionKey uint16 + Version uint8 + } + tests := []struct { + name string + fields fields + want string + }{ + { + "valid minimum value", + fields{AirDataRate: AirDataRate15625Bps}, + "AirDataRate15625Bps", + }, + { + "valid max value", + fields{AirDataRate: AirDataRate2148Bps}, + "AirDataRate2148Bps", + }, + { + "invalid value", + fields{AirDataRate: AirDataRate2148Bps + 1}, + "AirDataRate: invalid parameter", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + ModuleAddr: tt.fields.ModuleAddr, + UartSerialPortRate: tt.fields.UartSerialPortRate, + AirDataRate: tt.fields.AirDataRate, + SubPacket: tt.fields.SubPacket, + RssiAmbient: tt.fields.RssiAmbient, + TransmitPower: tt.fields.TransmitPower, + Channel: tt.fields.Channel, + RssiByte: tt.fields.RssiByte, + TransmitMethod: tt.fields.TransmitMethod, + WorCycleSetting: tt.fields.WorCycleSetting, + EncryptionKey: tt.fields.EncryptionKey, + Version: tt.fields.Version, + } + if got := c.airDataRateString(); got != tt.want { + t.Errorf("Config.airDataRateString() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestConfig_subPacketString(t *testing.T) { + type fields struct { + ModuleAddr uint16 + UartSerialPortRate uint8 + AirDataRate uint8 + SubPacket uint8 + RssiAmbient uint8 + TransmitPower uint8 + Channel uint8 + RssiByte uint8 + TransmitMethod uint8 + WorCycleSetting uint8 + EncryptionKey uint16 + Version uint8 + } + tests := []struct { + name string + fields fields + want string + }{ + { + "valid minimum value", + fields{SubPacket: SubPacket200Bytes}, + "SubPacket200Bytes", + }, + { + "valid max value", + fields{SubPacket: SubPacket32Bytes}, + "SubPacket32Bytes", + }, + { + "invalid value", + fields{SubPacket: SubPacket32Bytes + 1}, + "SubPacket: invalid parameter", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + ModuleAddr: tt.fields.ModuleAddr, + UartSerialPortRate: tt.fields.UartSerialPortRate, + AirDataRate: tt.fields.AirDataRate, + SubPacket: tt.fields.SubPacket, + RssiAmbient: tt.fields.RssiAmbient, + TransmitPower: tt.fields.TransmitPower, + Channel: tt.fields.Channel, + RssiByte: tt.fields.RssiByte, + TransmitMethod: tt.fields.TransmitMethod, + WorCycleSetting: tt.fields.WorCycleSetting, + EncryptionKey: tt.fields.EncryptionKey, + Version: tt.fields.Version, + } + if got := c.subPacketString(); got != tt.want { + t.Errorf("Config.subPacketString() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestConfig_rssiAmbientString(t *testing.T) { + type fields struct { + ModuleAddr uint16 + UartSerialPortRate uint8 + AirDataRate uint8 + SubPacket uint8 + RssiAmbient uint8 + TransmitPower uint8 + Channel uint8 + RssiByte uint8 + TransmitMethod uint8 + WorCycleSetting uint8 + EncryptionKey uint16 + Version uint8 + } + tests := []struct { + name string + fields fields + want string + }{ + { + "valid minimum value", + fields{RssiAmbient: RSSIAmbientDisable}, + "RSSIAmbientDisable", + }, + { + "valid max value", + fields{RssiAmbient: RSSIAmbientEnable}, + "RSSIAmbientEnable", + }, + { + "invalid value", + fields{RssiAmbient: RSSIAmbientEnable + 1}, + "RSSIAmbient: invalid parameter", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + ModuleAddr: tt.fields.ModuleAddr, + UartSerialPortRate: tt.fields.UartSerialPortRate, + AirDataRate: tt.fields.AirDataRate, + SubPacket: tt.fields.SubPacket, + RssiAmbient: tt.fields.RssiAmbient, + TransmitPower: tt.fields.TransmitPower, + Channel: tt.fields.Channel, + RssiByte: tt.fields.RssiByte, + TransmitMethod: tt.fields.TransmitMethod, + WorCycleSetting: tt.fields.WorCycleSetting, + EncryptionKey: tt.fields.EncryptionKey, + Version: tt.fields.Version, + } + if got := c.rssiAmbientString(); got != tt.want { + t.Errorf("Config.rssiAmbientString() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestConfig_transmitPowerString(t *testing.T) { + type fields struct { + ModuleAddr uint16 + UartSerialPortRate uint8 + AirDataRate uint8 + SubPacket uint8 + RssiAmbient uint8 + TransmitPower uint8 + Channel uint8 + RssiByte uint8 + TransmitMethod uint8 + WorCycleSetting uint8 + EncryptionKey uint16 + Version uint8 + } + tests := []struct { + name string + fields fields + want string + }{ + { + "valid minimum value", + fields{TransmitPower: TransmitPowerUnavailable}, + "TransmitPowerUnavailable", + }, + { + "valid max value", + fields{TransmitPower: TransmitPower0Dbm}, + "TransmitPower0Dbm", + }, + { + "invalid value", + fields{TransmitPower: TransmitPower0Dbm + 1}, + "TransmitPower: invalid parameter", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + ModuleAddr: tt.fields.ModuleAddr, + UartSerialPortRate: tt.fields.UartSerialPortRate, + AirDataRate: tt.fields.AirDataRate, + SubPacket: tt.fields.SubPacket, + RssiAmbient: tt.fields.RssiAmbient, + TransmitPower: tt.fields.TransmitPower, + Channel: tt.fields.Channel, + RssiByte: tt.fields.RssiByte, + TransmitMethod: tt.fields.TransmitMethod, + WorCycleSetting: tt.fields.WorCycleSetting, + EncryptionKey: tt.fields.EncryptionKey, + Version: tt.fields.Version, + } + if got := c.transmitPowerString(); got != tt.want { + t.Errorf("Config.transmitPowerString() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestConfig_rssiByteString(t *testing.T) { + type fields struct { + ModuleAddr uint16 + UartSerialPortRate uint8 + AirDataRate uint8 + SubPacket uint8 + RssiAmbient uint8 + TransmitPower uint8 + Channel uint8 + RssiByte uint8 + TransmitMethod uint8 + WorCycleSetting uint8 + EncryptionKey uint16 + Version uint8 + } + tests := []struct { + name string + fields fields + want string + }{ + { + "valid minimum value", + fields{RssiByte: RSSIByteDisable}, + "RSSIByteDisable", + }, + { + "valid max value", + fields{RssiByte: RSSIByteEnable}, + "RSSIByteEnable", + }, + { + "invalid value", + fields{RssiByte: RSSIByteEnable + 1}, + "RSSIByte: invalid parameter", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + ModuleAddr: tt.fields.ModuleAddr, + UartSerialPortRate: tt.fields.UartSerialPortRate, + AirDataRate: tt.fields.AirDataRate, + SubPacket: tt.fields.SubPacket, + RssiAmbient: tt.fields.RssiAmbient, + TransmitPower: tt.fields.TransmitPower, + Channel: tt.fields.Channel, + RssiByte: tt.fields.RssiByte, + TransmitMethod: tt.fields.TransmitMethod, + WorCycleSetting: tt.fields.WorCycleSetting, + EncryptionKey: tt.fields.EncryptionKey, + Version: tt.fields.Version, + } + if got := c.rssiByteString(); got != tt.want { + t.Errorf("Config.rssiByteString() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestConfig_transmitMethodString(t *testing.T) { + type fields struct { + ModuleAddr uint16 + UartSerialPortRate uint8 + AirDataRate uint8 + SubPacket uint8 + RssiAmbient uint8 + TransmitPower uint8 + Channel uint8 + RssiByte uint8 + TransmitMethod uint8 + WorCycleSetting uint8 + EncryptionKey uint16 + Version uint8 + } + tests := []struct { + name string + fields fields + want string + }{ + { + "valid minimum value", + fields{TransmitMethod: TransmitMethodTransparent}, + "TransmitMethodTransparent", + }, + { + "valid max value", + fields{TransmitMethod: TransmitMethodFixed}, + "TransmitMethodFixed", + }, + { + "invalid value", + fields{TransmitMethod: TransmitMethodFixed + 1}, + "TransmitMethod: invalid parameter", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + ModuleAddr: tt.fields.ModuleAddr, + UartSerialPortRate: tt.fields.UartSerialPortRate, + AirDataRate: tt.fields.AirDataRate, + SubPacket: tt.fields.SubPacket, + RssiAmbient: tt.fields.RssiAmbient, + TransmitPower: tt.fields.TransmitPower, + Channel: tt.fields.Channel, + RssiByte: tt.fields.RssiByte, + TransmitMethod: tt.fields.TransmitMethod, + WorCycleSetting: tt.fields.WorCycleSetting, + EncryptionKey: tt.fields.EncryptionKey, + Version: tt.fields.Version, + } + if got := c.transmitMethodString(); got != tt.want { + t.Errorf("Config.transmitMethodString() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestConfig_worCycleSettingString(t *testing.T) { + type fields struct { + ModuleAddr uint16 + UartSerialPortRate uint8 + AirDataRate uint8 + SubPacket uint8 + RssiAmbient uint8 + TransmitPower uint8 + Channel uint8 + RssiByte uint8 + TransmitMethod uint8 + WorCycleSetting uint8 + EncryptionKey uint16 + Version uint8 + } + tests := []struct { + name string + fields fields + want string + }{ + { + "valid minimum value", + fields{WorCycleSetting: WorCycleSetting500ms}, + "WorCycleSetting500ms", + }, + { + "valid max value", + fields{WorCycleSetting: WorCycleSetting4000ms}, + "WorCycleSetting4000ms", + }, + { + "invalid value", + fields{WorCycleSetting: WorCycleSetting4000ms + 1}, + "WorCycleSetting: invalid parameter", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + ModuleAddr: tt.fields.ModuleAddr, + UartSerialPortRate: tt.fields.UartSerialPortRate, + AirDataRate: tt.fields.AirDataRate, + SubPacket: tt.fields.SubPacket, + RssiAmbient: tt.fields.RssiAmbient, + TransmitPower: tt.fields.TransmitPower, + Channel: tt.fields.Channel, + RssiByte: tt.fields.RssiByte, + TransmitMethod: tt.fields.TransmitMethod, + WorCycleSetting: tt.fields.WorCycleSetting, + EncryptionKey: tt.fields.EncryptionKey, + Version: tt.fields.Version, + } + if got := c.worCycleSettingString(); got != tt.want { + t.Errorf("Config.worCycleSettingString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/e220/params.go b/e220/params.go new file mode 100644 index 000000000..d6e822c82 --- /dev/null +++ b/e220/params.go @@ -0,0 +1,90 @@ +// Package e220 implements a driver for the e220 LoRa module. +// +// https://dragon-torch.tech/wp-content/uploads/2022/08/data_sheet.pdf +// P23 - 27 +package e220 + +// E220RegisterLength represents register length of E220 +const ( + E220RegisterLength = 9 +) + +// UartSerialPortRate represents UART serial port rate of E220 +const ( + UartSerialPortRate1200Bps = 0b000 + UartSerialPortRate2400Bps = 0b001 + UartSerialPortRate4800Bps = 0b010 + UartSerialPortRate9600Bps = 0b011 + UartSerialPortRate19200Bps = 0b100 + UartSerialPortRate38400Bps = 0b101 + UartSerialPortRate57600Bps = 0b110 + UartSerialPortRate115200Bps = 0b111 +) + +// AirDataRate represents air data rate of E220 +const ( + AirDataRate15625Bps = 0b00000 + AirDataRate9375Bps = 0b00100 + AirDataRate5469Bps = 0b01000 + AirDataRate3125Bps = 0b01100 + AirDataRate1758Bps = 0b10000 + AirDataRate31250Bps = 0b00001 + AirDataRate18750Bps = 0b00101 + AirDataRate10938Bps = 0b01001 + AirDataRate6250Bps = 0b01101 + AirDataRate3516Bps = 0b10001 + AirDataRate1953Bps = 0b10101 + AirDataRate62500Bps = 0b00010 + AirDataRate37500Bps = 0b00110 + AirDataRate21875Bps = 0b01010 + AirDataRate12500Bps = 0b01110 + AirDataRate7031Bps = 0b10010 + AirDataRate3906Bps = 0b10110 + AirDataRate2148Bps = 0b11010 +) + +// SubPacket represents sub packet size of E220 +const ( + SubPacket200Bytes = 0b00 + SubPacket128Bytes = 0b01 + SubPacket64Bytes = 0b10 + SubPacket32Bytes = 0b11 +) + +// RSSIAmbient represents disable or enable of RSSI ambient of E220 +const ( + RSSIAmbientDisable = 0b0 + RSSIAmbientEnable = 0b1 +) + +// TransmitPower represents transmit power of E220 +const ( + TransmitPowerUnavailable = 0b00 + TransmitPower13Dbm = 0b01 + TransmitPower7Dbm = 0b10 + TransmitPower0Dbm = 0b11 +) + +// RSSIByte represents disable or enable of RSSI byte of E220 +const ( + RSSIByteDisable = 0b0 + RSSIByteEnable = 0b1 +) + +// TransmitMethod represents transmit method of E220 +const ( + TransmitMethodTransparent = 0b0 + TransmitMethodFixed = 0b1 +) + +// WorCycle represents WOR cycle of E220 +const ( + WorCycleSetting500ms = 0b000 + WorCycleSetting1000ms = 0b001 + WorCycleSetting1500ms = 0b010 + WorCycleSetting2000ms = 0b011 + WorCycleSetting2500ms = 0b100 + WorCycleSetting3000ms = 0b101 + WorCycleSetting3500ms = 0b110 + WorCycleSetting4000ms = 0b111 +) diff --git a/examples/e220/conf/main.go b/examples/e220/conf/main.go new file mode 100644 index 000000000..4dee0efe8 --- /dev/null +++ b/examples/e220/conf/main.go @@ -0,0 +1,63 @@ +// This is xample of writing and reading E220 Configuration +package main + +import ( + "fmt" + "machine" + "time" + + "tinygo.org/x/drivers/e220" +) + +var ( + uart = machine.DefaultUART + m0 = machine.D4 + m1 = machine.D5 + tx = machine.UART_TX_PIN + rx = machine.UART_RX_PIN + aux = machine.D6 +) + +func main() { + device := e220.New(uart, m0, m1, tx, rx, aux, 9600) + err := device.Configure(e220.Mode3) + if err != nil { + fail(err.Error()) + } + cfg := e220.Config{ + // Default Parameters + ModuleAddr: 0x0000, + UartSerialPortRate: e220.UartSerialPortRate9600Bps, + AirDataRate: e220.AirDataRate6250Bps, + SubPacket: e220.SubPacket200Bytes, + RssiAmbient: e220.RSSIAmbientDisable, + TransmitPower: e220.TransmitPowerUnavailable, + Channel: 15, + RssiByte: e220.RSSIByteDisable, + TransmitMethod: e220.TransmitMethodTransparent, + WorCycleSetting: e220.WorCycleSetting2000ms, + EncryptionKey: 0x0000, + Version: 0x00, + } + err = device.WriteConfig(cfg) + if err != nil { + fail(err.Error()) + } + // The EncryptionKey(byte6-7) is write-only, so a 0 value is read + response, err := device.ReadConfig() + if err != nil { + fail(err.Error()) + } + + for { + fmt.Println(response) + time.Sleep(time.Millisecond * 1000) + } +} + +func fail(msg string) { + for { + println(msg) + time.Sleep(1 * time.Second) + } +} diff --git a/examples/e220/passthrough/main.go b/examples/e220/passthrough/main.go new file mode 100644 index 000000000..881ccc98e --- /dev/null +++ b/examples/e220/passthrough/main.go @@ -0,0 +1,46 @@ +// This is example of passing through data. +package main + +import ( + "io" + "machine" + "os" + "time" + + "tinygo.org/x/drivers/e220" +) + +var ( + uart = machine.DefaultUART + m0 = machine.D4 + m1 = machine.D5 + tx = machine.UART_TX_PIN + rx = machine.UART_RX_PIN + aux = machine.D6 + led = machine.LED +) + +func main() { + device := e220.New(uart, m0, m1, tx, rx, aux, 9600) + err := device.Configure(e220.Mode0) + if err != nil { + fail(err.Error()) + } + addr := uint16(0xFFFF) + ch := uint8(15) + err = device.SetTxInfo(addr, ch, e220.TxMethodTransparent) + if err != nil { + fail(err.Error()) + } + go func() { + io.Copy(os.Stdout, device) + }() + w := io.MultiWriter(os.Stdout, device) + io.Copy(w, os.Stdin) +} +func fail(msg string) { + for { + println(msg) + time.Sleep(1 * time.Second) + } +} diff --git a/examples/e220/rx/main.go b/examples/e220/rx/main.go new file mode 100644 index 000000000..131f75896 --- /dev/null +++ b/examples/e220/rx/main.go @@ -0,0 +1,51 @@ +// This is example of reading data from E220. +package main + +import ( + "bufio" + "fmt" + "machine" + "time" + + "tinygo.org/x/drivers/e220" +) + +var ( + uart = machine.DefaultUART + m0 = machine.D4 + m1 = machine.D5 + tx = machine.UART_TX_PIN + rx = machine.UART_RX_PIN + aux = machine.D6 +) + +func main() { + device := e220.New(uart, m0, m1, tx, rx, aux, 9600) + err := device.Configure(e220.Mode3) + if err != nil { + fail(err.Error()) + } + + response, err := device.ReadConfig() + if err != nil { + fail(err.Error()) + } + println(fmt.Sprintf("read config: %X", response)) + + device.SetMode(e220.Mode0) + scanner := bufio.NewScanner(device) + for scanner.Scan() { + println(scanner.Text()) + } + // The following are not reached until EOF is given or an error occurs + if err := scanner.Err(); err != nil { + fail(err.Error()) + } +} + +func fail(msg string) { + for { + println(msg) + time.Sleep(1 * time.Second) + } +} diff --git a/examples/e220/tx/main.go b/examples/e220/tx/main.go new file mode 100644 index 000000000..bfa866bed --- /dev/null +++ b/examples/e220/tx/main.go @@ -0,0 +1,56 @@ +// This is example of writing data to E220. +package main + +import ( + "fmt" + "machine" + "time" + + "tinygo.org/x/drivers/e220" +) + +var ( + uart = machine.DefaultUART + m0 = machine.D4 + m1 = machine.D5 + tx = machine.UART_TX_PIN + rx = machine.UART_RX_PIN + aux = machine.D6 +) + +func main() { + device := e220.New(uart, m0, m1, tx, rx, aux, 9600) + err := device.Configure(e220.Mode3) + if err != nil { + fail(err.Error()) + } + + response, err := device.ReadConfig() + if err != nil { + fail(err.Error()) + } + println(fmt.Sprintf("read config: %X", response)) + + device.SetMode(e220.Mode0) + addr := uint16(0xFFFF) + ch := uint8(15) + err = device.SetTxInfo(addr, ch, e220.TxMethodTransparent) + if err != nil { + fail(err.Error()) + } + msg := "Hello,\nworld\n!!" + for cnt := 0; ; cnt++ { + _, err := fmt.Fprintf(device, "%s: %d\n", msg, cnt) + if err != nil { + fail(err.Error()) + } + time.Sleep(time.Millisecond * 1000) + } +} + +func fail(msg string) { + for { + println(msg) + time.Sleep(1 * time.Second) + } +}