sx127x: Driver for Semtech sx127x radio modules

This first version of the driver has been tested with BB-FRM9x module
This commit is contained in:
Olivier Fauchon
2021-01-19 00:35:45 +01:00
committed by Ron Evans
parent 877342d0ff
commit 28ddb2681d
6 changed files with 1158 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
// Receives data with LoRa.
package main
import (
"fmt"
"machine"
"strconv"
"strings"
"tinygo.org/x/drivers/lora/sx127x"
)
var loraConfig = sx127x.Config{
Frequency: 868100000,
SpreadingFactor: 12,
Bandwidth: 125000,
CodingRate: 6,
TxPower: 17,
}
const (
// Serial console
UART_TX_PIN = machine.PA9
UART_RX_PIN = machine.PA10
// RFM95 SPI Connection to Bluepill
SPI_SCK_PIN = machine.PA5
SPI_SDO_PIN = machine.PA7
SPI_SDI_PIN = machine.PA6
SPI_CS_PIN = machine.PB8
SPI_RST_PIN = machine.PB9
// DIO RFM95 Pin connection to BluePill
DIO0_PIN = machine.PA2
DIO0_PIN_MODE = machine.PinInputPulldown
DIO0_PIN_CHANGE = machine.PinRising
pollDelayMs int = 1000
)
// configureDioInt sets up the DIO0 interrupt handler
func configureDioInt(radio *sx127x.Device) {
// Set an interrupt on this pin.
err := DIO0_PIN.SetInterrupt(DIO0_PIN_CHANGE, func(machine.Pin) {
if DIO0_PIN.Get() {
radio.CheckIrq()
}
})
if err != nil {
println("could not configure pin interrupt:", err.Error())
}
}
func main() {
// Configure serial console
machine.Serial.Configure(machine.UARTConfig{TX: UART_TX_PIN, RX: UART_RX_PIN, BaudRate: 115200})
println("LoRa PingPong Example")
// Prepare GPIOS (SPI + DIO)
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
SPI_CS_PIN.Configure(machine.PinConfig{Mode: machine.PinOutput})
SPI_RST_PIN.Configure(machine.PinConfig{Mode: machine.PinOutput})
DIO0_PIN.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.LED.Set(true)
// Enable SPI
machine.SPI0.Configure(machine.SPIConfig{
SCK: SPI_SCK_PIN,
SDO: SPI_SDO_PIN,
SDI: SPI_SDI_PIN,
Frequency: 500000,
Mode: 0})
// Create and Reset Lora driver
loraRadio := sx127x.New(machine.SPI0, SPI_CS_PIN, SPI_RST_PIN)
loraRadio.Reset()
// Setup DIO0 interrupt Handling
configureDioInt(&loraRadio)
// Configure Lora settings (modulation, SF... etc )
var err = loraRadio.SetupLora(loraConfig)
if err != nil {
fmt.Println(err)
return
}
// ping counter is incremented after every retransmission
pingCount := 0
// Send first packet without using interrupts
println("*** Send a Lora PING ")
machine.LED.Set(false)
loraRadio.TxLora([]byte("PING " + strconv.Itoa(pingCount)))
machine.LED.Set(true)
println("*** Switch to RX Mode ")
for {
loraRadio.RxLora()
// Wait for Radio event
in := <-loraRadio.GetRadioEventChan()
// We have a packet
if in.EventType == sx127x.EventRxDone {
data := string(in.EventData)
println("RX: ", data)
machine.LED.Set(false)
// If it's ping, reply pong
if strings.Contains(data, "PING") {
println("Received PING, Sending PONG")
loraRadio.TxLora([]byte("PONG #" + strconv.Itoa(pingCount)))
} else if strings.Contains(data, "PONG") {
println("Received PONG, Sending PING")
loraRadio.TxLora([]byte("PING #" + strconv.Itoa(pingCount)))
}
machine.LED.Set(true)
pingCount++
}
}
}
+50
View File
@@ -0,0 +1,50 @@
// Receives data with LoRa.
package main
import (
"fmt"
"machine"
"tinygo.org/x/drivers/lora/sx127x"
)
var loraConfig = sx127x.Config{
Frequency: 433998500,
SpreadingFactor: 7,
Bandwidth: 125000,
CodingRate: 6,
TxPower: 17,
}
var packet [255]byte
func main() {
println("LoRa Receiver Example")
// SPI settings for Feather M0 LoRa board
csPin := machine.PB1
csPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
rstPin := machine.PB2
rstPin.Configure(machine.PinConfig{Mode: machine.PinOutput})
dio0Pin := machine.PB3
dio0Pin.Configure(machine.PinConfig{Mode: machine.PinOutput})
machine.SPI0.Configure(machine.SPIConfig{})
loraRadio := sx127x.New(machine.SPI0, csPin, rstPin)
var err = loraRadio.SetupLora(loraConfig)
if err != nil {
fmt.Println(err)
return
}
println("Receiving LoRa packets...")
for {
packetSize := loraRadio.ParsePacket(0)
if packetSize > 0 {
println("Got packet, RSSI=", loraRadio.LastPacketRSSI())
size := loraRadio.ReadPacket(packet[:])
println(string(packet[:size]))
}
}
}
+88
View File
@@ -0,0 +1,88 @@
// Sends data with LoRa.
package main
import (
"fmt"
"machine"
"strconv"
"time"
"tinygo.org/x/drivers/lora/sx127x"
)
var loraConfig = sx127x.Config{
Frequency: 868100000,
SpreadingFactor: 12,
Bandwidth: 125000,
CodingRate: 6,
TxPower: 17,
}
const (
SPI_SCK_PIN = machine.PA5
SPI_SDO_PIN = machine.PA7
SPI_SDI_PIN = machine.PA6
SPI_CS_PIN = machine.PB8
SPI_RST_PIN = machine.PB9
DIO0_PIN = machine.PA2
UART_TX_PIN = machine.PA9
UART_RX_PIN = machine.PA10
)
func main() {
// Configure serial console
machine.Serial.Configure(machine.UARTConfig{TX: UART_TX_PIN, RX: UART_RX_PIN, BaudRate: 115200})
println("LoRa Sender Example")
// Prepare GPIOS (SPI + DIO)
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
SPI_CS_PIN.Configure(machine.PinConfig{Mode: machine.PinOutput})
SPI_RST_PIN.Configure(machine.PinConfig{Mode: machine.PinOutput})
DIO0_PIN.Configure(machine.PinConfig{Mode: machine.PinOutput})
// Enable SPI
machine.SPI0.Configure(machine.SPIConfig{
SCK: SPI_SCK_PIN,
SDO: SPI_SDO_PIN,
SDI: SPI_SDI_PIN,
Frequency: 500000,
Mode: 0})
// Create and Reset Lora driver
loraRadio := sx127x.New(machine.SPI0, SPI_CS_PIN, SPI_RST_PIN)
loraRadio.Reset()
// Check module identification
if loraRadio.GetVersion() != 0x12 {
println("SX1276 module not found")
for {
}
} else {
println("Module version 0x12")
}
// Configure Lora settings (modulation, SF... etc )
var err = loraRadio.SetupLora(loraConfig)
if err != nil {
fmt.Println(err)
return
}
// Send a Lora packet every 5 sec
println("Sending LoRa packets every 5 seconds...")
for i := 0; ; i++ {
var packet = "TinyGo LoRa Sender: " + strconv.Itoa(i)
println("Sending:", packet)
machine.LED.Low()
time.Sleep(time.Millisecond * 150)
machine.LED.High()
loraRadio.TxLora([]byte(packet))
time.Sleep(5000 * time.Millisecond)
}
}
+124
View File
@@ -0,0 +1,124 @@
package main
// This example code enable continuous Wave/Preamble
import (
"machine"
"time"
"tinygo.org/x/drivers/sx127x"
)
const (
FREQ = 868100000
)
var (
loraRadio *sx127x.Device
// We assume the module is connected this way:
SX127X_PIN_RST = machine.PB9
SX127X_PIN_CS = machine.PB8
SX127X_PIN_DIO0 = machine.PA0
SX127X_SPI = machine.SPI0
)
func main() {
println("\n# TinyGo Lora continuous Wave/Preamble test")
println("# -----------------------------------------")
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
// SPI, RESET, CS configuration
SX127X_PIN_RST.Configure(machine.PinConfig{Mode: machine.PinOutput})
SX127X_PIN_CS.Configure(machine.PinConfig{Mode: machine.PinOutput})
SX127X_PIN_DIO0.Configure(machine.PinConfig{Mode: machine.PinInputPulldown}) // Checkthat
SX127X_SPI.Configure(machine.SPIConfig{Frequency: 500000, Mode: 0})
// Create the driver
loraRadio = sx127x.New(SX127X_SPI, SX127X_PIN_CS, SX127X_PIN_RST)
err := SX127X_PIN_DIO0.SetInterrupt(machine.PinRising, func(machine.Pin) {
loraRadio.HandleInterrupt()
})
if err != nil {
panic("Can't configure DIO int handler")
}
// Reset the radio module
loraRadio.Reset()
println("Try to detect sx127x radio")
state := loraRadio.DetectDevice()
if !state {
panic("sx127x not detected. ")
} else {
println("sx127x detected")
}
/*
// Prepare for Lora operation
loraConf := sx126x.LoraConfig{
Freq: FREQ,
Bw: sx126x.SX126X_LORA_BW_500_0,
Sf: sx126x.SX126X_LORA_SF9,
Cr: sx126x.SX126X_LORA_CR_4_7,
HeaderType: sx126x.SX126X_LORA_HEADER_EXPLICIT,
Preamble: 12,
Ldr: sx126x.SX126X_LORA_LOW_DATA_RATE_OPTIMIZE_OFF,
Iq: sx126x.SX126X_LORA_IQ_STANDARD,
Crc: sx126x.SX126X_LORA_CRC_ON,
SyncWord: sx126x.SX126X_LORA_MAC_PRIVATE_SYNCWORD,
LoraTxPowerDBm: 14,
}
loraRadio.LoraConfig(loraConf)
*/
println("Start Configure Lora")
loraRadio.SetOpModeLora()
loraRadio.SetOpMode(sx127x.SX127X_OPMODE_SLEEP)
loraRadio.SetTxPower(11, true)
loraRadio.SetFrequency(FREQ)
loraRadio.SetBandwidth(sx127x.SX127X_LORA_BW_125_0)
loraRadio.SetCodingRate(sx127x.SX127X_LORA_CR_4_5)
loraRadio.SetRxPayloadCrc(sx127x.SX127X_LORA_CRC_ON)
loraRadio.SetHeaderMode(sx127x.SX127X_LORA_HEADER_EXPLICIT)
loraRadio.SetLowDataRateOptim(sx127x.SX127X_LOW_DATARATE_OPTIM_OFF)
println("End Configure Lora")
for {
loraRadio.TxLora([]byte("Hello"))
machine.LED.Set(!machine.LED.Get())
time.Sleep(time.Second * 2)
}
/*
// Although LoraConfig has already configured most of Lora settings,
// the following lines are still required to enable Continuous Preamble/Wave
loraRadio.SetPacketType(sx126x.SX126X_PACKET_TYPE_LORA)
loraRadio.SetRfFrequency(loraConf.Freq)
loraRadio.SetModulationParams(loraConf.Sf, loraConf.Bw, loraConf.Cr, loraConf.Ldr)
loraRadio.SetTxParams(loraConf.LoraTxPowerDBm, sx126x.SX126X_PA_RAMP_200U)
for {
println("2 seconds in Continuous Preamble")
loraRadio.SetStandby()
loraRadio.SetTxContinuousPreamble()
time.Sleep(2 * time.Second)
println("Continuous Preamble Stopped")
loraRadio.SetStandby()
time.Sleep(10 * time.Second)
println("2 seconds in Continuous Wave")
loraRadio.SetTxContinuousWave()
time.Sleep(2 * time.Second)
println(" Continuous Wave Stopped")
loraRadio.SetStandby()
time.Sleep(60 * time.Second)
}
*/
}
+124
View File
@@ -0,0 +1,124 @@
package sx127x
const (
// registers
REG_FIFO = 0x00
REG_OP_MODE = 0x01
REG_FRF_MSB = 0x06
REG_FRF_MID = 0x07
REG_FRF_LSB = 0x08
REG_PA_CONFIG = 0x09
REG_PA_RAMP = 0x0a
REG_OCP = 0x0b
REG_LNA = 0x0c
REG_FIFO_ADDR_PTR = 0x0d
REG_FIFO_TX_BASE_ADDR = 0x0e
REG_FIFO_RX_BASE_ADDR = 0x0f
REG_FIFO_RX_CURRENT_ADDR = 0x10
REG_IRQ_FLAGS_MASK = 0x11
REG_IRQ_FLAGS = 0x12
REG_RX_NB_BYTES = 0x13
REG_PKT_SNR_VALUE = 0x19
REG_PKT_RSSI_VALUE = 0x1a
REG_RSSI_VALUE = 0x1b
REG_MODEM_CONFIG_1 = 0x1d
REG_MODEM_CONFIG_2 = 0x1e
REG_SYMB_TIMEOUT_LSB = 0x1f
REG_PREAMBLE_MSB = 0x20
REG_PREAMBLE_LSB = 0x21
REG_PAYLOAD_LENGTH = 0x22
REG_MAX_PAYLOAD_LENGTH = 0x23
REG_HOP_PERIOD = 0x24
REG_MODEM_CONFIG_3 = 0x26
REG_FREQ_ERROR_MSB = 0x28
REG_FREQ_ERROR_MID = 0x29
REG_FREQ_ERROR_LSB = 0x2a
REG_RSSI_WIDEBAND = 0x2c
REG_DETECTION_OPTIMIZE = 0x31
REG_INVERTIQ = 0x33
REG_DETECTION_THRESHOLD = 0x37
REG_SYNC_WORD = 0x39
REG_INVERTIQ2 = 0x3b
REG_DIO_MAPPING_1 = 0x40
REG_DIO_MAPPING_2 = 0x41
REG_VERSION = 0x42
REG_PA_DAC = 0x4d
// PA config
PA_BOOST = 0x80
// Bits masking the corresponding IRQs from the radio
IRQ_LORA_RXTOUT_MASK = uint8(0x80)
IRQ_LORA_RXDONE_MASK = uint8(0x40)
IRQ_LORA_CRCERR_MASK = uint8(0x20)
IRQ_LORA_HEADER_MASK = uint8(0x10)
IRQ_LORA_TXDONE_MASK = uint8(0x08)
IRQ_LORA_CDDONE_MASK = uint8(0x04)
IRQ_LORA_FHSSCH_MASK = uint8(0x02)
IRQ_LORA_CDDETD_MASK = uint8(0x01)
// DIO function mappings D0D1D2D3
MAP_DIO0_LORA_RXDONE = uint8(0x00) // 00------
MAP_DIO0_LORA_TXDONE = uint8(0x40) // 01------
MAP_DIO1_LORA_RXTOUT = uint8(0x00) // --00----
MAP_DIO1_LORA_NOP = uint8(0x30) // --11----
MAP_DIO2_LORA_NOP = uint8(0xC0) // ----11--
PAYLOAD_LENGTH = uint8(0x40)
// Low Noise Amp
LNA_MAX_GAIN = uint8(0x23)
LNA_OFF_GAIN = uint8(0x00)
LNA_LOW_GAIN = uint8(0x20)
// Header type
SX127X_LORA_HEADER_EXPLICIT = uint8(0x00)
SX127X_LORA_HEADER_IMPLICIT = uint8(0x01)
// CRC
SX127X_LORA_CRC_OFF = uint8(0x00)
SX127X_LORA_CRC_ON = uint8(0x01)
// IQ Polarity
SX127X_LORA_IQ_STANDARD = uint8(0x00)
SX127X_LORA_IQ_INVERTED = uint8(0x01)
// Coding Rate
SX127X_LORA_CR_4_5 = uint8(0x01)
SX127X_LORA_CR_4_6 = uint8(0x02)
SX127X_LORA_CR_4_7 = uint8(0x03)
SX127X_LORA_CR_4_8 = uint8(0x04)
// Bandwidth
SX127X_LORA_BW_7_8 = uint8(0x00)
SX127X_LORA_BW_10_4 = uint8(0x01)
SX127X_LORA_BW_15_6 = uint8(0x02)
SX127X_LORA_BW_20_8 = uint8(0x03)
SX127X_LORA_BW_31_25 = uint8(0x04)
SX127X_LORA_BW_41_7 = uint8(0x05)
SX127X_LORA_BW_62_5 = uint8(0x06)
SX127X_LORA_BW_125_0 = uint8(0x07)
SX127X_LORA_BW_250_0 = uint8(0x08)
SX127X_LORA_BW_500_0 = uint8(0x09)
// Spreading factor
SX127X_LORA_SF6 = uint8(0x06)
SX127X_LORA_SF7 = uint8(0x07)
SX127X_LORA_SF8 = uint8(0x08)
SX127X_LORA_SF9 = uint8(0x09)
SX127X_LORA_SF10 = uint8(0x0A)
SX127X_LORA_SF11 = uint8(0x0B)
SX127X_LORA_SF12 = uint8(0x0C)
// Optim Low DR
SX127X_LOW_DATARATE_OPTIM_OFF = uint8(0x00)
SX127X_LOW_DATARATE_OPTIM_ON = uint8(0x01)
// Automatic gain control
SX127X_AGC_AUTO_OFF = uint8(0x00)
SX127X_AGC_AUTO_ON = uint8(0x01)
// Operation modes
SX127X_OPMODE_LORA = uint8(0x80)
SX127X_OPMODE_MASK = uint8(0x07)
SX127X_OPMODE_SLEEP = uint8(0x00)
SX127X_OPMODE_STANDBY = uint8(0x01)
SX127X_OPMODE_FSTX = uint8(0x02)
SX127X_OPMODE_TX = uint8(0x03)
SX127X_OPMODE_FSRX = uint8(0x04)
SX127X_OPMODE_RX = uint8(0x05)
SX127X_OPMODE_RX_SINGLE = uint8(0x06)
SX127X_OPMODE_CAD = uint8(0x07)
)
+645
View File
@@ -0,0 +1,645 @@
// Package sx127x provides a driver for SX127x LoRa transceivers.
// References:
// https://electronics.stackexchange.com/questions/394296/can-t-get-simple-lora-receiver-to-work
// https://www.st.com/resource/en/user_manual/dm00300436-stm32-lora-expansion-package-for-stm32cube-stmicroelectronics.pdf
package sx127x
import (
"errors"
"machine"
"time"
"tinygo.org/x/drivers"
)
const (
RadioEventRxDone = iota
RadioEventTxDone = iota
RadioEventTimeout = iota
RadioEventWatchdog = iota
RadioEventCrcError = iota
RadioEventUnhandled = iota
)
// So we can keep track of the origin of interruption
const (
SPI_BUFFER_SIZE = 256
)
// RadioEvent are used for communicating in the radio Event Channel
type RadioEvent struct {
EventType int
IRQStatus uint8
EventData []byte
}
// Device wraps an SPI connection to a SX127x device.
type Device struct {
spi drivers.SPI // SPI bus for module communication
rstPin, csPin machine.Pin // GPIOs for reset and chip select
radioEventChan chan RadioEvent // Channel for Receiving events
loraConf LoraConfig // Current Lora configuration
deepSleep bool // Internal Sleep state
deviceType int // sx1261,sx1262,sx1268 (defaults sx1261)
spiBuffer [SPI_BUFFER_SIZE]uint8
packetIndex uint8 // FIXME ... useless ?
}
// Config holds the LoRa configuration parameters
type LoraConfig struct {
Freq uint32 // Frequency
Cr uint8 // Coding Rate
Sf uint8 // Spread Factor
Bw uint8 // Bandwidth
Ldr uint8 // Low Data Rate
Preamble uint16 // PreambleLength
SyncWord uint16 // Sync Word
HeaderType uint8 // Header : Implicit/explicit
Crc uint8 // CRC : Yes/No
Iq uint8 // iq : Standard/inverted
LoraTxPowerDBm int8 // Tx power in Dbm
}
// --------------------------------------------------
// Channel and events
// --------------------------------------------------
//NewRadioEvent() returns a new RadioEvent that can be used in the RadioChannel
func NewRadioEvent(eType int, irqStatus uint8, eData []byte) RadioEvent {
r := RadioEvent{EventType: eType, IRQStatus: irqStatus, EventData: eData}
return r
}
// Get the RadioEvent channel of the device
func (d *Device) GetRadioEventChan() chan RadioEvent {
return d.radioEventChan
}
// New creates a new SX127x connection. The SPI bus must already be configured.
func New(spi machine.SPI, csPin machine.Pin, rstPin machine.Pin) *Device {
k := Device{
spi: spi,
csPin: csPin,
rstPin: rstPin,
radioEventChan: make(chan RadioEvent, 10),
}
return &k
}
// Reset re-initialize the sx127x device
func (d *Device) Reset() {
d.rstPin.Low()
time.Sleep(100 * time.Millisecond)
d.rstPin.High()
time.Sleep(100 * time.Millisecond)
}
// DetectDevice checks if device responds on the SPI bus
func (d *Device) DetectDevice() bool {
id := d.GetVersion()
return (id == 0x12)
}
// ReadRegister reads register value
func (d *Device) ReadRegister(reg uint8) uint8 {
d.csPin.Low()
d.spi.Tx([]byte{reg & 0x7f}, nil)
var value [1]byte
d.spi.Tx(nil, value[:])
d.csPin.High()
return value[0]
}
// WriteRegister writes value to register
func (d *Device) WriteRegister(reg uint8, value uint8) uint8 {
var response [1]byte
d.csPin.Low()
d.spi.Tx([]byte{reg | 0x80}, nil)
d.spi.Tx([]byte{value}, response[:])
d.csPin.High()
return response[0]
}
// SetOpMode changes the sx1276 mode
func (d *Device) SetOpMode(mode uint8) {
cur := d.ReadRegister(REG_OP_MODE)
new := (cur & (^SX127X_OPMODE_MASK)) | mode
d.WriteRegister(REG_OP_MODE, new)
}
// SetOpMode changes the sx1276 mode
func (d *Device) SetOpModeLora() {
d.WriteRegister(REG_OP_MODE, SX127X_OPMODE_LORA)
}
// SetupLora configures sx127x Lora mode
func (d *Device) SetupLora(config LoraConfig) error {
d.loraConf = config
// Reset the device first
d.Reset()
// Switch to Lora mode
d.SetOpModeLora()
d.SetOpMode(SX127X_OPMODE_SLEEP)
// Access High Frequency Mode
d.SetLowFrequencyModeOn(false)
// Set PA Ramp time 50 uS
d.WriteRegister(REG_PA_RAMP, (d.ReadRegister(REG_PA_RAMP)&0xF0)|0x08) // set PA ramp-up time 50 uSec
// Enable power (manage Over Current, PA_Boost ... etc)
d.SetTxPower(11, true)
// Set Low Noise Amplifier to MAX
d.WriteRegister(REG_LNA, LNA_MAX_GAIN)
// Set Frequency
d.SetFrequency(d.loraConf.Freq)
// Set Bandwidth
d.SetBandwidth(d.loraConf.Bw)
//Set Coding Rate (TODO : Check)
d.SetCodingRate(d.loraConf.Cr)
// Set explicit header
d.SetHeaderMode(SX127X_LORA_HEADER_EXPLICIT)
// Enable CRC
d.SetRxPayloadCrc(SX127X_LORA_CRC_ON)
// Disable IQ Polarization
d.SetIQPolarity(SX127X_LORA_IQ_STANDARD)
// Disable HOP PERIOD
d.SetHopPeriod(0x00)
// Continuous Mode
d.SetTxContinuousMode(false)
// Set Lora Sync
d.SetSyncWord(0x34)
//Set Max payload length (default value)
d.WriteRegister(REG_MAX_PAYLOAD_LENGTH, 0xFF)
// Mandatory in Implicit header Mode (default value)
d.WriteRegister(REG_PAYLOAD_LENGTH, 0x01)
// AGC On
d.SetAgcAuto(SX127X_AGC_AUTO_ON)
// set FIFO base addresses
d.WriteRegister(REG_FIFO_TX_BASE_ADDR, 0)
d.WriteRegister(REG_FIFO_RX_BASE_ADDR, 0)
return nil
}
// TxLora sends a packet in Lora mode
// Intmode will enable interrupt mode.
// If disabled, function will probe registers for TXDone before
// returning
func (d *Device) TxLora(payload []byte) error {
// Are we already in Lora mode ?
r := d.ReadRegister(REG_OP_MODE)
if (r & SX127X_OPMODE_LORA) != SX127X_OPMODE_LORA {
return errors.New("Not in Lora mode")
}
// set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP
d.WriteRegister(REG_DIO_MAPPING_1, MAP_DIO0_LORA_TXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP)
// Clear all radio IRQ Flags
d.WriteRegister(REG_IRQ_FLAGS, 0xFF)
// Mask all but TxDone
d.WriteRegister(REG_IRQ_FLAGS_MASK, ^IRQ_LORA_TXDONE_MASK)
// initialize the payload size and address pointers
d.WriteRegister(REG_FIFO_TX_BASE_ADDR, 0)
d.WriteRegister(REG_FIFO_ADDR_PTR, 0)
d.WriteRegister(REG_PAYLOAD_LENGTH, uint8(len(payload)))
// Copy payload to FIFO // TODO: Bulk
for i := 0; i < len(payload); i++ {
d.WriteRegister(REG_FIFO, payload[i])
}
// Enable TX
d.SetOpMode(SX127X_OPMODE_TX)
msg := <-d.GetRadioEventChan()
if msg.EventType != RadioEventTxDone {
return errors.New("Unexpected Radio Event while TX")
}
return nil
}
/*
//CheckIrq can be called periodicaly to check for RXDONE,TXDONE,RXTOUT
//but It would be more efficient to call it on DIO0/1 pins rising edge
func (d *Device) CheckIrq() {
irqFlags := d.ReadRegister(REG_IRQ_FLAGS)
//println("sx21276: irq=", irqFlags)
// We have a packet
if (irqFlags & IRQ_LORA_RXDONE_MASK) > 0 {
//println("sx1276: RXDONE")
// Read current packet
buf := []byte{}
packetLength := d.ReadRegister(REG_RX_NB_BYTES)
d.WriteRegister(REG_FIFO_ADDR_PTR, d.ReadRegister(REG_FIFO_RX_CURRENT_ADDR)) // Reset FIFO Read Addr
for i := uint8(0); i < packetLength; i++ {
buf = append(buf, d.ReadRegister(REG_FIFO))
}
// Send RXDONE to the defined event channel
d.radioEventChan <- RadioEvent{EventType: EventRxDone, EventData: buf}
}
if (irqFlags & IRQ_LORA_TXDONE_MASK) > 0 {
//println("sx1276: TXDONE")
d.radioEventChan <- RadioEvent{EventType: EventTxDone, EventData: nil}
}
if (irqFlags & IRQ_LORA_RXTOUT_MASK) > 0 {
//println("sx1276: RXTOUT")
d.radioEventChan <- RadioEvent{EventType: EventRxTimeout, EventData: nil}
}
// Sigh: on some processors, for some unknown reason, doing this only once does not actually
// clear the radio's interrupt flag. So we do it twice. Why?
d.WriteRegister(REG_IRQ_FLAGS, irqFlags) // Clear all IRQ flags
d.WriteRegister(REG_IRQ_FLAGS, irqFlags) // Clear all IRQ flags
}
*/
/*
// SetRadioEventChan defines a channel so the driver can send its Radio Events
func (d *Device) SetRadioEventChan(channel chan RadioEvent) {
d.radioEventChan = channel
}
*/
/*
// Init reboots the SX1276 module
func (d *Device) Init(cfg LoraConfig) (err error) {
d.loraConf = cfg
d.csPin.High()
d.Reset()
return nil
}
*/
/*
// ConfigureLoraModem prepares for LORA communications
func (d *Device) ConfigureLoraModem() {
// Sleep mode required to go LOra
d.OpMode(OPMODE_SLEEP)
// Set Lora mode (from sleep)
d.OpModeLora()
// Switch to standby mode
d.OpMode(OPMODE_STANDBY)
// Set Bandwidth
d.SetBandwidth(d.cnf.Bandwidth)
// Disable IQ Polarization
d.SetInvertedIQ(false)
// Set implicit header
d.SetImplicitHeaderModeOn(false)
// We want CRC
d.SetRxPayloadCrc(true)
d.SetAgcAutoOn(true)
if d.GetBandwidth() == 125000 && (d.GetSpreadingFactor() == 11 || d.GetSpreadingFactor() == 12) {
d.SetLowDataRateOptimOn(true)
}
// Configure Output Power
d.WriteRegister(REG_PA_RAMP, (d.ReadRegister(REG_PA_RAMP)&0xF0)|0x08) // set PA ramp-up time 50 uSec
d.WriteRegister(REG_PA_CONFIG, 0xFF) //PA_BOOST MAX
d.SetOCP(140) // Over Current protection
// RX and premamble
d.WriteRegister(REG_PREAMBLE_MSB, 0x00) // Preamble set to 8 symp
d.WriteRegister(REG_PREAMBLE_LSB, 0x08) // -> 0x0008 + 4 = 12
d.WriteRegister(REG_SYMB_TIMEOUT_LSB, 0x25) //Rx Timeout 37 symbol
// set FIFO base addresses
d.WriteRegister(REG_FIFO_TX_BASE_ADDR, 0)
d.WriteRegister(REG_FIFO_RX_BASE_ADDR, 0)
}
*/
//GetVersion returns hardware version of sx1276 chipset
func (d *Device) GetVersion() uint8 {
return (d.ReadRegister(REG_VERSION))
}
// IsTransmitting tests if a packet transmission is in progress
func (d *Device) IsTransmitting() bool {
return (d.ReadRegister(REG_OP_MODE) & SX127X_OPMODE_TX) == SX127X_OPMODE_TX
}
// ReadPacket reads a received packet into a byte array
func (d *Device) ReadPacket(packet []byte) int {
available := int(d.ReadRegister(REG_RX_NB_BYTES) - d.packetIndex)
if available > len(packet) {
available = len(packet)
}
for i := 0; i < available; i++ {
d.packetIndex++
packet[i] = d.ReadRegister(REG_FIFO)
}
return available
}
// LastPacketRSSI gives the RSSI of the last packet received
func (d *Device) LastPacketRSSI() uint8 {
// section 5.5.5
var adjustValue uint8 = 157
if d.loraConf.Freq < 868000000 {
adjustValue = 164
}
return d.ReadRegister(REG_PKT_RSSI_VALUE) - adjustValue
}
// LastPacketSNR gives the SNR of the last packet received
func (d *Device) LastPacketSNR() uint8 {
return uint8(d.ReadRegister(REG_PKT_SNR_VALUE) / 4)
}
/*
// GetFrequency returns the frequency the LoRa module is using
func (d *Device) GetFrequency() uint32 {
f := uint64(d.ReadRegister(REG_FRF_LSB))
f += uint64(d.ReadRegister(REG_FRF_MID)) << 8
f += uint64(d.ReadRegister(REG_FRF_MSB)) << 16
f = (f * 32000000) >> 19 //FSTEP = FXOSC/2^19
return uint32(f)
}
*/
// SetFrequency updates the frequency the LoRa module is using
func (d *Device) SetFrequency(frequency uint32) {
d.loraConf.Freq = frequency
var frf = (uint64(frequency) << 19) / 32000000
d.WriteRegister(REG_FRF_MSB, uint8(frf>>16))
d.WriteRegister(REG_FRF_MID, uint8(frf>>8))
d.WriteRegister(REG_FRF_LSB, uint8(frf>>0))
}
// GetSpreadingFactor returns the spreading factor the LoRa module is using
func (d *Device) GetSpreadingFactor() uint8 {
return d.ReadRegister(REG_MODEM_CONFIG_2) >> 4
}
// GetRSSI returns current RSSI
func (d *Device) GetRSSI() uint8 {
return d.ReadRegister(REG_RSSI_VALUE)
}
/*
// GetBandwidth returns the bandwidth the LoRa module is using
func (d *Device) GetBandwidth() int32 {
return int32(d.loraConf.Bw)
}
*/
//SetSyncWord defines sync word
func (d *Device) SetSyncWord(syncWord uint8) {
d.WriteRegister(REG_SYNC_WORD, syncWord)
}
// SetIQPolarity Sets I/Q polarity configuration
func (d *Device) SetIQPolarity(val uint8) {
if val == SX127X_LORA_IQ_INVERTED {
//Invert IQ Back
d.WriteRegister(0x33, 0x67)
d.WriteRegister(0x3B, 0x19)
} else {
//Set IQ to normal values
d.WriteRegister(0x33, 0x27)
d.WriteRegister(0x3B, 0x1D)
}
}
// RxLora sets device in receive mode
func (d *Device) RxLora() {
// set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP
d.WriteRegister(REG_DIO_MAPPING_1, MAP_DIO0_LORA_RXDONE|MAP_DIO1_LORA_NOP|MAP_DIO2_LORA_NOP)
// Clear all radio IRQ Flags
d.WriteRegister(REG_IRQ_FLAGS, 0xFF)
// Mask all but TxDone
d.WriteRegister(REG_IRQ_FLAGS_MASK, ^IRQ_LORA_RXDONE_MASK)
d.SetOpMode(SX127X_OPMODE_RX) // RX Mode
}
/*
// setLdoFlag() enables LowDataRateOptimize bit (mandated when symbol length >16ms)
// LGTM
func (d *Device) setLdoFlag() {
// Section 4.1.1.5
var symbolDuration = 1000 / (d.GetBandwidth() / (1 << d.GetSpreadingFactor()))
var config3 = d.ReadRegister(REG_MODEM_CONFIG_3)
// Section 4.1.1.6
if symbolDuration > 16 {
config3 = config3 | 0x08
} else {
config3 = config3 & 0xF7
}
d.WriteRegister(REG_MODEM_CONFIG_3, config3)
}
*/
// SetTxPower sets the transmitter output power
func (d *Device) SetTxPower(txPower int8, paBoost bool) {
if !paBoost {
// RFO
if txPower < 0 {
txPower = 0
} else if txPower > 14 {
txPower = 14
}
d.WriteRegister(REG_PA_CONFIG, uint8(0x70)|uint8(txPower))
} else {
//PA_BOOST
if txPower > 17 {
if txPower > 20 {
txPower = 20
}
txPower -= 3
// High Power +20 dBm Operation (Semtech SX1276/77/78/79 5.4.3.)
d.WriteRegister(REG_PA_DAC, 0x87)
d.SetOCP(140)
} else {
if txPower < 2 {
txPower = 2
}
d.WriteRegister(REG_PA_DAC, 0x84)
d.SetOCP(100)
}
d.WriteRegister(REG_PA_CONFIG, uint8(PA_BOOST)|uint8(txPower-2))
}
}
// SetRxTimeout defines RX Timeout expressed as number of symbols
func (d *Device) SetRxTimeout(tmoutSymb uint8) {
d.WriteRegister(REG_SYMB_TIMEOUT_LSB, tmoutSymb)
}
// SetOCP defines Overload Current Protection configuration
func (d *Device) SetOCP(mA uint8) {
ocpTrim := uint8(27)
if mA < 45 {
mA = 45
}
if mA <= 120 {
ocpTrim = (mA - 45) / 5
} else if mA <= 240 {
ocpTrim = (mA + 30) / 10
}
d.WriteRegister(REG_OCP, 0x20|(0x1F&ocpTrim))
}
// ---------------
// RegModemConfig1
// ---------------
// SetBandwidth updates the bandwidth the LoRa module is using
func (d *Device) SetBandwidth(bw uint8) {
d.loraConf.Bw = bw
d.WriteRegister(REG_MODEM_CONFIG_1, (d.ReadRegister(REG_MODEM_CONFIG_1)&0x0f)|(bw<<4))
}
// SetCodingRate updates the coding rate the LoRa module is using
func (d *Device) SetCodingRate(cr uint8) {
d.loraConf.Cr = cr
d.WriteRegister(REG_MODEM_CONFIG_1, (d.ReadRegister(REG_MODEM_CONFIG_1)&0xf1)|(cr<<1))
}
// SetImplicitHeaderModeOn Enables implicit header mode ***
func (d *Device) SetHeaderMode(headerType uint8) {
d.loraConf.HeaderType = headerType
if headerType == SX127X_LORA_HEADER_IMPLICIT {
d.WriteRegister(REG_MODEM_CONFIG_1, d.ReadRegister(REG_MODEM_CONFIG_1)|0x01)
} else {
d.WriteRegister(REG_MODEM_CONFIG_1, d.ReadRegister(REG_MODEM_CONFIG_1)&0xfe)
}
}
// ---------------
// RegModemConfig2
// ---------------
// SetSpreadingFactor updates the spreading factor the LoRa module is using
func (d *Device) SetSpreadingFactor(sf uint8) {
d.loraConf.Sf = sf
if sf == SX127X_LORA_SF6 {
d.WriteRegister(REG_DETECTION_OPTIMIZE, 0xc5)
d.WriteRegister(REG_DETECTION_THRESHOLD, 0x0c)
} else {
d.WriteRegister(REG_DETECTION_OPTIMIZE, 0xc3)
d.WriteRegister(REG_DETECTION_THRESHOLD, 0x0a)
}
var newValue = (d.ReadRegister(REG_MODEM_CONFIG_2) & 0x0f) | ((sf << 4) & 0xf0)
d.WriteRegister(REG_MODEM_CONFIG_2, newValue)
}
// SetTxContinuousMode enable Continuous Tx mode
func (d *Device) SetTxContinuousMode(val bool) {
if val {
d.WriteRegister(REG_MODEM_CONFIG_2, d.ReadRegister(REG_MODEM_CONFIG_2)|0x08)
} else {
d.WriteRegister(REG_MODEM_CONFIG_2, d.ReadRegister(REG_MODEM_CONFIG_2)&0xf7)
}
}
// SetRxPayloadCrc Enable CRC generation and check on payload
func (d *Device) SetRxPayloadCrc(val uint8) {
if val == SX127X_LORA_CRC_ON {
d.WriteRegister(REG_MODEM_CONFIG_2, d.ReadRegister(REG_MODEM_CONFIG_2)|0x04)
} else {
d.WriteRegister(REG_MODEM_CONFIG_2, d.ReadRegister(REG_MODEM_CONFIG_2)&0xfb)
}
}
// ---------------
// RegModemConfig3
// ---------------
// SetAgcAutoOn enables Automatic Gain Control
func (d *Device) SetAgcAuto(val uint8) {
if val == SX127X_AGC_AUTO_ON {
d.WriteRegister(REG_MODEM_CONFIG_3, d.ReadRegister(REG_MODEM_CONFIG_3)|0x04)
} else {
d.WriteRegister(REG_MODEM_CONFIG_3, d.ReadRegister(REG_MODEM_CONFIG_3)&0xfb)
}
}
// SetLowDataRateOptimize enables Low Data Rate Optimization
func (d *Device) SetLowDataRateOptim(val uint8) {
if val == SX127X_LOW_DATARATE_OPTIM_ON {
d.WriteRegister(REG_MODEM_CONFIG_3, d.ReadRegister(REG_MODEM_CONFIG_3)|0x08)
} else {
d.WriteRegister(REG_MODEM_CONFIG_3, d.ReadRegister(REG_MODEM_CONFIG_3)&0xf7)
}
}
// SetLowFrequencyModeOn enables Low Data Rate Optimization
func (d *Device) SetLowFrequencyModeOn(val bool) {
if val {
d.WriteRegister(REG_OP_MODE, d.ReadRegister(REG_OP_MODE)|0x04)
} else {
d.WriteRegister(REG_OP_MODE, d.ReadRegister(REG_OP_MODE)&0xfb)
}
}
// SetHopPeriod sets number of symbol periods between frequency hops. (0 = disabled).
func (d *Device) SetHopPeriod(val uint8) {
d.WriteRegister(REG_HOP_PERIOD, val)
}
// HandleInterrupt must be called by main code on DIO state change.
func (d *Device) HandleInterrupt() {
// Get IRQ and clear
st := d.ReadRegister(REG_IRQ_FLAGS)
d.WriteRegister(REG_IRQ_FLAGS, 0xFF)
rChan := d.GetRadioEventChan()
if (st & IRQ_LORA_RXDONE_MASK) > 0 {
rChan <- NewRadioEvent(RadioEventRxDone, st, nil)
}
if (st & IRQ_LORA_TXDONE_MASK) > 0 {
rChan <- NewRadioEvent(RadioEventTxDone, st, nil)
}
if (st & IRQ_LORA_RXTOUT_MASK) > 0 {
rChan <- NewRadioEvent(RadioEventTimeout, st, nil)
}
if (st & IRQ_LORA_CRCERR_MASK) > 0 {
rChan <- NewRadioEvent(RadioEventCrcError, st, nil)
}
}
// PrintRegisters outputs the sx127x transceiver registers
func (d *Device) PrintRegisters(compact bool) {
for i := uint8(0); i < 128; i++ {
v := d.ReadRegister(i)
print(v, " ")
}
println()
}