Created
March 19, 2026 06:13
-
-
Save temberature/2814a034caf4030dd034b9e1e6a2e607 to your computer and use it in GitHub Desktop.
RID Radar v5.0 - ASTM F3411 Full Decode
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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);} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| [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