Lora 및 485 통신

2023. 3. 8. 15:52개발/Arduino

현재 연구팀에서 Lora 통신을 활용해

다양한 센서들을 웹 페이지에 등록하고

실시간 모니터링/제어 하는 시스템 구현중이다.

 

웹 페이지 등록전에

센서값을 일단 받아와야 한다.

보드는 위 Heltec WiFi Lora 32(v3) 를 사용했다.

기존에 ESP32(Dev module), ESP8266(D1 mini)를 사용해서

다양한 센서들을 등록했었다.

 

위 보드에 새로 다 등록을 하는 과정을 거친다.


1. 센서 데이터 추출

[Hunan Rika]RK520 토양센서를 보드와 연동해

온도/습도 데이터를 받아온다.

https://www.rikasensor.com/rk520-11-all-in-one-soil-sensor.html

 

Rk520-11 All-in-one Soil Sensor | Rika Sensors

Looking for soil temperature moisture sensor? Details about RK520-11 All-in-one Soil Sensor, soil ph probe, on Rika Sensors, Ask online!

www.rikasensor.com

위 링크 Data sheet에 485 프로토콜 정보가 자세하게 나와있다.

RS-485 데이터패킷을 아두이노에서 센서쪽으로 출력해

센서값을 시리얼 모니터에 받아온다.

#include "CRC.h"

unsigned int counter = 0;
unsigned long previousMillis = 0;     
const long interval = 2000; 
String inputString = "";         // 받은 문자열
float temp=0.,humi=0.;

void setup() {
  Serial.begin(9600);
  //Serial1.begin(19200, SERIAL_8N1, 6, 5);
  Serial2.begin(9600, SERIAL_8N1, 6, 5);

}

void loop() {
  tick();
  serial2Event();
}

void tick() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    Serial.println("tick");
    crd16Rtu();
  }  
}

void serial2Event() {
  if(Serial2.available() == false)
    return;
  while (Serial2.available()) {
    // get the new byte:
    char inChar = (char)Serial2.read();
    //Serial.print(inChar,HEX);
    // add it to the inputString:
    inputString += inChar;
  }
  Serial.println("");
  Serial.println(inputString);
  if(inputString.length() >= 9) {
    String ss="";
    ss=((float)inputString.charAt(3)*255+(float)inputString.charAt(4))/10;
    temp=ss.toFloat();
    Serial.println("온도 : "+ss+" 도");
    ss=((float)inputString.charAt(5)*255+(float)inputString.charAt(6))/10;
    humi=ss.toFloat();
    Serial.println("함수율 : "+ss+" %");
    inputString="";
    Serial.println("");
  }
}


// 아두이노에서 RS485 출력을 내보낸다.
void crd16Rtu() {
  //char str[24] =  {0x00,0x03,0x00,0x01,0x00,0x01,0x00,0x00,0x00};  //Read station number
  char str[24] =  {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00};  //Read Date
  String s;
  int si,sj,len;

  len=6;
  
  uint8_t * data = (uint8_t *) &str[0];
  si=crc16(data, len, 0x8005, 0xFFFF, 0x0000, true,  true  );
  sj=si&0xff;
  str[len]=sj;
  sj=si>>8;
  str[len+1]=sj;

  //Serial.println("");
  for(int i=0;i<len+2;i++) {
    Serial2.print (str[i]);
  }
}

crd16Rtu() 함수에서

Serial2 포트로 RS-485 데이터를 출력한다.

void crd16Rtu() {
  //char str[24] =  {0x00,0x03,0x00,0x01,0x00,0x01,0x00,0x00,0x00};  //Read station number
  char str[24] =  {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00};  //Read Date
  String s;
  int si,sj,len;

  len=6;
  
  uint8_t * data = (uint8_t *) &str[0];
  si=crc16(data, len, 0x8005, 0xFFFF, 0x0000, true,  true  );
  sj=si&0xff;
  str[len]=sj;
  sj=si>>8;
  str[len+1]=sj;

  //Serial.println("");
  for(int i=0;i<len+2;i++) {
    Serial2.print (str[i]);
  }
}

데이터값 계산하기

  • Example

응답 예시 프로토콜

Example respond 01 03 04 01 23 01 64 0A7E

4,5번째 바이트: 01 23
6,7번째 바이트: 01 64

  • Temperature = 0x0123 / 10 = 291 / 10 = 29.1
  • Humidity = 0x0164 / 10 = 356 / 10 = 35.6

데이터를 출력하면 위와 같이 16진수값이

센서로부터 출력된다.

바이트 연산을 거쳐서 온도/습도 값을 알 수 있다. 

센서 데이터를 시리얼 모니터에 정상적으로 출력했다.


2. Lora 송/수신

Client 소스 ( RK520 센서 데이터값 받아오는 보드 )

// 2023-03-08 RK520 센서 추가

#include <Wire.h>  
#include "LoRaWan_APP.h"
#include "Arduino.h"
#include <ArduinoJson.h>
#include "CRC.h"
#include "SPIFFS.h"


#include "HT_SSD1306Wire.h"

SSD1306Wire  displayOled(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // addr , freq , i2c group , resolution , rst


#define RF_FREQUENCY                                923000000 // Hz

#define TX_OUTPUT_POWER                             5        // dBm

#define LORA_BANDWIDTH                              0         // [0: 125 kHz,
                                                              //  1: 250 kHz,
                                                              //  2: 500 kHz,
                                                              //  3: Reserved]
#define LORA_SPREADING_FACTOR                       7         // [SF7..SF12]
#define LORA_CODINGRATE                             1         // [1: 4/5,
                                                              //  2: 4/6,
                                                              //  3: 4/7,
                                                              //  4: 4/8]
#define LORA_PREAMBLE_LENGTH                        8         // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT                         0         // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON                  false
#define LORA_IQ_INVERSION_ON                        false


#define RX_TIMEOUT_VALUE                            1000
#define BUFFER_SIZE                                 200 // Define the payload size here
#define TRIGGER_PIN 37 // trigger pin GPIO37

char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];

static RadioEvents_t RadioEvents;
void OnTxDone( void );
void OnTxTimeout( void );
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
void display(int no);
void tick();
void changeLoraTo();

typedef enum
{
    LOWPOWER,
    STATE_RX,
    STATE_TX
}States_t;

int16_t txNumber=0;
States_t state;
bool sleepMode = false;
int16_t Rssi,rxSize;

class Lora {
  public:
    String mac;
    int dire=0;
    int rssi=-200; //-80 보다 크면 아주 양호 -80~-90 양호  -90~-100 불량  -100보다 작음 나쁨
    int layer=100;
};
Lora loraC; // lora Client
Lora loraF; // lora onReceive 함수에서 From 임시로 전달받은 로라, 통시방향 관계없이 전달된 값
Lora loraT; // lora To 나에게 링크되어 메세지 서버쪽으로 전달할 로라

//String packet;
int packSize = 0;
String msgTo;
int msgType=0; //0=measure 1=전달(packet 을 lora로 보냄)

int type=13; 

unsigned int counter = 0;
unsigned long previousMillis = 0;     
const long interval = 5000; 
String inputString = "";
char mac[20];  //mac address

float temp=0.,humi=0.,pres=0.;

//json을 위한 설정
StaticJsonDocument<200> doc;
DeserializationError error;
JsonObject root;

void setup() {
    pinMode(TRIGGER_PIN, INPUT_PULLUP);
    Serial.begin(115200);
    Serial2.begin(9600, SERIAL_8N1, 6, 5);

    Serial.println("mac address");
    //이름 자동으로 생성
    uint64_t chipid;
    chipid=ESP.getEfuseMac();//The chip ID is essentially its MAC address(length: 6 bytes).
    loraC.mac=String(((uint16_t)(chipid>>32)),HEX)+String(((uint32_t)chipid),HEX);  
    Serial.println(loraC.mac);

    Mcu.begin();
    Rssi=0;

    RadioEvents.TxDone = OnTxDone;
    RadioEvents.TxTimeout = OnTxTimeout;
    RadioEvents.RxDone = OnRxDone;

    Radio.Init( &RadioEvents );
    Radio.SetChannel( RF_FREQUENCY );
    Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
                                   LORA_SPREADING_FACTOR, LORA_CODINGRATE,
                                   LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
                                   true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );

    Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
                                   LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
                                   LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
                                   0, true, 0, 0, LORA_IQ_INVERSION_ON, true );
    state=STATE_RX;

    displayOled.init();
    displayOled.setFont(ArialMT_Plain_10);
    displayOled.setTextAlignment(TEXT_ALIGN_LEFT);
    displayAct(0);

    readConfig();
}

void OnTxDone( void )
{
  Serial.print("TX done......");
  state=STATE_RX;
}

void OnTxTimeout( void )
{
    Radio.Sleep( );
    Serial.print("TX Timeout......");
    state=STATE_TX;
}
// 메세지 전달할 mac rssi 현재 lora의 layer 저장
void changeLoraTo() {
  //if(macFrom.length() !=12) return;
  loraT.mac="";
  loraT.mac+=loraF.mac;
  loraT.rssi=loraF.rssi;
  loraC.layer=loraF.layer+1;
  saveConfig();  
}


void displayAct(int no) {
  displayOled.clear();
  displayOled.drawString(0, 0, "Lola Client");
  displayOled.drawString(0, 10, "mac: "+loraC.mac);
  if(no==0) { //lora 시작
    displayOled.drawString(0, 20, "Start");
  }
  else if(no==1) {
    displayOled.drawString(0, 0, "Lora Start");
  }
  else if(no==2) {
    displayOled.drawString(0, 20, "temp "+String(counter));
  }
  else if(no==3) {
    displayOled.drawString(0, 20, "rxpacket");
  }
  else if(no==4) {
    displayOled.drawString(0, 20, "Out Out");
  }
  else if(no==5) {
    displayOled.drawString(0, 20, "Lora Reset");
  }
  displayOled.display();
}


void tickMeasure() {
  //온도 습도 측정
  //crd16Rtu();
  
  //측정값 로라로 전송
  //temp=0.,humi=0.,pres=0.;
  DynamicJsonDocument doc(1024);
  doc["mac"] = loraC.mac;
  doc["dire"]   = -1;
  doc["macTo"] = loraT.mac;
  doc["macFrom"] = loraF.mac;
  //doc["macFrom"] = loraC.mac;
  doc["type"] = type;
  doc["layer"] = loraC.layer;
  doc["rssi"] = loraF.rssi;
  
  doc["count"] = counter;
  doc["temp"] = temp;
  doc["humi"] = humi;
  doc["pres"] = pres;
  msgTo="";
  serializeJson(doc, msgTo); 
  //Serial.println(msgTo);
}


//LORA로 메세지 받으면 동작
//전달할 데이타중 조건에 맞으면 loraT로 SPIFF에 저장
//새로이 전달되어온 데이타는 loraF에 저장
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
  Rssi=rssi;
  rxSize=size;
  memcpy(rxpacket, payload, size );
  rxpacket[size]='\0';
  Radio.Sleep( );
  state=STATE_RX;
  //Serial.println(rxpacket);
  loraF.rssi=rssi;
  displayAct(3);

  String packet(rxpacket);
  deserializeJson(doc,packet);
  root = doc.as<JsonObject>();
  String macObj = root["macObj"];
  String macC = root["mac"];
  String macFrom = root["macFrom"];
  String macTo = root["macTo"];
  int dire = root["dire"];
  int layerIn = root["layer"];
  int func = root["func"];
  loraF.mac = "";
  loraF.mac += macFrom;
  loraF.dire = dire;
  loraF.layer = layerIn;

  //loraT.mac이 비어있으면 전송된 데이터를 loraT.mac으로 입력
  if( (loraT.mac.length()<6) && (loraF.rssi > -90)) {
    changeLoraTo();
  }

  if(dire==-1) {
    if(loraC.mac==macTo && loraC.layer!=0){
      packet.replace("\"layer\":"+String(layerIn),"\"layer\":"+String(loraC.layer));
      packet.replace("\"macFrom\":\""+macFrom,"\"macFrom\":\""+loraF.mac);
      packet.replace("\"macTo\":\""+macTo,"\"macTo\":\""+loraT.mac);
      packet.replace("\"mac\":\""+macC,"\"mac\":\""+loraC.mac);
      msgTo="";
      msgTo+=packet;
      msgType=1;
      Serial.println("Bridge(-전달) "+msgTo);
      state=STATE_TX;
      //msgType=0;
    }
  }

  if(dire==1) {
    //입력된 macTo 가 없을 때 macFrom 을 macTo로 입력
    // 더 좋은 수신호가 있을 때 macTo로 입력
    if((loraF.rssi-loraT.rssi) > 10 && (layerIn-loraC.layer) <= -1) {
      changeLoraTo();
    }
    // 메세지 전달 자기와 링크된 로라의 msg만 전달한다.
    if(loraF.mac==loraT.mac && macObj!=loraC.mac){
      //delay(random(0,3000));
      packet.replace("\"layer\":"+String(layerIn),"\"layer\":"+String(loraC.layer));
      packet.replace("\"macFrom\":\""+loraF.mac,"\"macFrom\":\""+loraC.mac);
      msgTo="";
      msgTo+=packet;
      msgType=1;
      Serial.println("Bridge(+전달) "+msgTo);
      state=STATE_TX;
      //msgType=0;
    }
    //명령수행
    //if(macObj==loraC.mac|| macObj=="ffffffffffff"){
    if(macObj==loraC.mac){
      Serial.println("msgFrom: "+packet);
      displayAct(4);
      if(func==255)
        factoryDefault();
    }
  }

}

void loop()
{
  tick();
  serial2Event();
  switch(state)
  {
    case STATE_TX:
      //Serial.println(msgTo);
      txNumber++;
      msgTo.toCharArray(txpacket,msgTo.length());
      Radio.Send( (uint8_t *)txpacket, strlen(txpacket) );
      msgType=0; 
      state=LOWPOWER;
      break;
    case STATE_RX:
      Serial.println("into RX mode");
      Radio.Rx( 0 );
      state=LOWPOWER;
      break;
    case LOWPOWER:
      Radio.IrqProcess( );
      break;
    default:
      break;
  }

  if ( digitalRead(TRIGGER_PIN) == LOW ) 
    factoryDefault();
}

void serial2Event() {
  if(Serial2.available() == false)
    return;
  while (Serial2.available()) {
    // get the new byte:
    char inChar = (char)Serial2.read();
    //Serial.print(inChar,HEX);
    // add it to the inputString:
    inputString += inChar;
  }
  Serial.println("");
  if(inputString.length() >= 9) {
    String ss="";
    ss=((float)inputString.charAt(3)*255+(float)inputString.charAt(4))/10;
    temp=ss.toFloat();
    Serial.println("온도 : "+ss+" 도");
    ss=((float)inputString.charAt(5)*255+(float)inputString.charAt(6))/10;
    humi=ss.toFloat();
    Serial.println("함수율 : "+ss+" %");
    inputString="";
    Serial.println("");
  }
}

void tick() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    crd16Rtu();
    if(msgType==0) {
      tickMeasure();
      state=STATE_TX;
      displayAct(2);
      counter++;
    }
  }  
}

void crd16Rtu() {
  //char str[24] =  {0x00,0x03,0x00,0x01,0x00,0x01,0x00,0x00,0x00};  //Read station number
  char str[24] =  {0x01, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00};  //Read Date
  String s;
  int si,sj,len;

  len=6;
  
  uint8_t * data = (uint8_t *) &str[0];
  si=crc16(data, len, 0x8005, 0xFFFF, 0x0000, true,  true  );
  sj=si&0xff;
  str[len]=sj;
  sj=si>>8;
  str[len+1]=sj;

  //Serial.println("");
  for(int i=0;i<len+2;i++) {
    Serial2.print (str[i]);
  }
}

gateway 소스

/* 프로토콜 설명
PC에서 오는 프로토콜
  mac: 이 프로그램이 동작하는 CPU mac address
  macObj:메세지 전송할 특정한 lora 주소  
  func:명령번호
lora 에서 추가되는 프로토콜
  layer: 서버는 0이고 lora를 거쳐서 전달할 때 +1 씩 증가한다.
  dire: 1=서버에서 lora로 전송 -1=lora 에서 서버로 전송
  macFrom:말단방향 lora 로부터 전송받은 lora mac 주소
  macTo: 서버방향르로 전송할 lora mac 주소
*/
#include <Wire.h>  
#include "LoRaWan_APP.h"
#include "Arduino.h"
#include <ArduinoJson.h>
#include "CRC.h"
#include "SPIFFS.h"
#include "HT_SSD1306Wire.h"
#include <PubSubClient.h>
#include <WiFi.h>
#include <WiFiUdp.h>

SSD1306Wire  displayOled(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // addr , freq , i2c group , resolution , rst


#define RF_FREQUENCY                                923000000 // Hz

#define TX_OUTPUT_POWER                             5        // dBm

#define LORA_BANDWIDTH                              0         // [0: 125 kHz,
                                                              //  1: 250 kHz,
                                                              //  2: 500 kHz,
                                                              //  3: Reserved]
#define LORA_SPREADING_FACTOR                       7         // [SF7..SF12]
#define LORA_CODINGRATE                             1         // [1: 4/5,
                                                              //  2: 4/6,
                                                              //  3: 4/7,
                                                              //  4: 4/8]
#define LORA_PREAMBLE_LENGTH                        8         // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT                         0         // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON                  false
#define LORA_IQ_INVERSION_ON                        false


#define RX_TIMEOUT_VALUE                            1000
#define BUFFER_SIZE                                 200 // Define the payload size here
#define TRIGGER_PIN 37 // trigger pin GPIO37
//HardwareSerial serial485(2);

char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];

static RadioEvents_t RadioEvents;
void OnTxDone( void );
void OnTxTimeout( void );
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr );
void display(int no);
void callback(char* topic, byte* payload, unsigned int length);
void displayAct(int no);
void readConfig();
void saveConfig();
void factoryDefault();
void bootWifiStation();
void tick();
void reconnect();
void udpEvent();

typedef enum
{
    LOWPOWER,
    STATE_RX,
    STATE_TX
}States_t;

int16_t txNumber;
States_t state;
bool sleepMode = false;
int16_t Rssi,rxSize;

class Lora {
  public:
    String mac;
    int dire=0;
    int rssi=-200; //-80 보다 크면 아주 양호 -80~-90 양호  -90~-100 불량  -100보다 작음 나쁨
    int layer=100;
};
Lora loraC; // lora Client
Lora loraF; // lora onReceive 함수에서 From 임시로 전달받은 로라, 통시방향 관계없이 전달된 값
Lora loraT; // lora To 나에게 링크되어 메세지 서버쪽으로 전달할 로라

//String packet;
int packSize = 0;
String msgTo;


unsigned int counter = 0;
unsigned long previousMillis = 0;     
const long interval = 5000; 
char mac[20];  //mac address
int mqttConnected=0; // 1=연결 0=끊김

float temp=0.,humi=0.,pres=0.;

//json을 위한 설정
StaticJsonDocument<200> doc;
DeserializationError error;
JsonObject root;

WiFiUDP Udp;
unsigned int localUdpPort = 4210; // local port to listen on
char incomingPacket[255]; // buffer for incoming packets

char ssid[40] = "와이파이 이름";
char password[50] = "비밀번호";
int type=999; // 999=lora gate way  
const char* outTopic = "/i2r/outTopic"; // 이름이 중복되지 않게 설정 기록
const char* inTopic = "/i2r/inTopic"; // 이름이 중복되지 않게 설정 기록
char clientName[30] = {0};  // setup 함수에서 자동생성
char ipMqtt[40]="";
char msg[500];

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
    pinMode(TRIGGER_PIN, INPUT_PULLUP);
    Serial.begin(115200);
    //serial485.begin(115200, SERIAL_8N1, 5, 6);

    Serial.println("mac address");
    //이름 자동으로 생성
    uint64_t chipid;
    chipid=ESP.getEfuseMac();//The chip ID is essentially its MAC address(length: 6 bytes).
    loraC.mac=String(((uint16_t)(chipid>>32)),HEX)+String(((uint32_t)chipid),HEX);  
    loraC.mac.toCharArray(clientName,loraC.mac.length()+1);
    Serial.println(loraC.mac);
    Serial.println(clientName);
    bootWifiStation();
    Udp.begin(localUdpPort);

    Mcu.begin();
    txNumber=0;
    Rssi=0;

    RadioEvents.TxDone = OnTxDone;
    RadioEvents.TxTimeout = OnTxTimeout;
    RadioEvents.RxDone = OnRxDone;

    Radio.Init( &RadioEvents );
    Radio.SetChannel( RF_FREQUENCY );
    Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
                                   LORA_SPREADING_FACTOR, LORA_CODINGRATE,
                                   LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
                                   true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );

    Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
                                   LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
                                   LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
                                   0, true, 0, 0, LORA_IQ_INVERSION_ON, true );
    state=STATE_RX;

    displayOled.init();
    displayOled.setFont(ArialMT_Plain_10);
    displayOled.setTextAlignment(TEXT_ALIGN_LEFT);
    displayAct(0);

    readConfig();
    // mqtt 설정
    client.setServer(ipMqtt, 1883);
    client.setCallback(callback);
}

void loop()
{
  tick();
  switch(state)
  {
    case STATE_TX:
      txNumber++;
      msgTo.toCharArray(txpacket,msgTo.length());
      Radio.Send( (uint8_t *)txpacket, strlen(txpacket) );
      state=LOWPOWER;
      break;
    case STATE_RX:
      Serial.println("into RX mode");
      Radio.Rx( 0 );
      state=LOWPOWER;
      break;
    case LOWPOWER:
      Radio.IrqProcess( );
      break;
    default:
      break;
  }

  if (!client.connected()) {
     reconnect();
  }
  else
    client.loop();
  udpEvent();

  if ( digitalRead(TRIGGER_PIN) == LOW ) 
    factoryDefault();
}

void bootWifiStation() {
  //referance: https://www.arduino.cc/en/Reference/WiFiStatus
  //WL_NO_SHIELD:255 WL_IDLE_STATUS:0 WL_NO_SSID_AVAIL:1 WL_SCAN_COMPLETED:2
  //WL_CONNECTED:3 WL_CONNECT_FAILED:4 WL_CONNECTION_LOST:5 WL_DISCONNECTED:6
  //WiFi 연결
  //bootMode=0; //0:station  1:AP
  Serial.println("Station Mode");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    //공장리셋
    if ( digitalRead(TRIGGER_PIN) == LOW ) 
      factoryDefault();
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP().toString());
}

void udpEvent() {
  int packetSize = Udp.parsePacket();
  if(packetSize) {
    // receive incoming UDP packets
    Serial.printf("Received %d bytes from %s, port %d\n", packetSize, Udp.remoteIP().toString().c_str(), Udp.remotePort());
    int len = Udp.read(incomingPacket, 255);
    if(len > 0) {
      incomingPacket[len] = 0;
    }
    Serial.printf("UDP packet contents: %s\n", incomingPacket);
    deserializeJson(doc,incomingPacket);
    root = doc.as<JsonObject>();
    String mqttIp = root["mqttIp"];
    mqttIp.toCharArray(ipMqtt, mqttIp.length()+1);
    Serial.println(ipMqtt);
    saveConfig();
  }  
}


// mqtt 통신에 지속적으로 접속한다.
void reconnect() {
  if(String(ipMqtt).length()<2) {
    displayAct(4);
    return;
  }
  Serial.println("reconnect");
  Serial.println(ipMqtt);
  //if(WiFi.status() == WL_DISCONNECTED)
  //  return;
  //Serial.println("====================================");
  //Serial.println(countMqtt);
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(clientName)) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      //client.publish(outTopic, "Reconnected");
      // ... and resubscribe
      client.subscribe(inTopic);
      mqttConnected=1;
      displayAct(2);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 180 seconds");
      mqttConnected=0;
      displayAct(3);
      // Wait 5 seconds before retrying
      //delay(5000);
      delay(500);
    }
  }
}


void OnTxDone( void )
{
  Serial.print("TX done......");
  state=STATE_RX;
}

void OnTxTimeout( void )
{
    Radio.Sleep( );
    Serial.print("TX Timeout......");
    state=STATE_TX;
}


void displayAct(int no) {
  displayOled.clear();
  displayOled.drawString(0, 0, "Lola Gateway");
  displayOled.drawString(0, 10, "mac: "+loraC.mac);
  if(no==0) { //lora 시작
    displayOled.drawString(0, 20, "Start");
  }
  else if(no==1) {
    displayOled.drawString(0, 0, "Lora Start");
  }
  else if(no==2) {
    displayOled.drawString(0, 20, "connected "+String(counter));
  }
  else if(no==3) {
    displayOled.drawString(0, 20, "mqtt connect fail");
  }
  else if(no==4) {
    displayOled.drawString(0, 20, "no Mqtt server IP");
  }
  else if(no==5) {
    displayOled.drawString(0, 20, "Lora Reset");
  }
  displayOled.display();
}


void tick() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    /*
      msgTo="count: "+ counter;
      state=STATE_TX;
      displayAct(2);
      counter++;
      */
    /*
    //서버로 전송 테스트
    if(client.connected()) {
      Serial.println(counter++);
      sprintf(msg, "%d", counter);
      client.publish(outTopic, msg);
      //client.publish(outTopic, "test");
    }
    */
  }  
}

//LORA로 메세지 받으면 동작
//전달할 데이타중 조건에 맞으면 loraT로 SPIFF에 저장
//새로이 전달되어온 데이타는 loraF에 저장
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
  Rssi=rssi;
  rxSize=size;
  memcpy(rxpacket, payload, size );
  rxpacket[size]='\0';
  Radio.Sleep( );
  state=STATE_RX;
  //Serial.println(rxpacket);
  loraF.rssi=rssi;
  displayAct(3);

  Serial.println(rxpacket);
  //if((macTo == loraC.mac) && dire==-1) {
    client.publish(outTopic, rxpacket);
  //}
}

// 와이파이 통신에서 문자가 들어오면 이 함수의 payload 배열에 저장된다.
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  String sMsg="";
  for (int i = 0; i < length; i++) {
    //Serial.print((char)payload[i]);
    sMsg+=(char)payload[i];
  }
  //Serial.println(sMsg);
  
  // json {}로 수신되어야 한며 }를 추가 메세지를 붙여서 보낸다.
  deserializeJson(doc,payload);
  root = doc.as<JsonObject>();
  String macIn = root["mac"];
  String sAdd=",\"mac\":\""+loraC.mac+"\",\"dire\":1,\"layer\":0}";
  sMsg.replace("}",sAdd);
  msgTo=sMsg;
  Serial.println(msgTo);
  state=STATE_TX;
}

COM11에 연결된 보드가 RK520 센서의

온도/습도 값을 받아온 뒤 Lora 통신을 사용해

gateway로 데이터를 전송한다.

COM14에 연결된 gateway 보드가

Lora통신값을 정상적으로 받아온다.

 

진행하면서

초기에

보드가 센서값을 못 읽어오는 오류가 발생했었는데

단순하게 센서의 입력전압을 높여줬더니

해결됐었다.

 

3일동안 고뇌하고 고뇌했는데

단순한 문제였다

 

'개발 > Arduino' 카테고리의 다른 글

Xiaomi Flower care + ESP32 / BLE통신  (0) 2023.03.13
ESP32 BLE & RS-232 RS-485통신  (0) 2023.03.02
ESP32 web server  (0) 2023.02.28
성능지표 테스트  (0) 2023.01.10
성능지표 및 Arduino 함수 트리거 처리  (2) 2023.01.04