Lopy Nano Gateway // TTGO as Node

  • I have been trying to send some Data from my TTGO Wemos China Node to a Lopy operating as Nano Gateway. It's a Lora-Mac / RAW connection but I have not managed to send one single massage. Maybe you (the experts) can help me with some code snippets. Thanks

    This is the Lopy nano Gateway code:

    from mqtt import MQTTClient
    import time
    import pycom
    import socket
    import struct
    from network import LoRa
    from network import WLAN
    from machine import WDT
    wdt = WDT(timeout=8000)
    # A basic package header
    # B: 1 byte for the deviceId
    # B: 1 byte for the pkg size
    # B: 1 byte for the messageId
    # %ds: Formated string for string
    _LORA_PKG_FORMAT = "!BBB%ds"
    # A basic ack package
    # B: 1 byte for the deviceId
    # B: 1 byte for the pkg size
    # B: 1 byte for the messageId
    # B: 1 byte for the Ok (200) or error messages
    # Open a Lora Socket, use rx_iq to avoid listening to our own messages
    lora = LoRa(mode=LoRa.LORA, rx_iq=True, frequency=864500000, sf=12)
    lora_sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
    broker_addr = "m23.cloudmqtt.com" 
    #broker_addr = "test.mosquitto.org"
    MYDEVID = "PMdev"
    wlan = WLAN(mode=WLAN.STA)
    wlan.connect("removed", auth=(WLAN.WPA2, "removed"), timeout=5000)
    def settimeout(duration):
    while not wlan.isconnected():
    print("connected to WIFI")
    client = MQTTClient(MYDEVID, broker_addr, user="removed", password="removed", port=removed)
    client.settimeout = settimeout
    print('Sending messages...')
    while True:
        #LOARA SETUP
        # Since the maximum body size in the protocol is 255 the request is limited to 512 bytes
        recv_pkg = lora_sock.recv(512)
        # If at least a message with the header is received process it
        if (len(recv_pkg) > 3):
            print("Package received")
            recv_pkg_len = recv_pkg[1]
            # If message is corrupted should not continue processing
            if (not len(recv_pkg) == recv_pkg_len + 3):
                # Unpack the message based on the protocol definition
                device_id, pkg_len, msg_id, msg = struct.unpack(_LORA_PKG_FORMAT % recv_pkg_len, recv_pkg)
                # Respond to the device with an acknoledge package
                ack_pkg = struct.pack(_LORA_PKG_ACK_FORMAT, device_id, 1, msg_id, 200)
                # publishing the data
                client.publish(MYDEVID+ "/value", str(msg))
                print("sent ack")

    And here is the TTGO Arduino code (Node):

    extern int transmission_mode;
    int transmission_mode = 1;
    uint8_t device_address = 3;  //  My own address as assigned by TP-IoT.
    uint8_t gateway_address = 4;  //  TP-IoT Gateway address.
    const int transmit_interval = 5000;  //  How often to send a message to gateway, in milliseconds.
    unsigned char packet_num = 1;  //  Number of packets sent to gateway so far.
    #include <lmic.h>
    #include <hal/hal.h>
    #include <SPI.h>
    osjob_t txjob, timeoutjob;  //  Transmission and timeout tasks.
    static void tx_func (osjob_t* job) {
      //  Send a message containing sample sensor data to the LoRa gateway, delimited by "|".
      //  The data sequence must match lora_gateway.py: temperature, humidity, light_level, message.  We may skip fields by sending "||".
      char sensor_data[] = "Texttotransfer";
      tx(sensor_data, txdone_func);  //  Send the data to gateway.
      // Send the next set of sensor data every TX_INTERVAL milliseconds, plus a bit of randomness to prevent systematic collisions.
      os_setTimedCallback(job, os_getTime() + ms2osticks(transmit_interval + random(500)), tx_func);
    //  Program starts here.
    void setup() {
      Serial.begin(9600); Serial.println("Starting");
      pinMode(LED_BUILTIN, OUTPUT);  //  Allow use of LED.
      os_init();  //  Initialize runtime environment.
      //  Set up these settings once, and use them for both sending and receiving.
      LMIC.freq = 864500000; // TP-IoT runs on Channel 10: 865.20 MHz.
      LMIC.datarate = DR_SF12;  //  TP-IoT Mode 1: Use Spreading Factor 12 for encoding data.
      LMIC.txpow = 14;  //  Maximum transmission power.
      // This sets the default Coding Rate and bandwidth, to be updated in radio.c when we transmit.
      LMIC.rps = updr2rps(LMIC.datarate);
      Serial.println("Started"); Serial.flush();
      // First task to be run is to send sensor data.
      os_setCallback(&txjob, tx_func);
    void loop() {
      // Loop repeatedly to execute scheduled tasks and handle events.
    //  Pin mapping for Dragino LoRa Shield.
    const lmic_pinmap lmic_pins = {
        .nss = 18,
        .rxtx = LMIC_UNUSED_PIN,
        .rst = 14,
        .dio = {26, 33, 32},
    void tx(const char *str, osjobcb_t func) {
      //  Transmit the given string to the LoRa gateway and call the given function afterwards.
      //  Message header contains destination address, source address, packet number, packet length.
      uint8_t header[3];
      header[0] = gateway_address;
      header[1] = device_address;
      header[2] = packet_num++;
      //header[3] = device_address;
      os_radio(RADIO_RST); //  Before sending, stop the receiving first.
      delay(1); //  Wait a bit, without this os_radio below asserts, apparently because the state hasn't changed yet.
      //  Copy the header into the message.
      LMIC.dataLen = 0;
      for (int i = 0; i < sizeof(header); i++)
        LMIC.frame[LMIC.dataLen++] = header[i];    
      //  Copy the data into the message.
      const char *s = str;
      while (*s)
        LMIC.frame[LMIC.dataLen++] = *s++;
      LMIC.osjob.func = func;
      //  Send the message.
      Serial.print("Sending to gateway "); Serial.print(gateway_address);
      Serial.print(": "); Serial.println(str);
    void rx(osjobcb_t func) {
      // Enable receive mode and call func when a packet is received.
      LMIC.osjob.func = func;
      LMIC.rxtime = os_getTime(); // RX _now_
      // Enable "continuous" RX (e.g. without a timeout, still stops after receiving a packet)
    static void rxtimeout_func(osjob_t *job) {
      //  Switch off the LED on timeout.
      digitalWrite(LED_BUILTIN, LOW);
    static void rx_func (osjob_t* job) {
      // Blink once to confirm reception and then keep the led on
      digitalWrite(LED_BUILTIN, LOW); // off
      digitalWrite(LED_BUILTIN, HIGH); // on
      //  TODO: Test whether this is necessary.
      // Timeout RX (i.e. update led status) after 3 periods without RX
      os_setTimedCallback(&timeoutjob, os_getTime() + ms2osticks(3*transmit_interval), rxtimeout_func);
      // Reschedule TX so that it should not collide with the other side's next TX
      os_setTimedCallback(&txjob, os_getTime() + ms2osticks(transmit_interval/2), tx_func);
      Serial.print("Got ");
      Serial.println(" bytes");
      Serial.write(LMIC.frame, LMIC.dataLen);
      // Restart RX
    static void txdone_func (osjob_t* job) {
      //  This is called just after sending a message to the gateway.
    // These callbacks are only used in over-the-air activation, so they are
    // left empty here (we cannot leave them out completely unless
    // DISABLE_JOIN is set in config.h, otherwise the linker will complain).
    void os_getArtEui (u1_t* buf) { }
    void os_getDevEui (u1_t* buf) { }
    void os_getDevKey (u1_t* buf) { }
    void onEvent (ev_t ev) { }
    #if !defined(DISABLE_INVERT_IQ_ON_RX)
    #error This program requires DISABLE_INVERT_IQ_ON_RX to be set. Update config.h in the lmic library to set it.

Pycom on Twitter