mirror of https://github.com/SQ9MDD/ESP32IGate.git
Add digital repeater mode
This commit is contained in:
parent
7491385819
commit
a5dbf60772
|
|
@ -10,7 +10,7 @@
|
||||||
#ifndef MAIN_H
|
#ifndef MAIN_H
|
||||||
#define MAIN_H
|
#define MAIN_H
|
||||||
|
|
||||||
#define VERSION "0.4"
|
#define VERSION "0.5"
|
||||||
|
|
||||||
#define DEBUG
|
#define DEBUG
|
||||||
//#define DEBUG_IS
|
//#define DEBUG_IS
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
#define FORMAT_SPIFFS_IF_FAILED true
|
#define FORMAT_SPIFFS_IF_FAILED true
|
||||||
|
|
||||||
#define PKGLISTSIZE 10
|
#define PKGLISTSIZE 10
|
||||||
|
#define PKGTXSIZE 5
|
||||||
|
|
||||||
const int timeZone = 7; // Bangkok
|
const int timeZone = 7; // Bangkok
|
||||||
|
|
||||||
|
|
@ -41,6 +42,7 @@ const int timeZone = 7; // Bangkok
|
||||||
#include <SD.h>
|
#include <SD.h>
|
||||||
#include <SPIFFS.h>
|
#include <SPIFFS.h>
|
||||||
#include "soc/rtc_wdt.h"
|
#include "soc/rtc_wdt.h"
|
||||||
|
#include <AX25.h>
|
||||||
|
|
||||||
#include "HardwareSerial.h"
|
#include "HardwareSerial.h"
|
||||||
#include "EEPROM.h"
|
#include "EEPROM.h"
|
||||||
|
|
@ -99,9 +101,12 @@ typedef struct Config_Struct {
|
||||||
char mqtt_user[10];
|
char mqtt_user[10];
|
||||||
char mqtt_pass[10];
|
char mqtt_pass[10];
|
||||||
char wifi_power;
|
char wifi_power;
|
||||||
|
uint16_t tx_timeslot;
|
||||||
|
uint16_t digi_delay;
|
||||||
|
|
||||||
}Configuration;
|
}Configuration;
|
||||||
|
|
||||||
typedef struct digiTLM_struct {
|
typedef struct igateTLM_struct {
|
||||||
uint16_t Sequence;
|
uint16_t Sequence;
|
||||||
unsigned long ParmTimeout;
|
unsigned long ParmTimeout;
|
||||||
unsigned long TeleTimeout;
|
unsigned long TeleTimeout;
|
||||||
|
|
@ -110,7 +115,7 @@ typedef struct digiTLM_struct {
|
||||||
uint8_t RX;
|
uint8_t RX;
|
||||||
uint8_t TX;
|
uint8_t TX;
|
||||||
uint8_t DROP;
|
uint8_t DROP;
|
||||||
}digiTLMType;
|
}igateTLMType;
|
||||||
|
|
||||||
typedef struct pkgListStruct {
|
typedef struct pkgListStruct {
|
||||||
time_t time;
|
time_t time;
|
||||||
|
|
@ -134,6 +139,23 @@ typedef struct statisticStruct {
|
||||||
uint32_t inet2rf;
|
uint32_t inet2rf;
|
||||||
}statusType;
|
}statusType;
|
||||||
|
|
||||||
|
typedef struct digiTLM_struct{
|
||||||
|
unsigned int Sequence;
|
||||||
|
unsigned int ParmTimeout;
|
||||||
|
unsigned int TeleTimeout;
|
||||||
|
unsigned char RxPkts;
|
||||||
|
unsigned char TxPkts;
|
||||||
|
unsigned char DropRx;
|
||||||
|
unsigned char ErPkts;
|
||||||
|
}digiTLMType;
|
||||||
|
|
||||||
|
typedef struct txQueue_struct{
|
||||||
|
bool Active;
|
||||||
|
long timeStamp;
|
||||||
|
int Delay;
|
||||||
|
char Info[300];
|
||||||
|
}txQueueType;
|
||||||
|
|
||||||
const char PARM[] = { "PARM.RF->INET,INET->RF,RxPkts,TxPkts,IGateDropRx" };
|
const char PARM[] = { "PARM.RF->INET,INET->RF,RxPkts,TxPkts,IGateDropRx" };
|
||||||
const char UNIT[] = { "UNIT.Pkts,Pkts,Pkts,Pkts,Pkts" };
|
const char UNIT[] = { "UNIT.Pkts,Pkts,Pkts,Pkts,Pkts" };
|
||||||
const char EQNS[] = { "EQNS.0,1,0,0,1,0,0,1,0,0,1,0,0,1,0" };
|
const char EQNS[] = { "EQNS.0,1,0,0,1,0,0,1,0,0,1,0,0,1,0" };
|
||||||
|
|
@ -150,5 +172,7 @@ void sort(pkgListType a[], int size);
|
||||||
void sortPkgDesc(pkgListType a[], int size);
|
void sortPkgDesc(pkgListType a[], int size);
|
||||||
int processPacket(String &tnc2);
|
int processPacket(String &tnc2);
|
||||||
String send_fix_location();
|
String send_fix_location();
|
||||||
|
int digiProcess(AX25Msg &Packet);
|
||||||
|
void printTime();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#include <driver/adc.h>
|
#include <driver/adc.h>
|
||||||
#include "esp_adc_cal.h"
|
#include "esp_adc_cal.h"
|
||||||
#include "cppQueue.h"
|
#include "cppQueue.h"
|
||||||
//#include "ButterworthFilter.h"
|
#include "ButterworthFilter.h"
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
|
@ -28,7 +28,7 @@ bool hw_afsk_dac_isr = false;
|
||||||
Afsk *AFSK_modem;
|
Afsk *AFSK_modem;
|
||||||
|
|
||||||
// //กรองความถี่สูงผ่าน >300Hz HPF Butterworth Filter. 0-300Hz ช่วงความถี่ต่ำใช้กับโทน CTCSS/DCS ในวิทยุสื่อสารจะถูกรองทิ้ง
|
// //กรองความถี่สูงผ่าน >300Hz HPF Butterworth Filter. 0-300Hz ช่วงความถี่ต่ำใช้กับโทน CTCSS/DCS ในวิทยุสื่อสารจะถูกรองทิ้ง
|
||||||
// ButterworthFilter hp_filter(300, 9600, ButterworthFilter::ButterworthFilter::Highpass, 2);
|
ButterworthFilter hp_filter(1300, 9600, ButterworthFilter::ButterworthFilter::Highpass, 1);
|
||||||
// //กรองความถี่ต่ำผ่าน <3KHz LPF Butterworth Filter. ความถี่เสียงที่มากกว่า 3.5KHz ไม่ใช่ความถี่เสียงคนพูดจะถูกกรองทิ้ง
|
// //กรองความถี่ต่ำผ่าน <3KHz LPF Butterworth Filter. ความถี่เสียงที่มากกว่า 3.5KHz ไม่ใช่ความถี่เสียงคนพูดจะถูกกรองทิ้ง
|
||||||
// ButterworthFilter lp_filter(3500, 9600, ButterworthFilter::ButterworthFilter::Lowpass, 2);
|
// ButterworthFilter lp_filter(3500, 9600, ButterworthFilter::ButterworthFilter::Lowpass, 2);
|
||||||
|
|
||||||
|
|
@ -411,7 +411,7 @@ uint8_t AFSK_dac_isr(Afsk *afsk)
|
||||||
}
|
}
|
||||||
|
|
||||||
int hdlc_flag_count = 0;
|
int hdlc_flag_count = 0;
|
||||||
bool hdlc_flage_end=false;
|
bool hdlc_flage_end = false;
|
||||||
static bool hdlcParse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo)
|
static bool hdlcParse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo)
|
||||||
{
|
{
|
||||||
// Initialise a return value. We start with the
|
// Initialise a return value. We start with the
|
||||||
|
|
@ -456,7 +456,7 @@ static bool hdlcParse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo)
|
||||||
hdlc->receiving = false;
|
hdlc->receiving = false;
|
||||||
LED_RX_OFF();
|
LED_RX_OFF();
|
||||||
hdlc_flag_count = 0;
|
hdlc_flag_count = 0;
|
||||||
hdlc_flage_end=false;
|
hdlc_flage_end = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everytime we receive a HDLC_FLAG, we reset the
|
// Everytime we receive a HDLC_FLAG, we reset the
|
||||||
|
|
@ -483,7 +483,7 @@ static bool hdlcParse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo)
|
||||||
hdlc->receiving = false;
|
hdlc->receiving = false;
|
||||||
LED_RX_OFF();
|
LED_RX_OFF();
|
||||||
hdlc_flag_count = 0;
|
hdlc_flag_count = 0;
|
||||||
hdlc_flage_end=false;
|
hdlc_flage_end = false;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,7 +493,7 @@ static bool hdlcParse(Hdlc *hdlc, bool bit, FIFOBuffer *fifo)
|
||||||
if (!hdlc->receiving)
|
if (!hdlc->receiving)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
hdlc_flage_end=true;
|
hdlc_flage_end = true;
|
||||||
|
|
||||||
// First check if what we are seeing is a stuffed bit.
|
// First check if what we are seeing is a stuffed bit.
|
||||||
// Since the different HDLC control characters like
|
// Since the different HDLC control characters like
|
||||||
|
|
@ -597,10 +597,19 @@ void AFSK_adc_isr(Afsk *afsk, int8_t currentSample)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// BUTTERWORTH Filter
|
// BUTTERWORTH Filter
|
||||||
|
// afsk->iirX[0] = afsk->iirX[1];
|
||||||
|
// afsk->iirX[1] = ((int8_t)fifo_pop(&afsk->delayFifo) * currentSample) / 6.027339492F;
|
||||||
|
// afsk->iirY[0] = afsk->iirY[1];
|
||||||
|
// afsk->iirY[1] = afsk->iirX[0] + afsk->iirX[1] + afsk->iirY[0] * 0.6681786379F;
|
||||||
|
|
||||||
|
//Fast calcultor
|
||||||
|
int16_t tmp16t;
|
||||||
afsk->iirX[0] = afsk->iirX[1];
|
afsk->iirX[0] = afsk->iirX[1];
|
||||||
afsk->iirX[1] = ((int8_t)fifo_pop(&afsk->delayFifo) * currentSample) / 6.027339492F;
|
tmp16t = (int16_t)((int8_t)fifo_pop(&afsk->delayFifo) * (int8_t)currentSample);
|
||||||
|
afsk->iirX[1] = (tmp16t >> 2) + (tmp16t >> 5);
|
||||||
afsk->iirY[0] = afsk->iirY[1];
|
afsk->iirY[0] = afsk->iirY[1];
|
||||||
afsk->iirY[1] = afsk->iirX[0] + afsk->iirX[1] + afsk->iirY[0] * 0.6681786379F;
|
tmp16t = (afsk->iirY[0] >> 2) + (afsk->iirY[0] >> 3) + (afsk->iirY[0] >> 4);
|
||||||
|
afsk->iirY[1] = afsk->iirX[0] + afsk->iirX[1] + tmp16t;
|
||||||
|
|
||||||
// We put the sampled bit in a delay-line:
|
// We put the sampled bit in a delay-line:
|
||||||
// First we bitshift everything 1 left
|
// First we bitshift everything 1 left
|
||||||
|
|
@ -776,6 +785,8 @@ extern bool afskSync;
|
||||||
|
|
||||||
long mVsum = 0;
|
long mVsum = 0;
|
||||||
int mVsumCount = 0;
|
int mVsumCount = 0;
|
||||||
|
long lastVrms=0;
|
||||||
|
bool VrmsFlag=false;
|
||||||
|
|
||||||
void AFSK_Poll()
|
void AFSK_Poll()
|
||||||
{
|
{
|
||||||
|
|
@ -857,7 +868,7 @@ void AFSK_Poll()
|
||||||
adcVal = (int)pcm_in[i];
|
adcVal = (int)pcm_in[i];
|
||||||
offset_new += adcVal;
|
offset_new += adcVal;
|
||||||
offset_count++;
|
offset_count++;
|
||||||
if (offset_count >= 192)
|
if (offset_count >= 192) //192
|
||||||
{
|
{
|
||||||
offset = offset_new / offset_count;
|
offset = offset_new / offset_count;
|
||||||
offset_count = 0;
|
offset_count = 0;
|
||||||
|
|
@ -867,29 +878,47 @@ void AFSK_Poll()
|
||||||
}
|
}
|
||||||
adcVal -= offset; // Convert unsinewave to sinewave
|
adcVal -= offset; // Convert unsinewave to sinewave
|
||||||
|
|
||||||
mV = abs((int)adcVal); //mVp-p
|
float adcF=hp_filter.Update((float)adcVal);
|
||||||
mV = (int)((float)mV / 3.68F); // mV=(mV*625)/36848;
|
adcVal=(int)adcF;
|
||||||
mVsum += powl(mV, 2);
|
|
||||||
mVsumCount++;
|
if (afskSync == false)
|
||||||
int8_t adcR = (int8_t)((int16_t)(adcVal >> 4)); // Reduce 12bit to 8bit
|
{
|
||||||
|
mV = abs((int)adcVal); // mVp-p
|
||||||
|
mV = (int)((float)mV / 3.68F); // mV=(mV*625)/36848;
|
||||||
|
mVsum += powl(mV, 2);
|
||||||
|
mVsumCount++;
|
||||||
|
}
|
||||||
|
//int8_t adcR = (int8_t)((int16_t)(adcVal >> 4)); // Reduce 12bit to 8bit
|
||||||
|
int8_t adcR=(int8_t)(adcVal/16);
|
||||||
|
|
||||||
AFSK_adc_isr(AFSK_modem, adcR); // Process signal IIR
|
AFSK_adc_isr(AFSK_modem, adcR); // Process signal IIR
|
||||||
if (i % 4 == 0)
|
if (i % 4 == 0)
|
||||||
APRS_poll(); // Poll check every 1 bit
|
APRS_poll(); // Poll check every 1 bit
|
||||||
}
|
}
|
||||||
//Get mVrms on Sync flage 0x7E
|
// Get mVrms on Sync flage 0x7E
|
||||||
if (hdlc_flag_count > 3 && hdlc_flage_end==true)
|
if (afskSync == false)
|
||||||
{
|
{
|
||||||
if (mVsumCount > 1920){
|
if (hdlc_flag_count > 3 && hdlc_flage_end == true)
|
||||||
mVrms = sqrtl(mVsum / mVsumCount);
|
{
|
||||||
mVsum=0;
|
if (mVsumCount > 960)
|
||||||
mVsumCount=0;
|
{
|
||||||
|
mVrms = sqrtl(mVsum / mVsumCount);
|
||||||
double Vrms=(double)mVrms/1000;
|
mVsum = 0;
|
||||||
dBV = 20.0F * log10(Vrms);
|
mVsumCount = 0;
|
||||||
//dBu = 20 * log10(Vrms / 0.7746);
|
lastVrms=millis()+500;
|
||||||
Serial.printf("mVrms=%d dBV=%0.1f agc=%0.2f\n",mVrms,dBV);
|
VrmsFlag=true;
|
||||||
afskSync=true;
|
//if(millis()>lastVrms){
|
||||||
|
// double Vrms=(double)mVrms/1000;
|
||||||
|
// dBV = 20.0F * log10(Vrms);
|
||||||
|
// //dBu = 20 * log10(Vrms / 0.7746);
|
||||||
|
// Serial.printf("mVrms=%d dBV=%0.1f agc=%0.2f\n",mVrms,dBV);
|
||||||
|
//afskSync = true;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(millis()>lastVrms && VrmsFlag){
|
||||||
|
afskSync = true;
|
||||||
|
VrmsFlag=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -188,7 +188,7 @@ inline static uint8_t sinSample(uint16_t i)
|
||||||
#include "driver/i2s.h"
|
#include "driver/i2s.h"
|
||||||
#include "driver/dac.h"
|
#include "driver/dac.h"
|
||||||
|
|
||||||
#define SAMPLE_RATE 9580 //9600 ปรับชดเชยแซมปลิงค์ของ I2S
|
#define SAMPLE_RATE 9600 //9580 ปรับชดเชยแซมปลิงค์ของ I2S
|
||||||
#define PIN_I2S_BCLK 26
|
#define PIN_I2S_BCLK 26
|
||||||
#define PIN_I2S_LRC 27
|
#define PIN_I2S_LRC 27
|
||||||
#define PIN_I2S_DIN 35
|
#define PIN_I2S_DIN 35
|
||||||
|
|
|
||||||
508
src/main.cpp
508
src/main.cpp
|
|
@ -57,16 +57,19 @@ bool aprsUpdate = false;
|
||||||
boolean gotPacket = false;
|
boolean gotPacket = false;
|
||||||
AX25Msg incomingPacket;
|
AX25Msg incomingPacket;
|
||||||
|
|
||||||
bool lastPkg=false;
|
bool lastPkg = false;
|
||||||
bool afskSync=false;
|
bool afskSync = false;
|
||||||
String lastPkgRaw="";
|
String lastPkgRaw = "";
|
||||||
float dBV=0;
|
float dBV = 0;
|
||||||
int mVrms=0;
|
int mVrms = 0;
|
||||||
|
|
||||||
cppQueue PacketBuffer(sizeof(AX25Msg), 5, IMPLEMENTATION); // Instantiate queue
|
cppQueue PacketBuffer(sizeof(AX25Msg), 5, IMPLEMENTATION); // Instantiate queue
|
||||||
|
|
||||||
statusType status;
|
statusType status;
|
||||||
RTC_DATA_ATTR digiTLMType digiTLM;
|
RTC_DATA_ATTR igateTLMType igateTLM;
|
||||||
|
RTC_DATA_ATTR digiTLMType digiLog;
|
||||||
|
RTC_DATA_ATTR txQueueType txQueue[PKGTXSIZE];
|
||||||
|
RTC_DATA_ATTR uint8_t digiCount = 0;
|
||||||
|
|
||||||
Configuration config;
|
Configuration config;
|
||||||
|
|
||||||
|
|
@ -177,6 +180,8 @@ void defaultConfig()
|
||||||
config.tnc_beacon = 0;
|
config.tnc_beacon = 0;
|
||||||
config.aprs_table = '/';
|
config.aprs_table = '/';
|
||||||
config.aprs_symbol = '&';
|
config.aprs_symbol = '&';
|
||||||
|
config.digi_delay = 2000;
|
||||||
|
config.tx_timeslot = 5000;
|
||||||
sprintf(config.aprs_path, "WIDE1-1");
|
sprintf(config.aprs_path, "WIDE1-1");
|
||||||
sprintf(config.aprs_comment, "ESP32 Internet Gateway");
|
sprintf(config.aprs_comment, "ESP32 Internet Gateway");
|
||||||
sprintf(config.tnc_comment, "ESP32 Build in TNC");
|
sprintf(config.tnc_comment, "ESP32 Build in TNC");
|
||||||
|
|
@ -288,6 +293,64 @@ void pkgListUpdate(char *call, bool type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool pkgTxUpdate(const char *info, int delay)
|
||||||
|
{
|
||||||
|
char *ecs = strstr(info, ">");
|
||||||
|
if (ecs == NULL)
|
||||||
|
return false;
|
||||||
|
// Replace
|
||||||
|
for (int i = 0; i < PKGTXSIZE; i++)
|
||||||
|
{
|
||||||
|
if (txQueue[i].Active)
|
||||||
|
{
|
||||||
|
if (!(strncmp(&txQueue[i].Info[0], info, info - ecs)))
|
||||||
|
{
|
||||||
|
strcpy(&txQueue[i].Info[0], info);
|
||||||
|
txQueue[i].Delay = delay;
|
||||||
|
txQueue[i].timeStamp = millis();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add
|
||||||
|
for (int i = 0; i < PKGTXSIZE; i++)
|
||||||
|
{
|
||||||
|
if (txQueue[i].Active == false)
|
||||||
|
{
|
||||||
|
strcpy(&txQueue[i].Info[0], info);
|
||||||
|
txQueue[i].Delay = delay;
|
||||||
|
txQueue[i].Active = true;
|
||||||
|
txQueue[i].timeStamp = millis();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pkgTxSend()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < PKGTXSIZE; i++)
|
||||||
|
{
|
||||||
|
if (txQueue[i].Active)
|
||||||
|
{
|
||||||
|
int decTime = millis() - txQueue[i].timeStamp;
|
||||||
|
if (decTime > txQueue[i].Delay)
|
||||||
|
{
|
||||||
|
APRS_setPreamble(350L);
|
||||||
|
APRS_sendTNC2Pkt(String(txQueue[i].Info)); // Send packet to RF
|
||||||
|
txQueue[i].Active = false;
|
||||||
|
#ifdef DEBUG_TNC
|
||||||
|
printTime();
|
||||||
|
Serial.println("TX->RF: " + String(txQueue[i].Info));
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t *packetData;
|
uint8_t *packetData;
|
||||||
//ฟังชั่นถูกเรียกมาจาก ax25_decode
|
//ฟังชั่นถูกเรียกมาจาก ax25_decode
|
||||||
void aprs_msg_callback(struct AX25Msg *msg)
|
void aprs_msg_callback(struct AX25Msg *msg)
|
||||||
|
|
@ -301,13 +364,13 @@ void aprs_msg_callback(struct AX25Msg *msg)
|
||||||
void printTime()
|
void printTime()
|
||||||
{
|
{
|
||||||
struct tm tmstruct;
|
struct tm tmstruct;
|
||||||
getLocalTime(&tmstruct, 5000);
|
getLocalTime(&tmstruct, 5000);
|
||||||
Serial.print("[");
|
Serial.print("[");
|
||||||
Serial.print(tmstruct.tm_hour);
|
Serial.print(tmstruct.tm_hour);
|
||||||
Serial.print(":");
|
Serial.print(":");
|
||||||
Serial.print(tmstruct.tm_min);
|
Serial.print(tmstruct.tm_min);
|
||||||
Serial.print(":");
|
Serial.print(":");
|
||||||
Serial.print( tmstruct.tm_sec);
|
Serial.print(tmstruct.tm_sec);
|
||||||
Serial.print("]");
|
Serial.print("]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -467,7 +530,8 @@ void setup()
|
||||||
{
|
{
|
||||||
defaultConfig();
|
defaultConfig();
|
||||||
Serial.println("Manual Default configure!");
|
Serial.println("Manual Default configure!");
|
||||||
while(digitalRead(0) == LOW);
|
while (digitalRead(0) == LOW)
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
//ตรวจสอบคอนฟิกซ์ผิดพลาด
|
//ตรวจสอบคอนฟิกซ์ผิดพลาด
|
||||||
|
|
@ -493,7 +557,7 @@ void setup()
|
||||||
xTaskCreatePinnedToCore(
|
xTaskCreatePinnedToCore(
|
||||||
taskAPRS, /* Function to implement the task */
|
taskAPRS, /* Function to implement the task */
|
||||||
"taskAPRS", /* Name of the task */
|
"taskAPRS", /* Name of the task */
|
||||||
8192, /* Stack size in words */
|
8192, /* Stack size in words */
|
||||||
NULL, /* Task input parameter */
|
NULL, /* Task input parameter */
|
||||||
1, /* Priority of the task */
|
1, /* Priority of the task */
|
||||||
&taskAPRSHandle, /* Task handle. */
|
&taskAPRSHandle, /* Task handle. */
|
||||||
|
|
@ -577,15 +641,11 @@ int processPacket(String &tnc2)
|
||||||
for (int i = 0; i < incomingPacket.rpt_count; i++)
|
for (int i = 0; i < incomingPacket.rpt_count; i++)
|
||||||
{
|
{
|
||||||
tnc2 += String(",");
|
tnc2 += String(",");
|
||||||
char *rptcall = incomingPacket.rpt_list[i].call;
|
tnc2 += String(incomingPacket.rpt_list[i].call);
|
||||||
tnc2 += String(rptcall);
|
|
||||||
if (incomingPacket.rpt_list[i].ssid > 0)
|
if (incomingPacket.rpt_list[i].ssid > 0)
|
||||||
{
|
{
|
||||||
tnc2 += String("-");
|
tnc2 += String("-");
|
||||||
char rssid[3];
|
tnc2 += String(incomingPacket.rpt_list[i].ssid);
|
||||||
itoa(incomingPacket.rpt_list[i].ssid, rssid, 3);
|
|
||||||
rssid[2] = 0;
|
|
||||||
tnc2 += String(rssid);
|
|
||||||
}
|
}
|
||||||
if (incomingPacket.rpt_flags & (1 << i))
|
if (incomingPacket.rpt_flags & (1 << i))
|
||||||
tnc2 += "*";
|
tnc2 += "*";
|
||||||
|
|
@ -594,10 +654,10 @@ int processPacket(String &tnc2)
|
||||||
tnc2 += String((const char *)incomingPacket.info);
|
tnc2 += String((const char *)incomingPacket.info);
|
||||||
tnc2 += String("\n");
|
tnc2 += String("\n");
|
||||||
|
|
||||||
#ifdef DEBUG_TNC
|
// #ifdef DEBUG_TNC
|
||||||
Serial.printf("[%d] ", ++pkgTNC_count);
|
// Serial.printf("[%d] ", ++pkgTNC_count);
|
||||||
Serial.print(tnc2);
|
// Serial.print(tnc2);
|
||||||
#endif
|
// #endif
|
||||||
return tnc2.length();
|
return tnc2.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -635,10 +695,12 @@ void sendIsPkgMsg(char *raw)
|
||||||
String tnc2Raw = String(str);
|
String tnc2Raw = String(str);
|
||||||
if (aprsClient.connected())
|
if (aprsClient.connected())
|
||||||
aprsClient.println(tnc2Raw); // Send packet to Inet
|
aprsClient.println(tnc2Raw); // Send packet to Inet
|
||||||
else
|
if (config.tnc)
|
||||||
APRS_sendTNC2Pkt(tnc2Raw); // Send packet to RF
|
pkgTxUpdate(str, 0);
|
||||||
|
// APRS_sendTNC2Pkt(tnc2Raw); // Send packet to RF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long timeSlot;
|
||||||
void taskAPRS(void *pvParameters)
|
void taskAPRS(void *pvParameters)
|
||||||
{
|
{
|
||||||
// long start, stop;
|
// long start, stop;
|
||||||
|
|
@ -653,8 +715,9 @@ void taskAPRS(void *pvParameters)
|
||||||
APRS_setPreamble(300);
|
APRS_setPreamble(300);
|
||||||
APRS_setTail(0);
|
APRS_setTail(0);
|
||||||
sendTimer = millis() - (config.aprs_beacon * 1000) + 30000;
|
sendTimer = millis() - (config.aprs_beacon * 1000) + 30000;
|
||||||
digiTLM.TeleTimeout = millis() + 60000; // 1Min
|
igateTLM.TeleTimeout = millis() + 60000; // 1Min
|
||||||
AFSKInitAct = true;
|
AFSKInitAct = true;
|
||||||
|
timeSlot = millis();
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
long now = millis();
|
long now = millis();
|
||||||
|
|
@ -663,6 +726,20 @@ void taskAPRS(void *pvParameters)
|
||||||
time(&timeStamp);
|
time(&timeStamp);
|
||||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||||
// serviceHandle();
|
// serviceHandle();
|
||||||
|
if (now > (timeSlot + 10))
|
||||||
|
{
|
||||||
|
if (!digitalRead(LED_PIN))
|
||||||
|
{ // RX State Fail
|
||||||
|
if (pkgTxSend())
|
||||||
|
timeSlot = millis() + config.tx_timeslot; // Tx Time Slot = 5sec.
|
||||||
|
else
|
||||||
|
timeSlot = millis();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timeSlot = millis() + 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (digitalRead(0) == LOW)
|
if (digitalRead(0) == LOW)
|
||||||
{
|
{
|
||||||
|
|
@ -689,7 +766,8 @@ void taskAPRS(void *pvParameters)
|
||||||
if (config.tnc)
|
if (config.tnc)
|
||||||
{
|
{
|
||||||
String tnc2Raw = send_fix_location();
|
String tnc2Raw = send_fix_location();
|
||||||
APRS_sendTNC2Pkt(tnc2Raw); // Send packet to RF
|
pkgTxUpdate(tnc2Raw.c_str(), 0);
|
||||||
|
// APRS_sendTNC2Pkt(tnc2Raw); // Send packet to RF
|
||||||
#ifdef DEBUG_TNC
|
#ifdef DEBUG_TNC
|
||||||
Serial.println("Manual TX: " + tnc2Raw);
|
Serial.println("Manual TX: " + tnc2Raw);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -718,6 +796,8 @@ void taskAPRS(void *pvParameters)
|
||||||
if (now > (sendTimer + (config.aprs_beacon * 1000)))
|
if (now > (sendTimer + (config.aprs_beacon * 1000)))
|
||||||
{
|
{
|
||||||
sendTimer = now;
|
sendTimer = now;
|
||||||
|
if (digiCount > 0)
|
||||||
|
digiCount--;
|
||||||
#ifdef SA818
|
#ifdef SA818
|
||||||
SA818_CHECK();
|
SA818_CHECK();
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -728,9 +808,10 @@ void taskAPRS(void *pvParameters)
|
||||||
String tnc2Raw = send_fix_location();
|
String tnc2Raw = send_fix_location();
|
||||||
if (aprsClient.connected())
|
if (aprsClient.connected())
|
||||||
aprsClient.println(tnc2Raw); // Send packet to Inet
|
aprsClient.println(tnc2Raw); // Send packet to Inet
|
||||||
APRS_sendTNC2Pkt(tnc2Raw); // Send packet to RF
|
pkgTxUpdate(tnc2Raw.c_str(), 0);
|
||||||
|
// APRS_sendTNC2Pkt(tnc2Raw); // Send packet to RF
|
||||||
#ifdef DEBUG_TNC
|
#ifdef DEBUG_TNC
|
||||||
Serial.println("TX: " + tnc2Raw);
|
// Serial.println("TX: " + tnc2Raw);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -744,10 +825,10 @@ void taskAPRS(void *pvParameters)
|
||||||
|
|
||||||
if (config.tnc_telemetry)
|
if (config.tnc_telemetry)
|
||||||
{
|
{
|
||||||
if (digiTLM.TeleTimeout < millis())
|
if (igateTLM.TeleTimeout < millis())
|
||||||
{
|
{
|
||||||
digiTLM.TeleTimeout = millis() + 600000; // 10Min
|
igateTLM.TeleTimeout = millis() + 600000; // 10Min
|
||||||
if ((digiTLM.Sequence % 6) == 0)
|
if ((igateTLM.Sequence % 6) == 0)
|
||||||
{
|
{
|
||||||
sendIsPkgMsg((char *)&PARM[0]);
|
sendIsPkgMsg((char *)&PARM[0]);
|
||||||
sendIsPkgMsg((char *)&UNIT[0]);
|
sendIsPkgMsg((char *)&UNIT[0]);
|
||||||
|
|
@ -755,22 +836,23 @@ void taskAPRS(void *pvParameters)
|
||||||
}
|
}
|
||||||
char rawTlm[100];
|
char rawTlm[100];
|
||||||
if (config.aprs_ssid == 0)
|
if (config.aprs_ssid == 0)
|
||||||
sprintf(rawTlm, "%s>APE32I:T#%03d,%d,%d,%d,%d,%d,00000000", config.aprs_mycall, digiTLM.Sequence, digiTLM.RF2INET, digiTLM.INET2RF, digiTLM.RX, digiTLM.TX, digiTLM.DROP);
|
sprintf(rawTlm, "%s>APE32I:T#%03d,%d,%d,%d,%d,%d,00000000", config.aprs_mycall, igateTLM.Sequence, igateTLM.RF2INET, igateTLM.INET2RF, igateTLM.RX, igateTLM.TX, igateTLM.DROP);
|
||||||
else
|
else
|
||||||
sprintf(rawTlm, "%s-%d>APE32I:T#%03d,%d,%d,%d,%d,%d,00000000", config.aprs_mycall,config.aprs_ssid, digiTLM.Sequence, digiTLM.RF2INET, digiTLM.INET2RF, digiTLM.RX, digiTLM.TX, digiTLM.DROP);
|
sprintf(rawTlm, "%s-%d>APE32I:T#%03d,%d,%d,%d,%d,%d,00000000", config.aprs_mycall, config.aprs_ssid, igateTLM.Sequence, igateTLM.RF2INET, igateTLM.INET2RF, igateTLM.RX, igateTLM.TX, igateTLM.DROP);
|
||||||
|
|
||||||
if (aprsClient.connected())
|
if (aprsClient.connected())
|
||||||
aprsClient.println(String(rawTlm)); // Send packet to Inet
|
aprsClient.println(String(rawTlm)); // Send packet to Inet
|
||||||
else
|
if (config.tnc)
|
||||||
APRS_sendTNC2Pkt(String(rawTlm)); // Send packet to RF
|
pkgTxUpdate(rawTlm, 0);
|
||||||
digiTLM.Sequence++;
|
// APRS_sendTNC2Pkt(String(rawTlm)); // Send packet to RF
|
||||||
if (digiTLM.Sequence > 999)
|
igateTLM.Sequence++;
|
||||||
digiTLM.Sequence = 0;
|
if (igateTLM.Sequence > 999)
|
||||||
digiTLM.DROP = 0;
|
igateTLM.Sequence = 0;
|
||||||
digiTLM.INET2RF = 0;
|
igateTLM.DROP = 0;
|
||||||
digiTLM.RF2INET = 0;
|
igateTLM.INET2RF = 0;
|
||||||
digiTLM.RX = 0;
|
igateTLM.RF2INET = 0;
|
||||||
digiTLM.TX = 0;
|
igateTLM.RX = 0;
|
||||||
|
igateTLM.TX = 0;
|
||||||
// client.println(raw);
|
// client.println(raw);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -784,8 +866,43 @@ void taskAPRS(void *pvParameters)
|
||||||
//นำข้อมูลแพ็จเกจจาก TNC ออกจากคิว
|
//นำข้อมูลแพ็จเกจจาก TNC ออกจากคิว
|
||||||
PacketBuffer.pop(&incomingPacket);
|
PacketBuffer.pop(&incomingPacket);
|
||||||
processPacket(tnc2);
|
processPacket(tnc2);
|
||||||
lastPkg=true;
|
|
||||||
lastPkgRaw=tnc2;
|
if (config.tnc_digi)
|
||||||
|
{
|
||||||
|
int dlyFlag = digiProcess(incomingPacket);
|
||||||
|
if (dlyFlag > 0)
|
||||||
|
{
|
||||||
|
int digiDelay;
|
||||||
|
if (dlyFlag == 1)
|
||||||
|
{
|
||||||
|
digiDelay = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (config.digi_delay == 0)
|
||||||
|
{ // Auto mode
|
||||||
|
if (digiCount > 20)
|
||||||
|
digiDelay = random(5000);
|
||||||
|
else if (digiCount > 10)
|
||||||
|
digiDelay = random(3000);
|
||||||
|
else if (digiCount > 0)
|
||||||
|
digiDelay = random(1500);
|
||||||
|
else
|
||||||
|
digiDelay = random(500);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
digiDelay = random(config.digi_delay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String digiPkg;
|
||||||
|
processPacket(digiPkg);
|
||||||
|
pkgTxUpdate(digiPkg.c_str(), digiDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastPkg = true;
|
||||||
|
lastPkgRaw = tnc2;
|
||||||
// ESP_BT.println(tnc2);
|
// ESP_BT.println(tnc2);
|
||||||
status.allCount++;
|
status.allCount++;
|
||||||
|
|
||||||
|
|
@ -797,10 +914,10 @@ void taskAPRS(void *pvParameters)
|
||||||
{
|
{
|
||||||
raw = (char *)malloc(tnc2.length() + 20);
|
raw = (char *)malloc(tnc2.length() + 20);
|
||||||
status.tncCount++;
|
status.tncCount++;
|
||||||
if (tnc2.indexOf("RFONLY", 10) > 0) //PATH RFONLY จะไม่ส่งเข้า APRS-IS
|
if (tnc2.indexOf("RFONLY", 10) > 0) // PATH RFONLY จะไม่ส่งเข้า APRS-IS
|
||||||
{
|
{
|
||||||
status.dropCount++;
|
status.dropCount++;
|
||||||
digiTLM.DROP++;
|
igateTLM.DROP++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -823,23 +940,23 @@ void taskAPRS(void *pvParameters)
|
||||||
tnc2 = String(raw);
|
tnc2 = String(raw);
|
||||||
aprsClient.println(tnc2);
|
aprsClient.println(tnc2);
|
||||||
status.rf2inet++;
|
status.rf2inet++;
|
||||||
digiTLM.RF2INET++;
|
igateTLM.RF2INET++;
|
||||||
digiTLM.TX++;
|
igateTLM.TX++;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printTime();
|
printTime();
|
||||||
Serial.print("RF->INET ");
|
Serial.print("RF->INET: ");
|
||||||
Serial.println(tnc2);
|
Serial.println(tnc2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
status.errorCount++;
|
status.errorCount++;
|
||||||
digiTLM.DROP++;
|
igateTLM.DROP++;
|
||||||
}
|
}
|
||||||
free(str);
|
free(str);
|
||||||
}
|
}
|
||||||
//memset(&raw[0], 0, sizeof(raw));
|
// memset(&raw[0], 0, sizeof(raw));
|
||||||
tnc2.toCharArray(&raw[0], start_val + 1);
|
tnc2.toCharArray(&raw[0], start_val + 1);
|
||||||
raw[start_val + 1] = 0;
|
raw[start_val + 1] = 0;
|
||||||
pkgListUpdate(&raw[0], 1);
|
pkgListUpdate(&raw[0], 1);
|
||||||
|
|
@ -961,7 +1078,7 @@ void taskNetwork(void *pvParameters)
|
||||||
// Serial.println("Config NTP");
|
// Serial.println("Config NTP");
|
||||||
// setSyncProvider(getNtpTime);
|
// setSyncProvider(getNtpTime);
|
||||||
Serial.println("Contacting Time Server");
|
Serial.println("Contacting Time Server");
|
||||||
configTime(3600 * timeZone, 0, "203.150.19.26", "110.170.126.101","77.68.122.252");
|
configTime(3600 * timeZone, 0, "203.150.19.26", "110.170.126.101", "77.68.122.252");
|
||||||
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
vTaskDelay(3000 / portTICK_PERIOD_MS);
|
||||||
time_t systemTime;
|
time_t systemTime;
|
||||||
time(&systemTime);
|
time(&systemTime);
|
||||||
|
|
@ -998,7 +1115,7 @@ void taskNetwork(void *pvParameters)
|
||||||
String msg_call = "::" + src_call;
|
String msg_call = "::" + src_call;
|
||||||
|
|
||||||
status.allCount++;
|
status.allCount++;
|
||||||
digiTLM.RX++;
|
igateTLM.RX++;
|
||||||
if (config.tnc && config.inet2rf)
|
if (config.tnc && config.inet2rf)
|
||||||
{
|
{
|
||||||
if (line.indexOf(msg_call) <= 0) // src callsign = msg callsign ไม่ใช่หัวข้อโทรมาตร
|
if (line.indexOf(msg_call) <= 0) // src callsign = msg callsign ไม่ใช่หัวข้อโทรมาตร
|
||||||
|
|
@ -1008,14 +1125,15 @@ void taskNetwork(void *pvParameters)
|
||||||
if (line.indexOf("::") > 0) //ข้อความเท่านั้น
|
if (line.indexOf("::") > 0) //ข้อความเท่านั้น
|
||||||
{ // message only
|
{ // message only
|
||||||
// raw[0] = '}';
|
// raw[0] = '}';
|
||||||
//line.toCharArray(&raw[1], line.length());
|
// line.toCharArray(&raw[1], line.length());
|
||||||
// tncTxEnable = false;
|
// tncTxEnable = false;
|
||||||
// SerialTNC.flush();
|
// SerialTNC.flush();
|
||||||
// SerialTNC.println(raw);
|
// SerialTNC.println(raw);
|
||||||
APRS_sendTNC2Pkt(line); // Send out RF by TNC build in
|
pkgTxUpdate(line.c_str(), 0);
|
||||||
// tncTxEnable = true;
|
// APRS_sendTNC2Pkt(line); // Send out RF by TNC build in
|
||||||
|
// tncTxEnable = true;
|
||||||
status.inet2rf++;
|
status.inet2rf++;
|
||||||
digiTLM.INET2RF++;
|
igateTLM.INET2RF++;
|
||||||
printTime();
|
printTime();
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
Serial.print("INET->RF ");
|
Serial.print("INET->RF ");
|
||||||
|
|
@ -1026,7 +1144,7 @@ void taskNetwork(void *pvParameters)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
digiTLM.DROP++;
|
igateTLM.DROP++;
|
||||||
Serial.print("INET Message TELEMETRY from ");
|
Serial.print("INET Message TELEMETRY from ");
|
||||||
Serial.println(src_call);
|
Serial.println(src_call);
|
||||||
}
|
}
|
||||||
|
|
@ -1061,3 +1179,277 @@ void taskNetwork(void *pvParameters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int digiProcess(AX25Msg &Packet)
|
||||||
|
{
|
||||||
|
int idx, j;
|
||||||
|
uint8_t ctmp;
|
||||||
|
// if(!DIGI) return;
|
||||||
|
// if(rx_data) return;
|
||||||
|
// if(digi_timeout<aprs_delay) return;
|
||||||
|
// digi_timeout = 65530;
|
||||||
|
// aprs_delay = 65535;
|
||||||
|
|
||||||
|
j = 0;
|
||||||
|
if (Packet.len < 5)
|
||||||
|
{
|
||||||
|
digiLog.ErPkts++;
|
||||||
|
return 0; // NO DST
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strncmp(&Packet.src.call[0], "NOCALL", 6))
|
||||||
|
{
|
||||||
|
digiLog.DropRx++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!strncmp(&Packet.src.call[0], "MYCALL", 6))
|
||||||
|
{
|
||||||
|
digiLog.DropRx++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destination SSID Trace
|
||||||
|
if (Packet.dst.ssid > 0)
|
||||||
|
{
|
||||||
|
uint8_t ctmp = Packet.dst.ssid & 0x1E; // Check DSSID
|
||||||
|
|
||||||
|
if (ctmp > 15)
|
||||||
|
ctmp = 0;
|
||||||
|
if (ctmp < 8)
|
||||||
|
{ // Edit PATH Change to TRACEn-N
|
||||||
|
if (ctmp > 0)
|
||||||
|
ctmp--;
|
||||||
|
Packet.dst.ssid = ctmp;
|
||||||
|
if (Packet.rpt_count > 0)
|
||||||
|
{
|
||||||
|
for (idx = 0; idx < Packet.rpt_count; idx++)
|
||||||
|
{
|
||||||
|
if (!strcmp(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0])) // Is path same callsign
|
||||||
|
{
|
||||||
|
if (Packet.rpt_list[idx].ssid == config.aprs_ssid) // IS path same SSID
|
||||||
|
{
|
||||||
|
if (Packet.rpt_flags & (1 << idx))
|
||||||
|
{
|
||||||
|
digiLog.DropRx++;
|
||||||
|
return 0; // bypass flag *
|
||||||
|
}
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Packet.rpt_flags & (1 << idx))
|
||||||
|
continue;
|
||||||
|
for (j = idx; j < Packet.rpt_count; j++)
|
||||||
|
{
|
||||||
|
if (Packet.rpt_flags & (1 << j))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Move current part to next part
|
||||||
|
for (; j >= idx; j--)
|
||||||
|
{
|
||||||
|
int n = j + 1;
|
||||||
|
strcpy(&Packet.rpt_list[n].call[0], &Packet.rpt_list[j].call[0]);
|
||||||
|
Packet.rpt_list[n].ssid = Packet.rpt_list[j].ssid;
|
||||||
|
if (Packet.rpt_flags & (1 << j))
|
||||||
|
Packet.rpt_flags |= (1 << n);
|
||||||
|
else
|
||||||
|
Packet.rpt_flags &= ~(1 << n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new part
|
||||||
|
Packet.rpt_count += 1;
|
||||||
|
strcpy(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0]);
|
||||||
|
Packet.rpt_list[idx].ssid = config.aprs_ssid;
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
return 2;
|
||||||
|
// j = 1;
|
||||||
|
// break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
idx = 0;
|
||||||
|
strcpy(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0]);
|
||||||
|
Packet.rpt_list[idx].ssid = config.aprs_ssid;
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
Packet.rpt_count += 1;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
digiLog.DropRx++;
|
||||||
|
return 0; // NO PATH
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx = 0; idx < Packet.rpt_count; idx++)
|
||||||
|
{
|
||||||
|
if (!strncmp(&Packet.rpt_list[idx].call[0], "qA", 2))
|
||||||
|
{
|
||||||
|
digiLog.DropRx++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx = 0; idx < Packet.rpt_count; idx++)
|
||||||
|
{
|
||||||
|
if (!strncmp(&Packet.rpt_list[idx].call[0], "TCP", 3))
|
||||||
|
{
|
||||||
|
digiLog.DropRx++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (idx = 0; idx < Packet.rpt_count; idx++)
|
||||||
|
{
|
||||||
|
if (Packet.rpt_flags & (1 << idx))
|
||||||
|
{
|
||||||
|
if (idx == (Packet.rpt_count - 1))
|
||||||
|
digiCount++;
|
||||||
|
continue; // bypass flag *
|
||||||
|
}
|
||||||
|
if (!strncmp(&Packet.rpt_list[idx].call[0], "WIDE", 4))
|
||||||
|
{
|
||||||
|
// Check WIDEn-N
|
||||||
|
if (Packet.rpt_list[idx].ssid > 0)
|
||||||
|
{
|
||||||
|
if (Packet.rpt_flags & (1 << idx))
|
||||||
|
continue; // bypass flag *
|
||||||
|
ctmp = Packet.rpt_list[idx].ssid & 0x1F;
|
||||||
|
if (ctmp > 0)
|
||||||
|
ctmp--;
|
||||||
|
if (ctmp > 15)
|
||||||
|
ctmp = 0;
|
||||||
|
if (ctmp == 0)
|
||||||
|
{
|
||||||
|
strcpy(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0]);
|
||||||
|
Packet.rpt_list[idx].ssid = config.aprs_ssid;
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
j = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Packet.rpt_list[idx].ssid = ctmp;
|
||||||
|
Packet.rpt_flags &= ~(1 << idx);
|
||||||
|
j = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
j = 2;
|
||||||
|
strcpy(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0]);
|
||||||
|
Packet.rpt_list[idx].ssid = config.aprs_ssid;
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strncmp(&Packet.rpt_list[idx].call[0], "TRACE", 5))
|
||||||
|
{
|
||||||
|
if (Packet.rpt_flags & (1 << idx))
|
||||||
|
continue; // bypass flag *
|
||||||
|
ctmp = Packet.rpt_list[idx].ssid & 0x1F;
|
||||||
|
if (ctmp > 0)
|
||||||
|
ctmp--;
|
||||||
|
if (ctmp > 15)
|
||||||
|
ctmp = 0;
|
||||||
|
if (ctmp == 0)
|
||||||
|
{
|
||||||
|
strcpy(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0]);
|
||||||
|
Packet.rpt_list[idx].ssid = config.aprs_ssid;
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
j = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (j = idx; j < Packet.rpt_count; j++)
|
||||||
|
{
|
||||||
|
if (Packet.rpt_flags & (1 << j))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Move current part to next part
|
||||||
|
for (; j >= idx; j--)
|
||||||
|
{
|
||||||
|
int n = j + 1;
|
||||||
|
strcpy(&Packet.rpt_list[n].call[0], &Packet.rpt_list[j].call[0]);
|
||||||
|
Packet.rpt_list[n].ssid = Packet.rpt_list[j].ssid;
|
||||||
|
if (Packet.rpt_flags & (1 << j))
|
||||||
|
Packet.rpt_flags |= (1 << n);
|
||||||
|
else
|
||||||
|
Packet.rpt_flags &= ~(1 << n);
|
||||||
|
}
|
||||||
|
// Reduce N part of TRACEn-N
|
||||||
|
Packet.rpt_list[idx + 1].ssid = ctmp;
|
||||||
|
|
||||||
|
// Add new part
|
||||||
|
Packet.rpt_count += 1;
|
||||||
|
strcpy(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0]);
|
||||||
|
Packet.rpt_list[idx].ssid = config.aprs_ssid;
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
j = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strncmp(&Packet.rpt_list[idx].call[0], "RFONLY", 6))
|
||||||
|
{
|
||||||
|
j = 2;
|
||||||
|
// strcpy(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0]);
|
||||||
|
// Packet.rpt_list[idx].ssid = config.aprs_ssid;
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!strncmp(&Packet.rpt_list[idx].call[0], "RELAY", 5))
|
||||||
|
{
|
||||||
|
j = 2;
|
||||||
|
strcpy(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0]);
|
||||||
|
Packet.rpt_list[idx].ssid = config.aprs_ssid;
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!strncmp(&Packet.rpt_list[idx].call[0], "GATE", 4))
|
||||||
|
{
|
||||||
|
j = 2;
|
||||||
|
strcpy(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0]);
|
||||||
|
Packet.rpt_list[idx].ssid = config.aprs_ssid;
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!strncmp(&Packet.rpt_list[idx].call[0], "ECHO", 4))
|
||||||
|
{
|
||||||
|
j = 2;
|
||||||
|
strcpy(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0]);
|
||||||
|
Packet.rpt_list[idx].ssid = config.aprs_ssid;
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!strcmp(&Packet.rpt_list[idx].call[0], &config.aprs_mycall[0])) // Is path same callsign
|
||||||
|
{
|
||||||
|
ctmp = Packet.rpt_list[idx].ssid & 0x1F;
|
||||||
|
if (ctmp == config.aprs_ssid) // IS path same SSID
|
||||||
|
{
|
||||||
|
if (Packet.rpt_flags & (1 << idx))
|
||||||
|
{
|
||||||
|
digiLog.DropRx++;
|
||||||
|
break; // bypass flag *
|
||||||
|
}
|
||||||
|
Packet.rpt_flags |= (1 << idx);
|
||||||
|
j = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
j = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
j = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ void setHTML(byte page)
|
||||||
webString += "<li role=\"presentation\"" + strActiveP2 + ">\n<a href=\"/data\" id=\"channel_link_api_keys\">Storage</a>\n</li>\n";
|
webString += "<li role=\"presentation\"" + strActiveP2 + ">\n<a href=\"/data\" id=\"channel_link_api_keys\">Storage</a>\n</li>\n";
|
||||||
#endif
|
#endif
|
||||||
webString += "<li role=\"presentation\"" + strActiveP3 + ">\n<a href=\"/config\" id=\"channel_link_settings\">Setting</a>\n</li>\n";
|
webString += "<li role=\"presentation\"" + strActiveP3 + ">\n<a href=\"/config\" id=\"channel_link_settings\">Setting</a>\n</li>\n";
|
||||||
webString += "<li role=\"presentation\"" + strActiveP4 + ">\n<a href=\"/service\" id=\"channel_link_service\">Server</a>\n</li>\n";
|
webString += "<li role=\"presentation\"" + strActiveP4 + ">\n<a href=\"/service\" id=\"channel_link_service\">Service</a>\n</li>\n";
|
||||||
webString += "<li role=\"presentation\"" + strActiveP5 + ">\n<a href=\"/system\" id=\"channel_link_system\">System</a>\n</li>\n";
|
webString += "<li role=\"presentation\"" + strActiveP5 + ">\n<a href=\"/system\" id=\"channel_link_system\">System</a>\n</li>\n";
|
||||||
webString += "<li role=\"presentation\"" + strActiveP7 + ">\n<a href=\"/test\" id=\"channel_link_system\">Test</a>\n</li>\n";
|
webString += "<li role=\"presentation\"" + strActiveP7 + ">\n<a href=\"/test\" id=\"channel_link_system\">Test</a>\n</li>\n";
|
||||||
webString += "<li role=\"presentation\"" + strActiveP6 + ">\n<a href=\"/firmware\" id=\"channel_link_firmware\">Firmware</a>\n</li>\n";
|
webString += "<li role=\"presentation\"" + strActiveP6 + ">\n<a href=\"/firmware\" id=\"channel_link_firmware\">Firmware</a>\n</li>\n";
|
||||||
|
|
@ -224,7 +224,7 @@ void setHTML(byte page)
|
||||||
|
|
||||||
webString += "<div>CPU Temp: " + String((temprature_sens_read() - 32) / 1.8, 1) + "C</div> \n";
|
webString += "<div>CPU Temp: " + String((temprature_sens_read() - 32) / 1.8, 1) + "C</div> \n";
|
||||||
webString += "<div>Free Heap:" + String(ESP.getFreeHeap()) + " Byte</div> \n";
|
webString += "<div>Free Heap:" + String(ESP.getFreeHeap()) + " Byte</div> \n";
|
||||||
String uptime = String(hour(tn), DEC) + ":" + String(minute(tn), DEC) + ":" + String(second(tn), DEC);
|
String uptime = String(day(tn) - 1, DEC) + "D " + String(hour(tn), DEC) + ":" + String(minute(tn), DEC) + ":" + String(second(tn), DEC);
|
||||||
webString += "<div>System Uptime: " + uptime + "</div> \n";
|
webString += "<div>System Uptime: " + uptime + "</div> \n";
|
||||||
|
|
||||||
webString += "</td></tr><tr><td>\n";
|
webString += "</td></tr><tr><td>\n";
|
||||||
|
|
@ -554,17 +554,17 @@ void handle_setting()
|
||||||
webString += "<div class = \"col-pad\">\n<h3>Fix Location</h3>\n";
|
webString += "<div class = \"col-pad\">\n<h3>Fix Location</h3>\n";
|
||||||
|
|
||||||
webString += "<div class=\"form-group\">\n";
|
webString += "<div class=\"form-group\">\n";
|
||||||
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Latitude</label>\n";
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Latitude(Deg.)</label>\n";
|
||||||
webString += "<div class=\"col-sm-3 col-xs-6\"><input class=\"form-control\" id=\"gpsLat\" name=\"gpsLat\" type=\"text\" value=\"" + String(config.gps_lat, 5) + "\" /></div>\n";
|
webString += "<div class=\"col-sm-3 col-xs-6\"><input class=\"form-control\" id=\"gpsLat\" name=\"gpsLat\" type=\"text\" value=\"" + String(config.gps_lat, 5) + "\" /></div>\n";
|
||||||
webString += "</div>\n";
|
webString += "</div>\n";
|
||||||
|
|
||||||
webString += "<div class=\"form-group\">\n";
|
webString += "<div class=\"form-group\">\n";
|
||||||
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Longitude</label>\n";
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Longitude(Deg.)</label>\n";
|
||||||
webString += "<div class=\"col-sm-3 col-xs-6\"><input class=\"form-control\" id=\"gpsLon\" name=\"gpsLon\" type=\"text\" value=\"" + String(config.gps_lon, 5) + "\" /></div>\n";
|
webString += "<div class=\"col-sm-3 col-xs-6\"><input class=\"form-control\" id=\"gpsLon\" name=\"gpsLon\" type=\"text\" value=\"" + String(config.gps_lon, 5) + "\" /></div>\n";
|
||||||
webString += "</div>\n";
|
webString += "</div>\n";
|
||||||
|
|
||||||
webString += "<div class=\"form-group\">\n";
|
webString += "<div class=\"form-group\">\n";
|
||||||
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Altitude</label>\n";
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Altitude(M.)</label>\n";
|
||||||
webString += "<div class=\"col-sm-3 col-xs-6\"><input class=\"form-control\" id=\"gpsAlt\" name=\"gpsAlt\" type=\"text\" value=\"" + String(config.gps_alt) + "\" /></div>\n";
|
webString += "<div class=\"col-sm-3 col-xs-6\"><input class=\"form-control\" id=\"gpsAlt\" name=\"gpsAlt\" type=\"text\" value=\"" + String(config.gps_alt) + "\" /></div>\n";
|
||||||
webString += "</div>\n";
|
webString += "</div>\n";
|
||||||
|
|
||||||
|
|
@ -579,12 +579,12 @@ void handle_setting()
|
||||||
webString += "</div>\n";
|
webString += "</div>\n";
|
||||||
|
|
||||||
webString += "<div class=\"form-group\">\n";
|
webString += "<div class=\"form-group\">\n";
|
||||||
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">APRS PATH</label>\n";
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">APRS Path</label>\n";
|
||||||
webString += "<div class=\"col-sm-6 col-xs-8\"><input class=\"form-control\" id=\"aprsPath\" name=\"aprsPath\" type=\"text\" value=\"" + String(config.tnc_path) + "\" /></div>\n";
|
webString += "<div class=\"col-sm-6 col-xs-8\"><input class=\"form-control\" id=\"aprsPath\" name=\"aprsPath\" type=\"text\" value=\"" + String(config.tnc_path) + "\" /></div>\n";
|
||||||
webString += "</div>\n";
|
webString += "</div>\n";
|
||||||
|
|
||||||
webString += "<div class=\"form-group\">\n";
|
webString += "<div class=\"form-group\">\n";
|
||||||
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Beacon interval</label>\n";
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Beacon interval(mSec)</label>\n";
|
||||||
webString += "<div class=\"col-sm-2 col-xs-3\"><input class=\"form-control\" id=\"beaconIntv\" name=\"beaconIntv\" type=\"text\" value=\"" + String(config.aprs_beacon) + "\" /></div>\n";
|
webString += "<div class=\"col-sm-2 col-xs-3\"><input class=\"form-control\" id=\"beaconIntv\" name=\"beaconIntv\" type=\"text\" value=\"" + String(config.aprs_beacon) + "\" /></div>\n";
|
||||||
webString += "</div>\n";
|
webString += "</div>\n";
|
||||||
|
|
||||||
|
|
@ -751,6 +751,26 @@ void handle_service()
|
||||||
inet2rfEn = true;
|
inet2rfEn = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (server.argName(i) == "digiDelay")
|
||||||
|
{
|
||||||
|
if (server.arg(i) != "")
|
||||||
|
{
|
||||||
|
if (isValidNumber(server.arg(i)))
|
||||||
|
config.digi_delay = server.arg(i).toInt();
|
||||||
|
if (config.digi_delay > 5000)
|
||||||
|
config.digi_delay = 5000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (server.argName(i) == "timeSlot")
|
||||||
|
{
|
||||||
|
if (server.arg(i) != "")
|
||||||
|
{
|
||||||
|
if (isValidNumber(server.arg(i)))
|
||||||
|
config.tx_timeslot = server.arg(i).toInt();
|
||||||
|
if (config.tx_timeslot > 10000)
|
||||||
|
config.tx_timeslot = 10000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if (server.argName(i) == "mqttEnable") {
|
// if (server.argName(i) == "mqttEnable") {
|
||||||
// if (server.arg(i) != "")
|
// if (server.arg(i) != "")
|
||||||
|
|
@ -826,7 +846,7 @@ void handle_service()
|
||||||
|
|
||||||
webString += "<div class=\"form-group\">\n";
|
webString += "<div class=\"form-group\">\n";
|
||||||
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Pass Code</label>\n";
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Pass Code</label>\n";
|
||||||
webString += "<div class=\"col-sm-2 col-xs-4\"><input class=\"form-control\" id=\"myPasscode\" name=\"myPasscode\" type=\"password\" value=\"" + String(config.aprs_passcode) + "\" /></div>\n";
|
webString += "<div class=\"col-sm-2 col-xs-4\"><input class=\"form-control\" id=\"myPasscode\" name=\"myPasscode\" type=\"password\" value=\"" + String(config.aprs_passcode) + "\" />From <a href=\"https://www.dprns.com/index.php?pid=5\">Here</a></div>\n";
|
||||||
webString += "</div>\n";
|
webString += "</div>\n";
|
||||||
|
|
||||||
// webString += "<div class=\"form-group\">\n";
|
// webString += "<div class=\"form-group\">\n";
|
||||||
|
|
@ -835,7 +855,7 @@ void handle_service()
|
||||||
// webString += "</div>\n";
|
// webString += "</div>\n";
|
||||||
|
|
||||||
webString += "<div class=\"form-group\">\n";
|
webString += "<div class=\"form-group\">\n";
|
||||||
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">APRS Host</label>\n";
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">APRS Server</label>\n";
|
||||||
webString += "<div class=\"col-sm-6 col-xs-8\"><input class=\"form-control\" id=\"aprsHost\" name=\"aprsHost\" type=\"text\" value=\"" + String(config.aprs_host) + "\" /><br />Web Service: <a href=\"http://aprs.dprns.com:14501\" target=\"_blank\">T2THAI</a></div>\n";
|
webString += "<div class=\"col-sm-6 col-xs-8\"><input class=\"form-control\" id=\"aprsHost\" name=\"aprsHost\" type=\"text\" value=\"" + String(config.aprs_host) + "\" /><br />Web Service: <a href=\"http://aprs.dprns.com:14501\" target=\"_blank\">T2THAI</a></div>\n";
|
||||||
webString += "</div>\n";
|
webString += "</div>\n";
|
||||||
|
|
||||||
|
|
@ -878,6 +898,53 @@ void handle_service()
|
||||||
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Inet->RF Enable</label>\n";
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Inet->RF Enable</label>\n";
|
||||||
webString += "<div class=\"col-sm-4 col-xs-8\"><input class=\"field_checkbox\" id=\"inet2rfEnable\" name=\"inet2rfEnable\" type=\"checkbox\" value=\"OK\" " + inet2rfFlage + "/></div>\n";
|
webString += "<div class=\"col-sm-4 col-xs-8\"><input class=\"field_checkbox\" id=\"inet2rfEnable\" name=\"inet2rfEnable\" type=\"checkbox\" value=\"OK\" " + inet2rfFlage + "/></div>\n";
|
||||||
webString += "</div>\n";
|
webString += "</div>\n";
|
||||||
|
String digiFlage = "";
|
||||||
|
if (config.tnc_digi)
|
||||||
|
digiFlage = "checked";
|
||||||
|
webString += "<div class=\"form-group\">\n";
|
||||||
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Digi Repeager</label>\n";
|
||||||
|
webString += "<div class=\"col-sm-4 col-xs-8\"><input class=\"field_checkbox\" id=\"digiEnable\" name=\"digiEnable\" type=\"checkbox\" value=\"OK\" " + digiFlage + "/></div>\n";
|
||||||
|
webString += "</div>\n";
|
||||||
|
|
||||||
|
webString += "<div class=\"form-group\">\n";
|
||||||
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">Digi Delay(mSec)</label>\n";
|
||||||
|
webString += "<div class=\"col-sm-2 col-xs-6\"><select name=\"digiDelay\" id=\"digiDelay\">\n";
|
||||||
|
for (uint16_t delay = 0; delay <= 5000; delay += 500)
|
||||||
|
{
|
||||||
|
if (config.digi_delay == delay)
|
||||||
|
{
|
||||||
|
if (delay == 0)
|
||||||
|
webString += "<option value=\"" + String(delay) + "\" selected>Auto</option>\n";
|
||||||
|
else
|
||||||
|
webString += "<option value=\"" + String(delay) + "\" selected>" + String(delay) + "</option>\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (delay == 0)
|
||||||
|
webString += "<option value=\"" + String(delay) + "\">Auto</option>\n";
|
||||||
|
else
|
||||||
|
webString += "<option value=\"" + String(delay) + "\">" + String(delay) + "</option>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
webString += "</select></div>\n";
|
||||||
|
webString += "</div>\n";
|
||||||
|
|
||||||
|
webString += "<div class=\"form-group\">\n";
|
||||||
|
webString += "<label class=\"col-sm-4 col-xs-12 control-label\">TX Time Slot(mSec)</label>\n";
|
||||||
|
webString += "<div class=\"col-sm-2 col-xs-6\"><select name=\"timeSlot\" id=\"timeSlot\">\n";
|
||||||
|
for (uint16_t delay = 0; delay <= 10000; delay += 1000)
|
||||||
|
{
|
||||||
|
if (config.tx_timeslot == delay)
|
||||||
|
{
|
||||||
|
webString += "<option value=\"" + String(delay) + "\" selected>" + String(delay) + "</option>\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
webString += "<option value=\"" + String(delay) + "\">" + String(delay) + "</option>\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
webString += "</select></div>\n";
|
||||||
|
webString += "</div>\n";
|
||||||
|
|
||||||
webString += "</div>\n<hr>\n"; // div aprs
|
webString += "</div>\n<hr>\n"; // div aprs
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue