/*********************************************************************
JI3BNB AX.25 HF PACKET (300baud) DECODER
TESTED ON ARDUINO MEGA 2560
v3.2 2026.04.11 ST7735 TFT出力テスト OK
/*********************************************************************/
/* ---------------------- ST7735 TFT CONFIG ---------------------- */
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <Adafruit_ST7789.h> // Hardware-specific library for ST7789
#include <SPI.h>
#ifdef ADAFRUIT_HALLOWING
#define TFT_CS 39 // Hallowing display control pins: chip select
#define TFT_RST 37 // Display reset
#define TFT_DC 38 // Display data/command select
#define TFT_BACKLIGHT 7 // Display backlight pin
#elif defined(ESP8266)
#define TFT_CS 4
#define TFT_RST 16
#define TFT_DC 5
#else
// For the breakout board, you can use any 2 or 3 pins.
// These pins will also work for the 1.8" TFT shield.
#define TFT_CS 10
#define TFT_RST 9 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC 8
#endif
// OPTION 1 (recommended) is to use the HARDWARE SPI pins, which are unique
// to each board and not reassignable. For Arduino Uno: MOSI = pin 11 and
// SCLK = pin 13. This is the fastest mode of operation and is required if
// using the breakout board's microSD card.
// For 1.44" and 1.8" TFT with ST7735 (including HalloWing) use:
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
// For 1.54" TFT with ST7789:
//Adafruit_ST7789 tft = Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST);
// OPTION 2 lets you interface the display using ANY TWO or THREE PINS,
// tradeoff being that performance is not as fast as hardware SPI above.
//#define TFT_MOSI 11 // Data out
//#define TFT_SCLK 13 // Clock out
//Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
/* ---------------------- TFT CONFIG END ---------------------- */
/*
* Digital input 19
* (external demodulator required)
*/
const int mode = 5;
/*
* Serial output 115200 baud
* mode 3: Raw Combined
* mode 4: Sync Monitor
* mode 5: Cut Packet
* mode 6: Verbose Mode
*/
#include <TimerOne.h>
volatile int pArray[11];
volatile int syncPoint;
volatile bool state_1;
volatile bool state_2;
volatile bool state_3;
volatile bool state_4;
volatile byte pktBuf[300];
volatile bool parsePkt;
void setup()
{
bufClear();
Timer1.initialize();
Timer1.attachInterrupt(timer_interrupt, 333); //300Baud
Serial.begin(115200);
/* ---------------------- ST7735 TFT CONFIG ---------------------- */
#ifdef ADAFRUIT_HALLOWING
// HalloWing is a special case. It uses a ST7735R display just like the
// breakout board, but the orientation and backlight control are different.
tft.initR(INITR_HALLOWING); // Initialize HalloWing-oriented screen
pinMode(TFT_BACKLIGHT, OUTPUT);
digitalWrite(TFT_BACKLIGHT, HIGH); // Backlight on
#else
// Use this initializer if using a 1.8" TFT screen:
tft.initR(INITR_BLACKTAB); // Init ST7735S chip, black tab
// OR use this initializer (uncomment) if using a 1.44" TFT:
//tft.initR(INITR_144GREENTAB); // Init ST7735R chip, green tab
// OR use this initializer (uncomment) if using a 0.96" 180x60 TFT:
//tft.initR(INITR_MINI160x80); // Init ST7735S mini display
// OR use this initializer (uncomment) if using a 1.54" 240x240 TFT:
//tft.init(240, 240); // Init ST7789 240x240
#endif
/* ---------------------- TFT CONFIG END ---------------------- */
init7735();
tft.print("READY");
delay(1000);
Serial.println("READY");
delay(500);
}
void init7735(void)
{
tft.fillScreen(ST77XX_BLACK);
tft.setCursor(0, 0);
tft.setTextSize(1);
tft.setTextColor(ST77XX_GREEN);
tft.setTextWrap(true);
}
void bufClear(void)
{
for(int i = 0; i < 300; i++)
{
pktBuf[i] = '\0';
}
}
void timer_interrupt(void)
{
/*
static bool statArray[6];
*/
static int syncClock;
static bool smplReady;
static int skips;
static long ones;
static bool fiveOnes;
static byte count8;
static byte buf8;
static byte buf8_;
static int addr;
static int addr2;
static bool savePkt;
static bool pktEnd;
/* ------------------------------------- */
/*
//save states
state_2 = digitalRead(19);
for(int s = 0; s < 6; s++)
{
statArray[s] = statArray[s + 1];
}
statArray[5] = state_2;
//save sync points
if(statArray[0] == false){
if(statArray[1] == false){
if(statArray[2] == false){
if(statArray[3] == true){
if(statArray[4] == true){
if(statArray[5] == true){ //edge +2
for(int p = 0; p < 5; p++)
{
pArray[p] = pArray[p + 1];
}
pArray[4] = syncClock;
}
}
}
}
}
}
*/
//save sync points
state_2 = digitalRead(19);
if(state_1 == 0 && state_2 == 1) //rising edge
{
for(int s = 0; s < 11; s++)
{
pArray[s] = pArray[s + 1];
}
pArray[10] = syncClock;
}
/* ------------------------------------- */
//sample and get byte
if(syncClock - syncPoint >= -1 && syncClock - syncPoint <= 1)
{
smplReady = true;
}
else if(syncClock - syncPoint == -9 || syncClock - syncPoint == 9)
{
smplReady = true;
}
else if(smplReady == true)
{
skips++;
if(skips == 6) //do sample now
{
state_4 = digitalRead(19);
if(state_3 != state_4) //(NRZI) inverted = 0
{
if(fiveOnes == true)
{
//zero insertion
//do nothing
}
else
{
buf8 = buf8 >> 1;
bitWrite(buf8, 7, 0);
if(mode == 3) //print binary
{
Serial.print('0');
}
count8++;
}
fiveOnes = false;
ones = 0;
}
else //(NRZI) no change = 1
{
buf8 = buf8 >> 1;
bitWrite(buf8, 7, 1);
if(mode == 3) //print binary
{
Serial.print('1');
}
ones++;
if(ones == 5)
{
fiveOnes = true;
}
else
{
fiveOnes = false;
}
count8++;
if(ones == 6) //flag
{
count8 = 7; //reset position
}
}
if(count8 == 8) //get byte now
{
if(mode == 3) //print hex + ascii
{
Serial.print(" ");
if(buf8 < 16)
{
Serial.print('0');
}
Serial.print(buf8, HEX);
if(buf8 != 0x7E && addr < 15) //address field
{
buf8 = buf8 >> 1;
}
Serial.print(" ");
if(buf8 < 32 || buf8 > 126)
{
Serial.print(' ');
}
else
{
Serial.print((char)buf8);
}
Serial.println();
}
count8 = 0;
addr++;
if(buf8 == 0x7E) //after flag
{
addr = 1;
}
/* -------------------- */
if(buf8_ == 0x7E && buf8 != 0x7E) //packet save start
{
if(parsePkt == true)
{
//Serial.println("BUSY!");
}
else
{
savePkt = true;
}
}
if(savePkt == true) //save routine active
{
pktBuf[addr2] = buf8_;
if(addr2 == 298) //length limit
{
pktBuf[addr2 + 1] = 0x7E;
parsePkt = true; //parse now
savePkt = false;
}
else if(buf8_ != 0x7E && buf8 == 0x7E) //packet end
{
pktBuf[addr2 + 1] = buf8;
parsePkt = true; //parse now
savePkt = false;
}
if(savePkt == true)
{
addr2++;
}
else
{
addr2 = 0;
}
}
/* -------------------- */
buf8_ = buf8;
}
smplReady = false;
skips = 0;
}
}
/* ------------------------------------- */
syncClock++;
if(syncClock > 9)
{
syncClock = 0;
}
state_1 = state_2;
state_3 = state_4;
}
uint8_t setMask(int j)
{
uint8_t mask;
if(j == 0){mask = 128;}
else if(j == 1){mask = 64;}
else if(j == 2){mask = 32;}
else if(j == 3){mask = 16;}
else if(j == 4){mask = 8;}
else if(j == 5){mask = 4;}
else if(j == 6){mask = 2;}
else if(j == 7){mask = 1;}
return mask;
}
void loop()
{
volatile int tMax;
int sumArray[] = {0,0,0,0,0,0,0,0,0,0};
/* ------------------------------------- */
//find the MOST SEEN point
for(int p = 0; p < 11; p++)
{
if(mode == 4)
{
Serial.print(pArray[p]);
Serial.print(' ');
}
for(int s = 0; s < 10; s++)
{
if(pArray[p] == s)
{
sumArray[s] += 1;
}
}
}
if(mode == 4)
{
Serial.print(" ");
for(int s = 0; s < 10; s++)
{
Serial.print(sumArray[s]);
Serial.print(' ');
}
}
tMax = -1;
syncPoint = -1;
for(int s = 0; s < 10; s++)
{
if(sumArray[s] > tMax)
{
tMax = sumArray[s];
syncPoint = s;
}
}
if(mode == 4)
{
Serial.print(" ");
Serial.print("tMax:");
Serial.print(tMax);
Serial.print(' ');
Serial.print("syncPoint:");
Serial.print(syncPoint);
Serial.print(' ');
Serial.println();
}
/* ------------------------------------- */
//parse packet and show result
if(mode == 5 || mode == 6)
{
if(parsePkt == true)
{
int i;
int pktLen;
/* -------------------- */
pktLen = 0;
i = 0;
while(true)
{
if(pktBuf[i] == '\0')
{
break;
}
i++;
}
pktLen = i; //including ~
if(pktLen < 19) //invalid
{
goto exitPrs;
}
for(int a = 1; a < 5; a++) //1,2,3,4
{
if(pktBuf[a] & B00000001 == true) //invalid
{
goto exitPrs;
}
}
for(int a = 7; a < 12; a++) //7,8,9,10,11
{
if(pktBuf[a] & B00000001 == true) //invalid
{
goto exitPrs;
}
}
/* -------------------- */
//-- top --
Serial.println("--X--X--X--X--X--X--X--");
//-- Address (A) Field --
byte addr3;
//--
addr3 = 1;
//--
Serial.println();
while(true)
{
if(addr3 == 1)
{
Serial.println("TO");
}
else if(addr3 == 8)
{
Serial.println("FROM");
}
else if(addr3 >= 15)
{
Serial.println("VIA");
}
for(int i = addr3; i < addr3 + 7; i++)
{
//-- BINARY --
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
/*
* B10000000
* B01000000
* B00100000
* B00010000
* B00001000
* B00000100
* B00000010
* B00000001
*/
if(mask & pktBuf[i])
{
Serial.print('1');
}
else
{
Serial.print('0');
}
}
//-- HEX --
Serial.print(' ');
if(pktBuf[i] < 16)
{
Serial.print('0');
}
Serial.print(pktBuf[i], HEX);
//-- ASCII --
Serial.print(' ');
if(i < addr3 + 6)
{
char ch = pktBuf[i] >> 1;
Serial.print((char)ch);
}
else if(i == addr3 + 6) //<--
{
byte CdCs = B10000000 & pktBuf[addr3 + 6];
CdCs = CdCs >> 7;
byte rsv1 = B01000000 & pktBuf[addr3 + 6];
rsv1 = rsv1 >> 6;
byte rsv2 = B00100000 & pktBuf[addr3 + 6];
rsv2 = rsv2 >> 5;
byte ssid = B00011110 & pktBuf[addr3 + 6];
ssid = ssid >> 1;
byte afEnd = B00000001 & pktBuf[addr3 + 6];
if(addr3 == 1)
{
Serial.print("Cd:");
}
else if(addr3 == 8)
{
Serial.print("Cs:");
}
else if(addr3 >= 15)
{
Serial.print("H:");
}
Serial.print(CdCs);
/*
* Cd Cs
* --------------------------
* 0 0 : previous versions
* 1 0 : Command(v2.0)
* 0 1 : Response(v2.0)
* 1 1 : previous versions
*
* H
* --------------------------
* 0 : has NOT been repeated
* 1 : has been repeated
*/
Serial.print(' ');
Serial.print("Rsv:");
Serial.print(rsv1); //reserve 1
Serial.print(rsv2); //reserve 2
Serial.print(' ');
Serial.print("SSID:");
Serial.print(ssid);
Serial.print(' ');
Serial.print("End:");
Serial.print(afEnd); //the last address octet or not
if(afEnd)
{
Serial.println();
goto showCField;
}
}
Serial.println();
}
Serial.println();
if(addr3 == 29) //1,8,15,22,29
{
break;
}
else
{
//--
addr3 += 7;
//--
}
}
/* -------------------- */
//-- Control (C) Field --
showCField: //-label-
bool reqPID;
reqPID = false;
//--
addr3 += 7;
//--
Serial.println();
Serial.println("C,PID");
//-- BINARY --
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
if(mask & pktBuf[addr3])
{
Serial.print('1');
}
else
{
Serial.print('0');
}
}
//-- HEX --
Serial.print(' ');
if(pktBuf[addr3] < 16)
{
Serial.print('0');
}
Serial.print(pktBuf[addr3], HEX);
Serial.print(' ');
Serial.print("Type:");
//-- Information (I) Frame --
byte masked;
masked = B00000001 & pktBuf[addr3];
if(masked == B00000000)
{
reqPID = true;
Serial.print('I');
Serial.print(' ');
Serial.print("N(R):"); //Receive Sequence Number
masked = B11100000 & pktBuf[addr3];
masked = masked >> 5;
Serial.print(masked);
Serial.print(' ');
Serial.print("N(S):"); //Send Sequence Number
masked = B00001110 & pktBuf[addr3];
masked = masked >> 1;
Serial.print(masked);
}
//-- Supervisory (S) Frame --
masked = B00000011 & pktBuf[addr3];
if(masked == B00000001)
{
Serial.print('S');
masked = B00001100 & pktBuf[addr3];
if(masked == B00000000)
{
Serial.print(">RR"); //Receive Ready
}
else if(masked == B00000100)
{
Serial.print(">RNR"); //Receive Not Ready
}
else if(masked == B00001000)
{
Serial.print(">REJ"); //Reject
}
Serial.print(' ');
Serial.print("N(R):"); //Receive Sequence Number
masked = B11100000 & pktBuf[addr3];
masked = masked >> 5;
{
Serial.print(masked);
}
}
//-- Unnumbered (U) Frame --
bool frmr;
frmr = false;
masked = B00000011 & pktBuf[addr3];
if(masked == B00000011)
{
Serial.print('U');
masked = B11101100 & pktBuf[addr3];
if(masked == B00101100)
{
Serial.print(">SABM"); //Set Asynchronous Balanced Mode
}
else if(masked == B01000000)
{
Serial.print(">DISC"); //Disconnect
}
else if(masked == B00001100)
{
Serial.print(">DM"); //Disconnected Mode
}
else if(masked == B01100000)
{
Serial.print(">UA"); //Unnumbered Acknowledge
}
else if(masked == B10000100)
{
frmr = true;
Serial.print(">FRMR"); //Frame Reject
}
else if(masked == B00000000)
{
reqPID = true;
Serial.print(">UI"); //Unnumbered Information
}
/*
frmr = true; //TEST
*/
}
//-- Poll/Final bit --
Serial.print(' ');
Serial.print("P/F:");
masked = B00010000 & pktBuf[addr3];
if(masked == B00010000)
{
Serial.print('1');
}
else
{
Serial.print('0');
}
Serial.println();
/* -------------------- */
//-- Protocol ID (PID) Field --
if(reqPID == true)
{
//--
addr3 += 1;
//--
//-- BINARY --
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
if(mask & pktBuf[addr3])
{
Serial.print('1');
}
else
{
Serial.print('0');
}
}
//-- HEX --
Serial.print(' ');
if(pktBuf[addr3] < 16)
{
Serial.print('0');
}
Serial.print(pktBuf[addr3], HEX);
//-- Type --
Serial.print(' ');
masked = B00110000 & pktBuf[addr3];
if(masked == B00010000)
{
Serial.print("AX.25 layer 3 implemented");
}
else if(masked == B00100000)
{
Serial.print("AX.25 layer 3 implemented");
}
else if(pktBuf[addr3] == B11001100)
{
Serial.print("Internet Protocol datagram layer 3 implemented");
}
else if(pktBuf[addr3] == B11001101)
{
Serial.print("Address resolution protocol layer 3 implemented");
}
else if(pktBuf[addr3] == B11110000)
{
Serial.print("No layer 3 implemented");
}
else if(pktBuf[addr3] == B11111111)
{
Serial.print("Escape character. Next octet contains more Level 3 protocol information.");
}
else
{
Serial.print("Reserved for future level 3 protocols");
}
Serial.println();
}
/* -------------------- */
//-- Information Field (Message Body) --
//--
addr3 += 1;
//--
if(frmr == true) //FRMR frame information field (3 octets, 24 bits)
{
Serial.println();
Serial.println("FRMR");
//-- BINARY --
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
if(mask & pktBuf[addr3])
{
Serial.print('1');
}
else
{
Serial.print('0');
}
}
//-- HEX --
Serial.print(' ');
if(pktBuf[addr3] < 16)
{
Serial.print('0');
}
Serial.print(pktBuf[addr3], HEX);
Serial.print(' ');
Serial.println("Rejected Frame Control Field");
//--
addr3 += 1;
//--
//-- BINARY --
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
if(mask & pktBuf[addr3])
{
Serial.print('1');
}
else
{
Serial.print('0');
}
}
//-- HEX --
Serial.print(' ');
if(pktBuf[addr3] < 16)
{
Serial.print('0');
}
Serial.print(pktBuf[addr3], HEX);
//-- parameters --
Serial.print(' ');
Serial.print("V(R):"); //Current Receive State Variable of the device reporting the rejection.
masked = B11100000 & pktBuf[addr3];
masked = masked >> 5;
Serial.print(masked);
Serial.print(' ');
Serial.print("CR:"); //The CR bit is set to zero to indicate the rejected frame was a command, or one if it was a response.
masked = B00010000 & pktBuf[addr3];
masked = masked >> 4;
Serial.print(masked);
Serial.print(' ');
Serial.print("V(S):"); //Current Send State Variable of the device reporting the rejection.
masked = B00001110 & pktBuf[addr3];
masked = masked >> 1;
Serial.print(masked);
Serial.print(' ');
Serial.println('0'); //always
//--
addr3 += 1;
//--
//-- BINARY --
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
if(mask & pktBuf[addr3])
{
Serial.print('1');
}
else
{
Serial.print('0');
}
}
//-- HEX --
Serial.print(' ');
if(pktBuf[addr3] < 16)
{
Serial.print('0');
}
Serial.print(pktBuf[addr3], HEX);
//-- parameters --
Serial.print(' ');
Serial.print("0000"); //always
Serial.print(' ');
Serial.print("Z:"); //If Z is set to 1, the control field received and returned in bits 1 to 8 contained an invalid N(R).
masked = B00001000 & pktBuf[addr3];
masked = masked >> 3;
Serial.print(masked);
Serial.print(' ');
Serial.print("Y:"); //If Y is set to 1, the information field of a received frame exceeded the maximum allowed under this recommendation in 2.4.7.3 below.
masked = B00000100 & pktBuf[addr3];
masked = masked >> 2;
Serial.print(masked);
Serial.print(' ');
Serial.print("X:"); //If X is set to 1, the frame that caused the reject condition was considered invalid because it was a U or S frame that had an information field that is not allowed. Bit W must be set to 1 in addition to the X bit.
masked = B00000010 & pktBuf[addr3];
masked = masked >> 1;
Serial.print(masked);
Serial.print(' ');
Serial.print("W:"); //If W is set to 1, the control field received was invalid or not implemented.
masked = B00000001 & pktBuf[addr3];
Serial.println(masked);
}
else //ASCII messages
{
int msgLen;
msgLen = 0;
Serial.println();
Serial.println("INFO");
//-- common --
Serial.print('"');
init7735(); //<--- --- ---
for(int i = addr3; i < pktLen - 3; i++)
{
//-- ASCII YOKO --
if(pktBuf[i] < 32 || pktBuf[i] > 126)
{
Serial.print(' ');
}
else
{
Serial.print((char)pktBuf[i]);
tft.print((char)pktBuf[i]); //<--- --- ---
}
msgLen++;
}
Serial.print('"');
Serial.print(' ');
Serial.print("Len:");
Serial.println(msgLen);
//-- Verbose Mode --
if(mode == 6)
{
Serial.println("----");
for(i = addr3; i < pktLen - 3; i++)
{
//-- BINARY --
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
if(mask & pktBuf[i])
{
Serial.print('1');
}
else
{
Serial.print('0');
}
}
//-- HEX --
Serial.print(' ');
if(pktBuf[i] < 16)
{
Serial.print('0');
}
Serial.print(pktBuf[i], HEX); //UI:0xF0
//-- ASCII TATE --
Serial.print(' ');
if(pktBuf[i] < 32 || pktBuf[i] > 126)
{
Serial.println(' ');
}
else
{
Serial.println((char)pktBuf[i]);
}
}
}
}
/* -------------------- */
//-- Frame Check Sequence (FCS) --
Serial.println();
Serial.println("FCS");
//-- 1st byte --
byte fcs1Rev;
fcs1Rev = 0;
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
if(mask & pktBuf[pktLen - 3]) //<--
{
bitWrite(fcs1Rev, j, 1);
}
}
//-- BINARY --
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
if(mask & fcs1Rev)
{
Serial.print('1');
}
else
{
Serial.print('0');
}
}
//-- HEX --
Serial.print(' ');
if(fcs1Rev < 16)
{
Serial.print('0');
}
Serial.println(fcs1Rev, HEX); //FCS(1)
//-- 2nd byte --
byte fcs2Rev;
fcs2Rev = 0;
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
if(mask & pktBuf[pktLen - 2]) //<--
{
bitWrite(fcs2Rev, j, 1);
}
}
//-- BINARY --
for(int j = 0; j < 8; j++)
{
uint8_t mask;
mask = setMask(j);
if(mask & fcs2Rev)
{
Serial.print('1');
}
else
{
Serial.print('0');
}
}
//-- HEX --
Serial.print(' ');
if(fcs2Rev < 16)
{
Serial.print('0');
}
Serial.println(fcs2Rev, HEX); //FCS(2)
Serial.println();
/* -------------------- */
exitPrs: //-label-
bufClear();
parsePkt = false;
}
}
/* ------------------------------------- */
delay(1);
}