text: // ---------------------------------------------------- //
  // SimpleLMIC.cpp
  // ---------------------------------------------------- //
  
  #include <Arduino.h>
  #include "SimpleLMIC.h"
  
  #include <SPI.h>
  #include "lmic.h"
  #include "hal/hal.h"
  #include "arduino_lmic_hal_boards.h"
  
  SimpleLMICClass *pSimpleLMIC = NULL;
  
  SimpleLMICClass::SimpleLMICClass()
  {
    pSimpleLMIC = this;
    setTimeout(0);
  }
  
  /***********************************************************************************/
  
  bool SimpleLMICClass::begin()
  {
    os_init();
    LMIC_reset();
  //   LMIC_setupChannel (0, 868100000, DR_RANGE_MAP (DR_SF12, DR_SF7), BAND_CENTI);      // g-band
  //   LMIC_setupChannel (1, 868300000, DR_RANGE_MAP (DR_SF12, DR_SF7B), BAND_CENTI);      // g-band
  //   LMIC_setupChannel (2, 868500000, DR_RANGE_MAP (DR_SF12, DR_SF7), BAND_CENTI);      // g-band
  //   LMIC_setupChannel (3, 867100000, DR_RANGE_MAP (DR_SF12, DR_SF7), BAND_CENTI);      // g-band
  //   LMIC_setupChannel (4, 867300000, DR_RANGE_MAP (DR_SF12, DR_SF7), BAND_CENTI);      // g-band
  //   LMIC_setupChannel (5, 867500000, DR_RANGE_MAP (DR_SF12, DR_SF7), BAND_CENTI);      // g-band
  //   LMIC_setupChannel (6, 867700000, DR_RANGE_MAP (DR_SF12, DR_SF7), BAND_CENTI);      // g-band
  //   LMIC_setupChannel (7, 867900000, DR_RANGE_MAP (DR_SF12, DR_SF7), BAND_CENTI);      // g-band
  //   LMIC_setupChannel (8, 868800000, DR_RANGE_MAP (DR_FSK, DR_FSK), BAND_MILLI);      // g2-band
    //LMIC.dn2Dr = DR_SF9;
    return true;
  }
  
  bool SimpleLMICClass::begin(const void *pPinMap)
  {
    if (!os_init_ex(pPinMap))
      return false;
  
    LMIC_reset();
    return true;
  }
  
  void SimpleLMICClass::join()
  {
    LMIC_startJoining();
  }
  
  void SimpleLMICClass::join(const char *_aeui, const char *_akey)
  {
    setAppKey(_akey);
    setAppEui(_aeui);
    join();
  }
  
  void SimpleLMICClass::join(const char *_deui, const char *_aeui, const char *_akey)
  {
    setDevEui(_deui);
    setAppKey(_akey);
    setAppEui(_aeui);
    join();
  }
  
  /***********************************************************************************/
  
  void SimpleLMICClass::setLink(bool link)
  {
    LMIC_setLinkCheckMode(link);
  }
  
  void SimpleLMICClass::setAdr(bool adr)
  {
    LMIC_setAdrMode(adr);
  }
  
  void SimpleLMICClass::setSubBand(uint8_t band)
  {
  #if CFG_LMIC_US_like
    LMIC_selectSubBand(band - 1);
  #endif
  }
  
  void SimpleLMICClass::setError(uint8_t error)
  {
    LMIC_setClockError(MAX_CLOCK_ERROR * error / 100);
  }
  
  void SimpleLMICClass::setSF (enum _dr_eu868_t sf)
  {
      if (sf >= EU868_DR_SF12 && sf <= EU868_DR_NONE) {
          LMIC_setDrTxpow (sf, KEEP_TXPOW);
      }
  }
  
  /***********************************************************************************/
  
  bool SimpleLMICClass::isBusy()
  {
    return (LMIC.opmode & OP_TXDATA) || (LMIC.opmode & OP_TXRXPEND) || (LMIC.opmode & OP_POLL) || (LMIC.opmode & OP_JOINING);
  }
  
  bool SimpleLMICClass::isLink()
  {
    return !(LMIC.opmode & OP_LINKDEAD);
  }
  
  void SimpleLMICClass::debug(bool _bug)
  {
    _debug = _bug;
  }
  
  void SimpleLMICClass::loop()
  {
    os_runloop_once();
  }
  
  void SimpleLMICClass::sleep()
  {
    os_radio(RADIO_RST);
    SPI.end();
  }
  
  void SimpleLMICClass::end()
  {
    LMIC_shutdown();
    SPI.end();
  }
  
  /***********************************************************************************/
  
  size_t SimpleLMICClass::write(uint8_t c)
  {
    if (LMIC.pendTxLen == SIZEOFEXPR(LMIC.pendTxData))
      return 0;
    LMIC.pendTxData[LMIC.pendTxLen++] = c;
    return 1;
  };
  
  size_t SimpleLMICClass::write(const uint8_t *buffer, size_t size)
  {
    size_t n = 0;
    for (size_t i = 0; i < size; i++)
    {
      n += write(*(buffer + i));
    }
    return n;
  }
  
  int SimpleLMICClass::available()
  {
    return LMIC.dataLen - bufferPosition;
  }
  
  int SimpleLMICClass::read()
  {
    return bufferPosition < LMIC.dataLen ? (LMIC.frame + LMIC.dataBeg)[bufferPosition++] : -1;
  }
  
  int SimpleLMICClass::peek()
  {
    return bufferPosition < LMIC.dataLen ? (LMIC.frame + LMIC.dataBeg)[bufferPosition] : -1;
  }
  
  void SimpleLMICClass::flush()
  {
  }
  
  void SimpleLMICClass::send(uint8_t port, bool confirm)
  {
    LMIC.pendTxConf = confirm;
    LMIC.pendTxPort = port;
    LMIC_setTxData();
  }
  
  int16_t SimpleLMICClass::read16()
  {
    if (available() < 2)
      return 0;
    return (read() << 8 | read());
  }
  
  int32_t SimpleLMICClass::read24()
  {
    if (available() < 3)
      return 0;
    return (read() << 24 | read() << 16 | read() << 8) >> 8;
  }
  
  int32_t SimpleLMICClass::read32()
  {
    if (available() < 4)
      return 0;
    return (read() << 24 | read() << 16 | read() << 8 | read());
  }
  
  uint8_t *SimpleLMICClass::buffer()
  {
    return ((uint8_t *)(LMIC.frame + LMIC.dataBeg + bufferPosition));
  }
  
  void SimpleLMICClass::clear()
  {
    LMIC.pendTxLen = 0;
    bufferPosition = 0;
  }
  
  unsigned int SimpleLMICClass::readInt()
  {
    if (available() < 2)
      return 0;
    unsigned int i = 0;
    i += read() << 8;
    i += read();
    return i;
  }
  
  unsigned long SimpleLMICClass::readLong()
  {
    if (available() < 4)
      return 0;
    unsigned long i = 0;
    i += read() << 24;
    i += read() << 16;
    i += read() << 8;
    i += read();
    return i;
  }
  
  /***********************************************************************************/
  
  // ---------------------------------------------------- //
  // callback
  // ---------------------------------------------------- //
  
  void SimpleLMICClass::onMessage(void (*cb)(uint8_t *payload, size_t size, uint8_t port))
  {
    messageCallback = cb;
  }
  
  void SimpleLMICClass::onJoin (void(*cb)(uint8_t sf, uint32_t netid, uint32_t devaddr, uint8_t* nwkKey, uint8_t* artKey)) {
      nodeJoined = cb;
  }
  
  void SimpleLMICClass::message(uint8_t *payload, size_t size, uint8_t port)
  {
    if (messageCallback)
    {
      messageCallback(LMIC.frame + LMIC.dataBeg, LMIC.dataLen, port);
    }
  }
  
  // ---------------------------------------------------- //
  // join keys
  // ---------------------------------------------------- //
  
  void SimpleLMICClass::setAppKey(uint8_t *_akey)
  {
    memcpy(AppKey, _akey, 16);
  }
  
  void SimpleLMICClass::setAppKey(const char *_akey)
  {
    LORA_HEX_TO_BYTE(AppKey, _akey, 16);
  }
  
  void SimpleLMICClass::setAppEui(uint8_t *_aeui)
  {
    memcpy(AppEui, _aeui, 8);
  }
  
  void SimpleLMICClass::setAppEui(const char *_aeui)
  {
    LORA_HEX_TO_BYTE(AppEui, _aeui, 8);
  }
  
  void SimpleLMICClass::setDevEui(uint8_t *_deui)
  {
    memcpy(DevEui, _deui, 8);
  }
  
  void SimpleLMICClass::setDevEui(const char *_deui)
  {
    LORA_HEX_TO_BYTE(DevEui, _deui, 8);
  }
  
  void SimpleLMICClass::personalize(const char *_devAddr, const char *_nwkSKey, const char *_appSKey)
  {
    uint8_t devAddr[4];
    uint8_t nwkSKey[16];
    uint8_t appSKey[16];
    LORA_HEX_TO_BYTE((char *)devAddr, _devAddr, 4);
    LORA_HEX_TO_BYTE((char *)nwkSKey, _nwkSKey, 16);
    LORA_HEX_TO_BYTE((char *)appSKey, _appSKey, 16);
    LMIC_setSession(0x13, LORA_DEVADDR(devAddr), nwkSKey, appSKey);
  }
  
  void SimpleLMICClass::joined (uint8_t sf, uint32_t netid, uint32_t devaddr, uint8_t* nwkKey, uint8_t* artKey) {
      if (nodeJoined){
          nodeJoined (sf, netid, devaddr, nwkKey, artKey);
      }
  }
  
  // ---------------------------------------------------- //
  // LMIC
  // ---------------------------------------------------- //
  
  void os_getArtEui(u1_t *buf)
  {
    for (int i = 0; i < 8; i++)
      buf[i] = pSimpleLMIC->AppEui[7 - i];
  }
  
  void os_getDevEui(u1_t *buf)
  {
    for (int i = 0; i < 8; i++)
      buf[i] = pSimpleLMIC->DevEui[7 - i];
  }
  
  void os_getDevKey(u1_t *buf)
  {
    memcpy(buf, pSimpleLMIC->AppKey, 16);
  }
  
  void onEvent(ev_t ev)
  { 
      uint32_t netid = 0;
      devaddr_t devaddr = 0;
      u1_t nwkKey[16];
      u1_t artKey[16];
      
    if (pSimpleLMIC->_debug)
    {
      Serial.print(osticks2ms(os_getTime()));
      Serial.print(": ");
      Serial.print(LMIC.freq / 1000000.0);
      Serial.print(" | ");
  
      const char *const evNames[] = {LMIC_EVENT_NAME_TABLE__INIT};
  
      if (ev < sizeof(evNames) / sizeof(evNames[0]))
      {
        Serial.print(evNames[ev]);
      }
      else
      {
        Serial.print(F("Unknown event: "));
        Serial.print((unsigned)ev);
      }
      Serial.println();
    }
  
    switch (ev)
    {
    case EV_JOINING:
      break;
    case EV_JOINED:
        LMIC_getSessionKeys (&netid, &devaddr, nwkKey, artKey);
        pSimpleLMIC->joined (LMIC.datarate, netid, devaddr, nwkKey, artKey);
      //LMIC_getSessionKeys(&pSimpleLMIC->NetID, &pSimpleLMIC->DevAddr, pSimpleLMIC->NwkSKey, pSimpleLMIC->AppSKey);
      break;
    case EV_JOIN_FAILED:
      break;
    case EV_REJOIN_FAILED:
      break;
    case EV_TXCOMPLETE:
      pSimpleLMIC->clear();
      if (LMIC.dataLen != 0 || LMIC.dataBeg != 0)
      {
        uint8_t port = 0;
        if (LMIC.txrxFlags & TXRX_PORT)
        {
          port = LMIC.frame[LMIC.dataBeg - 1];
        }
        pSimpleLMIC->message(LMIC.frame + LMIC.dataBeg, LMIC.dataLen, port);
      }
      break;
    case EV_LOST_TSYNC:
      break;
    case EV_RESET:
      break;
    case EV_RXCOMPLETE:
      break;
    case EV_LINK_DEAD:
      break;
    case EV_LINK_ALIVE:
      break;
    case EV_TXSTART:
      break;
    }
  }
  
  // ---------------------------------------------------- //
  // STM32
  // Change the HAL_GetTick
  // to fix the noInterrupt() problem.
  // ---------------------------------------------------- //
  
  #if defined(ARDUINO_ARCH_STM32)
  uint32_t HAL_GetTick(void)
  {
    extern __IO uint32_t uwTick;
  
    /* Read PRIMASK register, check interrupt status before disable them */
    /* Returns 0 if they are enabled, or non-zero if disabled */
    if (__get_PRIMASK())
    {
      if (SCB->ICSR & SCB_ICSR_PENDSTSET_Msk)
      {
        SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk;
        HAL_IncTick();
      }
    }
  
    return uwTick;
  }
  #endif