Wednesday, October 26, 2016

Arduino with FSX (Part 4)

Rudder Pedals and Brakes — 


Early in the FSX Flight Training, one exercise uses rudder steering and brakes to taxi around an airport. That requirement was the only motivation required to build a set of rudder pedals with toe brakes for use with FSX. To be honest, rudder and brakes have been rarely used in the VFR flight training course, but I have an interest in flying older military aircraft that do not have auto-pilot, auto-rudder, and many modern conveniences. It has taken a bit of work to make the rudders and brakes function as desired, mainly because I want pedals that have some resistance to being applied, that they re-center as pressure is reduced, and that the distance and angle of my feet and legs provide good ability to apply the rudders and brakes in a comfortable manner.

The prototype pedals followed the design of Bruce May (How To Build Rudder Pedals, 2006). After using those pedals a short while, I was unhappy with the lack of smoothness of the pedals sliding when pressure was applied by my feet. The pedals worked smoothly when moved by hand, but the pressure of my feet and legs made the prototype fail to work satisfactorily. On the other hand, the rubber balls under the toe brakes provided excellent press tension and return  when pressure was released.


I chose to use potentiometers as the rotational sensor devices. A modified version of the Arduino Uno 8-axis, 40-button Joystick discussed in Part 3 of this blog series was used as the sensor controller. Only 3 axes (rudder, left brake, right brake) were attached to pins A0, A1, and A2. The potentiometers were connected between +5V and ground with the wiper being connected to an analog pin. To reduce noise, all other axes were connected to ground. No digital pins were connected as buttons.

In FSUIPC, the 3 axes were calibrated and worked fairly well. The largest issues I experienced were discussed above--pedals not operating smoothly.

In the second iteration, rotary encoders were used in place of the potentiometers. That test failed to be satisfactory, since the hardware linkage connecting to the encoders did not make them rotate through sufficient detents (clicks) to register a satisfactory signal. While a 90 degree turn of a potentiometer makes a big change in its resistance value, the same 90 degree turn of a rotary encoder is just 5 clicks which was not sufficient to provide an adequate signal. To use rotary encoders would require re-design of the sensor linkage, such as, using a track and gear. I found gears that fit 6mm rotary encoder shafts fairly easily at a reasonable cost on eBay, and the matching track was not too expensive, but the shipping was like $50 for 12 lbs of track. I just need about 2 feet of track, not 12 lbs. I might have to try to fabricate some track.

I reviewed the design of rudder pedals in FlightSim Rudder Pedals. That design used small plastic rollers to provide smooth pedal movements. In addition, springs were used to provide tension and centering. Those seemed like really needed improvements.

I also reviewed the design of FlightSim "RogerioS" DIY Rudder pedals and toe brakes. That design included drawer slides to provide smooth pedal action. The slides look pretty heavy duty, but I suspect they provide good rolling action to handle the strong pressure from legs and feet.

Another concern I have with my prototype is that as the aluminum bar rotates due to leg/foot pressure, the whole bar is also under pressure to the rear from both legs pushing towards the rear, with some relief when pressure is reduced by one or both legs. The rotational axis of that bar must be under quite a bit of pressure where the center axis bolt is located.

I have also been reducing friction between various connections that rub as the pedals are moved, such as, the pins that move the potentiometers. Milled edges in aluminum tend to be rough and need to be polished to reduce friction. Aluminum against aluminum caused much friction, steel against aluminum reduced that friction, and the next improvement will test steel against steel which I believe will reduce friction even further. This may not be a revelation to people with metal working experience.

The third iteration re-installed the potentiometers, improved the strength of the overall hardware and their fittings, added some small plastic rollers to improve movement of the pedals under pressure, and added screen door springs to: 1) add tension against leg/foot pressure for a more realistic feel; and, 2) to help re-center the pedals to their neutral position as leg/foot pressure is reduced. Lastly, I added an upright shield that provides a foot rest for moving the pedals. The foot has to be rotated over that rest to apply the toe brakes but the motion feels satisfactory. These changes made big improvements in my satisfaction with the pedals and brakes. The pedal-brake may be re-designed in a future iteration.

Arduino 3-Axis Sketch for Pedals and Rudder --

"riclamer" modified Darran's Arduino Joystick code to output axis (A0-A5) and pin (D2-D13) data that are sensed by an Arduino Uno. Some users had problems with the code which turned out to be an array not being defined properly. That bug is fixed in the code below. While testing the code, I realized that the axis values (potentiometer readings) were extremely variable due to noise from the inexpensive devices. I have added some code to average 5 readings of each analog pin (potentiometer wiper) and use the average as the reading. I also compare the current reading to the past reading to determine if there is a change greater than 100. If so, the value gets used. Otherwise the value is ignored. If any one of the analog values (analog A0, A1, A2) varies by more than 100 from its previous value (or any D pin changes value), then the joyReport gets transmitted to FSX with the updated values. Using averaged values seems to have reduced the sending of widely fluctuating values to FSX while not causing delays to the controls. Ground any unused analog pin to minimize noise on the axes. The digital pins do not need to be grounded if they are not connected, since they are set to INPUT_PULLUP.


/*
   riclamer_big_joystik_mod

   Lowell Bahner
   October, 2016

   This sketch includes a fix for array dimension btn[] from 12 to 13.
   Riclamer's code is limited to use only 12 pins on the Uno (pins D2-D13)
   even though joyReport is dimensioned for 40 pins.
   Riclamer added an offset of 16 in the original code, which moves the
   pin number up by 16 in the total of 32 pins used by FSX. Offset has been
   reset to 0, since the Arduino Joystick is a separate device to FSX
   which provides an additional 32 pins (only 12 used in this sketch).

   The axis values were quite variable due to noisy signals of potentiometers.
   The axis values are now read 5 times and averaged to reduce variability
   of the pot values due to noise.
   A change of > 100 is also required from the previous value
   before a new value is transmitted.

   Both of these changes can be adjusted by the user.

   Only 3 Axes are programmed in for use with rudder pedals (1) and brakes (2).
   Digital pins are not used by the foot pedals.

   joyReport is only transmitted to FSX when there is a change of an axis or pin

*/


/* Arduino USB Joystick HID demo */

/* Author: Darran Hunt
   Released into the public domain.
   Update by RICLAMER in 25/03/2014 to use Analog ports and digital ports
   This code is to be used with Arduino UNO (6 axis and 12 Buttons)
   This code is compatible with Arduino Mega.
*/

/* INSTALLATION
  Just install POT to each analog port. Using the _Grnd _Analog _5V Arduino.
  Like this image: http://arduino.cc/en/uploads/Tutorial/joy_sch_480.jpg
  To setup the buttons, just install your prefered button switch between GND and Port Digital 02~13.
  Use Flip to erease and burn this firmware DFU: https://github.com/harlequin-tech/arduino-usb/blob/master/firmwares/Arduino-big-joystick.hex
  I used Arduino R3 with Atmega 16U2.
*/

// to turn on DEBUG, define DEBUG. Make sure to undef DEBUG for joystick use
//#undef DEBUG
#define DEBUG

#define NUM_BUTTONS 40     // you don't need to change this value
#define NUM_AXES 8        // 6 axes to UNO, and 8 to MEGA. If you are using UNO, don't need to change this value.

typedef struct joyReport_t {
  int16_t axis[NUM_AXES];
  uint8_t button[(NUM_BUTTONS + 7) / 8]; // 8 buttons per byte
} joyReport_t;

joyReport_t joyReport;
joyReport_t prevjoyReport;
uint8_t sendFlag;

uint8_t btn[13]; // fix riclamer's [12] dimension to [13]
// the array counts from 0 for 13 values, [0,1,2,...12]. 12 pins (D2-D13) are counted as 1,2,3...12.
int fulloff = 0;
uint8_t offset = 0;

void setup(void);
void loop(void);
void setButton(joyReport_t *joy, uint8_t button);
void clearButton(joyReport_t *joy, uint8_t button);
void sendJoyReport(joyReport_t *report);


void setup()
{
  //set pin to input Button
  for ( int portId = 02; portId < 13; portId ++ )
  {
    pinMode( portId, INPUT_PULLUP);
  }
  Serial.begin(115200);
  delay(200);
  sendFlag = 0; // clear one shot sendFlag

  for (uint8_t ind = 0; ind < 8; ind++) {
    joyReport.axis[ind] = ind * 1000;
  }
  for (uint8_t ind = 0; ind < sizeof(joyReport.button); ind++) {
    joyReport.button[ind] = 0;
  }
}

// Send an HID report to the USB interface
void sendJoyReport(struct joyReport_t *report)
{
#ifndef DEBUG
  //Serial.write((uint8_t *)report, sizeof(joyReport_t));
  // do not send duplicate values
  //
  if (memcmp( report, &prevjoyReport, sizeof( joyReport_t ) ) != 0)
  {
    Serial.write((uint8_t *)report, sizeof(joyReport_t));
    memcpy ( &prevjoyReport, report, sizeof( joyReport_t ) );
    sendFlag = 1; // set sendFlag=1 report was different
  } else {
    sendFlag = 0; // report did not change
  }
  //
  // end do not send duplicate values
#else
  // dump human readable output for debugging
  for (uint8_t ind = 0; ind < NUM_AXES; ind++) {
    Serial.print("axis[");
    Serial.print(ind);
    Serial.print("]= ");
    Serial.print(report->axis[ind]);
    Serial.print(" ");
  }
  Serial.println();
  for (uint8_t ind = 0; ind < NUM_BUTTONS / 8; ind++) {
    Serial.print("button[");
    Serial.print(ind);
    Serial.print("]= ");
    Serial.print(report->button[ind], HEX);
    Serial.print(" ");
  }
  Serial.println();
#endif
}

// turn a button on
void setButton(joyReport_t *joy, uint8_t button)
{
  uint8_t index = button / 8;
  uint8_t bit = button - 8 * index;

  joy->button[index] |= 1 << bit;
}

// turn a button off
void clearButton(joyReport_t *joy, uint8_t button)
{
  uint8_t index = button / 8;
  uint8_t bit = button - 8 * index;

  joy->button[index] &= ~(1 << bit);
}

/*
   Read Digital port for Button
   Read Analog port for axis
*/
void loop()
{
  // Create Array with value of switch button
  for (int bt = 1; bt < 13; bt ++)
  {
    btn[bt] = digitalRead(bt + 1); // reads pins D2-D13
  }
  // Power ON button if it is pressed. buton 17 at 28
  for (int on = 1; on < 13; on++)
  {
    if (btn[on] == LOW)
    {
      setButton(&joyReport, on + offset - 1);
      //  Serial.println("botao ON");
      //  Serial.println(on+16);
    }
    //else {
    //  fulloff = fulloff++;
    //}   //Power off all Button, if all button are not press
    //if (fulloff == 11)
    //{
    //  for (uint8_t cont = 0; cont < 40; cont++)
    //  {
    //    clearButton(&joyReport, cont);
    //  }
    //  Serial.println("Every button is off");
    //}
  }

  uint8_t diffCopy = 0;

  /* Adjust Analog axes on A0, A1, A2*/
  /* Arduino UNO has 6 analog pins of 8 possible. Set pin to 0 if not used */
  for (uint8_t axis = 0; axis < 3; axis++) {
    int tmp = joyReport.axis[axis]; // copy previous axis value
    // Average 5 readings of port to get better values from noisy pots
    int sumAxis = 0;
    int avg = 0;
    for (int i = 0; i < 5; i++) {
      sumAxis = sumAxis + analogRead(axis);
    }
    avg = sumAxis / 5;
    joyReport.axis[axis] = map(avg, 0, 1023, -32768, 32767 );
    //joyReport.axis[axis] = map(analogRead(axis), 0, 1023, -32768, 32767 );

    // only change axis if avg reading changes by > 100
    // flag change in axis value > 100
    if (abs(joyReport.axis[axis] - tmp) > 100) diffCopy = 1;
  }
  //Set un-used analog pins to 0 to reduce spurious values in joyReport.
  //joyReport.axis[0] = 0;
  //joyReport.axis[1] = 0;
  //joyReport.axis[2] = 0;
  joyReport.axis[3] = 0;
  joyReport.axis[4] = 0;
  joyReport.axis[5] = 0;
  joyReport.axis[6] = 0;
  joyReport.axis[7] = 0;

  if (diffCopy > 0) {
    //Send Data to HID
    sendJoyReport(&joyReport);
    diffCopy = 0;
    
    // clear the digital pins from re-sending data
    for (uint8_t cont = 0; cont < 40; cont++)
    {
      clearButton(&joyReport, cont);
    }
  }

  delay(100);
  fulloff = 0;
}



Useful references -

(Oct 26, 2016)

Friday, October 21, 2016

Arduino with FSX (Part 3)

Arduino as a HID Joystick — 
To a new user this will be confusing--USB, HID, joysticks, hardware ports, and USB drivers. We all know that USB just works--you plug it in and by magic it works. Well, that is true if there is a USB driver on the PC which works with the device at the other end of the USB cable. The following discussion is about making the Arduino look like a USB device (not a vanilla Arduino serial device) and getting it to work on a USB port with a USB driver on the PC. 
A vanilla Arduino Uno or Mega uses the on-board USB chip (ATmega 16U2) for communicating through a USB cable with the Arduino IDE on a Mac/PC/Unix computer. The Arduino IDE communicates with the Arduino through Serial-USB and is used to download programs to the Arduino, and for sending and receiving data between the computer and the Arduino.
On a PC, a driver is required to connect an external USB device to a particular software. Windows uses different HID-USB drivers to connect to keyboards, mice, game controls, or joysticks  (HID, Human Input Device). FSX can accept data from a HID-USB joystick connected to a USB port using the Microsoft Game Controller driver in Windows. Some companies provide their own USB drivers to interface to their hardware, rather than relying on the MS drivers included with Windows. 
The MS Game Controller driver provided with Windows allows the PC to communicate through a USB port to external joystick hardware and obtain data from potentiometers for adjusting rotating axes (joystick up-down, left-right, back-and-forth) or from buttons and switches. In FSX, there is a limit of 32 buttons/switches per USB device. Those who are building large and complex aircraft cockpits run into this limitation of 32 buttons/switches pretty quickly. A future blog will examine alternatives so that additional buttons/switches might be accommodated.
As previously stated, the ATmega 16U2 USB chip on an Arduino Uno or Mega is normally programmed as a Serial-USB device. On the PC, a serial-USB driver is used to connect the Arduino through USB to the Arduino IDE. To use an Arduino as an FSX device, that USB chip has to be reprogrammed as a HID-USB device that can communicate through a USB port using the MS Game Controller driver. FSX can then accept data from the external Arduino hardware through USB to FSX running on the PC.
I run FSX on a dedicated PC, but I do my software development work primarily on a Mac. In my case, all of the process of writing and testing Arduino sketches, and programming the Arduino USB chip is conducted on the Mac. The Arduino (as a joystick controller) is then plugged into the PC USB for use with FSX.
Why not just use MobiFlight as the controller for joysticks that use rotational controls (potentiometers)? As of 2016, MobiFlight software did not connect to potentiometers. There have been some requests by users with the MobiFlight developer about adding that functionality. At this time, lacking MobiFlight as an option, adapting an Arduino to act as a joystick device is a workable, but somewhat tedious, solution.
The process of creating an Arduino Joystick— 
Several things are needed to implement an Arduino joystick:
  1. Arduino R3 Uno or Mega with ATmega 16U2 USB chip (not a CH340 USB chip).
  2. Arduino sketch that converts potentiometer values and button/switch signals to a defined string of data that is transmitted through USB to the PC.
  3. Software and/or hardware for reprogramming the 16U2 USB chip.
  4. The Arduino USB chip reprogrammed as a HID-USB device that can send and receive formatted data strings to/from a PC. The PC USB driver adjusts to that USB data definition and delivers the string of data to FSX.
  5. Patience to deal with technical issues.
The simplest approach for using an Arduino as a joystick, is to use one Arduino as one joystick controller, each with a unique ID. FSX can connect to each Arduino joystick as a separate device with axis controls and/or 32 buttons/switches. Each Arduino would connect through a USB cable. The 8-axes 40-button joystick described below, is such a device. Since the Uno does not have 32 digital pins, a Mega 2560 with 54 digital and 16 analog pins can to be used. For development and testing, an Uno with 14 digital and 6 analog pins can be used with the understanding that the number of functional pins will be limited. Additional pins can be added to an Arduino, either by using a matrix or port expanders. Adding port expanders will be discussed in future blogs.

The 8-Axis 40-Button Arduino Joystick— 
The original code for a 40-button Arduino joystick was published on the web by Darran (ref: http::hunt.net.nz/users/darran/weblog/15f92/Arduino_UNO_Big_Joystick_HID_firmware.html, Jan 22, 2013), however that web page was discontinued during 2016. A copy of that webpage was found at (ref: archive.is/KbXom). Darran's web page is a must read since he clearly outlined the steps for creating an Arduino HID-USB joystick with 8 axes and 40 buttons. That software will form the basis for many of the discussions in this blog series. 
Joystick Process Steps:
1) Load your sketch into Arduino using the standard USB cable and the Arduino 16U2 chip programmed as Serial-USB. (the hex files for Uno and Mega can be downloaded from https::github.com/arduino/Arduino/tree/master/hardware/arduino/avr/firmwares/atmegaxxu2/arduino-usbserial. No need to "make" any files. )
2) Put the R3 Uno or Mega into DFU mode. This simply requires momentarily shorting two pins of the USB-ICSP nearest the USB jack with a metal wire.
3) Flash (program) the Arduino USB 16U2 chip as HID-USB using Darran's "Arduino-big-joystick.hex".
4) Attach Arduino Joystick to PC USB.
Copies of Darran's sketch and USB .hex firmware are online at github.com/harlequin-tech.
-- An Example Sketch
The example sketch "big_joystick_demo.ino" provided by Darran is a bit confusing at first. To see the code work, change the "#undef DEBUG" to "#define DEBUG" so the debugging Print.serial statements will display on the Serial Monitor. That will demonstrate that the code does in fact work. The code loops to sequentially turn on and off one button after another, so you might wonder "How can I use this with FSX? Hold that thought for a while, while you perfect the other steps required for making an Arduino joystick work. Remember to "#undef DEBUG" before flashing the USB chip, since the data sent through USB can not be corrupted with the Serial.print debug data that can be viewed on Serial Monitor.
-- Put the Arduino into DFU mode and Flash the chip
The 16U2 chip is placed in dfu mode by shorting two pins of the USB-ICSP. On a PC, use "FLIP" to program the 16U2 chip. On a Mac, the dfu software needs to be downloaded and installed. Then Terminal app is used for entering the dfu commands for flashing the 16U2 chip. Flashing the 16U2 chip is an easy process once you do it a couple of times. See ref: www.instructables.com/id/DFU-programmer-on-Mac-OS-X/ for the steps for using dfu. FLIP or dfu will be used to flash the desired .hex file into the 16U2 chip.
-- Test the configuration to make sure axes and buttons are working as desired
At the hardware/firmware level you can check the USB port.


  • On a Mac, System Information:USB can be used to see what USB devices are attached. An Arduino (Serial-USB) will display as "Communication Device":Product ID: 0x0043, etc. If the Arduino has been put into dfu mode, the device will display as "Composite Device":Product ID: 0x2fef, etc. Once the HID-USB hex file is flashed, the device will display as "Arduino Joystick":   Product ID: 0x2043, etc.


  • With "Arduino Joystick" connected to PC USB port: Open "Devices", locate the MS Game Controller icon that represents the "Arduino Joystick", right click the icon and choose "Game controller settings". Choose "Arduino Joystick" from list, and click Properties. The Properties window will display with the 6 Axes (horizontal bars) and 32 buttons. The axes will move in small increments. The 32 buttons will cycle on and off.
If these tests check out, the HID-USB Arduino Joystick has been successfully installed.

Other tests that can be conducted to determine what the USB configuration looks like to the PC include:



(Oct 21, 2016)



Monday, October 17, 2016

Arduino with FSX (Part 2)

Trim and Flaps Control — 

Trim and flaps are basic to flying any aircraft. I found trying to adjust trim and flaps using the mouse or keyboard keys while flying with a stick was very difficult and unnatural. I wanted to adjust trim using trim 
wheels and the flaps to be controlled by moving a lever up and down to provide the various flap positions required for flying.


The options that I found on the internet were: 1) commercial control boxes; or, 2) various plans for DIY constructed control boxes. Since I like to build things, I chose the latter option. The next question was how would it work? 


I found that most users referenced and relied on a software product, FSUIPC, and that it was an essential software tool. 

I found another software product, MobiFlight, which used an Arduino Mega 2560 with the MobiFlight software through USB to interact with FSX via FSUIPC. This seemed like an attractive approach since it used an Arduino as the controller computer, it had a user-friendly interface, and it connected to the PC running FSX through USB.


The first road block I found was that MobiFlight did not connect to potentiometers, but did connect to rotary encoders. I had to learn about rotary encoders.


I ordered KY040 rotary encoders from eBay, and while I was waiting for them to arrive, I watched the Mobiflight you-tube training about constructing and testing hardware for FSX. That seemed like a solid approach. When the KY040 rotary encoders arrived, I successfully tested them with Mobiflight to send trim signals to FSX. Once that testing was completed, I ordered Arduino Mega 2560 boards from eBay. The first two boards I received were not Arduino boards even though the description on eBay stated they were R3 Mega 2560 boards. Instead they used CH340 USB chips and the clock ran on a 12 MHz crystal, rather than 16 MHz. It turned out that MobiFlight ran fine on these knock-off boards, but I decided to place another order for R3 Mega 2560 boards from an eBay vendor that documented their boards used the proper USB chip. The critical difference in these boards is the USB controller chip. The CH340 USB chip in the knock-off board is a dedicated serial-USB chip which works for most vanilla Arduino applications. The Arduino Mega uses the ATmega 16U2 USB chip which can be reprogrammed to be serial-USB or HID-USB. HID-USB will be discussed in later blogs about building joyStick devices.

 


The Arduino Uno and Mega 2560 use the ATmega 16U2 USB chip (left picture) which can communicate through, and be programmed through the USB-ICSP pins. A clone board that uses a CH340 Serial-USB chip (right photo, 16-pin IC) does not have the USB-ICSP pins. One other thing, while discussing eBay Arduino boards--some knock-off boards use chips that are not pre-programmed. You may have to program the USB chip or the main processor chip to make the board work as an Arduino. It can be distressing when you get your new board, plug it in, and it just does not work. It takes some time to figure out if there is a hardware or software problem. And it takes more time to fix the problem, if it can be fixed.

Working Trim-Flaps-Gear Controller Prototype --


I fly using a stick in my right hand. I wanted to control trim, flaps, gear, hook, and speed brake with my left hand, as used in some Navy aircraft. The picture shows how I assembled these functions into one box that can be mounted for left hand use. The Trim, Flaps, and Gear Control Box continues to be a work in progress, but it has become fairly mature. The Control Box, with Arduino Mega 2560 and MobiFlight, now provides:
  • Elevator Trim Wheel using KY040
  • Aileron Trim Wheel using KY040
  • Rudder Trim Wheel using KY040
  • Flaps Control using 4 position rotary switch
  • Gear Control using 2 position rotary switch
  • Speed Brake using DPDT toggle switch
  • Tail Hook using DPDT toggle switch
  • 4 LED's for flap position indicators
  • 3 LED's for gear position indicators
  • 1 LED for speed brake position indicator
  • 1 LED for tail hook position indicator
  • 8-digit readouts for elevator trim count and % elevator
  • 8-digit readouts for aileron trim count and % aileron
  • 8-digit readouts for rudder trim count and % rudder
My first build focused on the Trim wheels using rotary encoders. Flap and gear switches and LED's were then added and tested. After struggling with trim during flights, I added the out-board displays so I could better understand how trim was affecting the flight simulations. Initially I had a few problems when controls did not respond quickly while flying simulations, such as, flaps down on approach. This problem was fixed by turning off debug logging (both in FSX and MobiFlight) which interfered with control actions being taken in a timely manner.
MobiFlight uses FSUIPC offset addresses for many actions. Therefore, purchase a license for FSUIPC, so that you will have that tool available for use in all your controllers.

TODO - 

1) Figure out how to check the position of a switch and reset FSX to use that switch setting, in case the initial switch pulse was missed due to the processor being busy and missing the switch.

2) MobiFlight Control Mapping - make a table of the control, the Mobi device, the FSX control, the control mode (keypress, FSUIPC offset, FSX direct), size (byte, word, int4, int8), value (0,1, 16384, etc), transforms, pre-conditions.

(Oct 17, 2016)


    Thursday, October 13, 2016

    Arduino with FSX (Part 1)

    Short History — 

    FSX is flight simulator software that continues to have a large following of professional and non-professional pilots, and gamers who want realistic flying experiences. FS was marketed by Microsoft beginning in 1982 as Flight Simulator 1.0, and progressed through multiple revisions as FS2000, FS2002, FS2004 (aka FS9), and FSX in 2006. Microsoft closed down further work on FS in 2009. Dovetail Games obtained the rights to FS in 2014 and develop the Microsoft Flight Simulator X: Steam Edition. (ref: https://en.wikipedia.org/wiki/Microsoft_Flight_Simulator_X)


    FS has progressed from 8-bit and 16-bit DOS and Windows machines using only a mouse, keyboard, or game controller for flying the simulator missions, to 64-bit PC's with multiple flat-screen views and full-scale 737 cockpits with all the buttons, switches, and instruments.


    This blog shares my experience as a beginner FSX simulator pilot that has added hardware control devices with knobs, buttons, switches, and displays, as flight sticks, rudder pedals, trim controls, and digital readouts. 


    While several sources of hardware and software add-ons for FSX exist, there are a large number of FSX users that build their own hardware and adapt software to use their simulator cockpit hardware with FSX.


    This blog primarily centers on Microsoft FSX Gold Edition as the flight simulation software with add-ons that will be described in later parts of this series of blogs. For these blogs, my FSX runs in a Windows 8.1 Bootcamp partition on a Mac Mini (2012, 16 Gb RAM, 64-bit). Multiple USB 2.0 and 3.0 port expanders are used for connecting hardware to the PC. Two flat-screen displays are used for various FSX windows.


    Future blogs in this series will discuss the Arduino microprocessor as a USB device for adding switches, digital displays, rotary encoders, and port expanders to FSX. These switches and displays then connect through USB to the PC for sending and receiving data that ultimately controls or logs some action of the airplane during the FSX flight simulation.


    The development work discussed in this blog builds on work done by so many and shared on the web through many web sites and discussion forums. Thanks to all of you who share your ideas and software so that others can benefit.


    Arduino and FSX


    I have been tinkering with Arduino's for a few years as a hobby, and when I began using FSX Flight Simulator, I had several thoughts: 1) I can not fly FSX using a keyboard and mouse. That just did not work for me; so, 2) I purchased a Saitek X-52 flight stick which works pretty well, but the button mappings to FSX was a new area of confusion. The FSX software could be used to map the buttons and axes, and the Saitek software could also map the buttons and axes. Then there were drivers from other software companies that seemed to replace the Saitek drivers. Each one had their own interface and means of adjusting and tuning the various controls. All of this may seem pretty simple to someone who has some FSX experience, but to a new user, the goal was to learn to fly the flight school lessons successfully, and not be mired down trying to figure out how to make a button work.

    I had some military flight training many years ago, and I can remember what that felt like. One goal for this flight simulator training is to regain the fun of flying, which translates into hands-on hardware--trim wheels, flight stick, gear controls, rudder pedals, brakes, and at some point, guns, rockets, and bombs (simulated that is).

    It seemed to me that there had to be a way to use the Arduino as a controller for hands-on hardware used with FSX. It took a while to sort through all the scattered bits and pieces on the internet, but several people have succeeded. The work presented here is based on those previous works, of which I am grateful.

    Through this series of blogs, I will try to assemble those various bits and pieces of knowledge that I have gained into one source which may be of use to someone that desires to follow this path. 

    I ask that anyone that has implemented Arduino's with FSX and want to share that information, provide their comments and links.

    (Oct 13, 2016)


    Monday, May 2, 2016

    YellowJacket Arduino with WiShield Wifi (Part 22)

    UDP Revisited — 
    Quoting : “Network Basics: TCP/UDP Socket and Port Overview”, By Edward Tetz from Cisco Networking All-in-One For Dummies (Ref: http://www.dummies.com/how-to/content/network-basics-tcpudp-socket-and-port-overview.html)
    To manage the connection between application layer network protocols, TCP and UDP use ports and sockets. TCP and UDP operate at the host-to-host layer in the IP communication model and provide host-to-host communication services for the application layer protocol. This means an application layer protocol is on one IP host connecting to an application layer protocol on another IP host.
    In most situations, these host-to-host connections have a server process running on one host and a client process running on the other host. Examples of this host-to-host connection include a web browser connecting to a web server; or a Secure Copy Protocol (SCP) client (such as WinSCP) connecting to an SCP server.
    A port is a TCP or UDP connection point. Think of them as receptacles on an old-fashioned telephone switchboard. There are 65,536 (or 2^16) ports available for a host to manage connections, numbered from 0 to 65,535 for each TCP and UDP. When you establish an application server running on an IP host, you configure that server to be used (or bound to) a specific TCP or UDP port.
    By associating the application layer server to use a specific port, you have created a destination that a remote IP host can connect to.
    When the remote IP host connects to an application layer server, the connection the host makes is to a port operating on a specific IP host (identified by an IP address). This pairing of an IP address and a port as a connection endpoint is a socket.
    In that old-fashioned switchboard analogy, the socket has two connectors connected to each client’s phone: one is a receptacle, and the other is a plug. Think of these connectors as the ports, but because the port is associated with a phone, together they make a socket, such as the TCP or UDP port, when paired with an IP address is a socket.
    To make a phone connection for a client, the “operator” takes the plug for one client and connects it to the socket for the other client. With IP, the client application has a port that it operates on, so on the client host, there is an IP address and port for the client side of the connection; this is a socket.
    On the server side of the connection is an IP address for the server and a port to make a socket on the server host. To establish a connection between the client application layer and the server application layer is a virtual connection between these two sockets.
    This example will walk you thru the process of connecting to a website, such as Wiley. You would open your web browser (like Mozilla Firefox) and type www.wiley.com into the address bar. Your web browser uses a Domain Name System (DNS) server to look up the name www.wiley.com to identify its IP address is. For this example, the address is 192.0.2.100.
    Firefox makes a connection to the 192.0.2.100 address and to the port where the application layer web server is operating. Firefox knows what port to expect because it is a well-known port . The well-known port for a web server is TCP port 80.
    The destination socket that Firefox attempts to connect is written as socket:port, or in this example, 192.0.2.100:80. This is the server side of the connect, but the server needs to know where to send the web page you want to view in Mozilla Firefox, so you have a socket for the client side of the connection also.
    The client side connection is made up of your IP address, such as 192.168.1.25, and a randomly chosen dynamic port number. The socket associated with Firefox looks like 192.168.1.25:49175. Because web servers operate on TCP port 80, both of these sockets are TCP sockets, whereas if you were connecting to a server operating on a UDP port, both the server and client sockets would be UDP sockets.
    Create UDP Server on Arduino —
    In the previous UDP examples in this series, the UDP server was either a Processing App or python app running an a Mac which broadcasted a “Hello” string on the server ip:port, 10.0.1.64:6000 or 10.0.1.64:3333, and the client YJ57 UDP ip:port was set to 10.0.1.57:1023.
    The purpose of this example is to set up the UDP server on a YellowJacket Arduino and send the UDP packets over wifi to a YellowJacket Arduino client. In this “unicast” example, both the server and client ip:ports are known in advance and are coded into the sketch. 
    In this example, the server will operate on the YJ56 rather than using Processing or python on a Mac. The YJ56 uses the ip:port 10.0.1.56:3333 to send a packet over wifi to a UDP client listening on ip:port 10.0.1.57:3333.
    /*
     * UDPAppWifiSendString_V14_01
     * 2016-04-25
     * 84Park
     *
     * This sketch sends a UDP Unicast packet on server_port using a YJ WiShield.
     *
     * Compiled with WiShield_V14 library (ref: github.com/hamityanik/WiShield_user_contrib) and Arduino 1.6.5
     *  This code works 2016-04-25 with YJ56 board
     *  Pre-calculated security_data[] inits in 6 seconds
     *
     * Example Output to Serial Monitor
     * 
     * === Initializing WiShield UDP Server over Wifi ===
     * 
     * Local server ip:10,0,1,56
     * Local server port:3333
     * Remote client ip:10,0,1,57
     * Remote client port:3333
     * === Serving Data over Wifi ===
     * 
     * UDP Send: Hello from UDP Server
     * 
     * UDP Send: Hello from UDP Server
     *
     *
    // Requires APP_SOCKAPP and APP_UDPAPP to be defined in apps-conf.h
    //  APP_SOCKAPP  - for the TCP sockets components of the sketch
    //  APP_UDPAPP   - for the UDP/DNS components of the sketch
     *
     * Adapted from:
     * UDPApp
     * This sketch sends a UDP packet on server_port using a WiFi shield.
     *
     */
    
    #include <WiShield.h>
    extern "C" {
    #include "uip.h"
    }
    
    // Wireless configuration parameters ----------------------------------------
    unsigned char local_ip[]    = {10, 0, 1, 56};   // IP address of WiShield
    unsigned char gateway_ip[]  = {10, 0, 1, 1};   // router or gateway IP address
    unsigned char subnet_mask[] = {255, 255, 255, 0}; // subnet mask for the local network
    u16_t dns_ip[]              = {10, 0, 1, 1};   // DNS server addr
    char ssid[]                 = {"Network"};   // max 32 bytes
    unsigned char security_type = 5;  // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2; 4 - WPA Precalc; 5 - WPA2 Precalc
    
    // Depending on your security_type, uncomment the appropriate type of security_data
    // 0 - None (open)
    // const char security_data[] PROGMEM = {};
    
    // 1 - WEP
    // UIP_WEP_KEY_LEN. 5 bytes for 64-bit key, 13 bytes for 128-bit key
    // Only supply the appropriate key, do not specify 4 keys and then try to specify which to use
    //const char security_data[] PROGMEM = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, };
    
    // 2, 3 - WPA/WPA2 Passphrase
    // 8 to 63 characters which will be used to generate the 32 byte calculated key
    // Expect the g2100 to take 30 seconds to calculate the key from a passphrase
    // const char security_data[] PROGMEM = {"Network_Passcode"};
    
    // 4, 5 - WPA/WPA2 Precalc
    // The 32 byte precalculate WPA/WPA2 key. This can be calculated in advance to save boot time
    //const prog_char security_data[] PROGMEM = {
    //    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    //    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f, 0x1f,
    //};
    // Calc the Precalc:
    // http://jorisvr.nl/wpapsk.html
    // The derived key will appear in the form as a sequence of 64 hexadecimal digits.
    /*0599967ce21000026c2cf439ea561c3d37abbf48085738cbe9b9a9ca093a6adb*/
    const char security_data[] PROGMEM = {
      0x05, 0x99, 0x96, 0x7c, 0xe2, 0x10, 0x00, 0x02, 0x6c, 0x2c, 0xf4, 0x39, 0xea, 0x56, 0x1c, 0x3d,
      0x37, 0xab, 0xbf, 0x48, 0x08, 0x57, 0x38, 0xcb, 0xe9, 0xb9, 0xa9, 0xca, 0x09, 0x3a, 0x6a, 0xdb,
    };
    
    // setup the wireless mode; infrastructure - connect to AP; adhoc - connect to another WiFi device
    //#define WIRELESS_MODE_INFRA  1
    //#define WIRELESS_MODE_ADHOC 2
    unsigned char wireless_mode = WIRELESS_MODE_INFRA;
    // End of wireless configuration parameters ----------------------------------------
    
    uint16_t server_ip[]    = {10, 0, 1, 56};   // IP address of this server
    uint16_t server_port = 3333;       // server port to send from
    uint16_t dest_ip[]    = {10, 0, 1, 57};   // IP address of client
    uint16_t dest_port = 3333;       // destination client port to send to
    
    //char  packetBuffer[255];      //buffer to hold outgoing packet
    //char  ReplyBuffer[] = "acknowledged";       // a string to send back
    boolean connectAndSendUDP = true; // false;
    uip_ipaddr_t srvaddr;
    
    //---------------------------------------------------------------------------
    void setup()
    {
      //Initialize serial and wait for port to open:
      Serial.begin(115200);
    
      Serial.println ("\n=== Initializing WiShield UDP Server over Wifi ===");
    
      Serial.print("\nLocal server ip:");
      Serial.print(server_ip[0]); Serial.print(",");
      Serial.print (server_ip[1]); Serial.print(",");
      Serial.print (server_ip[2]); Serial.print(",");
      Serial.println (server_ip[3]);
    
      Serial.print("Local server port:");
      Serial.print(server_port);
    
      Serial.print("\nRemote client ip:");
      Serial.print(dest_ip[0]); Serial.print(",");
      Serial.print (dest_ip[1]); Serial.print(",");
      Serial.print (dest_ip[2]); Serial.print(",");
      Serial.println (dest_ip[3]);
    
      Serial.print("Remote client port:");
      Serial.print(dest_port);
    
     WiFi.init();
    
        Serial.println ("\n=== Serving Data over Wifi ===\n");
    
    }
    
    void loop()
    {
     WiFi.run();
    }
    
    
    extern "C"
    {
      // These uIP callbacks are unused for the purposes of this simple DNS example
      // but they must exist.
      void socket_app_init(void)
      {
      }
    
      void socket_app_appcall(void)
      {
      }
    
        /* c code trace */
      void printx(char* data)
      {
        Serial.print(data);
      }  
      
      /* c code trace */
      void printlnx(char* data)
      {
        Serial.println(data);
      }
      
    }
    
    
    The code which sends the packet to the client is in the local version of udpapp.c. The function “static void send_response(void)” was modified to send the packet “Hello from UDP Server\n”, and the function “static PT_THREAD(handle_connection(void))” was modified to just send rather than listen, receive, and respond to client requests.
    /******************************************************************************
    
      Filename: udpapp.c server for UDPAppWifiSendString_V14_01.ino
      Description: UDP app for the WiShield 1.0
    
     ******************************************************************************
    
      TCP/IP stack and driver for the WiShield 1.0 wireless devices
    
      Copyright(c) 2009 Async Labs Inc. All rights reserved.
    
      This program is free software; you can redistribute it and/or modify it
      under the terms of version 2 of the GNU General Public License as
      published by the Free Software Foundation.
    
      This program is distributed in the hope that it will be useful, but WITHOUT
      ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      more details.
    
      You should have received a copy of the GNU General Public License along with
      this program; if not, write to the Free Software Foundation, Inc., 59
      Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    
      Contact Information:
      <asynclabs@asynclabs.com>
    
       Author               Date        Comment
      ---------------------------------------------------------------
       AsyncLabs   07/11/2009 Initial version
    
     *****************************************************************************/
    
    #include "uip.h"
    #include <string.h>
    #include "udpapp.h"
    #include "config_udp.h"
    
    #define STATE_INIT       0
    #define STATE_LISTENING       1
    #define STATE_HELLO_RECEIVED  2
    #define STATE_NAME_RECEIVED   3
    
    static struct udpapp_state s;
    
    void dummy_app_appcall(void)
    {
    }
    
    void udpapp_init(void)
    {
     uip_ipaddr_t addr;
     struct uip_udp_conn *c;
    
                //uip_ipaddr(&addr, 10,0,1,57);
            uip_ipaddr(&addr, dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3]);
      
            c = uip_udp_new(&addr, HTONS(server_port));
                //c = uip_udp_new(&addr, HTONS(3333));
     if(c != NULL) {
            uip_udp_bind(c, HTONS(dest_port)); // or, bind to a specified port
                //uip_udp_bind(c, HTONS(3333)); // or, bind to a specified port
    
     }
    
     s.state = STATE_INIT;
    
     PT_INIT(&s.pt);
    }
    
    static unsigned char parse_msg(void)
    {
     if (memcmp(uip_appdata, "Hello", 5) == 0) {
      return 1;
     }
    
     return 0;
    }
    
    static void send_request(void)
    {
     char str[] = "Hello. What is your name?\n";
    
     memcpy(uip_appdata, str, strlen(str));
     uip_send(uip_appdata, strlen(str));
    }
    
    static void send_response(void)
    {
     char i = 0;
     char str[] = "Hello from UDP Server\n";
    
            // send the data "str[]"
            memcpy(uip_appdata, str, strlen(str));
            uip_send(uip_appdata, strlen(str));
            printx("UDP Send: "); printx(str); printlnx();
    /*
     while ( ( ((char*)uip_appdata)[i] != '\n') && i < 9) {
      s.inputbuf[i] = ((char*)uip_appdata)[i];
      i++;
     }
     s.inputbuf[i] = '\n';
    
     memcpy(uip_appdata, str, 6);
     memcpy(uip_appdata+6, s.inputbuf, i+1);
     uip_send(uip_appdata, i+7);
    */
    
    }
    
    static PT_THREAD(handle_connection(void))
    {
     PT_BEGIN(&s.pt);
    
     //s.state = STATE_NAME_SEND;
            s.state = STATE_HELLO_RECEIVED;
    
    /*
     do {
      PT_WAIT_UNTIL(&s.pt, uip_newdata());
    
      if(uip_newdata() && parse_msg()) {
       s.state = STATE_HELLO_RECEIVED;
       uip_flags &= (~UIP_NEWDATA);
       break;
      }
     } while(s.state != STATE_HELLO_RECEIVED);
    
     do {
      send_request();
      PT_WAIT_UNTIL(&s.pt, uip_newdata());
    
      if(uip_newdata()) {
       s.state = STATE_NAME_RECEIVED;
       uip_flags &= (~UIP_NEWDATA);
       break;
      }
     } while(s.state != STATE_NAME_RECEIVED);
    */
     send_response();
    
     s.state = STATE_INIT;
    
     PT_END(&s.pt);
    }
    
    void udpapp_appcall(void)
    {
     handle_connection();
    }
    
    And the YJ57 UDP client sketch which receives the packet from YJ56 is as follows:
    /*
     * UDPAppWifiRcvString_V14_01
     * 2016-04-25
     * 84Park
     *
     * This sketch receives a UDP Unicast packet on client dest_port using a YJ WiShield.
     *
     * Compiled with WiShield_V14 library (ref: github.com/hamityanik/WiShield_user_contrib) and Arduino 1.6.5
     *  This code works 2016-04-25 with YJ57 board
     *  Pre-calculated security_data[] inits in 6 seconds
     *
     * Example Output to Serial Monitor
     *
     *
     * 
     * 
     * === Initializing WiShield UDP Client over Wifi ===
     * 
     * Server ip:10,0,1,56
     * Server port:3333
     * Client ip:10,0,1,57
     * Client port:3333
     * === Receiving Data over Wifi ===
     * 
     * UDP Receive: Hello from UDP Server
     * UDP Receive v1 strlen: 21
     * UDP Receive: Hello from UDP Server
     * UDP Receive v1 strlen: 21
     * 
     *
     *
    // Requires APP_SOCKAPP and APP_UDPAPP to be defined in apps-conf.h
    //  APP_SOCKAPP  - for the TCP sockets components of the sketch
    //  APP_UDPAPP   - for the UDP/DNS components of the sketch
     *
     * Adapted from:
     * UDPApp
     * This sketch receives a UDP packet on client dest_port using a WiFi shield.
     *
     */
    
    #include <WiShield.h>
    extern "C" {
    #include "uip.h"
    }
    
    // Wireless configuration parameters ----------------------------------------
    unsigned char local_ip[]    = {10, 0, 1, 57};   // IP address of WiShield
    unsigned char gateway_ip[]  = {10, 0, 1, 1};   // router or gateway IP address
    unsigned char subnet_mask[] = {255, 255, 255, 0}; // subnet mask for the local network
    u16_t dns_ip[]              = {10, 0, 1, 1};   // DNS server addr
    char ssid[]                 = {"Network"};   // max 32 bytes
    unsigned char security_type = 5;  // 0 - open; 1 - WEP; 2 - WPA; 3 - WPA2; 4 - WPA Precalc; 5 - WPA2 Precalc
    
    // Depending on your security_type, uncomment the appropriate type of security_data
    // 0 - None (open)
    // const char security_data[] PROGMEM = {};
    
    // 1 - WEP
    // UIP_WEP_KEY_LEN. 5 bytes for 64-bit key, 13 bytes for 128-bit key
    // Only supply the appropriate key, do not specify 4 keys and then try to specify which to use
    //const char security_data[] PROGMEM = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, };
    
    // 2, 3 - WPA/WPA2 Passphrase
    // 8 to 63 characters which will be used to generate the 32 byte calculated key
    // Expect the g2100 to take 30 seconds to calculate the key from a passphrase
    // const char security_data[] PROGMEM = {"Network_Passcode"};
    
    // 4, 5 - WPA/WPA2 Precalc
    // The 32 byte precalculate WPA/WPA2 key. This can be calculated in advance to save boot time
    //const prog_char security_data[] PROGMEM = {
    //    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
    //    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f, 0x1f,
    //};
    // Calc the Precalc:
    // http://jorisvr.nl/wpapsk.html
    // The derived key will appear in the form as a sequence of 64 hexadecimal digits.
    /*0599967ce21000026c2cf439ea561c3d37abbf48085738cbe9b9a9ca093a6adb*/
    const char security_data[] PROGMEM = {
      0x05, 0x99, 0x96, 0x7c, 0xe2, 0x10, 0x00, 0x02, 0x6c, 0x2c, 0xf4, 0x39, 0xea, 0x56, 0x1c, 0x3d,
      0x37, 0xab, 0xbf, 0x48, 0x08, 0x57, 0x38, 0xcb, 0xe9, 0xb9, 0xa9, 0xca, 0x09, 0x3a, 0x6a, 0xdb,
    };
    
    // setup the wireless mode; infrastructure - connect to AP; adhoc - connect to another WiFi device
    //#define WIRELESS_MODE_INFRA  1
    //#define WIRELESS_MODE_ADHOC 2
    unsigned char wireless_mode = WIRELESS_MODE_INFRA;
    // End of wireless configuration parameters ----------------------------------------
    
    uint16_t server_ip[]    = {10, 0, 1, 56};   // IP address of UDP server
    uint16_t server_port = 3333;       // server port to send from
    uint16_t dest_ip[]    = {10, 0, 1, 57};   // IP address of client
    uint16_t dest_port = 3333;       // destination client port to send to
    
    //char  packetBuffer[255];      //buffer to hold outgoing packet
    //char  ReplyBuffer[] = "acknowledged";       // a string to send back
    boolean connectAndSendUDP = false;
    uip_ipaddr_t srvaddr;
    
    //---------------------------------------------------------------------------
    void setup()
    {
      //Initialize serial and wait for port to open:
      Serial.begin(115200);
    
      Serial.println ("\n=== Initializing WiShield UDP Client over Wifi ===");
    
      Serial.print("\nServer ip:");
      Serial.print(server_ip[0]); Serial.print(",");
      Serial.print (server_ip[1]); Serial.print(",");
      Serial.print (server_ip[2]); Serial.print(",");
      Serial.println (server_ip[3]);
    
      Serial.print("Server port:");
      Serial.print(server_port);
    
      Serial.print("\nClient ip:");
      Serial.print(dest_ip[0]); Serial.print(",");
      Serial.print (dest_ip[1]); Serial.print(",");
      Serial.print (dest_ip[2]); Serial.print(",");
      Serial.println (dest_ip[3]);
    
      Serial.print("Client port:");
      Serial.print(dest_port);
    
      WiFi.init();
    
      Serial.println ("\n=== Receiving Data over Wifi ===\n");
    
    }
    
    void loop()
    {
     WiFi.run();
    }
    
    
    extern "C"
    {
      // These uIP callbacks are unused for the purposes of this simple DNS example
      // but they must exist.
      void socket_app_init(void)
      {
      }
    
      void socket_app_appcall(void)
      {
      }
    
    // printx functions print char arrays returned from c code to Serial Monitor
        /* c code trace */
      void printx(char* data)
      {
        Serial.print(data);
      }  
      
      /* c code trace */
      void printlnx(char* data)
      {
        Serial.println(data);
      }
      
    }
    
    
    
    The code which receives the packet from the server is in the local version of udpapp.c. The function parsemsg(), “static unsigned char parse_msg(void)”, was modified to parse the “Hello” from the incoming packet “Hello from UDP Server\n”. If there was a match, the whole packet was copied into the char array str[50] and a “\0” was added after the packet to terminate str after the characters that were copied. The sprintf function was used to convert length “i” into a char array “v1[]” and \0 terminated. Both str[] and v1[] were printed using the printx function to Serial Monitor. The functions send_request and send_response were not used.
    /******************************************************************************
    
      Filename: udpapp.c client for UDPAppWifiRcvString_V14_01.ino
      Description: UDP app for the WiShield 1.0
    
     ******************************************************************************
    
      TCP/IP stack and driver for the WiShield 1.0 wireless devices
    
      Copyright(c) 2009 Async Labs Inc. All rights reserved.
    
      This program is free software; you can redistribute it and/or modify it
      under the terms of version 2 of the GNU General Public License as
      published by the Free Software Foundation.
    
      This program is distributed in the hope that it will be useful, but WITHOUT
      ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
      more details.
    
      You should have received a copy of the GNU General Public License along with
      this program; if not, write to the Free Software Foundation, Inc., 59
      Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    
      Contact Information:
      <asynclabs@asynclabs.com>
    
       Author               Date        Comment
      ---------------------------------------------------------------
       AsyncLabs   07/11/2009 Initial version
    
     *****************************************************************************/
    
    #include "uip.h"
    #include <string.h>
    #include "udpapp.h"
    #include "config_udp.h"
    
    #define STATE_INIT       0
    #define STATE_LISTENING       1
    #define STATE_HELLO_RECEIVED  2
    #define STATE_NAME_RECEIVED   3
    
    static struct udpapp_state s;
    
    void dummy_app_appcall(void)
    {
    }
    
    void udpapp_init(void)
    {
      uip_ipaddr_t addr;
      struct uip_udp_conn *c;
    
          //uip_ipaddr(&addr, 10,0,1,57);
          //uip_ipaddr(&addr, dest_ip[0], dest_ip[1], dest_ip[2], dest_ip[3]); // server sends to client ip
      uip_ipaddr(&addr, server_ip[0], server_ip[1], server_ip[2], server_ip[3]);     // client receives from server ip
    
      c = uip_udp_new(&addr, HTONS(dest_port));
          //c = uip_udp_new(&addr, HTONS(3333));
      if (c != NULL) {
        uip_udp_bind(c, HTONS(server_port)); // or, bind to a specified port
            //uip_udp_bind(c, HTONS(3333)); // or, bind to a specified port
    
      }
    
      s.state = STATE_INIT;
    
      PT_INIT(&s.pt);
    }
    
    static unsigned char parse_msg(void)
    {
      if (memcmp(uip_appdata, "Hello", 5) == 0) {
        int i = 0;
        char str[50]; // char array to hold incoming data
        char v1[5]; // char array to hold length(str)
    
        //s.state = STATE_HELLO_RECEIVED;
    
        // read the data "str[]"
        while ( ( ((char*)uip_appdata)[i] != '\n')) {
          str[i] = ((char*)uip_appdata)[i];
          i++;
        }
        str[i] = '\0'; // terminate char string
        printx("UDP Receive: "); printx(str); printlnx();
    
        sprintf(v1, "%d\0", i); // converts int to char[] array (length(str))
        printx("UDP Receive strlen: "); printx(v1); printlnx();
    
        // or
        // sprintf(v1, "%d\0", strlen(str)); // converts int to char[] array (length(str))
        // printx("UDP Receive strlen: "); printx(v1); printlnx();
    
      }
      return 0;
    }
    
    static void send_request(void)
    {
      char str[] = "Hello. What is your name?\n";
    
      memcpy(uip_appdata, str, strlen(str));
      uip_send(uip_appdata, strlen(str));
    }
    
    static void send_response(void)
    {
      char i = 0;
      //char str[] = "Hello from UDP Server\n";
      char str[] = "Send Hello";
    
      // send the data "str[]"
      memcpy(uip_appdata, str, strlen(str));
      uip_send(uip_appdata, strlen(str));
      printx("UDP Send: "); printx(str); printlnx();
      /*
       while ( ( ((char*)uip_appdata)[i] != '\n') && i < 9) {
        s.inputbuf[i] = ((char*)uip_appdata)[i];
        i++;
       }
       s.inputbuf[i] = '\n';
    
       memcpy(uip_appdata, str, 6);
       memcpy(uip_appdata+6, s.inputbuf, i+1);
       uip_send(uip_appdata, i+7);
      */
    
    }
    
    static PT_THREAD(handle_connection(void))
    {
      PT_BEGIN(&s.pt);
      parse_msg();
      /*
        if (parse_msg()) {
          char str[20]; // char array to hold incoming data
    
          //s.state = STATE_NAME_SEND;
          s.state = STATE_HELLO_RECEIVED;
          // read the data "str[]"
          memcpy(str, uip_appdata, strlen(uip_appdata));
          printx("UDP Receive: "); printx(str); printlnx();
          printx("UDP Receive strlen: "); printx(strlen(uip_appdata)); printlnx();
        }
        */
      /*
       do {
        PT_WAIT_UNTIL(&s.pt, uip_newdata());
    
        if(uip_newdata() && parse_msg()) {
         s.state = STATE_HELLO_RECEIVED;
         uip_flags &= (~UIP_NEWDATA);
         break;
        }
       } while(s.state != STATE_HELLO_RECEIVED);
    
       do {
        send_request();
        PT_WAIT_UNTIL(&s.pt, uip_newdata());
    
        if(uip_newdata()) {
         s.state = STATE_NAME_RECEIVED;
         uip_flags &= (~UIP_NEWDATA);
         break;
        }
       } while(s.state != STATE_NAME_RECEIVED);
      */
      //send_response();
    
      s.state = STATE_INIT;
    
      PT_END(&s.pt);
    }
    
    void udpapp_appcall(void)
    {
      handle_connection();
    }
    
    
    And the Serial Monitor display:
    === Initializing WiShield UDP Client over Wifi ===
    
    Server ip:10,0,1,56
    Server port:3333
    Client ip:10,0,1,57
    Client port:3333
    === Receiving Data over Wifi ===
    
    UDP Receive: Hello from UDP Server
    UDP Receive v1 strlen: 21
    UDP Receive: Hello from UDP Server
    UDP Receive v1 strlen: 21
    
    
    Summary: In these examples, a UDP server was implemented on YJ56 to send out a character string to a UDP client on YJ57, both using port 3333. The same character string was transmitted without stop, with no feedback from YJ57 to YJ56. Any packets that were not received or were received with errors were not checked or corrected. An identification substring (Hello) was used to verify that a valid packet was received by the client from the server, then the client acted on the received packet by printing it to Serial Monitor.
    (May 2, 2016)