Skip to content

Instantly share code, notes, and snippets.

@temberature
Created March 19, 2026 06:13
Show Gist options
  • Select an option

  • Save temberature/2814a034caf4030dd034b9e1e6a2e607 to your computer and use it in GitHub Desktop.

Select an option

Save temberature/2814a034caf4030dd034b9e1e6a2e607 to your computer and use it in GitHub Desktop.
RID Radar v5.0 - ASTM F3411 Full Decode
#include <Arduino.h>
#include <Wire.h>
#include <TFT_eSPI.h>
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_gap_ble_api.h"
#include "esp_wifi.h"
#include "esp_wifi_types.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "freertos/semphr.h"
#include <string.h>
#include <math.h>
// ============================================================
// RID Radar v5.0 - ASTM F3411 Full Decode
// ============================================================
// --- XL9555 ---
#define I2C_SDA 41
#define I2C_SCL 42
#define XL9555_ADDR 0x20
static uint8_t xl_p0=0xFF;
void xl_w(uint8_t r,uint8_t v){Wire.beginTransmission(XL9555_ADDR);Wire.write(r);Wire.write(v);Wire.endTransmission();}
bool xl_init(){Wire.begin(I2C_SDA,I2C_SCL,400000);Wire.beginTransmission(XL9555_ADDR);if(Wire.endTransmission()!=0)return false;xl_w(0x06,0x00);xl_w(0x07,0xF0);xl_w(0x02,xl_p0);return true;}
void lcd_bl(bool on){if(on)xl_p0|=2;else xl_p0&=~2;xl_w(0x02,xl_p0);}
void lcd_rst(){xl_p0&=~1;xl_w(0x02,xl_p0);delay(100);xl_p0|=1;xl_w(0x02,xl_p0);delay(120);}
// --- TFT ---
TFT_eSPI tft=TFT_eSPI();
#define C_BG 0x0000
#define C_TB 0x1A3F
#define C_HD TFT_CYAN
#define C_TX TFT_WHITE
#define C_DM TFT_DARKGREY
#define C_AL TFT_RED
#define C_OK TFT_GREEN
#define C_WN TFT_YELLOW
#define C_DB 0x0A1F
#define C_WF 0xFD20
// --- DroneInfo ---
struct DroneInfo {
char mac[18],sn[21],ssid[33];
uint8_t uaType,idType;
double lat,lon;
float geoAlt,pressAlt,height,speed,vspeed,heading;
uint8_t opStatus,heightType;
float hAcc,vAcc;
double opLat,opLon;
bool hasOpLoc;
uint16_t areaRadius;
int rssi;uint32_t lastSeen;
bool hasLoc,hasId;uint8_t src,msgCnt;
};
#define MXD 8
static DroneInfo drones[MXD];
static int dCnt=0;
static volatile uint32_t bPk=0,wPk=0,oPk=0;
static volatile uint8_t curCh=6;
static SemaphoreHandle_t mtx=NULL;
static void fmtMAC(char*b,const uint8_t*a){sprintf(b,"%02X:%02X:%02X:%02X:%02X:%02X",a[0],a[1],a[2],a[3],a[4],a[5]);}
static DroneInfo*findDrone(const char*mac){
for(int i=0;i<dCnt;i++)if(strcmp(drones[i].mac,mac)==0)return&drones[i];
if(dCnt<MXD){auto*d=&drones[dCnt++];memset(d,0,sizeof(DroneInfo));strncpy(d->mac,mac,17);return d;}
uint32_t o=UINT32_MAX;int x=0;for(int i=0;i<MXD;i++)if(drones[i].lastSeen<o){o=drones[i].lastSeen;x=i;}
memset(&drones[x],0,sizeof(DroneInfo));strncpy(drones[x].mac,mac,17);return&drones[x];
}
static const char*uaStr(uint8_t t){switch(t){case 0:return"N/A";case 1:return"Plane";case 2:return"Copter";case 3:return"Gyro";case 4:return"VTOL";case 5:return"Ornith";case 6:return"Glider";case 7:return"Kite";case 8:return"Balloon";default:return"Other";}}
static const char*stStr(uint8_t s){switch(s){case 0:return"Undecl";case 1:return"Ground";case 2:return"Airborne";case 3:return"Emergency";case 4:return"RIDFail";default:return"?";}}
static float accLUT(uint8_t v){static const float t[]={-1,18520,7408,3704,1852,926,555.6f,185.2f,92.6f,30,10,3,1};return v>12?-1:t[v];}
// --- ODID Parser ---
static void parseODID(DroneInfo*d,const uint8_t*m,int len){
if(len<25)return;
uint8_t t=(m[0]>>4)&0x0F;
if(t==0){d->idType=(m[1]>>4)&0x0F;d->uaType=m[1]&0x0F;memset(d->sn,0,21);memcpy(d->sn,&m[2],20);d->hasId=true;}
else if(t==1){
d->opStatus=(m[1]>>4)&0x0F;d->heightType=(m[1]>>3)&0x01;
uint8_t ew=(m[1]>>2)&1,sm=(m[1]>>1)&1;
d->heading=(m[2]<180)?(float)m[2]+ew*180.0f:-1;
if(m[3]!=255){d->speed=sm?(m[3]*0.75f+63.75f):(m[3]*0.25f);if(m[3]==0&&d->opStatus==1)d->speed=0;}
int8_t vs=(int8_t)m[4];d->vspeed=(vs!=-63)?vs*0.5f:0;
int32_t la,lo;memcpy(&la,&m[5],4);memcpy(&lo,&m[9],4);
if(la!=0&&la!=0x7FFFFFFF){d->lat=la/1e7;d->lon=lo/1e7;d->hasLoc=true;}
uint16_t pr,ge,ht;memcpy(&pr,&m[13],2);memcpy(&ge,&m[15],2);memcpy(&ht,&m[17],2);
d->pressAlt=(pr!=0xFFFF)?pr*0.5f-1000.0f:-999;
d->geoAlt=(ge!=0xFFFF)?ge*0.5f-1000.0f:-999;
d->height=(ht!=0xFFFF)?ht*0.5f-1000.0f:-999;
d->hAcc=accLUT((m[19]>>4)&0x0F);d->vAcc=accLUT(m[19]&0x0F);
}
else if(t==4){
int32_t ola,olo;memcpy(&ola,&m[2],4);memcpy(&olo,&m[6],4);
if(ola!=0&&ola!=0x7FFFFFFF){d->opLat=ola/1e7;d->opLon=olo/1e7;d->hasOpLoc=true;}
d->areaRadius=m[12]*10;
}
else if(t==0xF&&len>=3){uint8_t sz=m[1];uint8_t c=m[2];if(!sz)sz=25;for(int i=0;i<c&&(3+(i+1)*sz)<=len;i++)parseODID(d,&m[3+i*sz],sz);}
}
// --- WiFi ---
typedef struct{uint16_t fc;uint16_t dur;uint8_t a1[6],a2[6],a3[6];uint16_t seq;}__attribute__((packed))mhdr_t;
static bool parseBeacon(DroneInfo*d,const uint8_t*p,int len){
if(len<12)return false;
const uint8_t*ie=p+12;int il=len-12,off=0;bool found=false;
while(off+2<=il){
uint8_t id=ie[off],el=ie[off+1];if(off+2+el>il)break;
if(id==0&&el>0&&el<=32){memset(d->ssid,0,33);memcpy(d->ssid,&ie[off+2],el);}
if(id==0xDD&&el>=4){
const uint8_t*v=&ie[off+2];
if(v[0]==0xFA&&v[1]==0x0B&&v[2]==0xBC&&el>=30&&v[3]==0x0D){
d->msgCnt=v[4];parseODID(d,&v[5],el-5);found=true;
}
if(!found&&v[0]==0x26&&v[1]==0x37&&v[2]==0x12&&el>=20)found=true;
}
off+=2+el;
}
if(!found&&d->ssid[0]){
if(strncmp(d->ssid,"RID-",4)==0){if(!d->hasId){snprintf(d->sn,21,"%s",d->ssid+4);d->hasId=true;}found=true;}
else if(strncmp(d->ssid,"DJI-",4)==0){if(!d->hasId){snprintf(d->sn,21,"%s",d->ssid+4);d->hasId=true;}found=true;}
}
return found;
}
static bool parseNAN(DroneInfo*d,const uint8_t*p,int len){
if(len<8||p[0]!=0x04||p[1]!=0x09||p[2]!=0x50||p[3]!=0x6F||p[4]!=0x9A||p[5]!=0x13)return false;
int off=6;while(off+3<=len){uint8_t ai=p[off];uint16_t al=p[off+1]|(p[off+2]<<8);if(off+3+al>len)break;
if(ai==0x03&&al>=24){const uint8_t*sd=&p[off+3];for(int i=0;i+27<=(int)al;i++){if(sd[i]==0x0D){parseODID(d,&sd[i+2],al-i-2);return true;}}}
off+=3+al;}return false;
}
static uint8_t lockedCh=0;static uint32_t lockUntil=0;
static void IRAM_ATTR wifi_cb(void*buf,wifi_promiscuous_pkt_type_t type){
if(type!=WIFI_PKT_MGMT)return;
const wifi_promiscuous_pkt_t*pk=(wifi_promiscuous_pkt_t*)buf;
const uint8_t*fr=pk->payload;int fl=pk->rx_ctrl.sig_len;
if(fl<(int)sizeof(mhdr_t))return;wPk++;
const mhdr_t*h=(mhdr_t*)fr;uint8_t st=(h->fc&0xF0)>>4;
if(((h->fc&0x0C)>>2)!=0)return;if(st!=8&&st!=5&&st!=13)return;
const uint8_t*body=fr+sizeof(mhdr_t);int bl=fl-sizeof(mhdr_t);if(bl<=0)return;
char mac[18];fmtMAC(mac,h->a2);
DroneInfo tmp;memset(&tmp,0,sizeof(tmp));strncpy(tmp.mac,mac,17);
bool ok=false;
if(st==8||st==5){ok=parseBeacon(&tmp,body,bl);if(ok)tmp.src=2;}
else if(st==13){ok=parseNAN(&tmp,body,bl);if(ok)tmp.src=3;}
if(ok){
oPk++;lockedCh=curCh;lockUntil=millis()+30000;
if(xSemaphoreTake(mtx,pdMS_TO_TICKS(5))==pdTRUE){
DroneInfo*d=findDrone(mac);d->rssi=pk->rx_ctrl.rssi;d->lastSeen=millis();d->src=tmp.src;
if(tmp.ssid[0])memcpy(d->ssid,tmp.ssid,33);d->msgCnt=tmp.msgCnt;
if(tmp.hasId){memcpy(d->sn,tmp.sn,21);d->hasId=true;d->uaType=tmp.uaType;d->idType=tmp.idType;}
if(tmp.hasLoc){d->lat=tmp.lat;d->lon=tmp.lon;d->hasLoc=true;d->geoAlt=tmp.geoAlt;d->pressAlt=tmp.pressAlt;d->height=tmp.height;d->speed=tmp.speed;d->vspeed=tmp.vspeed;d->heading=tmp.heading;d->opStatus=tmp.opStatus;d->heightType=tmp.heightType;d->hAcc=tmp.hAcc;d->vAcc=tmp.vAcc;}
if(tmp.hasOpLoc){d->opLat=tmp.opLat;d->opLon=tmp.opLon;d->hasOpLoc=true;d->areaRadius=tmp.areaRadius;}
xSemaphoreGive(mtx);
}
Serial.printf("\n[RID] %s RSSI=%d #%d\n",mac,pk->rx_ctrl.rssi,tmp.msgCnt);
if(tmp.hasId)Serial.printf(" SN:%s Type:%s\n",tmp.sn,uaStr(tmp.uaType));
if(tmp.hasLoc){Serial.printf(" %.7f,%.7f %s\n",tmp.lat,tmp.lon,stStr(tmp.opStatus));Serial.printf(" GeoAlt:%.1f PressAlt:%.1f Height:%.1f(%s)\n",tmp.geoAlt,tmp.pressAlt,tmp.height,tmp.heightType?"AGL":"TKO");Serial.printf(" Spd:%.1f VS:%.1f Hdg:%.0f\n",tmp.speed,tmp.vspeed,tmp.heading);}
if(tmp.hasOpLoc)Serial.printf(" OpPos:%.7f,%.7f R:%dm\n",tmp.opLat,tmp.opLon,tmp.areaRadius);
}
}
// WiFi init + hop
static const uint8_t chList[]={6,1,6,11,6,2,6,3,6,4,6,5,6,7,6,8,6,9,6,10,6,12,6,13};
static int chIdx=0;
static void wifi_init(){nvs_flash_init();esp_event_loop_create_default();wifi_init_config_t c=WIFI_INIT_CONFIG_DEFAULT();esp_wifi_init(&c);esp_wifi_set_storage(WIFI_STORAGE_RAM);esp_wifi_set_mode(WIFI_MODE_STA);esp_wifi_start();wifi_promiscuous_filter_t f={};f.filter_mask=WIFI_PROMIS_FILTER_MASK_MGMT;esp_wifi_set_promiscuous_filter(&f);esp_wifi_set_promiscuous_rx_cb(wifi_cb);esp_wifi_set_promiscuous(true);esp_wifi_set_channel(6,WIFI_SECOND_CHAN_NONE);curCh=6;}
static void wifi_hop(){if(lockedCh>0&&millis()<lockUntil){if(curCh!=lockedCh){curCh=lockedCh;esp_wifi_set_channel(curCh,WIFI_SECOND_CHAN_NONE);}return;}lockedCh=0;chIdx=(chIdx+1)%(sizeof(chList)/sizeof(chList[0]));curCh=chList[chIdx];esp_wifi_set_channel(curCh,WIFI_SECOND_CHAN_NONE);}
// --- BLE ---
static bool parseBleAd(DroneInfo*d,const uint8_t*data,uint16_t dl){uint16_t o=0;while(o<dl){uint8_t al=data[o];if(al==0||o+al>=dl)break;uint8_t at=data[o+1];const uint8_t*ad=&data[o+2];uint8_t adl=al-1;if(at==0x16&&adl>=4){uint16_t u=ad[0]|(ad[1]<<8);if(u==0xFFFA){const uint8_t*p=&ad[2];int pl=adl-2;if(pl>=27&&p[0]==0x0D){parseODID(d,&p[2],pl-2);return true;}else if(pl>=25){parseODID(d,p,pl);return true;}}}if(at==0xFF&&adl>=27){const uint8_t*p=&ad[2];int pl=adl-2;if(pl>=27&&p[0]==0x0D){parseODID(d,&p[2],pl-2);return true;}}o+=al+1;}return false;}
static void gap_cb(esp_gap_ble_cb_event_t ev,esp_ble_gap_cb_param_t*p){
switch(ev){
case ESP_GAP_BLE_EXT_ADV_REPORT_EVT:{auto*r=&p->ext_adv_report.params;bPk++;if(r->adv_data_len>0&&r->adv_data){char m[18];fmtMAC(m,r->addr);DroneInfo t;memset(&t,0,sizeof(t));strncpy(t.mac,m,17);if(parseBleAd(&t,r->adv_data,r->adv_data_len)){oPk++;if(xSemaphoreTake(mtx,pdMS_TO_TICKS(5))==pdTRUE){DroneInfo*d=findDrone(m);d->rssi=r->rssi;d->lastSeen=millis();d->src=1;if(t.hasId){memcpy(d->sn,t.sn,21);d->hasId=true;d->uaType=t.uaType;}if(t.hasLoc){d->lat=t.lat;d->lon=t.lon;d->hasLoc=true;d->geoAlt=t.geoAlt;d->height=t.height;d->speed=t.speed;}if(t.hasOpLoc){d->opLat=t.opLat;d->opLon=t.opLon;d->hasOpLoc=true;}xSemaphoreGive(mtx);}}}break;}
case ESP_GAP_BLE_SCAN_RESULT_EVT:{auto*s=&p->scan_rst;if(s->search_evt==ESP_GAP_SEARCH_INQ_RES_EVT){bPk++;if(s->adv_data_len>0){char m[18];fmtMAC(m,s->bda);DroneInfo t;memset(&t,0,sizeof(t));strncpy(t.mac,m,17);if(parseBleAd(&t,s->ble_adv,s->adv_data_len)){oPk++;if(xSemaphoreTake(mtx,pdMS_TO_TICKS(5))==pdTRUE){DroneInfo*d=findDrone(m);d->rssi=s->rssi;d->lastSeen=millis();d->src=0;if(t.hasId){memcpy(d->sn,t.sn,21);d->hasId=true;d->uaType=t.uaType;}if(t.hasLoc){d->lat=t.lat;d->lon=t.lon;d->hasLoc=true;d->geoAlt=t.geoAlt;d->height=t.height;d->speed=t.speed;}xSemaphoreGive(mtx);}}}}else if(s->search_evt==ESP_GAP_SEARCH_INQ_CMPL_EVT)esp_ble_gap_start_scanning(0);break;}
case ESP_GAP_BLE_SET_EXT_SCAN_PARAMS_COMPLETE_EVT:if(p->set_ext_scan_params.status==0)esp_ble_gap_start_ext_scan(0,0);break;
case ESP_GAP_BLE_EXT_SCAN_START_COMPLETE_EVT:Serial.println("[BLE5]OK");break;
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:if(p->scan_param_cmpl.status==0)esp_ble_gap_start_scanning(0);break;
case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:Serial.println("[BLE4]OK");break;
default:break;}
}
static void ble_init(){esp_bt_controller_config_t c=BT_CONTROLLER_INIT_CONFIG_DEFAULT();if(esp_bt_controller_init(&c))return;if(esp_bt_controller_enable(ESP_BT_MODE_BLE))return;if(esp_bluedroid_init())return;if(esp_bluedroid_enable())return;esp_ble_gap_register_callback(gap_cb);esp_ble_scan_params_t lp={};lp.scan_type=BLE_SCAN_TYPE_PASSIVE;lp.own_addr_type=BLE_ADDR_TYPE_PUBLIC;lp.scan_filter_policy=BLE_SCAN_FILTER_ALLOW_ALL;lp.scan_interval=1600;lp.scan_window=16;lp.scan_duplicate=BLE_SCAN_DUPLICATE_DISABLE;esp_ble_gap_set_scan_params(&lp);esp_ble_ext_scan_params_t ep={};ep.own_addr_type=BLE_ADDR_TYPE_PUBLIC;ep.filter_policy=BLE_SCAN_FILTER_ALLOW_ALL;ep.scan_duplicate=BLE_SCAN_DUPLICATE_DISABLE;ep.cfg_mask=ESP_BLE_GAP_EXT_SCAN_CFG_UNCODE_MASK|ESP_BLE_GAP_EXT_SCAN_CFG_CODE_MASK;ep.uncoded_cfg={.scan_type=BLE_SCAN_TYPE_PASSIVE,.scan_interval=1600,.scan_window=16};ep.coded_cfg={.scan_type=BLE_SCAN_TYPE_PASSIVE,.scan_interval=3200,.scan_window=16};esp_ble_gap_set_ext_scan_params(&ep);}
// --- Screen ---
static void draw(){
tft.fillRect(0,0,240,28,C_TB);tft.setTextColor(C_HD,C_TB);tft.setTextFont(2);tft.drawString("RID RADAR v5",60,6);
tft.fillRect(0,30,240,22,C_BG);char b[48];sprintf(b,"B:%lu W:%lu R:%lu Ch%d",bPk,wPk,oPk,curCh);tft.setTextColor(C_DM,C_BG);tft.setTextFont(2);tft.drawString(b,6,32);
tft.fillRect(0,54,240,30,C_BG);tft.setTextFont(4);
if(!dCnt){tft.setTextColor(C_OK,C_BG);tft.drawString("NO DRONES",50,56);}
else{tft.setTextColor(C_AL,C_BG);sprintf(b,"!! %d DRONE%s !!",dCnt,dCnt>1?"S":"");tft.drawString(b,20,56);}
tft.fillRect(0,88,240,232,C_BG);
if(dCnt==1){
DroneInfo*d=&drones[0];int y=88;tft.setTextFont(2);
// SN
tft.setTextColor(C_HD,C_BG);if(d->hasId){sprintf(b,"SN:%.20s",d->sn);tft.drawString(b,4,y);}
sprintf(b,"%s",uaStr(d->uaType));tft.setTextColor(C_DM,C_BG);tft.drawString(b,180,y);
// MAC+src
y+=16;tft.setTextColor(C_TX,C_BG);tft.drawString(d->mac,4,y);
const char*ss;switch(d->src){case 0:ss="BLE";break;case 1:ss="BL5";break;case 2:ss="WiFi";break;default:ss="NAN";}
sprintf(b,"%ddBm %s",d->rssi,ss);tft.setTextColor(C_WF,C_BG);tft.drawString(b,150,y);
// Status
y+=16;sprintf(b,"Status:%s #%d",stStr(d->opStatus),d->msgCnt);
tft.setTextColor(d->opStatus==2?C_AL:C_OK,C_BG);tft.drawString(b,4,y);
// Position
y+=20;
if(d->hasLoc){
tft.setTextColor(C_OK,C_BG);sprintf(b,"Lat: %.7f",d->lat);tft.drawString(b,4,y);
y+=16;sprintf(b,"Lon: %.7f",d->lon);tft.drawString(b,4,y);
y+=18;tft.setTextColor(C_WN,C_BG);sprintf(b,"Alt:%.0fm H:%.0fm(%s)",d->geoAlt,d->height,d->heightType?"AGL":"TKO");tft.drawString(b,4,y);
y+=16;tft.setTextColor(C_TX,C_BG);sprintf(b,"Spd:%.1fm/s VS:%.1fm/s",d->speed,d->vspeed);tft.drawString(b,4,y);
if(d->heading>=0){sprintf(b,"H:%.0f",d->heading);tft.drawString(b,195,y);}
y+=16;if(d->hAcc>0){tft.setTextColor(C_DM,C_BG);sprintf(b,"Acc:H<%.0fm V<%.0fm",d->hAcc,d->vAcc);tft.drawString(b,4,y);}
}else{tft.setTextColor(C_DM,C_BG);tft.drawString("Waiting for GPS...",4,y);y+=16;}
// Operator
y+=18;if(d->hasOpLoc){tft.setTextColor(C_WF,C_BG);tft.drawString("- Operator/Takeoff -",30,y);y+=16;sprintf(b,"%.7f, %.7f",d->opLat,d->opLon);tft.drawString(b,4,y);if(d->areaRadius>0){y+=16;tft.setTextColor(C_DM,C_BG);sprintf(b,"Radius:%dm",d->areaRadius);tft.drawString(b,4,y);}}
}else if(dCnt>1){
int sh=min(dCnt,3),ix[MXD];for(int i=0;i<dCnt;i++)ix[i]=i;
for(int i=0;i<dCnt-1;i++)for(int j=i+1;j<dCnt;j++)if(drones[ix[j]].lastSeen>drones[ix[i]].lastSeen){int t=ix[i];ix[i]=ix[j];ix[j]=t;}
for(int s=0;s<sh;s++){DroneInfo*d=&drones[ix[s]];int y=88+s*72;tft.fillRoundRect(4,y,232,68,4,C_DB);tft.setTextFont(2);
tft.setTextColor(C_HD,C_DB);if(d->hasId)sprintf(b,"%.17s",d->sn);else sprintf(b,"%s",d->mac);tft.drawString(b,10,y+4);
uint32_t ago=(millis()-d->lastSeen)/1000;const char*ss;uint16_t sc;switch(d->src){case 0:ss="BLE";sc=C_HD;break;case 1:ss="BL5";sc=C_HD;break;case 2:ss="WiFi";sc=C_WF;break;default:ss="NAN";sc=C_WF;}
sprintf(b,"%d %ds %s",d->rssi,(int)ago,ss);tft.setTextColor(sc,C_DB);tft.drawString(b,130,y+4);
sprintf(b,"%s %s",stStr(d->opStatus),uaStr(d->uaType));tft.setTextColor(d->opStatus==2?C_AL:C_OK,C_DB);tft.drawString(b,10,y+22);
if(d->hasLoc){sprintf(b,"%.4f,%.4f",d->lat,d->lon);tft.setTextColor(C_OK,C_DB);tft.drawString(b,10,y+40);sprintf(b,"%.0fm %.1fm/s",d->height,d->speed);tft.setTextColor(C_WN,C_DB);tft.drawString(b,148,y+40);}
else{tft.setTextColor(C_DM,C_DB);tft.drawString("No GPS",10,y+40);}
}
if(dCnt>3){sprintf(b,"+%d more",dCnt-3);tft.setTextColor(C_DM,C_BG);tft.setTextFont(2);tft.drawString(b,90,305);}
}
}
// --- Main ---
static uint32_t lHop=0,lDrw=0;
void setup(){Serial.begin(115200);delay(2000);Serial.println("\n=== RID Radar v5 ===");mtx=xSemaphoreCreateMutex();xl_init();lcd_rst();lcd_bl(true);tft.init();tft.setRotation(0);tft.fillScreen(C_BG);tft.setTextColor(C_OK,C_BG);tft.setTextFont(4);tft.drawString("RID RADAR v5",20,60);tft.setTextFont(2);tft.drawString("ASTM F3411 Full Decode",15,120);tft.drawString("Initializing...",60,150);wifi_init();ble_init();delay(2000);tft.fillScreen(C_BG);Serial.println("[OK] v5 running!");}
void loop(){uint32_t now=millis();if(now-lHop>2000){wifi_hop();lHop=now;}if(now-lDrw>1000){if(xSemaphoreTake(mtx,pdMS_TO_TICKS(50))==pdTRUE){for(int i=dCnt-1;i>=0;i--)if(now-drones[i].lastSeen>60000){for(int j=i;j<dCnt-1;j++)drones[j]=drones[j+1];dCnt--;}draw();xSemaphoreGive(mtx);}lDrw=now;}delay(10);}
[env:esp32-s3-devkitc-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
monitor_speed = 115200
; ATK-MWS3S = N16R8
board_build.arduino.memory_type = qio_opi
board_upload.flash_size = 16MB
board_build.partitions = default_16MB.csv
lib_deps =
bodmer/TFT_eSPI@^2.5.43
build_flags =
-DBOARD_HAS_PSRAM=1
; --- TFT_eSPI ---
-DUSER_SETUP_LOADED=1
-DST7789_DRIVER=1
-DTFT_WIDTH=240
-DTFT_HEIGHT=320
-DTFT_MOSI=11
-DTFT_SCLK=12
-DTFT_CS=21
-DTFT_DC=40
-DTFT_RST=-1
-DLOAD_GLCD=1
-DLOAD_FONT2=1
-DLOAD_FONT4=1
-DSPI_FREQUENCY=40000000
-DUSE_HSPI_PORT=1
; --- BLE 5.0 ---
-DCONFIG_BT_BLE_50_FEATURES_SUPPORTED=1
-DCONFIG_BT_BLE_42_FEATURES_SUPPORTED=1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment