Packages

This is a module for transmitter data using LoRa Radios. Radios: Semtech SX1276/77/78/79 based boards.

Current section

Files

Jump to
lora lib modem.ex
Raw

lib/modem.ex

defmodule LoRa.Modem do
use Bitwise
require Logger
alias ElixirALE.GPIO
alias LoRa.Communicator
alias LoRa.Parameters
# def transmitting?(spi) do
# irq_flags = Communicator.read_register(spi, Parameters.register.irq_flags)
# unless (irq_flags &&& Parameters.irq.tx_done_mask) == 0,
# do: Communicator.write_register(spi, Parameters.register.irq_flags, Parameters.irq.tx_done_mask)
# if (Communicator.read_register(spi, Parameters.register.op_mode) &&& Parameters.mode.tx) == Parameters.mode.tx, do: true, else: false
# end
def begin(frequency, spi, power \\ 17) do
# Sleep mode
sleep(spi)
# Set frequency
set_frequency(frequency, spi)
set_base_address(spi)
# Set LNA boost
set_LNA_boost(spi)
# Set auto AGC
set_auto_AGC(spi)
# Set output power to 17 dBm
set_tx_power(power, spi)
# put in standby mode
idle(spi)
end
def end_packet(from, spi, async? \\ false) do
Communicator.write_register(
spi,
Parameters.register().op_mode,
Parameters.mode().long_range_mode ||| Parameters.mode().tx
)
unless async? do
pid = spawn_link(__MODULE__, :verify_end_packet, [spi, from])
ref = Process.monitor(pid)
Task.yield(%Task{pid: pid, ref: ref, owner: from}, 2000)
end
end
def verify_end_packet(spi, from, counter \\ 0) do
flags = Communicator.read_register(spi, Parameters.register().irq_flags)
if (flags &&& Parameters.irq().tx_done_mask) == 0 do
:timer.sleep(1)
if counter <= Parameters.max().end_packet_cycles,
do: verify_end_packet(spi, from, counter + 1),
else: Logger.error("LoRa: send timeout")
else
# Reset flags
Logger.debug("LoRa: verify end packet: iterations: #{counter}")
send(from, :send_ok)
end
end
def sleep(spi) do
Communicator.write_register(
spi,
Parameters.register().op_mode,
Parameters.mode().long_range_mode ||| Parameters.mode().sleep
)
end
def idle(spi) do
Communicator.write_register(
spi,
Parameters.register().op_mode,
Parameters.mode().long_range_mode ||| Parameters.mode().stdby
)
end
def read(frequency, owner, spi, index \\ 0, msg \\ []) do
r_byte = Communicator.read_register(spi, Parameters.register().fifo)
nb_bytes = Communicator.read_register(spi, Parameters.register().rx_nb_bytes) - 1
if nb_bytes - index + 1 > 0,
do: read(frequency, owner, spi, index + 1, msg ++ [r_byte]),
else:
Kernel.send(
owner,
{:lora,
%{
packet: List.to_string(msg),
rssi: rssi(frequency, spi),
snr: snr(spi),
time: DateTime.now!("Etc/UTC")
}}
)
end
def snr(spi), do: Communicator.read_register(spi, Parameters.register().pkt_snr_value) * 0.25
def rssi(frequency, spi) do
rssi_value = Communicator.read_register(spi, Parameters.register().pkt_rssi_value)
rssi_value - if frequency < 868.0e6, do: 164, else: 157
end
def parse_packet(from, spi, size \\ 0) do
irq_flags = Communicator.read_register(spi, Parameters.register().irq_flags)
if size > 0 do
set_header_mode(false, spi)
Communicator.write_register(spi, Parameters.register().payload_length, size &&& 0xFF)
else
set_header_mode(true, spi)
end
# Return irq flags
Communicator.write_register(spi, Parameters.register().irq_flags, irq_flags)
rx_done = irq_flags &&& Parameters.irq().rx_done_mask
payload_crc = irq_flags &&& Parameters.irq().payload_crc_error_mask
if rx_done != 0 and payload_crc != 0 == false do
if size > 0 do
packet_length = Communicator.read_register(spi, Parameters.register().payload_length)
set_fifo_current_addr(spi)
send(from, {:receive_msg, packet_length})
else
packet_length = Communicator.read_register(spi, Parameters.register().rx_nb_bytes)
set_fifo_current_addr(spi)
send(from, {:receive_msg, packet_length})
end
else
op_mode = Communicator.read_register(spi, Parameters.register().op_mode)
if op_mode != (Parameters.mode().long_range_mode ||| Parameters.mode().rx_single) do
Communicator.write_register(spi, Parameters.register().fifo_addr_ptr, 0)
new_op_mode = Parameters.mode().long_range_mode ||| Parameters.mode().rx_single
Communicator.write_register(spi, Parameters.register().op_mode, new_op_mode)
end
false
end
end
defp set_fifo_current_addr(spi) do
current_addr = Communicator.read_register(spi, Parameters.register().fifo_rx_current_addr)
Communicator.write_register(spi, Parameters.register().fifo_addr_ptr, current_addr)
idle(spi)
end
def reset(rst) do
GPIO.write(rst, 1)
:timer.sleep(20)
GPIO.write(rst, 0)
:timer.sleep(20)
GPIO.write(rst, 1)
:timer.sleep(10)
end
def tx_done_flag(spi) do
Communicator.write_register(
spi,
Parameters.register().irq_flags,
Parameters.irq().tx_done_mask
)
end
def reset_fifo_payload(spi) do
# Reset FIFO address and payload length
Communicator.write_register(spi, Parameters.register().fifo_addr_ptr, 0)
Communicator.write_register(spi, Parameters.register().payload_length, 0)
end
def set_frequency(freq, spi) do
frt = trunc((trunc(freq) <<< 19) / 32_000_000)
Communicator.write_register(spi, Parameters.register().frf_msb, frt >>> 16)
Communicator.write_register(spi, Parameters.register().frf_mid, frt >>> 8)
Communicator.write_register(spi, Parameters.register().frf_lsb, frt >>> 0)
end
# def set_tx_power(spi, level, output_pin) when output_pin == Parameters.pa.output_rfo_pin do
# cond do
# level < 0 -> Communicator.write_register(spi, Parameters.register.pa_config, 0x70 ||| 0)
# level > 14 -> Communicator.write_register(spi, Parameters.register.pa_config, 0x70 ||| 14)
# level >= 0 -> Communicator.write_register(spi, Parameters.register.pa_config, 0x70 ||| level)
# end
# end
def set_tx_power(level, spi) do
if level > 17 do
Communicator.write_register(spi, Parameters.register().pa_dac, 0x87)
set_ocp(140, spi)
if level > 20,
do:
Communicator.write_register(
spi,
Parameters.register().pa_config,
Parameters.pa().boost ||| 15
),
else:
Communicator.write_register(
spi,
Parameters.register().pa_config,
Parameters.pa().boost ||| level - 5
)
else
Communicator.write_register(spi, Parameters.register().pa_dac, 0x84)
set_ocp(100, spi)
if level < 2,
do:
Communicator.write_register(
spi,
Parameters.register().pa_config,
Parameters.pa().boost ||| 0
),
else:
Communicator.write_register(
spi,
Parameters.register().pa_config,
Parameters.pa().boost ||| level - 2
)
end
end
def set_ocp(ocp, spi) do
cond do
ocp <= 120 ->
Communicator.write_register(
spi,
Parameters.register().ocp,
0x20 ||| (0x1F &&& uint8((uint8(ocp) - 45) / 5))
)
ocp <= 240 ->
Communicator.write_register(
spi,
Parameters.register().ocp,
0x20 ||| (0x1F &&& uint8((uint8(ocp) + 30) / 10))
)
ocp > 240 ->
Communicator.write_register(spi, Parameters.register().ocp, 0x20 ||| (0x1F &&& 27))
end
end
def set_ldo_flag(spi) do
spf = get_spreading_factor(spi)
bw = get_signal_band_width(spi)
unless bw == nil do
symbol_duration = 1000 / (bw / (1 <<< spf))
ldo_on = if symbol_duration > 16, do: 1, else: 0
Communicator.write_register(
spi,
Parameters.register().modem_config_3,
bit_write(
Communicator.read_register(spi, Parameters.register().modem_config_3),
3,
ldo_on
)
)
end
end
def set_spreading_factor(sf, spi) do
if sf == 6 do
Communicator.write_register(spi, Parameters.register().detection_optimize, 0xC5)
Communicator.write_register(spi, Parameters.register().detection_threshold, 0x0C)
else
Communicator.write_register(spi, Parameters.register().detection_optimize, 0xC3)
Communicator.write_register(spi, Parameters.register().detection_threshold, 0x0A)
end
config2 = Communicator.read_register(spi, Parameters.register().modem_config_2)
sf_ = (config2 &&& 0x0F) ||| (sf <<< 4 &&& 0xF0)
Communicator.write_register(spi, Parameters.register().modem_config_2, sf_)
set_ldo_flag(spi)
end
def set_bandwidth(sbw, spi) do
reg = Communicator.read_register(spi, Parameters.register().modem_config_1)
Parameters.bw_freqs()
|> Enum.filter(fn {_i, f} -> sbw <= f end)
|> List.first()
|> set_bw(spi, reg)
set_ldo_flag(spi)
end
defp set_bw({bw, _freq}, spi, reg) do
Communicator.write_register(
spi,
Parameters.register().modem_config_1,
(reg &&& 0x0F) ||| bw <<< 4
)
end
def set_base_address(spi) do
# Set base addresses
Communicator.write_register(spi, Parameters.register().fifo_tx_base_addr, 0)
Communicator.write_register(spi, Parameters.register().fifo_rx_base_addr, 0)
end
def set_LNA_boost(spi),
do:
Communicator.write_register(
spi,
Parameters.register().lna,
Communicator.read_register(spi, Parameters.register().lna) ||| 0x03
)
def set_auto_AGC(spi),
do: Communicator.write_register(spi, Parameters.register().modem_config_3, 0x04)
def set_header_mode(expl, spi) do
modem_config_1 = Communicator.read_register(spi, Parameters.register().modem_config_1)
if expl,
do:
Communicator.write_register(
spi,
Parameters.register().modem_config_1,
modem_config_1 &&& Parameters.header(expl)
),
else:
Communicator.write_register(
spi,
Parameters.register().modem_config_1,
modem_config_1 ||| Parameters.header(expl)
)
reset_fifo_payload(spi)
end
def enable_crc(spi),
do:
Communicator.write_register(
spi,
Parameters.register().modem_config_2,
Communicator.read_register(spi, Parameters.register().modem_config_2) ||| 0x04
)
def disable_crc(spi),
do:
Communicator.write_register(
spi,
Parameters.register().modem_config_2,
Communicator.read_register(spi, Parameters.register().modem_config_2) ||| 0xFB
)
def get_signal_band_width(spi) do
bw = Communicator.read_register(spi, Parameters.register().modem_config_1) >>> 4
Parameters.bw_freqs()[bw]
end
def get_spreading_factor(spi) do
config = Communicator.read_register(spi, Parameters.register().modem_config_2)
config >>> 4
end
def get_version(spi), do: Communicator.read_register(spi, Parameters.register().version)
def bit_write(value, bit, subs) do
{ini, fim} = list_bits(value) |> add_zeros(bit) |> Enum.split(bit)
[_h | t] = fim
Enum.reverse(ini ++ [subs] ++ t) |> listbits_to_integer()
end
defp add_zeros(list, bit, state \\ [], i \\ 0) do
if length(list) <= bit and length(state) <= bit do
if i <= length(list) - 1 do
add_zeros(list, bit, state ++ [Enum.at(list, i)], i + 1)
else
add_zeros(list, bit, state ++ [0], i + 1)
end
else
if bit <= length(list) - 1, do: list, else: state
end
end
defp listbits_to_integer(list, state \\ 0, pot \\ 0) do
if pot < length(list) do
val = list |> Enum.reverse() |> Enum.at(pot)
listbits_to_integer(list, state + :math.pow(2, pot) * val, pot + 1)
else
trunc(state)
end
end
defp list_bits(value, state \\ []) do
unless div(value, 2) == 0 do
list_bits(div(value, 2), state ++ [rem(value, 2)])
else
state ++ [rem(value, 2)]
end
end
defp uint8(val) do
cond do
val < 0 ->
teste = 256 + trunc(val)
if teste < 0 do
uint8(teste)
else
teste
end
val <= 255 ->
trunc(val)
val > 255 ->
rem(trunc(val), 256)
end
end
# defp change_third_bit(value, bit), do: if(bit == 0, do: value &&& 0xF7, else: value ||| 8)
end