Radar Kompas Pelacak Teman is a friend-tracking simulation project that demonstrates how two devices can exchange location data and calculate relative positions. Built on the Raspberry Pi Pico, this project models the core logic for peer discovery, position sharing, and distance/bearing calculations using GPS coordinates.
This guide provides a complete firmware implementation that simulates two tracking units exchanging location updates over a virtual link. Makers will learn how to structure a peer-to-peer tracking system, handle GPS data validation, manage connection states, and compute compass bearings between two points. The project includes a parts list, wiring diagram for the Pico board, and step-by-step assembly instructions to get the simulation running on hardware.
Wiring diagram

Assemble it in 1 step
1. Gunakan board saja untuk simulasi firmware
Hubungkan board Waveshare RP2350-LCD-1.28 NON-TOUCH ke komputer dengan kabel USB-C data. Simulasi memakai USB saja, tidak mengakses layar atau GPIO.
- Jangan memasang baterai atau modul tambahan berdasarkan proyek simulasi ini; sambungan listriknya belum dirancang.
Deploy the firmware
#include <Arduino.h>
#include <math.h>
// Model perilaku saja. Tidak ada radio, NFC, kunci rahasia atau GPIO nyata.
struct Unit {
const char *id;
bool pending=false, approved=false, active=false, gps=true, hasPosition=false;
double lat, lon, peerLat=0, peerLon=0;
unsigned long deadline=0, measured=0;
};
Unit a{"SIM-A"}, b{"SIM-B"};
unsigned long nowSim=0;
bool linkUp=true;
void resetAll(){
a=Unit{"SIM-A"}; b=Unit{"SIM-B"};
a.lat=-6.2000; a.lon=106.8166; b.lat=-6.1990; b.lon=106.8176;
nowSim=0; linkUp=true;
}
void expire(){
if(a.deadline && nowSim>=a.deadline){a.deadline=0;a.pending=false;a.approved=false;}
if(b.deadline && nowSim>=b.deadline){b.deadline=0;b.pending=false;b.approved=false;}
}
void transmit(Unit &from, Unit &to){
if(!linkUp || !from.active || !to.active) return;
if(!from.gps){to.hasPosition=false;return;}
to.peerLat=from.lat;to.peerLon=from.lon;to.measured=nowSim;to.hasPosition=true;
}
void show(Unit &u){
Serial.print(u.id); Serial.print(u.active?" ACTIVE ":u.pending?" PENDING ":" NO_CONTACT ");
Serial.print(u.gps?"GPS_OK ":"GPS_INVALID ");
if(!u.hasPosition){Serial.println("NO_PEER_POSITION");return;}
unsigned long age=nowSim-u.measured;
Serial.print("age_s=");Serial.print(age/1000);
if(age>120000){Serial.println(" LOST/HIDDEN");return;}
if(!u.gps){Serial.println(" OWN_GPS_INVALID: distance unavailable");return;}
double p=u.lat*DEG_TO_RAD, q=u.peerLat*DEG_TO_RAD;
double dp=q-p, dl=(u.peerLon-u.lon)*DEG_TO_RAD;
double h=sin(dp/2)*sin(dp/2)+cos(p)*cos(q)*sin(dl/2)*sin(dl/2);
h=fmax(0.0,fmin(1.0,h));
double d=6371000.0*2*atan2(sqrt(h),sqrt(1-h));
double bearing=fmod(atan2(sin(dl)*cos(q),cos(p)*sin(q)-sin(p)*cos(q)*cos(dl))*RAD_TO_DEG+360,360);
Serial.print(" distance_m=");Serial.print(d,1);Serial.print(" bearing_deg=");Serial.print(bearing,1);
Serial.println(age>30000?" STALE":" FRESH");
}
void status(){expire();show(a);show(b);}
void command(char c){
switch(c){
case 'p': a.deadline=b.deadline=nowSim+60000; a.pending=b.pending=false;a.approved=b.approved=false;Serial.println("Virtual hold 5s: pairing window 60s");break;
case 'n': if(a.deadline && b.deadline){a.pending=b.pending=true;Serial.println("VIRTUAL NFC: SIM-A <-> SIM-B. Confirm both (a,b). NOT cryptographic pairing.");}break;
case 'a': if(a.pending)a.approved=true;break;
case 'b': if(b.pending)b.approved=true;break;
case 'x': a.deadline=b.deadline=0;a.pending=b.pending=false;a.approved=b.approved=false;break;
case 't': nowSim+=10000;break;
case 'g': b.gps=!b.gps;break;
case 'l': linkUp=!linkUp;break;
case 's': transmit(a,b);transmit(b,a);break;
case 'r': // simulated reboot: retain contact records, clear volatile state
a.pending=b.pending=false;a.approved=b.approved=false;a.deadline=b.deadline=0;a.hasPosition=b.hasPosition=false;break;
case 'd': a.active=b.active=false;a.hasPosition=b.hasPosition=false;break;
case '0': resetAll();break;
default: break;
}
expire();
if(a.pending && b.pending && a.approved && b.approved){
a.active=b.active=true;a.pending=b.pending=false;a.deadline=b.deadline=0;
Serial.println("SIMULATED ACTIVE: one contact per unit, no duplicate");
}
status();
}
void setup(){Serial.begin(115200);resetAll();}
void loop(){
static bool announced=false;
if(Serial && !announced){announced=true;Serial.println("SIMULATION ONLY | p=hold5s n=NFC a/b=confirm x=cancel t=+10s g=GPS-B l=link s=send r=virtual reboot d=delete 0=reset ?=status");status();}
while(Serial.available())command((char)Serial.read());
delay(5);
}
// GPIO audit: simulation uses USB Serial only; no built-in peripheral is driven.Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace, diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.




