zcraversteven west
Published

Active Aero Wing MEGR 3171

Active Aero Wing

AdvancedFull instructions providedOver 2 days106
Active Aero Wing MEGR 3171

Things used in this project

Hardware components

3 way toggle switch
×1
Push Button
×1
Photon
Particle Photon
×2
GPS
×1
Breadboard (generic)
Breadboard (generic)
×1
Resistor 220 ohm
Resistor 220 ohm
×2
Jumper wires (generic)
Jumper wires (generic)
×17
Relay (generic)
×1
Linear actuator
33 lb
×1
Bolts
×1
Lock Nuts
×1
Swedge tube
×1
Heim joint
×1

Software apps and online services

Particle Build Web IDE
Particle Build Web IDE
IFTTT
Google Sheets
Google Sheets
Fritzing

Hand tools and fabrication machines

Tig welder
Mig welder
Vertical band saw
Belt sander
Drill press
Files
3D Printer (generic)
3D Printer (generic)
Hand tap
Jig saw
Riv-nut installer
Grinder

Story

Read more

Custom parts and enclosures

Linear actuator mount

Trunk Hinge

Wing Hinge

Linear Actuator Adapter

Heim Joint Wing Mount

Schematics

3D Printed brackets

Rough Cut Brackets

Welded and Filed Brackets

Linear Actuator Adapter

Heim Joint Wing Mount

Wing Mock Up

Linear Actuator Rear Mount

Wing Testing

Graph of Activation Speeds

This graph shows the speeds of each time the wing is used from Newton to Charlotte North Carolina. All speeds shown are above 15 mph except for the two initial speeds due to the GPS not acquiring a signal at that time.

Circuit Diagram

Code

GPS Signal and Actuator Code

C/C++
// This #include statement was automatically added by the Particle IDE.
#include <TinyGPS++.h>

// This #include statement was automatically added by the Particle IDE.
#include <elapsedMillis.h>



//This Sketch decodes the GPS and outputs a frequency based on the speed, D0 and  D2 is a frequency based on # of satellites
//Mode can be changed from the cloud. 0 = gps based freq output
//test frequency can be changed from the cloud.  frequency is in hertz.


/*
   This sample code demonstrates the normal use of a TinyGPS++ (TinyGPSPlus) object.
   It requires the use of SoftwareSerial, and assumes that you have a
   230400-baud serial GPS device hooked up on pins 4(rx) and 3(tx).
*/



/* PWM NOTES
On the Photon and Electron, this function works on pins D0, D1, D2, D3, A4, A5, WKP, RX and TX with a caveat: 
PWM timer peripheral is duplicated on two pins (A5/D2) and (A4/D3) for 7 total independent PWM outputs. 
For example: PWM may be used on A5 while D2 is used as a GPIO, or D2 as a PWM while A5 is used as an analog input. 
However A5 and D2 cannot be used as independently controlled PWM outputs at the same time.
Additionally on the Electron, this function works on pins B0, B1, B2, B3, C4, C5.

The PWM frequency must be the same for pins in the same timer group.
On the Core, the timer groups are D0/D1, A0/A1/RX/TX, A4/A5/A6/A7.
On the Photon, the timer groups are D0/D1, D2/D3/A4/A5, WKP, RX/TX.
On the P1, the timer groups are D0/D1, D2/D3/A4/A5/P1S0/P1S1, WKP, RX/TX.
On the Electron, the timer groups are D0/D1/C4/C5, D2/D3/A4/A5/B2/B3, WKP, RX/TX, B0/B1.
*/

static const uint32_t GPSBaud1 = 9600;
static const uint32_t GPSBaud2 = 230400;

// The TinyGPS++ object
TinyGPSPlus gps;

double freqout = 0;
double gpssats = 0;
double gpscal = 2.28;//2.2727 //400hz converts to 176mph
double gpsmph = 0;
double gpsfix = 0;
int caladdress = 0;
double gpseeprom = 0;
int speed;
int led = D7;
const int buttonPin = D1; 
const int buttonPin1 = D2;// the number of the pushbutton pin
const int ledPin =  D4; // the number of the LED pin
const int ledPin1 =  D3;
int buttonState = 0;  
int buttonState1 = 0; 




byte setgpsbaud1[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x00, 0x84, 0x03, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xE8};
byte setgpsbaud2[] = {0xB5, 0x62, 0x06, 0x00, 0x01, 0x00, 0x01, 0x08, 0x22};
byte setgpsbaud3[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00, 0x00, 0x84, 0x03, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xE8};
byte setgpsbaud4[] = {0xB5, 0x62, 0x06, 0x00, 0x01, 0x00, 0x01, 0x08, 0x22};

byte setgpsrate10a[] = {0xB5, 0x62, 0x06, 0x08, 0x06, 0x00, 0x64, 0x00, 0x01, 0x00, 0x01, 0x00, 0x7A, 0x12};
byte setgpsrate10b[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x0E, 0x30};

byte setgpstalkera[] = {0xB5, 0x62, 0x06, 0x17, 0x0C, 0x00, 0x00, 0x23, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01 ,0x00, 0x50, 0xF9};
byte setgpstalkerb[] = {0xB5, 0x62, 0x06, 0x17, 0x00, 0x00, 0x1D, 0x5D};

unsigned long lastprint = 0;
unsigned int rssiinterval = 10000;

unsigned int freqmode = 0; //mode to control freq out source
double freqtestval = 0;

elapsedMillis lastprintgps;
elapsedMillis rssitimer;

//ApplicationWatchdog wd(5000, System.reset); //5 second application watchdog
SYSTEM_MODE(SEMI_AUTOMATIC);

struct MyObject {
  uint8_t version;
  float field1;  //field1 = gpscal eeprom storage object
  uint16_t field2;
  char name[10];
};
MyObject myObj;
int addr = 0;

void setup()
{
  Particle.variable("gpsfreq", freqout);     
  Particle.variable("gpsmph", gpsmph);
  //Particle.variable("gpseeprom", gpseeprom);
  //Particle.variable("freqmode", freqmode); 
  Particle.variable("gpscal", gpscal);
  Particle.variable("gpssats", gpssats);
  Particle.variable("gpsfix", gpsfix);
  Particle.function("setmode", setmode); //functions still come up even if not in loop
  Particle.function("setfreq", setfreq);
  Particle.function("setcal", setcal);
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin1, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
  pinMode(buttonPin1, INPUT);
  pinMode(led, OUTPUT);

  EEPROM.get(addr, myObj);

  if(myObj.version != 0) {
    // EEPROM was empty -> initialize myObj
    MyObject defaultObj = { 0, 2.28f, 0, "GPSCAL" }; //2.8 is a default calibration for new units.
    //myObj = defaultObj;
    EEPROM.put(addr, defaultObj); //write new values to eeprom
    delay(100);
    EEPROM.get(addr, myObj);
  }

  gpseeprom = myObj.field1; //assign gpseeprom the bootup eeprom value
  gpscal = gpseeprom; //assign gpscal to be equal to gpseeprom
  
  Serial.begin(230400);
  //begin adafruit ultimate gps 3.0 config, start at 9600 baud, send commands to change to 115200 and update rate to 10hz.
  Serial1.begin(GPSBaud1);
  delay(50);
  Serial1.println("$PMTK251,115200*1F");//for adafruit
  delay(50);
  Serial1.write(setgpsbaud1, sizeof(setgpsbaud1)); //for m8n
  delay(50);
  Serial1.write(setgpsbaud2, sizeof(setgpsbaud2)); //for m8n
  delay(50);
  Serial1.write(setgpsbaud3, sizeof(setgpsbaud3)); //for m8n
  delay(50);
  Serial1.write(setgpsbaud4, sizeof(setgpsbaud4)); //for m8n
  
  delay(50);
  Serial1.begin(GPSBaud2);
  Serial1.println("$PMTK220,100*2F*71");
  delay(50);
  Serial1.write(setgpsrate10a, sizeof(setgpsrate10a)); //for m8n
  delay(50);
  Serial1.write(setgpsrate10b, sizeof(setgpsrate10b)); //for m8n
  delay(50);
  Serial1.write(setgpstalkera, sizeof(setgpstalkera)); //for m8n
  delay(50);
  Serial1.write(setgpstalkerb, sizeof(setgpstalkerb)); //for m8n
  

  Serial.println(F("FullExample.ino"));
  Serial.println(F("An extensive example of many interesting TinyGPS++ features"));
  Serial.print(F("Testing TinyGPS++ library v. ")); Serial.println(TinyGPSPlus::libraryVersion());
  Serial.println(F("by Mikal Hart"));
  Serial.println();
  Serial.println(F("Sats HDOP Latitude   Longitude   Fix  Date       Time     Date Alt    Course Speed Card  Distance Course Card  Chars Sentences Checksum"));
  Serial.println(F("          (deg)      (deg)       Age                      Age  (m)    --- from GPS ----  ---- to London  ----  RX    RX        Fail"));
  Serial.println(F("---------------------------------------------------------------------------------------------------------------------------------------"));
}

void loop()
{
      if (System.buttonPushed() > 500) {
        //RGB.color(255, 255, 0); // YELLOW
        if (Particle.connected() == false) {  //if not connected, delay, 5000ms before attempting reconnect.  without delay was causing gps to fail.
            Serial.println("Connecting to wifi");
		    Particle.connect();
		    delay(1000);
        } else {
            //Particle.disconnect();
            WiFi.off();
            delay(1000);
        }   
    }
  
    if (rssitimer > rssiinterval)   //publish rssi signal
    {
        char publishString[40];
        rssitimer = 0;
        int rssival = WiFi.RSSI();
        sprintf(publishString,"%d",rssival);
        Particle.publish("RSSI",publishString);
    }        
            
  static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002;

  //if (lastprintgps> 500){
     //lastprintgps= 0;
    printInt(gps.satellites.value(), gps.satellites.isValid(), 5);
    printInt(gps.hdop.value(), gps.hdop.isValid(), 5);
    printFloat(gps.location.lat(), gps.location.isValid(), 11, 6);
    printFloat(gps.location.lng(), gps.location.isValid(), 12, 6);
    printInt(gps.location.age(), gps.location.isValid(), 5);
    printDateTime(gps.date, gps.time);
    printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2);
    printFloat(gps.course.deg(), gps.course.isValid(), 7, 2);
    printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2);
    printStr(gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.value()) : "*** ", 6);
    Serial.println();
  //}
  //delay(1);
  //Serial.println("             ");
  
  
  
  
  
  
  
  
        if ( gps.speed.mph() == 0 ) {
   digitalWrite (D7, HIGH);
}

else { digitalWrite (D7 , LOW); }
  
  
        if ( gps.speed.mph() > 15 ) {
   digitalWrite (D7, HIGH);
}

else { digitalWrite (D7 , LOW); }











    
    if((gps.satellites.age()>1500)||(gps.satellites.isValid()==FALSE)){
        gpssats = 0;
    }
    
    else {
        gpssats = gps.satellites.value();
    }

    if (gps.altitude.isValid()==TRUE) {
      gpsfix = 3;
    }else if (gps.speed.isValid()==TRUE) {
      gpsfix = 2;
  } else {
      gpsfix = 0;
    }

    switch (freqmode) 
        {
            case 0:
                if (gps.speed.isValid()==TRUE && (millis() >8000)){    //if a valid speed is output, then update freq output
                    gpsmph = gps.speed.mph();
                    freqout = gpsmph*gpscal;
                } else { //if no valid speed
                    gpssats = min(gpssats,8);
                    freqout = (gpssats*11)*gpscal; //if no speed is valid, output # of satellites , 1sat = 11 2sat = 22 3sat =33
                }
                
                if (gps.speed.age() > 1000){ //if the fix is too old, in this mode turn off the outputs.
                gpsmph = 0;
                gpsfix = 0;
                freqout = 0;
                gpssats = 0;
                }
                
                break;
            case 1: //mode 1 is output the test value frequency directly
                freqout = freqtestval;
                break;
            case 2: //mode 2 is output the test value times the calibration factor. for verifying mph.
                    freqout = freqtestval*gpscal;
                break;
        } 
        
        
        
        
        
        
        
        
        
        
        
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);
  buttonState1 = digitalRead(buttonPin1);

  // check if the pushbutton is pressed. If it is, the buttonState is HIGH:
  if (buttonState == LOW) {
    // turn LED on:
    digitalWrite(ledPin, LOW);
  }
else 
{ digitalWrite(ledPin, HIGH);

}
  if (buttonState1 == HIGH) {
    // turn LED on:
    digitalWrite(ledPin1, HIGH);
    
    
    Particle.publish("Wing_Up",String(gpsmph),PRIVATE);
    delay(1000);
  }
else 
{ digitalWrite(ledPin1, LOW);
}
 
 
        
       
       
       
       
       
        
        
        
    if (freqout < .5)
    {
        digitalWrite(D0,LOW);  //if frequency output is less than .5hz, then silence the pulse output.
    } else {
        analogWrite(D0, 50, freqout);
    }


    if (gpssats < .1)
    {
        digitalWrite(D2,LOW);  //if no sats, the digitalwrite was required to silence the pwm output.
    } else {
        analogWrite(D2, 50, gpssats);
    }

  
  if (millis() > 5000 && gps.charsProcessed() < 10)
    Serial.println(F("No GPS data received: check wiring"));
}

// This custom version of delay() ensures that the gps object
// is being "fed".
static void smartDelay(unsigned long ms)
{
  unsigned long start = millis();
  do 
  {
    while (Serial1.available())
      gps.encode(Serial1.read());
  } while (millis() - start < ms);
}

static void printFloat(float val, bool valid, int len, int prec)
{
  if (!valid)
  {
    while (len-- > 1)
      Serial.print('*');
    Serial.print(' ');
  }
  else
  {
    Serial.print(val, prec);
    int vi = abs((int)val);
    int flen = prec + (val < 0.0 ? 2 : 1); // . and -
    flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
    for (int i=flen; i<len; ++i)
      Serial.print(' ');
  }
  smartDelay(0);
}

static void printInt(unsigned long val, bool valid, int len)
{
  char sz[32] = "*****************";
  if (valid)
    sprintf(sz, "%ld", val);
  sz[len] = 0;
  for (int i=strlen(sz); i<len; ++i)
    sz[i] = ' ';
  if (len > 0) 
    sz[len-1] = ' ';
  Serial.print(sz);
  smartDelay(0);
}

static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
{
  if (!d.isValid())
  {
    Serial.print(F("********** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
    Serial.print(sz);
  }
  
  if (!t.isValid())
  {
    Serial.print(F("******** "));
  }
  else
  {
    char sz[32];
    sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
    Serial.print(sz);
  }

  printInt(d.age(), d.isValid(), 5);
  smartDelay(0);
}

static void printStr(const char *str, int len)
{
  int slen = strlen(str);
  for (int i=0; i<len; ++i)
    Serial.print(i<slen ? str[i] : ' ');
  smartDelay(0);
}

int setmode(String controlstring) //function to set the gps freq output mode.
{
    int controlint = controlstring.toInt();
    freqmode = controlint;
    return controlint;
}

int setfreq(String controlstring) //function to set the gps freq output mode.
{
    int controlint = controlstring.toInt();
    freqtestval = controlint;
    return controlint;
}

int setcal(String controlstring) //function to set the gps freq output mode.
{
    float controlval = -1;
    if (controlstring.toFloat() > 0)
    {
        controlval = controlstring.toFloat();
        MyObject myObj; //declare new object to read eprom values, using a different object to be sure a read was succesful
        EEPROM.get(addr, myObj); //read eeprom
        float tmpfloat =myObj.field2+1;
        MyObject myObjx = { 0, controlval, tmpfloat, "GPSCAL" }; //declare new value to store values.
        gpscal = double(controlval); //copy controlval to gpscal, which is a double 
        EEPROM.put(addr, myObjx); //write new values to eeprom
        delay(100); //delay seemed to be necessary to allow write to occur
        EEPROM.get(addr, myObj); //read eeprom
        Serial.println();
        Serial.println(myObj.field1); //debug eeprom calibration print
        gpseeprom = myObj.field1; //allows for double checking that eeprom was correctly written.
        delay(100);
    }
    
    

    
    return controlval;
    
  
    }

Button and Switch Input Code

C/C++
const int buttonPin = D0;     // the number of the pushbutton pin
const int ledPin =  D2; // the number of the LED pin
const int ledPin1 =  D1;
const int streetPin =  D3;
const int dragPin =  D4;
const int gpsPin =  D7;



int buttonState = 0;  
int buttonState1 = 0;
int buttonState2 = 0;
int buttonState3 = 0;
int up = 0;// variable for reading the pushbutton status

SYSTEM_MODE(SEMI_AUTOMATIC);

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  pinMode(ledPin1, OUTPUT);
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);
  pinMode(streetPin, INPUT);
  pinMode(dragPin, INPUT);
  pinMode(gpsPin, INPUT);
}

void loop() {
  // read the state of the pushbutton value:
    buttonState = digitalRead(buttonPin);
    buttonState1 = digitalRead(streetPin);
    buttonState2 = digitalRead(dragPin);
    buttonState3 = digitalRead(gpsPin);

   
    
 if (System.buttonPushed() > 500) {
        //RGB.color(255, 255, 0); // YELLOW
        if (Particle.connected() == false) {  //if not connected, delay, 5000ms before attempting reconnect.  without delay was causing gps to fail.
            Serial.println("Connecting to wifi");
		    Particle.connect();
		    delay(1000);
        } else {
            //Particle.disconnect();
            WiFi.off();
            delay(1000);
        }   
    }

 
  if (buttonState == LOW && buttonState3 == HIGH) //
    {
    digitalWrite(ledPin, HIGH);
    digitalWrite(ledPin1, LOW);
       up = 1;
    
  }
 if (buttonState == HIGH && up == 1){
    digitalWrite(ledPin, LOW);
    digitalWrite(ledPin1, LOW);
    up = 2;
}
 if (up == 2 && buttonState1 ==LOW) {
   digitalWrite(ledPin, LOW);
   digitalWrite(ledPin1, HIGH);
   delay(400); //wing retract time in street mode
   digitalWrite(ledPin1, LOW);
   up = 0;
 }
  if (up == 2 && buttonState2 ==LOW) {
   digitalWrite(ledPin, LOW);
   digitalWrite(ledPin1, HIGH);
   delay(600); //wing retract time in drag mode
   digitalWrite(ledPin1, LOW);
   up = 0;
 }
 }
 

Credits

zcraver

zcraver

1 project • 0 followers
steven west

steven west

1 project • 0 followers
Thanks to John McAlpine.

Comments