sx1231

package
v0.0.0-...-6570e0f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 25, 2017 License: Apache-2.0 Imports: 7 Imported by: 0

README

RFM69 Driver Info

Notes about the sx1231

There are some 20 settings that all interact in undocumented ways, so getting to a robust driver is tricky.

The biggest area of trouble is around AFC and AGC. What seems to happen is that once the RSSI threshold is crossed the radio chooses an LNA setting and a frequency correction. It is then stuck with that until it decides to restart RX. The conditions for that restart are not documented, but experiments show that the RSSI threshold has a big influence, and thus one has to assume the restart happens when RSSI drops below the threshold. Once sync match occurs the situation is different and it's the packet length that drives the state machine.

Some situations that lead to trouble are a weak noise burst that crosses the RSSI threshold, causes the radio to make a frequency adjustment and set maximum LNA gain, and then a real packet comes in strongly but overdrives the receiver due to the LNA gain set to max. A typical symptom is systematic CRC errors for such packets. Or a real packet comes in weakly but is missed because AFC got dragged to one side of the center frequency due to the noise and the packet is at the other side.

A possible solution is to move the RSSI threshold up, but that will ignore packets that are just above noise and it appears difficult to auto-adjust the RSSI threshold.

This leaves two primary modes of operation of the radio:

  • Minimize interrupts by using interrupt on sync match, let the radio restart RX on its own when RSSI gets crossed and no sync match occurs, ensure that the RSSI threshold is reasonable and doesn't make the radio lock onto background noise for too long, and accept that some packets may be missed because the radio is stuck in the wrong AGC/AFC setting due to a burst of noise.
  • Minimize packet loss by using interrupt on rssi, reset rx after a timeout to prevent the radio from being stuck in the wrong AFC/AGC setting, accept the fact that there will be many spurious interrupts.

It should be possible to avoid the "AFC/AGC stuck in the wrong setting due to noise" issue by disabling AFC and AGC. It appears that disabling AFC causes no frequency error measurement to be made, that makes it impossible to dynamically tune the radios for crystal freq error. Disabling AGC means that all transmitters have to adjust their TX power so they all come in about at the same strength. While these things are certainly possible they don't look simple either...

This driver ends up using the interrupt-on-rssi approach and resets RX after 80 byte times have passed (66 bytes max payload plus preamble, sync bytes, and CRC amount to 70-odd bytes). It tunes the RSSI threshold every 10 seconds such that the number of interrupts falls between 2.5 and 10 interrupts per second. (This is not expected to be foolproof, alas).

The FEI and AGC measurements can be used to tune the frequencies of two radios to match, but there are some tricks. If AFC low-beta offset is enabled, the AFC value reported will include the offset, so that must be subtracted out. Also, the FEI value reported seems to be after AFC is applied, at least it doesn't seem usable. Since the AFC value is reset when Rx is restarted by emptying the FIFO it must be captured before that.

This driver assumes settings with a modulation index >2 and disables the AFC low-beta offset.

Documentation

Overview

The SX1231 package interfaces with a HopeRF RFM69 radio connected to an SPI bus.

The RFM69 modules use a Semtech SX1231 or SX1231H radio chip and this package should work fine with other radio modules using the same chip. The only real difference will be the power output section where different modules use different output stage configurations.

The driver is fully interrupt driven and requires that the radio's DIO0 pin be connected to an interrupt capable GPIO pin. The transmit and receive interface uses a pair of tx and rx channels, each having a small amount of buffering.

In general, other than a few user errors (such as passing too large a packet to Send) there should be no errors during the radio's operation unless there is a hardware failure. For this reason radio interface errors are treated as fatal: if such an error occurs the rx channel is closed and the error is recorded in the Radio struct where it can be retrieved using the Error function. The object will be unusable for further operation and the client code will have to create and initialize a fresh object which will re-establish communication with the radio chip.

This driver does not do a number of things that other sx1231 drivers tend to do with the goal of leaving these tasks to higher-level drivers. This driver does not use the address filtering capability: it recevies all packets because that's simpler and the few extra interrupts should not matter to a system that can run Golang. It also accepts packets that have a CRC error and simply flags the error. It does not constrain the sync bytes, the frequency, or the data rates.

The main limitations of this driver are that it operates the sx1231 in FSK variable-length packet mode and limits the packet size to the 66 bytes that fit into the FIFO, meaning that the payloads pushed into the TX channel must be 65 bytes or less, leaving one byte for the required packet length.

The methods on the Radio object are not concurrency safe. Since they all deal with configuration this should not pose difficulties. The Error function may be called from multiple goroutines and obviously the TX and RX channels work well with concurrency.

Index

Constants

View Source
const (
	REG_FIFO        = 0x00
	REG_OPMODE      = 0x01
	REG_DATAMODUL   = 0x02
	REG_BITRATEMSB  = 0x03
	REG_FDEVMSB     = 0x05
	REG_FRFMSB      = 0x07
	REG_AFCCTRL     = 0x0B
	REG_VERSION     = 0x10
	REG_PALEVEL     = 0x11
	REG_LNAVALUE    = 0x18
	REG_RXBW        = 0x19
	REG_AFCBW       = 0x1A
	REG_AFCFEI      = 0x1E
	REG_AFCMSB      = 0x1F
	REG_AFCLSB      = 0x20
	REG_FEIMSB      = 0x21
	REG_FEILSB      = 0x22
	REG_RSSICONFIG  = 0x23
	REG_RSSIVALUE   = 0x24
	REG_DIOMAPPING1 = 0x25
	REG_IRQFLAGS1   = 0x27
	REG_IRQFLAGS2   = 0x28
	REG_RSSITHRES   = 0x29
	REG_SYNCCONFIG  = 0x2E
	REG_SYNCVALUE1  = 0x2F
	REG_SYNCVALUE2  = 0x30
	REG_NODEADDR    = 0x39
	REG_BCASTADDR   = 0x3A
	REG_FIFOTHRESH  = 0x3C
	REG_PKTCONFIG2  = 0x3D
	REG_AESKEYMSB   = 0x3E
	REG_TESTPA1     = 0x5A
	REG_TESTPA2     = 0x5C
	REG_TESTAFC     = 0x71

	MODE_SLEEP    = 0 << 2
	MODE_STANDBY  = 1 << 2
	MODE_FS       = 2 << 2
	MODE_TRANSMIT = 3 << 2
	MODE_RECEIVE  = 4 << 2

	START_TX = 0xC2
	STOP_TX  = 0x42

	RCCALSTART     = 0x80
	IRQ1_MODEREADY = 1 << 7
	IRQ1_RXREADY   = 1 << 6
	IRQ1_PLLLOCK   = 1 << 4
	IRQ1_RSSI      = 1 << 3
	IRQ1_TIMEOUT   = 1 << 2
	IRQ1_SYNCMATCH = 1 << 0

	IRQ2_FIFONOTEMPTY = 1 << 6
	IRQ2_PACKETSENT   = 1 << 3
	IRQ2_PAYLOADREADY = 1 << 2
	IRQ2_CRCOK        = 1 << 1

	DIO_MAPPING  = 0x31
	DIO_RSSI     = 0xC0
	DIO_SYNC     = 0x80
	DIO_PAYREADY = 0x40
	DIO_PKTSENT  = 0x00
)

Variables

View Source
var Rates = map[uint32]Rate{
	49230: {45000, 0, 0x4A, 0x42},
	49231: {180000, 0, 0x49, 0x49},
	49232: {45000, 0, 0x52, 0x4A},
	49233: {51660, 0, 0x52, 0x4A},
	50000: {90000, 0, 0x42, 0x42},
}

Rates is the table of supported bit rates and their corresponding register settings. The map key is the bit rate in bits per second. In order to operate at a new bit rate the table can be extended by the client.

Functions

func JLDecode

func JLDecode(grp byte, payload []byte) (
	src, dst byte, ack bool, outPayload []byte, err error,
)

JLDecode decodes a JeeLabs packet. See JLEncode for a description of the packet format. The outPayload it returns has the src/dst stripped.

func JLEncode

func JLEncode(grp, src, dst byte, ack bool, payload []byte) []byte

JLEncode encodes a JeeLabs packet.

Jeelabs native rfm69 packet format

From the radio's perspective, a packet has 5 preamble bytes, 2 sync bytes, a length byte, a dest address byte, up to 64 payload bytes, and 2-byte CRC. The length counts the payload plus the dest address.

The JeeLabs format defines the 5-byte preamble (the radio allows many options) and the 2 sync bytes. The first sync byte is 0x2d, the second is the group ID (network number). The JeeLabs format also uses the first payload byte as a source address. This means that the application payload can hold up to 63 bytes. Finally, in newer JeeLabs usage the first payload byte (after the src address) is a packet type format byte.

The destination address byte is calculated as the 6-bit destination node ID and two sync parity bits at the top. Bit 7 (MSB) is calculated as the group's b7^b5^b3^b1 and bit 6 as the group's b6^b4^b2^b0.

The source address byte is calculated as the 6-bit source node ID and two control bits. Bit 7 is an ACK request bit and bit 6 is unassigned.

A packet with destination ID 0 is a broadcast packet, a node ID of 62 is used for anonymouns tx-only nodes, and a node ID of 63 is used on the receiving end to denote a node that receives all packets regardless of destination (promiscuous mode).

func JeeLabsSync

func JeeLabsSync(grp byte) []byte

JeeLabsSync returns a byte array of sync bytes given the group number.

func MakeJLAck

func MakeJLAck(grp byte, payload []byte) []byte

MakeJLAck returns an ACK packet given a received payload with the ack bit set.

Types

type LogPrintf

type LogPrintf func(format string, v ...interface{})

LogPrintf is a function used by the driver to print logging info.

type Radio

type Radio struct {

	// state
	sync.Mutex // guard concurrent access to the radio
	// contains filtered or unexported fields
}

Radio represents a Semtech SX1231 radio as used in HopeRF's RFM69 modules.

func New

func New(port spi.Port, intr gpio.PinIn, opts RadioOpts) (*Radio, error)

New initializes an sx1231 Radio given an spi.Conn and an interrupt pin, and places the radio in receive mode.

The SPI bus must be set to 10Mhz max and mode 0.

The RF sync bytes used are specified using the sync array, the frequency is specified in Hz, Khz, or Mhz, and the data bitrate is specified in bits per second and must match one of the rates in the Rates table.

To transmit, push packet payloads into the returned txChan. Received packets will be sent on the returned rxChan, which has a small amount of buffering. The rxChan will be closed if a persistent error occurs when communicating with the device, use the Error() function to retrieve the error.

func (*Radio) Receive

func (r *Radio) Receive() (*RxPacket, error)

func (*Radio) SetFrequency

func (r *Radio) SetFrequency(freq uint32)

SetFrequency changes the center frequency at which the radio transmits and receives. The frequency can be specified at any scale (hz, khz, mhz). The frequency value is not checked and invalid values will simply cause the radio not to work particularly well.

func (*Radio) SetPower

func (r *Radio) SetPower(dbm byte)

SetPower configures the radio for the specified output power (TODO: should be in dBm).

func (*Radio) SetRate

func (r *Radio) SetRate(rate uint32)

SetRate sets the bit rate according to the Rates table. The rate requested must use one of the values from the Rates table. If it is not, nothing is changed.

func (*Radio) Transmit

func (r *Radio) Transmit(payload []byte) error

Transmit switches the radio's mode and starts transmitting a packet.

type RadioOpts

type RadioOpts struct {
	Sync    []byte    // RF sync bytes
	Freq    uint32    // frequency in Hz, Khz, or Mhz
	Rate    uint32    // data bitrate in bits per second, must exist in Rates table
	PABoost bool      // true: use PA1+PA2, false: use PA0
	Logger  LogPrintf // function to use for logging
}

RadioOpts contains options used when initilizing a Radio.

type Rate

type Rate struct {
	Fdev    int  // TX frequency deviation in Hz
	Shaping byte // 0:none, 1:gaussian BT=1, 2:gaussian BT=0.5, 3:gaussian BT=0.3
	RxBw    byte // value for rxBw register (0x19)
	AfcBw   byte // value for afcBw register (0x1A)
}

Rate describes the SX1231 configuration to achieve a specific bit rate.

The datasheet is somewhat confused and confusing about what Fdev and RxBw really mean. Fdev is defined as the deviation between the center freq and the modulated freq, while conventionally the frequency deviation fdev is the difference between the 0 and 1 freq's, thus the conventional fdev is Fdev*2.

Similarly the RxBw is specified as the single-sided bandwidth while conventionally the signal or channel bandwidths are defined using the total bandwidths.

Given that the sx1231 is a zero-if receiver it is recommended to configure a modulation index greater than 1.2, e.g. best approx 2. Modulation index is defined as fdev/bit-rate. This means that Fdev should be approx equal to bps. [Martyn, or are you targeting a modulation index of 4?]

The signal bandwidth (20dB roll-off) can be approximated by fdev + bit-rate. Since RxBw is specified as the single-sided bandwidth it needs to be at least (fdev+bit-rate)/2. Or, in sx1231 config terms, Fdev + bitrate/2. If AFC is used, in order to accommodate a crystal offset between Tx and Rx of Fdelta the AFC bandwidth should be approx fdev + bit-rate + 2*Fdelta.

type RxPacket

type RxPacket struct {
	Payload []byte    // payload, from address to last data byte, excluding length & crc
	Rssi    int       // rssi value for current packet
	Snr     int       // rssi - noise floor for current packet
	Fei     int       // frequency error for current packet
	At      time.Time // time of rx interrupt
}

RxPacket is a received packet with stats.

type Temporary

type Temporary interface {
	Temporary() bool
}

Temporary is an interface implemented by errors that are temporary and thus worth retrying.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL