├── README └── main /README: -------------------------------------------------------------------------------- 1 | Here is some sample Arduino code that registers over UDP with X-Plane (10 or 11) 2 | and receives the datarefs back at the requested frequency. Note that this is 3 | written specifically for the ESP8266 variant (WeMOS D1 and D1 Mini), and would 4 | need to be reworked to run on a Arduino Mega (or other variant) with an ethernet 5 | shield. (I would have ported it already, but I don't have an ethernet shield handy). 6 | 7 | The real beauty of this approach is that it requires no plugins or any other "middleware". 8 | 9 | If you have any questions about the code, just ask. I would be happy to explain any part. 10 | 11 | 12 | Notice two things that can be gotchas: 13 | 14 | 1. In subscribe() it seems that X-Plane will only honor your request if you pass a "complete" 15 | request packet, i.e. 400 bytes of request area, and it must also be initialized to spaces (0x20) 16 | in all the unused parts. If X-Plane is not happy with your request, it simply IGNORES it, it does 17 | not reply with any error message. 18 | 19 | 2. Alignment can be an issue. Obviously, for speed you want to load a complete int or float 20 | instead of doing four byte loads from the buffer, but to do this, the fields in the received buffer 21 | need to be on a four-byte aligned boundary. This means that you have to offset the udp read to 22 | make everything line up. 23 | 24 | Anyway, just some observations. I have this code running cleanly on a D1 over Wifi. It finds X-Plane, 25 | subscribes, and receives data refs. 26 | 27 | Dave 28 | 29 | 30 | -------------------------------------------------------------------------------- /main: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------- 2 | // Sample X-Plane UDP Communications for Arduino ESP8266 Variant 3 | // Copyright(c) 2019 by David Prue 4 | // 5 | // You may freely use this code or any derivitive for any 6 | // non-commercial purpose as long as the above copyright notice 7 | // is included. For commercial use information email me. 8 | //--------------------------------------------------------------- 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | // Change these two lines to match your Wifi SSID and Password 15 | 16 | String ssid = "MyWifi"; 17 | String pskey = "MyPassword"; 18 | 19 | 20 | WiFiUDP udp; 21 | 22 | IPAddress my_IP; 23 | IPAddress multicastIP (239,255,1,1); // Do not change this 24 | IPAddress xplane_ip; 25 | 26 | uint8_t beacon_major_version; 27 | uint8_t beacon_minor_version; 28 | uint32_t application_host_id; 29 | uint32_t versionNumber; 30 | uint32_t receive_port; 31 | uint32_t role; 32 | uint16_t port; 33 | char xp_hostname[32]; 34 | 35 | #define STATE_IDLE 0 36 | #define STATE_SEARCH 1 37 | #define STATE_READY 2 38 | 39 | char buffer[1024]; 40 | 41 | int state = STATE_IDLE; 42 | unsigned int my_port = 3017; // Could be anything, this just happens to be my favorite port number 43 | unsigned int xplane_port = 49000; // Don't change this 44 | unsigned int beacon_port = 49707; // or this... 45 | int retry = 30; 46 | 47 | void setup() { 48 | retry = 30; 49 | Serial.begin(115200); 50 | Serial.println("\nX-Plane UDP Interface 0.9\n"); 51 | 52 | WiFi.mode(WIFI_STA); 53 | WiFi.begin(ssid, pskey); 54 | 55 | Serial.print("Attempting Connection to Network: "); 56 | Serial.print(ssid); 57 | 58 | // Wait for connection 59 | while ((retry-- > 0) && (WiFi.status() != WL_CONNECTED)) { 60 | delay(500); 61 | Serial.print("."); 62 | } 63 | 64 | if (retry > 0) { 65 | my_IP = WiFi.localIP(); 66 | Serial.print("connected. My IP = "); 67 | Serial.println(my_IP); 68 | } 69 | else { 70 | Serial.println("Unable to connect"); 71 | return; 72 | } 73 | 74 | Serial.print("Searching for X-Plane"); 75 | retry = 30; 76 | udp.beginMulticast(my_IP, multicastIP, beacon_port); 77 | state = STATE_SEARCH; 78 | } 79 | 80 | void loop() { 81 | 82 | // If we were unable to connect to WiFi 83 | // try setting up again 84 | 85 | if (state == STATE_IDLE) { 86 | setup(); 87 | return; 88 | } 89 | 90 | // See if we have a UDP Packet 91 | int packetSize = udp.parsePacket(); 92 | 93 | if (!packetSize) { 94 | if (state == STATE_SEARCH) { 95 | Serial.print("."); 96 | delay(500); 97 | if (!retry--) { 98 | Serial.println("not found"); 99 | delay(1000); 100 | setup(); 101 | return; 102 | } 103 | } 104 | } 105 | else { 106 | switch(state) { 107 | case STATE_SEARCH: 108 | if (udp.destinationIP() == multicastIP) { 109 | char *buff = &buffer[1]; // For Alignment 110 | xplane_ip = udp.remoteIP(); 111 | udp.read(buff, packetSize); 112 | beacon_major_version = buff[5]; 113 | beacon_minor_version = buff[6]; 114 | application_host_id = *((int *) (buff+7)); 115 | versionNumber = *((int *) (buff+11)); 116 | receive_port = *((int *) (buff+15)); 117 | strcpy(xp_hostname, &buff[21]); 118 | 119 | String version = String(versionNumber/10000)+"."+String((versionNumber%10000)/100)+"r"+String(versionNumber%100); 120 | String heading = " Found Version "+version+" running on "+String(xp_hostname)+" at IP "; 121 | Serial.print(heading); 122 | Serial.println(udp.remoteIP()); 123 | 124 | state = STATE_READY; 125 | udp.begin(my_port); 126 | 127 | // Subscribe to whatever Data Refs you want here 128 | // The argumants are: subscribe(dataref, freq, unique) 129 | // The freq is how many times per second you want to get 130 | // the data, unique is simply a unique number 131 | // so that we can identify the data as it comes in. 132 | 133 | subscribe("sim/cockpit2/autopilot/airspeed_dial_kts", 1, 42); 134 | subscribe("sim/cockpit2/autopilot/heading_dial_deg_mag_pilot", 2, 43); 135 | 136 | } 137 | break; 138 | 139 | case STATE_READY: 140 | char *buff = &buffer[3]; // For alignment 141 | udp.read(buff, packetSize); 142 | String type = String(buff).substring(0,4); 143 | if (type == "RREF") { 144 | for (int offset = 5; offset < packetSize; offset += 8) { 145 | int code = *((int *) (buff+offset)); 146 | float value = *((float *) (buff+offset+4)); 147 | switch(code) { 148 | 149 | // Here you do whatevery you want to with the incoming data 150 | // for instance, write it to a 7 segment LED display 151 | // I am just outputting it to serial for demo purposes 152 | 153 | case 42: 154 | Serial.print("Airspeed Dial: "); 155 | Serial.println(String(value, 0)); 156 | break; 157 | 158 | case 43: 159 | String theValue = "000" + String(value,0); 160 | theValue = theValue.substring(theValue.length() - 3); 161 | Serial.println("Heading Dial: " + theValue); 162 | break; 163 | } 164 | } 165 | } 166 | } 167 | } 168 | } 169 | 170 | void subscribe(char *dref, uint32_t freq, uint32_t index) { 171 | struct { 172 | char dummy[3]; // For alignment 173 | char hdr[5] = "RREF"; 174 | uint32_t dref_freq; 175 | uint32_t dref_en; 176 | char dref_string[400]; 177 | } req __attribute__((packed)); 178 | 179 | req.dref_freq = freq; 180 | req.dref_en = index; 181 | for (int x = 0; x < sizeof(req.dref_string); x++) 182 | req.dref_string[x] = 0x20; 183 | strcpy((char *) req.dref_string, (char *) dref); 184 | 185 | udp.beginPacket(xplane_ip, xplane_port); 186 | udp.write(req.hdr, sizeof(req) - sizeof(req.dummy)); 187 | udp.endPacket(); 188 | Serial.print("Subscribed to dref \""); 189 | Serial.print(dref); 190 | Serial.println("\""); 191 | } 192 | --------------------------------------------------------------------------------