<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[LoPy1 to LoPy1 Nanogateway]]></title><description><![CDATA[<p dir="auto">Hi everyone,</p>
<p dir="auto">I have been trying for quite sometime to get a LoPy1 to LoPy1 nanogateway setup. I've checked throughout the forums many times and I cannot seem to find what I am doing incorrectly I cannot get OTAA or ABP to show up. Please any suggestions would be greatly appreciated. My firmware for both is at the latest 1.17.3. I use one board attached to a pyTrack and powered on, and the other I power on through a breadboard with 5V, 3.3V and GND. Both have good working antennas on. (Also when using the OTAA for US915 there is a join data rate issue, none of the values are within range).</p>
<p dir="auto">Below is my code for my LoPy1 Node for ABP_US915 which appears to run fine it says it sends a packet but only once even though it is supposed to be looping. It has never shown up on my TTN applications. With that code I also include my <a href="http://config.py" target="_blank" rel="noopener noreferrer nofollow">config.py</a> in the libraries folder in the flash.</p>
<p dir="auto"><a href="http://main.py" target="_blank" rel="noopener noreferrer nofollow">main.py</a> of LoPy NODE</p>
<pre><code>from network import LoRa
import socket
import binascii
import struct
import time
import config
import pycom
import math
# initialize LoRa in LORAWAN mode.
# Please pick the region that matches where you are using the device:
# Asia = LoRa.AS923
# Australia = LoRa.AU915
# Europe = LoRa.EU868
# United States = LoRa.US915
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.US915)
pycom.heartbeat(False)
i = 97
# create an ABP authentication params
dev_addr = struct.unpack(&quot;&gt;l&quot;, binascii.unhexlify('26021B6A'.replace(' ','')))[0]
nwk_swkey = binascii.unhexlify('6A9EC55DD96E7559C309725BA03073F1'.replace(' ',''))
app_swkey = binascii.unhexlify('F0F2462EE9D24C97A047537E733DA1A4'.replace(' ',''))

# remove all the channels
for channel in range(0, 72):
    lora.remove_channel(channel)

# set all channels to the same frequency (must be before sending the OTAA join request)
for channel in range(0, 72):
    lora.add_channel(channel, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=3)

pycom.rgbled(0x7f7f00) # yellow
time.sleep(2.5)
# join a network using ABP (Activation By Personalization)

lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
join = lora.has_joined()
print(&quot;Join value %s&quot; % join)

while True:
    pycom.rgbled(0x7f7f00) # yellow
    time.sleep(2.5)
    if not lora.has_joined():
        print('Not joined yet...')
        join_wait += 1
        if join_wait == 5:
            lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
            join_wait = 0
    else:
        break
# create a LoRa socket
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)

# set the LoRaWAN data rate
s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR)

# make the socket blocking
s.setblocking(False)
pycom.rgbled(0x007f00) # green
while True:
    pkt = b'PKT #' + bytes([i])
    print('Sending:', pkt)
    s.send(pkt)
    pycom.rgbled(0x0000ff) # blue
    time.sleep(5)
    i = i+1
</code></pre>
<p dir="auto"><img src="/assets/uploads/files/1527871864792-3e362b46-f630-44fa-a199-c7b1aa1edfcc-image.png" alt="0_1527871865761_3e362b46-f630-44fa-a199-c7b1aa1edfcc-image.png" class="img-responsive img-markdown" /><br />
<img src="/assets/uploads/files/1527872631795-b34235f9-d409-4d9b-862d-084d3852d63b-image.png" alt="0_1527872633256_b34235f9-d409-4d9b-862d-084d3852d63b-image.png" class="img-responsive img-markdown" /></p>
<p dir="auto"><a href="http://CONFIG.PY" target="_blank" rel="noopener noreferrer nofollow">CONFIG.PY</a></p>
<pre><code class="language-&quot;&quot;&quot;">
import machine
import ubinascii

WIFI_MAC = ubinascii.hexlify(machine.unique_id()).lower()
# Set  the Gateway ID to be the first 3 bytes of MAC address + 'FFFE' + last 3 bytes of MAC address
#GATEWAY_ID = WIFI_MAC[:6] + &quot;ffee&quot; + WIFI_MAC[6:12]
GATEWAY_ID = &quot;aabbcc1711004411&quot;
print(&quot;GID %s&quot; % GATEWAY_ID)
SERVER = 'router.us.thethings.network'
PORT = 1700

NTP = &quot;pool.ntp.org&quot;
NTP_PERIOD_S = 3600

WIFI_SSID = 'Removed'
WIFI_PASS = 'Removed this'

# for EU868
#LORA_FREQUENCY = 868100000
#LORA_GW_DR = &quot;SF7BW125&quot; # DR_5
#LORA_NODE_DR = 5

# for US915
LORA_FREQUENCY = 903900000
LORA_GW_DR = &quot;SF7BW125&quot; # DR_1
LORA_NODE_DR = 3
print(&quot;Setup Complete&quot;)
</code></pre>
<p dir="auto">Next is my gateway lopy. I get push and pull ack as well as it appears on TTN but I never got any traffic or join requests, even though my node passes the join code. Note I am use robert-hh's code for the nanogateway due to a timing issue he informed me of, but changed to US915. It uses that same config file in the library</p>
<p dir="auto"><a href="http://main.py" target="_blank" rel="noopener noreferrer nofollow">main.py</a> of LoPy nanogateway</p>
<pre><code>&quot;&quot;&quot; LoPy LoRaWAN Nano Gateway example usage &quot;&quot;&quot;

import config
from nanogateway import NanoGateway

if __name__ == '__main__':
    nanogw = NanoGateway(
        id=config.GATEWAY_ID,
        frequency=config.LORA_FREQUENCY,
        datarate=config.LORA_GW_DR,
        ssid=config.WIFI_SSID,
        password=config.WIFI_PASS,
        server=config.SERVER,
        port=config.PORT,
        ntp_server=config.NTP,
        ntp_period=config.NTP_PERIOD_S
        )

    nanogw.start()
    nanogw._log('You may now press ENTER to enter the REPL')
    input()
</code></pre>
<p dir="auto"><a href="http://nanogateway.py" target="_blank" rel="noopener noreferrer nofollow">nanogateway.py</a></p>
<pre><code>&quot;&quot;&quot; LoPy LoRaWAN Nano Gateway. Can be used for both EU868 and US915. &quot;&quot;&quot;

import errno
import machine
import ubinascii
import ujson
import uos
import usocket
import utime
import _thread
import gc
from micropython import const
from network import LoRa
from network import WLAN
from machine import Timer
from machine import WDT

PROTOCOL_VERSION = const(2)

PUSH_DATA = const(0)
PUSH_ACK = const(1)
PULL_DATA = const(2)
PULL_ACK = const(4)
PULL_RESP = const(3)

TX_ERR_NONE = 'NONE'
TX_ERR_TOO_LATE = 'TOO_LATE'
TX_ERR_TOO_EARLY = 'TOO_EARLY'
TX_ERR_COLLISION_PACKET = 'COLLISION_PACKET'
TX_ERR_COLLISION_BEACON = 'COLLISION_BEACON'
TX_ERR_TX_FREQ = 'TX_FREQ'
TX_ERR_TX_POWER = 'TX_POWER'
TX_ERR_GPS_UNLOCKED = 'GPS_UNLOCKED'

UDP_THREAD_CYCLE_MS = const(10)
WDT_TIMEOUT = const(120000)


STAT_PK = {
    'stat': {
        'time': '',
        'lati': 0,
        'long': 0,
        'alti': 0,
        'rxnb': 0,
        'rxok': 0,
        'rxfw': 0,
        'ackr': 100.0,
        'dwnb': 0,
        'txnb': 0
    }
}

RX_PK = {
    'rxpk': [{
        'time': '',
        'tmst': 0,
        'chan': 0,
        'rfch': 0,
        'freq': 0,
        'stat': 1,
        'modu': 'LORA',
        'datr': '',
        'codr': '4/5',
        'rssi': 0,
        'lsnr': 0,
        'size': 0,
        'data': ''
    }]
}

TX_ACK_PK = {
    'txpk_ack': {
        'error': ''
    }
}


class NanoGateway:
    &quot;&quot;&quot;
    Nano gateway class, set up by default for use with TTN, but can be configured
    for any other network supporting the Semtech Packet Forwarder.
    Only required configuration is wifi_ssid and wifi_password which are used for
    connecting to the Internet.
    &quot;&quot;&quot;

    def __init__(self, id, frequency, datarate, ssid, password, server, port, ntp_server='pool.ntp.org', ntp_period=3600):
        self.id = id
        self.server = server
        self.port = port

        self.frequency = frequency
        self.datarate = datarate

        self.ssid = ssid
        self.password = password

        self.ntp_server = ntp_server
        self.ntp_period = ntp_period

        self.server_ip = None

        self.rxnb = 0
        self.rxok = 0
        self.rxfw = 0
        self.dwnb = 0
        self.txnb = 0

        self.sf = self._dr_to_sf(self.datarate)
        self.bw = self._dr_to_bw(self.datarate)

        self.stat_alarm = None
        self.pull_alarm = None
        self.uplink_alarm = None

        self.wlan = None
        self.sock = None
        self.udp_stop = False
        self.udp_lock = _thread.allocate_lock()

        self.lora = None
        self.lora_sock = None

        self.rtc = machine.RTC()

    def start(self):
        &quot;&quot;&quot;
        Starts the LoRaWAN nano gateway.
        &quot;&quot;&quot;

        self._log('Starting LoRaWAN nano gateway with id: {}', self.id)

        # setup WiFi as a station and connect
        self.wlan = WLAN(mode=WLAN.STA)
        self._connect_to_wifi()

        # get a time sync
        self._log('Syncing time with {} ...', self.ntp_server)
        self.rtc.ntp_sync(self.ntp_server, update_period=self.ntp_period)
        while not self.rtc.synced():
            utime.sleep_ms(50)
        self._log(&quot;RTC NTP sync complete&quot;)

        # get the server IP and create an UDP socket
        self.server_ip = usocket.getaddrinfo(self.server, self.port)[0][-1]
        self._log('Opening UDP socket to {} ({}) port {}...', self.server, self.server_ip[0], self.server_ip[1])
        self.sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM, usocket.IPPROTO_UDP)
        self.sock.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1)
        self.sock.setblocking(False)

        # push the first time immediatelly
        self._push_data(self._make_stat_packet())

        # create the alarms
        self.stat_alarm = Timer.Alarm(handler=lambda t: self._push_data(self._make_stat_packet()), s=60, periodic=True)
        self.pull_alarm = Timer.Alarm(handler=lambda u: self._pull_data(), s=25, periodic=True)

        # start the UDP receive thread
        self.udp_stop = False
        _thread.start_new_thread(self._udp_thread, ())

        # initialize the LoRa radio in LORA mode
        self._log('Setting up the LoRa radio at {} Mhz using {}', self._freq_to_float(self.frequency), self.datarate)
        self.lora = LoRa(
            mode=LoRa.LORA,
            region=LoRa.US915,
            frequency=self.frequency,
            bandwidth=self.bw,
            sf=self.sf,
            preamble=8,
            coding_rate=LoRa.CODING_4_5,
            tx_iq=True
        )

        # create a raw LoRa socket
        self.lora_sock = usocket.socket(usocket.AF_LORA, usocket.SOCK_RAW)
        self.lora_sock.setblocking(False)
        self.lora_tx_done = False

        self.lora.callback(trigger=(LoRa.RX_PACKET_EVENT | LoRa.TX_PACKET_EVENT), handler=self._lora_cb)

        if uos.uname()[0] == &quot;LoPy&quot;:
            self.window_compensation = -1000
        else:
            self.window_compensation = -6000

        self._log('LoRaWAN nano gateway online')

    def stop(self):
        &quot;&quot;&quot;
        Stops the LoRaWAN nano gateway.
        &quot;&quot;&quot;

        self._log('Stopping...')

        # send the LoRa radio to sleep
        self.lora.callback(trigger=None, handler=None)
        self.lora.power_mode(LoRa.SLEEP)

        # stop the NTP sync
        self.rtc.ntp_sync(None)

        # cancel all the alarms
        self.stat_alarm.cancel()
        self.pull_alarm.cancel()

        # signal the UDP thread to stop
        self.udp_stop = True
        while self.udp_stop:
            utime.sleep_ms(50)

        # disable WLAN
        self.wlan.disconnect()
        self.wlan.deinit()

    def _connect_to_wifi(self):
        self.wlan.connect(self.ssid, auth=(None, self.password))
        while not self.wlan.isconnected():
            utime.sleep_ms(50)
        self._log('WiFi connected to: {}', self.ssid)

    def _dr_to_sf(self, dr):
        sf = dr[2:4]
        if sf[1] not in '0123456789':
            sf = sf[:1]
        return int(sf)

    def _dr_to_bw(self, dr):
        bw = dr[-5:]
        if bw == 'BW125':
            return LoRa.BW_125KHZ
        elif bw == 'BW250':
            return LoRa.BW_250KHZ
        else:
            return LoRa.BW_500KHZ

    def _sf_bw_to_dr(self, sf, bw):
        dr = 'SF' + str(sf)
        if bw == LoRa.BW_125KHZ:
            return dr + 'BW125'
        elif bw == LoRa.BW_250KHZ:
            return dr + 'BW250'
        else:
            return dr + 'BW500'

    def _lora_cb(self, lora):
        &quot;&quot;&quot;
        LoRa radio events callback handler.
        &quot;&quot;&quot;

        events = lora.events()
        if events &amp; LoRa.RX_PACKET_EVENT:
            self.rxnb += 1
            self.rxok += 1
            rx_data = self.lora_sock.recv(256)
            stats = lora.stats()
            packet = self._make_node_packet(rx_data, self.rtc.now(), stats.rx_timestamp, stats.sfrx, self.bw, stats.rssi, stats.snr)
            self._push_data(packet)
            self._log('Received packet: {}', packet)
            self.rxfw += 1
        if events &amp; LoRa.TX_PACKET_EVENT:
            self.txnb += 1
            lora.init(
                mode=LoRa.LORA,
                region=LoRa.US915,
                frequency=self.frequency,
                bandwidth=self.bw,
                sf=self.sf,
                preamble=8,
                coding_rate=LoRa.CODING_4_5,
                tx_iq=True
                )

    def _freq_to_float(self, frequency):
        &quot;&quot;&quot;
        MicroPython has some inprecision when doing large float division.
        To counter this, this method first does integer division until we
        reach the decimal breaking point. This doesn't completely elimate
        the issue in all cases, but it does help for a number of commonly
        used frequencies.
        &quot;&quot;&quot;

        divider = 6
        while divider &gt; 0 and frequency % 10 == 0:
            frequency = frequency // 10
            divider -= 1
        if divider &gt; 0:
            frequency = frequency / (10 ** divider)
        return frequency

    def _make_stat_packet(self):
        now = self.rtc.now()
        STAT_PK[&quot;stat&quot;][&quot;time&quot;] = &quot;%d-%02d-%02d %02d:%02d:%02d GMT&quot; % (now[0], now[1], now[2], now[3], now[4], now[5])
        STAT_PK[&quot;stat&quot;][&quot;rxnb&quot;] = self.rxnb
        STAT_PK[&quot;stat&quot;][&quot;rxok&quot;] = self.rxok
        STAT_PK[&quot;stat&quot;][&quot;rxfw&quot;] = self.rxfw
        STAT_PK[&quot;stat&quot;][&quot;dwnb&quot;] = self.dwnb
        STAT_PK[&quot;stat&quot;][&quot;txnb&quot;] = self.txnb
        return ujson.dumps(STAT_PK)

    def _make_node_packet(self, rx_data, rx_time, tmst, sf, bw, rssi, snr):
        RX_PK[&quot;rxpk&quot;][0][&quot;time&quot;] = &quot;%d-%02d-%02dT%02d:%02d:%02d.%dZ&quot; % (rx_time[0], rx_time[1], rx_time[2], rx_time[3], rx_time[4], rx_time[5], rx_time[6])
        RX_PK[&quot;rxpk&quot;][0][&quot;tmst&quot;] = tmst
        RX_PK[&quot;rxpk&quot;][0][&quot;freq&quot;] = self._freq_to_float(self.frequency)
        RX_PK[&quot;rxpk&quot;][0][&quot;datr&quot;] = self._sf_bw_to_dr(sf, bw)
        RX_PK[&quot;rxpk&quot;][0][&quot;rssi&quot;] = rssi
        RX_PK[&quot;rxpk&quot;][0][&quot;lsnr&quot;] = snr
        RX_PK[&quot;rxpk&quot;][0][&quot;data&quot;] = ubinascii.b2a_base64(rx_data)[:-1]
        RX_PK[&quot;rxpk&quot;][0][&quot;size&quot;] = len(rx_data)
        return ujson.dumps(RX_PK)

    def _push_data(self, data):
        token = uos.urandom(2)
        packet = bytes([PROTOCOL_VERSION]) + token + bytes([PUSH_DATA]) + ubinascii.unhexlify(self.id) + data
        with self.udp_lock:
            try:
                self.sock.sendto(packet, self.server_ip)
            except Exception as ex:
                self._log('Failed to push uplink packet to server: {}', ex)

    def _pull_data(self):
        token = uos.urandom(2)
        packet = bytes([PROTOCOL_VERSION]) + token + bytes([PULL_DATA]) + ubinascii.unhexlify(self.id)
        with self.udp_lock:
            try:
                self.sock.sendto(packet, self.server_ip)
            except Exception as ex:
                self._log('Failed to pull downlink packets from server: {}', ex)

    def _ack_pull_rsp(self, token, error):
        TX_ACK_PK[&quot;txpk_ack&quot;][&quot;error&quot;] = error
        resp = ujson.dumps(TX_ACK_PK)
        packet = bytes([PROTOCOL_VERSION]) + token + bytes([PULL_ACK]) + ubinascii.unhexlify(self.id) + resp
        with self.udp_lock:
            try:
                self.sock.sendto(packet, self.server_ip)
            except Exception as ex:
                self._log('PULL RSP ACK exception: {}', ex)

    def _send_down_link(self, data, tmst, datarate, frequency):
        &quot;&quot;&quot;
        Transmits a downlink message over LoRa.
        &quot;&quot;&quot;

        self.lora.init(
            mode=LoRa.LORA,
            region=LoRa.US915,
            frequency=frequency,
            bandwidth=self._dr_to_bw(datarate),
            sf=self._dr_to_sf(datarate),
            preamble=8,
            coding_rate=LoRa.CODING_4_5,
            tx_iq=True
            )
        while utime.ticks_diff(utime.ticks_cpu(), tmst) &gt; 0:
            pass
        self.lora_sock.settimeout(1)
        self.lora_sock.send(data)
        self.lora_sock.setblocking(False)
        self._log(
            'Sent downlink packet scheduled on {:.3f}, at {:,d} Hz using {}: {}',
            tmst / 1000000,
            frequency,
            datarate,
            data
        )

    def _udp_thread(self):
        &quot;&quot;&quot;
        UDP thread, reads data from the server and handles it.
        &quot;&quot;&quot;

        while not self.udp_stop:
            gc.collect()
            try:
                data, src = self.sock.recvfrom(1024)
                _token = data[1:3]
                _type = data[3]
                if _type == PUSH_ACK:
                    self._log(&quot;Push ack&quot;)
                elif _type == PULL_ACK:
                    self._log(&quot;Pull ack&quot;)
                elif _type == PULL_RESP:
                    self.dwnb += 1
                    ack_error = TX_ERR_NONE
                    tx_pk = ujson.loads(data[4:])
                    payload = ubinascii.a2b_base64(tx_pk[&quot;txpk&quot;][&quot;data&quot;])
                    # depending on the board, pull the downlink message 1 or 6 ms upfronnt
                    tmst = utime.ticks_add(tx_pk[&quot;txpk&quot;][&quot;tmst&quot;], self.window_compensation)
                    t_us = utime.ticks_diff(utime.ticks_cpu(), utime.ticks_add(tmst, -15000))
                    if 1000 &lt; t_us &lt; 10000000:
                        self.uplink_alarm = Timer.Alarm(
                            handler=lambda x: self._send_down_link(
                                payload,
                                tmst, tx_pk[&quot;txpk&quot;][&quot;datr&quot;],
                                int(tx_pk[&quot;txpk&quot;][&quot;freq&quot;] * 1000 + 0.0005) * 1000
                            ),
                            us=t_us
                        )
                    else:
                        ack_error = TX_ERR_TOO_LATE
                        self._log('Downlink timestamp error!, t_us: {}', t_us)
                    self._ack_pull_rsp(_token, ack_error)
                    self._log(&quot;Pull rsp&quot;)
            except usocket.timeout:
                pass
            except OSError as ex:
                if ex.errno != errno.EAGAIN:
                    self._log('UDP recv OSError Exception: {}', ex)
            except Exception as ex:
                self._log('UDP recv Exception: {}', ex)

            # wait before trying to receive again
            utime.sleep_ms(UDP_THREAD_CYCLE_MS)

        # we are to close the socket
        self.sock.close()
        self.udp_stop = False
        self._log('UDP thread stopped')

    def _log(self, message, *args):
        &quot;&quot;&quot;
        Outputs a log message to stdout.
        &quot;&quot;&quot;

        print('[{:&gt;10.3f}] {}'.format(
            utime.ticks_ms() / 1000,
            str(message).format(*args)
))
</code></pre>
<p dir="auto"><img src="/assets/uploads/files/1527872474862-b93a6844-3774-4b61-9535-35fed3028c9b-image.png" alt="0_1527872476272_b93a6844-3774-4b61-9535-35fed3028c9b-image.png" class="img-responsive img-markdown" /></p>
<p dir="auto"><img src="/assets/uploads/files/1527872704966-8c706136-c99e-4cb7-bbc3-fd2ffd5ce2be-image.png" alt="0_1527872706415_8c706136-c99e-4cb7-bbc3-fd2ffd5ce2be-image.png" class="img-responsive img-markdown" /></p>
]]></description><link>https://forum.pycom.io/topic/3328/lopy1-to-lopy1-nanogateway</link><generator>RSS for Node</generator><lastBuildDate>Wed, 22 Apr 2026 02:04:51 GMT</lastBuildDate><atom:link href="https://forum.pycom.io/topic/3328.rss" rel="self" type="application/rss+xml"/><pubDate>Fri, 01 Jun 2018 17:16:10 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to LoPy1 to LoPy1 Nanogateway on Mon, 04 Jun 2018 14:18:58 GMT]]></title><description><![CDATA[<p dir="auto">Hi everyone,</p>
<p dir="auto">I have been trying for quite sometime to get a LoPy1 to LoPy1 nanogateway setup. I've checked throughout the forums many times and I cannot seem to find what I am doing incorrectly I cannot get OTAA or ABP to show up. Please any suggestions would be greatly appreciated. My firmware for both is at the latest 1.17.3. I use one board attached to a pyTrack and powered on, and the other I power on through a breadboard with 5V, 3.3V and GND. Both have good working antennas on. (Also when using the OTAA for US915 there is a join data rate issue, none of the values are within range).</p>
<p dir="auto">Below is my code for my LoPy1 Node for ABP_US915 which appears to run fine it says it sends a packet but only once even though it is supposed to be looping. It has never shown up on my TTN applications. With that code I also include my <a href="http://config.py" target="_blank" rel="noopener noreferrer nofollow">config.py</a> in the libraries folder in the flash.</p>
<p dir="auto"><a href="http://main.py" target="_blank" rel="noopener noreferrer nofollow">main.py</a> of LoPy NODE</p>
<pre><code>from network import LoRa
import socket
import binascii
import struct
import time
import config
import pycom
import math
# initialize LoRa in LORAWAN mode.
# Please pick the region that matches where you are using the device:
# Asia = LoRa.AS923
# Australia = LoRa.AU915
# Europe = LoRa.EU868
# United States = LoRa.US915
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.US915)
pycom.heartbeat(False)
i = 97
# create an ABP authentication params
dev_addr = struct.unpack(&quot;&gt;l&quot;, binascii.unhexlify('26021B6A'.replace(' ','')))[0]
nwk_swkey = binascii.unhexlify('6A9EC55DD96E7559C309725BA03073F1'.replace(' ',''))
app_swkey = binascii.unhexlify('F0F2462EE9D24C97A047537E733DA1A4'.replace(' ',''))

# remove all the channels
for channel in range(0, 72):
    lora.remove_channel(channel)

# set all channels to the same frequency (must be before sending the OTAA join request)
for channel in range(0, 72):
    lora.add_channel(channel, frequency=config.LORA_FREQUENCY, dr_min=0, dr_max=3)

pycom.rgbled(0x7f7f00) # yellow
time.sleep(2.5)
# join a network using ABP (Activation By Personalization)

lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
join = lora.has_joined()
print(&quot;Join value %s&quot; % join)

while True:
    pycom.rgbled(0x7f7f00) # yellow
    time.sleep(2.5)
    if not lora.has_joined():
        print('Not joined yet...')
        join_wait += 1
        if join_wait == 5:
            lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey))
            join_wait = 0
    else:
        break
# create a LoRa socket
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)

# set the LoRaWAN data rate
s.setsockopt(socket.SOL_LORA, socket.SO_DR, config.LORA_NODE_DR)

# make the socket blocking
s.setblocking(False)
pycom.rgbled(0x007f00) # green
while True:
    pkt = b'PKT #' + bytes([i])
    print('Sending:', pkt)
    s.send(pkt)
    pycom.rgbled(0x0000ff) # blue
    time.sleep(5)
    i = i+1
</code></pre>
<p dir="auto"><img src="/assets/uploads/files/1527871864792-3e362b46-f630-44fa-a199-c7b1aa1edfcc-image.png" alt="0_1527871865761_3e362b46-f630-44fa-a199-c7b1aa1edfcc-image.png" class="img-responsive img-markdown" /><br />
<img src="/assets/uploads/files/1527872631795-b34235f9-d409-4d9b-862d-084d3852d63b-image.png" alt="0_1527872633256_b34235f9-d409-4d9b-862d-084d3852d63b-image.png" class="img-responsive img-markdown" /></p>
<p dir="auto"><a href="http://CONFIG.PY" target="_blank" rel="noopener noreferrer nofollow">CONFIG.PY</a></p>
<pre><code class="language-&quot;&quot;&quot;">
import machine
import ubinascii

WIFI_MAC = ubinascii.hexlify(machine.unique_id()).lower()
# Set  the Gateway ID to be the first 3 bytes of MAC address + 'FFFE' + last 3 bytes of MAC address
#GATEWAY_ID = WIFI_MAC[:6] + &quot;ffee&quot; + WIFI_MAC[6:12]
GATEWAY_ID = &quot;aabbcc1711004411&quot;
print(&quot;GID %s&quot; % GATEWAY_ID)
SERVER = 'router.us.thethings.network'
PORT = 1700

NTP = &quot;pool.ntp.org&quot;
NTP_PERIOD_S = 3600

WIFI_SSID = 'Removed'
WIFI_PASS = 'Removed this'

# for EU868
#LORA_FREQUENCY = 868100000
#LORA_GW_DR = &quot;SF7BW125&quot; # DR_5
#LORA_NODE_DR = 5

# for US915
LORA_FREQUENCY = 903900000
LORA_GW_DR = &quot;SF7BW125&quot; # DR_1
LORA_NODE_DR = 3
print(&quot;Setup Complete&quot;)
</code></pre>
<p dir="auto">Next is my gateway lopy. I get push and pull ack as well as it appears on TTN but I never got any traffic or join requests, even though my node passes the join code. Note I am use robert-hh's code for the nanogateway due to a timing issue he informed me of, but changed to US915. It uses that same config file in the library</p>
<p dir="auto"><a href="http://main.py" target="_blank" rel="noopener noreferrer nofollow">main.py</a> of LoPy nanogateway</p>
<pre><code>&quot;&quot;&quot; LoPy LoRaWAN Nano Gateway example usage &quot;&quot;&quot;

import config
from nanogateway import NanoGateway

if __name__ == '__main__':
    nanogw = NanoGateway(
        id=config.GATEWAY_ID,
        frequency=config.LORA_FREQUENCY,
        datarate=config.LORA_GW_DR,
        ssid=config.WIFI_SSID,
        password=config.WIFI_PASS,
        server=config.SERVER,
        port=config.PORT,
        ntp_server=config.NTP,
        ntp_period=config.NTP_PERIOD_S
        )

    nanogw.start()
    nanogw._log('You may now press ENTER to enter the REPL')
    input()
</code></pre>
<p dir="auto"><a href="http://nanogateway.py" target="_blank" rel="noopener noreferrer nofollow">nanogateway.py</a></p>
<pre><code>&quot;&quot;&quot; LoPy LoRaWAN Nano Gateway. Can be used for both EU868 and US915. &quot;&quot;&quot;

import errno
import machine
import ubinascii
import ujson
import uos
import usocket
import utime
import _thread
import gc
from micropython import const
from network import LoRa
from network import WLAN
from machine import Timer
from machine import WDT

PROTOCOL_VERSION = const(2)

PUSH_DATA = const(0)
PUSH_ACK = const(1)
PULL_DATA = const(2)
PULL_ACK = const(4)
PULL_RESP = const(3)

TX_ERR_NONE = 'NONE'
TX_ERR_TOO_LATE = 'TOO_LATE'
TX_ERR_TOO_EARLY = 'TOO_EARLY'
TX_ERR_COLLISION_PACKET = 'COLLISION_PACKET'
TX_ERR_COLLISION_BEACON = 'COLLISION_BEACON'
TX_ERR_TX_FREQ = 'TX_FREQ'
TX_ERR_TX_POWER = 'TX_POWER'
TX_ERR_GPS_UNLOCKED = 'GPS_UNLOCKED'

UDP_THREAD_CYCLE_MS = const(10)
WDT_TIMEOUT = const(120000)


STAT_PK = {
    'stat': {
        'time': '',
        'lati': 0,
        'long': 0,
        'alti': 0,
        'rxnb': 0,
        'rxok': 0,
        'rxfw': 0,
        'ackr': 100.0,
        'dwnb': 0,
        'txnb': 0
    }
}

RX_PK = {
    'rxpk': [{
        'time': '',
        'tmst': 0,
        'chan': 0,
        'rfch': 0,
        'freq': 0,
        'stat': 1,
        'modu': 'LORA',
        'datr': '',
        'codr': '4/5',
        'rssi': 0,
        'lsnr': 0,
        'size': 0,
        'data': ''
    }]
}

TX_ACK_PK = {
    'txpk_ack': {
        'error': ''
    }
}


class NanoGateway:
    &quot;&quot;&quot;
    Nano gateway class, set up by default for use with TTN, but can be configured
    for any other network supporting the Semtech Packet Forwarder.
    Only required configuration is wifi_ssid and wifi_password which are used for
    connecting to the Internet.
    &quot;&quot;&quot;

    def __init__(self, id, frequency, datarate, ssid, password, server, port, ntp_server='pool.ntp.org', ntp_period=3600):
        self.id = id
        self.server = server
        self.port = port

        self.frequency = frequency
        self.datarate = datarate

        self.ssid = ssid
        self.password = password

        self.ntp_server = ntp_server
        self.ntp_period = ntp_period

        self.server_ip = None

        self.rxnb = 0
        self.rxok = 0
        self.rxfw = 0
        self.dwnb = 0
        self.txnb = 0

        self.sf = self._dr_to_sf(self.datarate)
        self.bw = self._dr_to_bw(self.datarate)

        self.stat_alarm = None
        self.pull_alarm = None
        self.uplink_alarm = None

        self.wlan = None
        self.sock = None
        self.udp_stop = False
        self.udp_lock = _thread.allocate_lock()

        self.lora = None
        self.lora_sock = None

        self.rtc = machine.RTC()

    def start(self):
        &quot;&quot;&quot;
        Starts the LoRaWAN nano gateway.
        &quot;&quot;&quot;

        self._log('Starting LoRaWAN nano gateway with id: {}', self.id)

        # setup WiFi as a station and connect
        self.wlan = WLAN(mode=WLAN.STA)
        self._connect_to_wifi()

        # get a time sync
        self._log('Syncing time with {} ...', self.ntp_server)
        self.rtc.ntp_sync(self.ntp_server, update_period=self.ntp_period)
        while not self.rtc.synced():
            utime.sleep_ms(50)
        self._log(&quot;RTC NTP sync complete&quot;)

        # get the server IP and create an UDP socket
        self.server_ip = usocket.getaddrinfo(self.server, self.port)[0][-1]
        self._log('Opening UDP socket to {} ({}) port {}...', self.server, self.server_ip[0], self.server_ip[1])
        self.sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM, usocket.IPPROTO_UDP)
        self.sock.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1)
        self.sock.setblocking(False)

        # push the first time immediatelly
        self._push_data(self._make_stat_packet())

        # create the alarms
        self.stat_alarm = Timer.Alarm(handler=lambda t: self._push_data(self._make_stat_packet()), s=60, periodic=True)
        self.pull_alarm = Timer.Alarm(handler=lambda u: self._pull_data(), s=25, periodic=True)

        # start the UDP receive thread
        self.udp_stop = False
        _thread.start_new_thread(self._udp_thread, ())

        # initialize the LoRa radio in LORA mode
        self._log('Setting up the LoRa radio at {} Mhz using {}', self._freq_to_float(self.frequency), self.datarate)
        self.lora = LoRa(
            mode=LoRa.LORA,
            region=LoRa.US915,
            frequency=self.frequency,
            bandwidth=self.bw,
            sf=self.sf,
            preamble=8,
            coding_rate=LoRa.CODING_4_5,
            tx_iq=True
        )

        # create a raw LoRa socket
        self.lora_sock = usocket.socket(usocket.AF_LORA, usocket.SOCK_RAW)
        self.lora_sock.setblocking(False)
        self.lora_tx_done = False

        self.lora.callback(trigger=(LoRa.RX_PACKET_EVENT | LoRa.TX_PACKET_EVENT), handler=self._lora_cb)

        if uos.uname()[0] == &quot;LoPy&quot;:
            self.window_compensation = -1000
        else:
            self.window_compensation = -6000

        self._log('LoRaWAN nano gateway online')

    def stop(self):
        &quot;&quot;&quot;
        Stops the LoRaWAN nano gateway.
        &quot;&quot;&quot;

        self._log('Stopping...')

        # send the LoRa radio to sleep
        self.lora.callback(trigger=None, handler=None)
        self.lora.power_mode(LoRa.SLEEP)

        # stop the NTP sync
        self.rtc.ntp_sync(None)

        # cancel all the alarms
        self.stat_alarm.cancel()
        self.pull_alarm.cancel()

        # signal the UDP thread to stop
        self.udp_stop = True
        while self.udp_stop:
            utime.sleep_ms(50)

        # disable WLAN
        self.wlan.disconnect()
        self.wlan.deinit()

    def _connect_to_wifi(self):
        self.wlan.connect(self.ssid, auth=(None, self.password))
        while not self.wlan.isconnected():
            utime.sleep_ms(50)
        self._log('WiFi connected to: {}', self.ssid)

    def _dr_to_sf(self, dr):
        sf = dr[2:4]
        if sf[1] not in '0123456789':
            sf = sf[:1]
        return int(sf)

    def _dr_to_bw(self, dr):
        bw = dr[-5:]
        if bw == 'BW125':
            return LoRa.BW_125KHZ
        elif bw == 'BW250':
            return LoRa.BW_250KHZ
        else:
            return LoRa.BW_500KHZ

    def _sf_bw_to_dr(self, sf, bw):
        dr = 'SF' + str(sf)
        if bw == LoRa.BW_125KHZ:
            return dr + 'BW125'
        elif bw == LoRa.BW_250KHZ:
            return dr + 'BW250'
        else:
            return dr + 'BW500'

    def _lora_cb(self, lora):
        &quot;&quot;&quot;
        LoRa radio events callback handler.
        &quot;&quot;&quot;

        events = lora.events()
        if events &amp; LoRa.RX_PACKET_EVENT:
            self.rxnb += 1
            self.rxok += 1
            rx_data = self.lora_sock.recv(256)
            stats = lora.stats()
            packet = self._make_node_packet(rx_data, self.rtc.now(), stats.rx_timestamp, stats.sfrx, self.bw, stats.rssi, stats.snr)
            self._push_data(packet)
            self._log('Received packet: {}', packet)
            self.rxfw += 1
        if events &amp; LoRa.TX_PACKET_EVENT:
            self.txnb += 1
            lora.init(
                mode=LoRa.LORA,
                region=LoRa.US915,
                frequency=self.frequency,
                bandwidth=self.bw,
                sf=self.sf,
                preamble=8,
                coding_rate=LoRa.CODING_4_5,
                tx_iq=True
                )

    def _freq_to_float(self, frequency):
        &quot;&quot;&quot;
        MicroPython has some inprecision when doing large float division.
        To counter this, this method first does integer division until we
        reach the decimal breaking point. This doesn't completely elimate
        the issue in all cases, but it does help for a number of commonly
        used frequencies.
        &quot;&quot;&quot;

        divider = 6
        while divider &gt; 0 and frequency % 10 == 0:
            frequency = frequency // 10
            divider -= 1
        if divider &gt; 0:
            frequency = frequency / (10 ** divider)
        return frequency

    def _make_stat_packet(self):
        now = self.rtc.now()
        STAT_PK[&quot;stat&quot;][&quot;time&quot;] = &quot;%d-%02d-%02d %02d:%02d:%02d GMT&quot; % (now[0], now[1], now[2], now[3], now[4], now[5])
        STAT_PK[&quot;stat&quot;][&quot;rxnb&quot;] = self.rxnb
        STAT_PK[&quot;stat&quot;][&quot;rxok&quot;] = self.rxok
        STAT_PK[&quot;stat&quot;][&quot;rxfw&quot;] = self.rxfw
        STAT_PK[&quot;stat&quot;][&quot;dwnb&quot;] = self.dwnb
        STAT_PK[&quot;stat&quot;][&quot;txnb&quot;] = self.txnb
        return ujson.dumps(STAT_PK)

    def _make_node_packet(self, rx_data, rx_time, tmst, sf, bw, rssi, snr):
        RX_PK[&quot;rxpk&quot;][0][&quot;time&quot;] = &quot;%d-%02d-%02dT%02d:%02d:%02d.%dZ&quot; % (rx_time[0], rx_time[1], rx_time[2], rx_time[3], rx_time[4], rx_time[5], rx_time[6])
        RX_PK[&quot;rxpk&quot;][0][&quot;tmst&quot;] = tmst
        RX_PK[&quot;rxpk&quot;][0][&quot;freq&quot;] = self._freq_to_float(self.frequency)
        RX_PK[&quot;rxpk&quot;][0][&quot;datr&quot;] = self._sf_bw_to_dr(sf, bw)
        RX_PK[&quot;rxpk&quot;][0][&quot;rssi&quot;] = rssi
        RX_PK[&quot;rxpk&quot;][0][&quot;lsnr&quot;] = snr
        RX_PK[&quot;rxpk&quot;][0][&quot;data&quot;] = ubinascii.b2a_base64(rx_data)[:-1]
        RX_PK[&quot;rxpk&quot;][0][&quot;size&quot;] = len(rx_data)
        return ujson.dumps(RX_PK)

    def _push_data(self, data):
        token = uos.urandom(2)
        packet = bytes([PROTOCOL_VERSION]) + token + bytes([PUSH_DATA]) + ubinascii.unhexlify(self.id) + data
        with self.udp_lock:
            try:
                self.sock.sendto(packet, self.server_ip)
            except Exception as ex:
                self._log('Failed to push uplink packet to server: {}', ex)

    def _pull_data(self):
        token = uos.urandom(2)
        packet = bytes([PROTOCOL_VERSION]) + token + bytes([PULL_DATA]) + ubinascii.unhexlify(self.id)
        with self.udp_lock:
            try:
                self.sock.sendto(packet, self.server_ip)
            except Exception as ex:
                self._log('Failed to pull downlink packets from server: {}', ex)

    def _ack_pull_rsp(self, token, error):
        TX_ACK_PK[&quot;txpk_ack&quot;][&quot;error&quot;] = error
        resp = ujson.dumps(TX_ACK_PK)
        packet = bytes([PROTOCOL_VERSION]) + token + bytes([PULL_ACK]) + ubinascii.unhexlify(self.id) + resp
        with self.udp_lock:
            try:
                self.sock.sendto(packet, self.server_ip)
            except Exception as ex:
                self._log('PULL RSP ACK exception: {}', ex)

    def _send_down_link(self, data, tmst, datarate, frequency):
        &quot;&quot;&quot;
        Transmits a downlink message over LoRa.
        &quot;&quot;&quot;

        self.lora.init(
            mode=LoRa.LORA,
            region=LoRa.US915,
            frequency=frequency,
            bandwidth=self._dr_to_bw(datarate),
            sf=self._dr_to_sf(datarate),
            preamble=8,
            coding_rate=LoRa.CODING_4_5,
            tx_iq=True
            )
        while utime.ticks_diff(utime.ticks_cpu(), tmst) &gt; 0:
            pass
        self.lora_sock.settimeout(1)
        self.lora_sock.send(data)
        self.lora_sock.setblocking(False)
        self._log(
            'Sent downlink packet scheduled on {:.3f}, at {:,d} Hz using {}: {}',
            tmst / 1000000,
            frequency,
            datarate,
            data
        )

    def _udp_thread(self):
        &quot;&quot;&quot;
        UDP thread, reads data from the server and handles it.
        &quot;&quot;&quot;

        while not self.udp_stop:
            gc.collect()
            try:
                data, src = self.sock.recvfrom(1024)
                _token = data[1:3]
                _type = data[3]
                if _type == PUSH_ACK:
                    self._log(&quot;Push ack&quot;)
                elif _type == PULL_ACK:
                    self._log(&quot;Pull ack&quot;)
                elif _type == PULL_RESP:
                    self.dwnb += 1
                    ack_error = TX_ERR_NONE
                    tx_pk = ujson.loads(data[4:])
                    payload = ubinascii.a2b_base64(tx_pk[&quot;txpk&quot;][&quot;data&quot;])
                    # depending on the board, pull the downlink message 1 or 6 ms upfronnt
                    tmst = utime.ticks_add(tx_pk[&quot;txpk&quot;][&quot;tmst&quot;], self.window_compensation)
                    t_us = utime.ticks_diff(utime.ticks_cpu(), utime.ticks_add(tmst, -15000))
                    if 1000 &lt; t_us &lt; 10000000:
                        self.uplink_alarm = Timer.Alarm(
                            handler=lambda x: self._send_down_link(
                                payload,
                                tmst, tx_pk[&quot;txpk&quot;][&quot;datr&quot;],
                                int(tx_pk[&quot;txpk&quot;][&quot;freq&quot;] * 1000 + 0.0005) * 1000
                            ),
                            us=t_us
                        )
                    else:
                        ack_error = TX_ERR_TOO_LATE
                        self._log('Downlink timestamp error!, t_us: {}', t_us)
                    self._ack_pull_rsp(_token, ack_error)
                    self._log(&quot;Pull rsp&quot;)
            except usocket.timeout:
                pass
            except OSError as ex:
                if ex.errno != errno.EAGAIN:
                    self._log('UDP recv OSError Exception: {}', ex)
            except Exception as ex:
                self._log('UDP recv Exception: {}', ex)

            # wait before trying to receive again
            utime.sleep_ms(UDP_THREAD_CYCLE_MS)

        # we are to close the socket
        self.sock.close()
        self.udp_stop = False
        self._log('UDP thread stopped')

    def _log(self, message, *args):
        &quot;&quot;&quot;
        Outputs a log message to stdout.
        &quot;&quot;&quot;

        print('[{:&gt;10.3f}] {}'.format(
            utime.ticks_ms() / 1000,
            str(message).format(*args)
))
</code></pre>
<p dir="auto"><img src="/assets/uploads/files/1527872474862-b93a6844-3774-4b61-9535-35fed3028c9b-image.png" alt="0_1527872476272_b93a6844-3774-4b61-9535-35fed3028c9b-image.png" class="img-responsive img-markdown" /></p>
<p dir="auto"><img src="/assets/uploads/files/1527872704966-8c706136-c99e-4cb7-bbc3-fd2ffd5ce2be-image.png" alt="0_1527872706415_8c706136-c99e-4cb7-bbc3-fd2ffd5ce2be-image.png" class="img-responsive img-markdown" /></p>
]]></description><link>https://forum.pycom.io/post/20102</link><guid isPermaLink="true">https://forum.pycom.io/post/20102</guid><dc:creator><![CDATA[Burgeh]]></dc:creator><pubDate>Mon, 04 Jun 2018 14:18:58 GMT</pubDate></item><item><title><![CDATA[Reply to LoPy1 to LoPy1 Nanogateway on Mon, 11 Jun 2018 18:21:30 GMT]]></title><description><![CDATA[<p dir="auto">Anyone with a LoPy to LoPy gateway got any suggestions?</p>
]]></description><link>https://forum.pycom.io/post/20232</link><guid isPermaLink="true">https://forum.pycom.io/post/20232</guid><dc:creator><![CDATA[Burgeh]]></dc:creator><pubDate>Mon, 11 Jun 2018 18:21:30 GMT</pubDate></item><item><title><![CDATA[Reply to LoPy1 to LoPy1 Nanogateway on Fri, 23 Aug 2019 22:22:20 GMT]]></title><description><![CDATA[<p dir="auto">I'm having a similar issue with mine - US, 915 mhz, on the dr=None allows the function to run but it never really joins.</p>
]]></description><link>https://forum.pycom.io/post/29229</link><guid isPermaLink="true">https://forum.pycom.io/post/29229</guid><dc:creator><![CDATA[gcapilot]]></dc:creator><pubDate>Fri, 23 Aug 2019 22:22:20 GMT</pubDate></item></channel></rss>