Shop OBEX P1 Docs P2 Docs Learn Events
Contest Entry - Universal Telemetry System Demonstrated with Unmanned Aerial Vehicle — Parallax Forums

Contest Entry - Universal Telemetry System Demonstrated with Unmanned Aerial Vehicle

pthartmannpthartmann Posts: 27
edited 2011-10-13 12:28 in Accessories
Universal Telemetry System (UTS) Demonstrated with Unmanned Aerial Vehicle (UAV)

My colleagues and I designed a Parallax Propeller based UAV that warranted an innovative telemetry system. Fortunately, Parallax released the Spinneret at just the right time.

Our goals were to design and build a complete telemetry system capable of capturing sensor feedback, controlling outputs, and transmitting video. These tasks had to be accomplished with web based products and in real time. By utilizing the Parallax community and our combined knowledge of SPIN, asynchronous JavaScript, XML parsing, and HTML; we were able to accomplish our system. The main components of our system include the Spinneret Webserver, Planex MZK-MF150 router, and the Lorex LNE1001 IP camera.

Comments

  • xanaduxanadu Posts: 3,347
    edited 2011-05-18 20:25
    Wicked. Any sneak peak at the UAV available?
  • pthartmannpthartmann Posts: 27
    edited 2011-07-28 14:12
    Our code. The following includes all SPIN, original and referenced, and web based code:

    Our code does not fit. Apparently there's a limit to 100000 characters.
    I have included some sample code:
    ''continue working on PID and flight routines. Test Display implementation!!!! 02/24/2011
    '''create conditions for pitch and roll orientation, do not use PID loop.
    '''use a ranged value, set point pitch of 0, if pitch is inbetween -5 and 5 then OKAY!
    {{
    COG COUNT
    Cog 0 = Main Program Loop
    Cog 1 = ESC Motor Driver
    Cog 2 = FullDuplexSerial
    Cog 3 = F32
    Cog 4 = Flight Control_PID_Kalman
    Cog 5 = 
    Cog 6 = 
    Cog 7 = 
     
    Flight_Control(PID, Kalman Filter, Wii Objects)
    ''''OLD''''''
    Possible Solutions to Free Up Cogs
    Combine Flight Control/PID into main program loop.
    Do not give Propeller to Spinneret Communication it's own cog.
    Eliminate the need for 2 cogs for the Floating Point Math.
    The above free's up 4 cogs, including the Display Test Values.
    Results:
    Flight_control is no longer using a cog, I have put a call to that funtion in each of the Decision engine's routine.
    I eliminated the 2nd cog for floating math, I am using F32 instead.....
    For the moment I took out the propeller to spinneret communication, the display test values is using its cog.
    Total Cogs Used : 4
    Total Free Cogs : 4
    IF no communication and no display test values then total free cogs = 5
    }}
    CON
    _CLKMODE = XTAL1 + PLL16X
    _XINFREQ = 5_000_000
    CLOCKS_PER_MICROSECOND = 5*16 ' simple xin*pll / 1_000_000 
    CLOCKS_PER_MILLISECOND = 5000*16 ' simple xin*pll / 1_000
    Error_LED = 0
    ON = 1
    OFF = 0
    RXPIN = 11
    TXPIN = 12
     
    SCL = 28
    SDA = 29
    Mtr_A = 0
    Mtr_B = 1
    Mtr_C = 2
    Mtr_D = 3 
    Auto_Forward = 50 ''FIXED POINT VALUES ONLY
    Auto_Reverse = -50
    Auto_Left = -50
    Auto_Right = 50
    Auto_Rotate = 50
    CLS = 16, CR = 13, CLREOL = 11, CRSRXY = 2
     
    MP_ONLY = 4 'MotionPlus only
    MP_NUN = 5 'MotionPlus w/ Nunchuck
    MP_CLASSIC = 7 'MotionPlus w/ Classic Controller
     
    VAR
    long Accel_X, Accel_Y
    long Gx_F, Gy_F, Gz_F
    word Alt,Thrust 
    word Heading, Bearing, Temp
    long SP_Alt, SP_Head
    byte Error_Status, User_Control, i
    byte Wants_Control, Has_Control
    byte Landing, TakingOff, Holding, Rotating, Moving
    long RI_IR, LE_IR, FR_IR, RR_IR, TO_IR, BO_IR
    long count
    long cur_State
    byte startbtn
    long M_A, M_B, M_C, M_D 
     
    'long Received_Commands, RecBuffer[6], SenBuffer[6]
    'long stack[200]
    OBJ
    Ser : "FullDuplexSerialPlus"
    FC : "Flight_Control0429"
    SCG : "sparecogs"
    SIR : "Sharp_IR"
    ESC : "ESC_Spin"
    Clk : "Clock"
    'fString : "FloatString" 
    'TxRx : "FullDuplexSerialPlus"
    PUB MAIN | q 
    '''''''''''''''''''''''INITIALIZING OBJECT DRIVERS'''''''''''''''''''''''''''''''''''''''
     
    'TxRx.start(11, 12, 0, 115200)
    Ser.start(31,30,0,115200)
    ESC.Start
    FC.Init(SCL, SDA, MP_NUN)
    Delay_MS(2000) 
    '''''''''''''''''''''''INITIALIZE ALL CONDITIONS''''''''''''''''''''''
    Error_Status := Wants_Control := Has_Control := User_Control := 0 
    SP_Alt := Alt := Temp := Thrust := 0
    FR_IR := RR_IR := LE_IR := RI_IR := TO_IR := BO_IR := 0
    Landing := TakingOff := Rotating := Moving := Holding := 0
    Thrust := 0
     
    '''''''''''''''''''''''MAIN PROGRAM CODE'''''''''''''''''''''''''''''''''''''''''''''''''' 
    startbtn := 0
     
    DIRA[14]~~
    OUTA[14]~~
    DIRA[15]~
    Delay_MS(1000)
    REPEAT UNTIL startbtn == 1
    IF INA[15] == 1
    Delay_MS(500)
    IF INA[15] == 1
    Delay_MS(500) 
    IF INA[15] == 1
    startbtn := 1
     
    TestDisplay
    Delay_MS(2000) 
     
    '''''''''''''''''''''''INITIALIZE ESC's AND MOTOR's'''''''''''''''''''''' 
    ESC.Servo(Mtr_A,1000)
    ESC.Servo(Mtr_B,1000)
    ESC.Servo(Mtr_C,1000)
    ESC.Servo(Mtr_D,1000)
    Delay_MS(10000) 
    ''//////////////////Slow Start Sequence///////////////////////////
    Decision_Engine(9)
     
     
    ''//////////////////Take-Off Sequence///////////////////////////
    'Decision_Engine(0)
     
    ''//////////////////Hold-Position Sequence///////////////////////////
     
    'Decision_Engine(2) 'Hover, get initial IR distance readings, and Pause to stabalize
     
    ''//////////////////Start Of Auto Routines/////////////////////////// }
     
    REPEAT WHILE startbtn == 1
    Repeat 150
    FC.PID(0,0,0,0,0,Thrust) 
    Get_Data
    Motor_Output
    TestDisplay
    IF Accel_X > 10 OR Accel_Y > 10
    ShutDownProcess
    Decision_Engine(1)
    ShutDownProcess
     
     
    IF INA[15] == 1
    Delay_MS(500)
    IF INA[15] == 1
    Delay_MS(500) 
    IF INA[15] == 1
    startbtn := 0
    IF startbtn == 0
    ShutDownProcess
     
     
    PUB Decision_Engine(Control_Variable) | Command
    '''''''''''''''''''''''Decision ENGINE''''''''''''''''''''''''''''''''''''''''''''''''''
    '''FC.PID(Pitch_Orient, Roll_Orient, Rotate_Command, Alt_Command) ''''
    Has_Control := Control_Variable
    CASE Has_Control
    0: 'Take Off
    cur_State := string("Taking-Off")
    SP_Alt := 30 
    Thrust := 10
    SIR.Get_IR
    REPEAT WHILE SIR.IR_BO > 22
    SIR.Get_IR
    Thrust := Thrust + 1
    FC.PID(0,0,0,0,0,Thrust)
    REPEAT UNTIL SIR.IR_BO > (SP_Alt - 5) AND SIR.IR_BO < (SP_Alt + 5)
    SIR.Get_IR 
    IF SIR.IR_BO < (SP_Alt - 5) 
    Thrust := Thrust + 1
    FC.PID(0,0,0,0,0,Thrust)
    ELSEIF SIR.IR_BO > (SP_Alt + 5)
    Thrust := (Thrust - 1) #>0
    FC.PID(0,0,0,0,0,Thrust)
     
    1: 'Land
    cur_State := string("Landing") 
    REPEAT UNTIL Thrust == 0
    SIR.Get_IR
    Alt := SIR.IR_BO
    Delay_MS(50)
    FC.PID(0,0,0,0,0,(Thrust - 10))
    FC.PID(0,0,0,0,0,0)
    ShutDownProcess
     
    2: 'Hold Position
    cur_State := string("Hovering")
    FC.PID(0,0,0,0,0,Thrust)
    SIR.Get_IR
    IF SIR.IR_FR < 50
    FC.PID(Auto_Reverse, 0,0,0, 0, Thrust)
    IF SIR.IR_LE < 50
    FC.PID(0, Auto_Right,0,0, 0, Thrust)
    IF SIR.IR_RI < 50
    FC.PID(0, Auto_Left,0,0, 0, Thrust)
    IF SIR.IR_RR < 50
    FC.PID(Auto_Forward, 0,0,0, 0, Thrust) 
    IF SIR.IR_TO < 25
    Thrust := (Thrust - 5)#>0
    FC.PID(0, 0,0,0, 0, Thrust)
    'IF SIR.IR_BO < 30
    'Thrust := Thrust + 5
    'FC.PID(0, 0,0,0, 0, Thrust)
     
    3: 'Rotate
    cur_State := string("Rotating") 
    IF Heading > (SP_Head + 20)
    Command := 50
    FC.PID(0,0,0,0,Command,Thrust) 
    ElseIF Heading < (SP_Head - 20)
    Command := -50
    FC.PID(0,0,0,0,Command,Thrust) 
    ELSE
    Command := 0
    FC.PID(0,0,0,0,Command,Thrust) 
     
    4: 'Move Forward
    cur_State := string("Forward")
    IF FR_IR > 20 
    FC.PID(Auto_Forward,0,0,0,0,0)
    Else
    FC.PID(0,0,0,0,0,Thrust)
    'Error_Status := 1
    5: 'Move Reverse
    cur_State := string("Reverse")
    IF RR_IR > 20
    FC.PID(Auto_Reverse,0,0,0,0,Thrust)
    Else
    FC.PID(0,0,0,0,0,Thrust) 
     
    6: 'Move Left
    cur_State := string("Left")
    IF LE_IR > 20
    FC.PID(0,Auto_Left,0,0,0,Thrust)
    Else
    FC.PID(0,0,0,0,0,Thrust) 
     
    7: 'Move Right
    cur_State := string("Right")
    IF RI_IR > 20
    FC.PID(0,Auto_Right,0,0,0,Thrust)
    Else
    FC.PID(0,0,0,0,0,Thrust) 
    8: 'Change Altitude
    cur_State := string("Throttle")
    FC.PID(0,0,0,0,0,Thrust)
    9: 'Slow Start
    cur_State := string("Slow Start")
    Repeat Until Thrust == 375
    Thrust := Thrust + 5
    FC.PID(0,0,0,0,0,Thrust)
    Get_Data
    Motor_Output
    TestDisplay
    Delay_MS(50)
    'Delay_MS(3000)
    'ShutDownProcess
    PUB Motor_Output 
     
    ESC.Servo(Mtr_A, M_A) 'SEND OUTPUT PULSE TO MOTORS A, B, C, AND D
    ESC.Servo(Mtr_B, M_B) 
    ESC.Servo(Mtr_C, M_C) 
    ESC.Servo(Mtr_D, M_D)
    PUB ShutDownProcess
    M_A := M_B := M_C := M_D := 1000
    Motor_Output
    ESC.Stop
    FC.Stop
    SIR.Stop
    Clk.Init(5_000_000) 
    Clk.SetClock(RCSLOW)
    REPEAT
    IF INA[15] == 1
    REBOOT
     
    PUB Get_Heading | n, avgvalue
     
    IF avgvalue > 350 OR avgvalue < 10
    Bearing := string("North")
    ELSEIF avgValue > 11 AND avgvalue < 31
    Bearing := string("North-East")
    ELSEIF avgValue > 32 AND avgvalue < 52
    Bearing := string("East")
    ELSEIF avgValue > 53 AND avgvalue < 73
    Bearing := string("South-East")
    ELSEIF avgValue > 74 AND avgvalue < 94
    Bearing := string("South")
    ELSEIF avgValue > 95 AND avgvalue < 115
    Bearing := string("South-West")
    ELSEIF avgValue > 116 AND avgvalue < 136
    Bearing := string("West")
    ELSEIF avgValue > 137 AND avgvalue < 157
    Bearing := string("North-West")
    ELSE
    Bearing := string("Unknown")
     
     
    return avgvalue
    PUB Get_Data
    Gx_F := FC.GetGX
    Gy_F := FC.GetGY
    Gz_F := FC.GetGZ
    Accel_X := FC.GetAccelX
    Accel_Y := FC.GetAccelY
    'RawAx := FC.GetRawAx
    'RawAy := FC.GetRawAy
    'RawAz := FC.GetRawAz
    'Ax := FC.GetAx
    'Ay := FC.GetAy
    'Az := FC.GetAz 
    Heading := FC.GetHeading
    count := FC.Return_Count
    M_A := FC.Motor_A
    M_B := FC.Motor_B
    M_C := FC.Motor_C
    M_D := FC.Motor_D
    SIR.Get_IR
    FR_IR := SIR.IR_FR
    RR_IR := SIR.IR_RR
    LE_IR := SIR.IR_LE
    RI_IR := SIR.IR_RI
    TO_IR := SIR.IR_TO
    BO_IR := SIR.IR_BO 
    {PUB Comm 
    '''''''''''''''''''''''INTER-PROPELLER COMMUNICATION'''''''''''''''''''''''''''''''''''''''''''''''''' 
    Repeat
    TxRx.str(string("!v=")) 'SEND SYNC PULSE
    Repeat i from 0 to 8 'SEND ENTIRE BUFFER
    TxRx.PutLong(SenBuffer[i])
    TxRx.waitstr(string("!x*"), clkfreq*20)
    Repeat i from 0 to 8 'RECEIVE ENTIRE BUFFER
    RecBuffer[i] := TxRx.GetLong
    IF RecBuffer[0] <>= 9847_3817
    'COGINIT(7, Comm, @stack[0])
     
    RecBuffer[1] := User_Control
    RecBuffer[2] := Received_Commands
    RecBuffer[3] := 0
    RecBuffer[4] := 0
    RecBuffer[5] := 0
    RecBuffer[6] := 0
    RecBuffer[7] := 0
    RecBuffer[8] := 0
    RecBuffer[9] := 0
    SenBuffer[0] := Gx_F
    SenBuffer[1] := Gy_F
    SenBuffer[2] := Gz_F
    SenBuffer[3] := Ax
    SenBuffer[4] := Ay
    SenBuffer[5] := Az
    SenBuffer[6] := 0
    SenBuffer[7] := 0
    SenBuffer[8] := Heading 
    SenBuffer[9] := cur_State 
    }
    PUB TestDisplay | tempe
     
    Ser.tx(CLS)
    Ser.str(string("Test Data From UAV..............", CR))
    ser.str(string("Cogs Free: "))
    ser.dec(SCG.freecount)
    ser.tx(13)
     
    Ser.str(string(CR, "Start Button : "))
    ser.dec(startbtn)
    Ser.str(string(CR, "INA pin 15 : "))
    ser.dec(INA[15])
     
    ser.str(string(CR, CR, "GX : "))
    ser.dec(Gx_F)
    Ser.str(string(CR, "GY : "))
    ser.dec(Gy_F)
    Ser.str(string(CR, "GZ : ")) 
    ser.dec(Gz_F) 
    Ser.str(string(CR, CR, "Pitch : "))
    ser.dec(Accel_X)
    Ser.str(string(CR, "Roll : "))
    ser.dec(Accel_Y)
    { Ser.str(string(CR, CR, "Raw Ax : "))
    ser.dec(RawAx)
    Ser.str(string(CR, "Raw Ay : "))
    ser.dec(RawAy) 
    Ser.str(string(CR, "Raw Az : "))
    ser.dec(RawAz)
    Ser.str(string(CR, CR, "Ax : "))
    ser.dec(Ax)
    Ser.str(string(CR, "Ay : "))
    ser.dec(Ay) 
    Ser.str(string(CR, "Az : "))
    ser.dec(Az)
    SER.str(string(CR,CR, "Heading: "))
    ser.dec(Heading)
    Ser.tx(CR) }
    Ser.str(string(CR, "Motor A: "))
    ser.dec(M_A)
    Ser.str(string(CR, "Motor B: "))
    ser.dec(M_B)
    Ser.str(string(CR, "Motor C: "))
    ser.dec(M_C)
    Ser.str(string(CR, "Motor D: "))
    ser.dec(M_D)
    ser.tx(CR) 
    ser.dec(count)
    Delay_MS(100) 
     
    ' /////////////////////////////////////////////////////////////////////////////
    PUB Delay_MS( time )
    {{
    Delays "time" milliseconds and returns.
    }}
    waitcnt (cnt + time*CLOCKS_PER_MILLISECOND)
    ' // end Delay_MS
    ' /////////////////////////////////////////////////////////////////////////////
    PUB Delay_US( time )
    {{
    Delays "time" microseconds and returns.
    }}
    waitcnt (cnt + time*CLOCKS_PER_MICROSECOND)
     
    ' // end Delay_US
    '//////////////////////////////////////////////////////////////////////////////
     
    {{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}}
    '' From Parallax Inc. Propeller Education Kit - Objects Lab v1.1
    {{
    &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;
    File: FullDuplexSerialPlus.spin
    Version: 1.2
    Copyright (c) 2008 Parallax, Inc.
    See end of file for terms of use.
    This is the FullDuplexSerial object v1.1 from the Propeller Tool's Library
    folder with modified documentation and methods for converting text strings
    into numeric values in several bases.
    v1.2 - Added WaitStr, GetLong and PutLong methods.
    &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;
    }}
     
    CON ''
    ''Parallax Serial Terminal Control Character Constants
    ''&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;
    HOME = 1 ''HOME = 1 
    CRSRXY = 2 ''CRSRXY = 2 
    CRSRLF = 3 ''CRSRLF = 3 
    CRSRRT = 4 ''CRSRRT = 4 
    CRSRUP = 5 ''CRSRUP = 5 
    CRSRDN = 6 ''CRSRDN = 6 
    BELL = 7 ''BELL = 7 
    BKSP = 8 ''BKSP = 8 
    TAB = 9 ''TAB = 9 
    LF = 10 ''LF = 10 
    CLREOL = 11 ''CLREOL = 11 
    CLRDN = 12 ''CLRDN = 12 
    CR = 13 ''CR = 13 
    CRSRX = 14 ''CRSRX = 14 
    CRSRY = 15 ''CRSRY = 15 
    CLS = 16 ''CLS = 16 
     
    VAR
    long cog 'cog flag/id
    long rx_head '9 contiguous longs
    long rx_tail
    long tx_head
    long tx_tail
    long rx_pin
    long tx_pin
    long rxtx_mode
    long bit_ticks
    long buffer_ptr
     
    byte rx_buffer[16] 'transmit and receive buffers
    byte tx_buffer[16] 
     
    PUB start(rxpin, txpin, mode, baudrate) : okay
    {{
    Starts serial driver in a new cog
    rxpin - input receives signals from peripheral's TX pin
    txpin - output sends signals to peripheral's RX pin
    mode - bits in this variable configure signaling
    bit 0 inverts rx
    bit 1 inverts tx
    bit 2 open drain/source tx
    bit 3 ignor tx echo on rx
    baudrate - bits per second
     
    okay - returns false if no cog is available.
    }}
    stop
    longfill(@rx_head, 0, 4)
    longmove(@rx_pin, @rxpin, 3)
    bit_ticks := clkfreq / baudrate
    buffer_ptr := @rx_buffer
    okay := cog := cognew(@entry, @rx_head) + 1
     
    PUB stop
    '' Stops serial driver - frees a cog
    if cog
    cogstop(cog~ - 1)
    longfill(@rx_head, 0, 9)
     
    PUB tx(txbyte)
    '' Sends byte (may wait for room in buffer)
    repeat until (tx_tail <> (tx_head + 1) & $F)
    tx_buffer[tx_head] := txbyte
    tx_head := (tx_head + 1) & $F
    if rxtx_mode & %1000
    rx
    PUB rx : rxbyte
    '' Receives byte (may wait for byte)
    '' rxbyte returns $00..$FF
    repeat while (rxbyte := rxcheck) < 0
    PUB rxflush
    '' Flush receive buffer
    repeat while rxcheck => 0
     
    PUB rxcheck : rxbyte
    '' Check if byte received (never waits)
    '' rxbyte returns -1 if no byte received, $00..$FF if byte
    rxbyte--
    if rx_tail <> rx_head
    rxbyte := rx_buffer[rx_tail]
    rx_tail := (rx_tail + 1) & $F
    PUB rxtime(ms) : rxbyte | t
    '' Wait ms milliseconds for a byte to be received
    '' returns -1 if no byte received, $00..$FF if byte
    t := cnt
    repeat until (rxbyte := rxcheck) => 0 or (cnt - t) / (clkfreq / 1000) > ms
    PUB str(stringptr)
    '' Send zero terminated string that starts at the stringptr memory address
    repeat strsize(stringptr)
    tx(byte[stringptr++])
    PUB getstr(stringptr) | index
    '' Gets a carriage return terminated string and stores it (starting at stringptr)
    '' as a zero terminated string
    index~
    repeat until ((byte[stringptr][index++] := rx) == 13)
    byte[stringptr][--index]~
    PUB waitstr(stringptr, timeout) : success | index, tstart
    {{Wait for a string.
    Parameters: 
    stringptr - points to the comparison string
    timeout - number of clock ticks to wait
    Returns:
    success - true if string found or false if timeout exceeded
    }}
    tstart := cnt
    index~
    repeat until byte[stringptr][index] == 0
    if (cnt - tstart) > timeout
    return
    if byte[stringptr][index] == rx
    index++
    else
    index~
    success~~
    PUB dec(value) | i
    '' Prints a decimal number
    if value < 0
    -value
    tx("-")
    i := 1_000_000_000
    repeat 10
    if value => i
    tx(value / i + "0")
    value //= i
    result~~
    elseif result or i == 1
    tx("0")
    i /= 10
     
    PUB GetDec : value | tempstr[11]
    '' Gets decimal character representation of a number from the terminal
    '' Returns the corresponding value
    GetStr(@tempstr)
    value := StrToDec(@tempstr) 
    PUB StrToDec(stringptr) : value | char, index, multiply
    '' Converts a zero terminated string representation of a decimal number to a value
    value := index := 0
    repeat until ((char := byte[stringptr][index++]) == 0)
    if char => "0" and char =< "9"
    value := value * 10 + (char - "0")
    if byte[stringptr] == "-"
    value := - value
     
    PUB bin(value, digits)
    '' Sends the character representation of a binary number to the terminal.
    value <<= 32 - digits
    repeat digits
    tx((value <-= 1) & 1 + "0")
    PUB GetBin : value | tempstr[11]
    '' Gets binary character representation of a number from the terminal
    '' Returns the corresponding value
     
    GetStr(@tempstr)
    value := StrToBin(@tempstr) 
     
    PUB StrToBin(stringptr) : value | char, index
    '' Converts a zero terminated string representaton of a binary number to a value
     
    value := index := 0
    repeat until ((char := byte[stringptr][index++]) == 0)
    if char => "0" and char =< "1"
    value := value * 2 + (char - "0")
    if byte[stringptr] == "-"
    value := - value
     
    PUB hex(value, digits)
    '' Print a hexadecimal number
    value <<= (8 - digits) << 2
    repeat digits
    tx(lookupz((value <-= 4) & $F : "0".."9", "A".."F"))
    PUB GetHex : value | tempstr[11]
    '' Gets hexadecimal character representation of a number from the terminal
    '' Returns the corresponding value
    GetStr(@tempstr)
    value := StrToHex(@tempstr) 
    PUB StrToHex(stringptr) : value | char, index
    '' Converts a zero terminated string representaton of a hexadecimal number to a value
    value := index := 0
    repeat until ((char := byte[stringptr][index++]) == 0)
    if (char => "0" and char =< "9")
    value := value * 16 + (char - "0")
    elseif (char => "A" and char =< "F")
    value := value * 16 + (10 + char - "A")
    elseif(char => "a" and char =< "f") 
    value := value * 16 + (10 + char - "a")
    if byte[stringptr] == "-"
    value := - value
    PUB GetLong : value | index
    ''Receives four serial bytes that comprise an int in little endian format.
    ''Returns an int.
    repeat index from 0 to 3
    byte[@value][index]:=rx
    PUB PutLong(value) | index
    ''Transmits and int value parameter as four bytes in little endian format.
    repeat index from 0 to 3
    tx(byte[@value][index])
     
    DAT
    '***********************************
    '* Assembly language serial driver *
    '***********************************
    org
    '
    '
    ' Entry
    '
    entry mov t1,par 'get structure address
    add t1,#4 << 2 'skip past heads and tails
    rdlong t2,t1 'get rx_pin
    mov rxmask,#1
    shl rxmask,t2
    add t1,#4 'get tx_pin
    rdlong t2,t1
    mov txmask,#1
    shl txmask,t2
    add t1,#4 'get rxtx_mode
    rdlong rxtxmode,t1
    add t1,#4 'get bit_ticks
    rdlong bitticks,t1
    add t1,#4 'get buffer_ptr
    rdlong rxbuff,t1
    mov txbuff,rxbuff
    add txbuff,#16
    test rxtxmode,#%100 wz 'init tx pin according to mode
    test rxtxmode,#%010 wc
    if_z_ne_c or outa,txmask
    if_z or dira,txmask
    mov txcode,#transmit 'initialize ping-pong multitasking
    '
    '
    ' Receive
    '
    receive jmpret rxcode,txcode 'run chunk of tx code, then return
    test rxtxmode,#%001 wz 'wait for start bit on rx pin
    test rxmask,ina wc
    if_z_eq_c jmp #receive
    mov rxbits,#9 'ready to receive byte
    mov rxcnt,bitticks
    shr rxcnt,#1
    add rxcnt,cnt 
    :bit add rxcnt,bitticks 'ready next bit period
    :wait jmpret rxcode,txcode 'run chunk of tx code, then return
    mov t1,rxcnt 'check if bit receive period done
    sub t1,cnt
    cmps t1,#0 wc
    if_nc jmp #:wait
    test rxmask,ina wc 'receive bit on rx pin
    rcr rxdata,#1
    djnz rxbits,#:bit
    shr rxdata,#32-9 'justify and trim received byte
    and rxdata,#$FF
    test rxtxmode,#%001 wz 'if rx inverted, invert byte
    if_nz xor rxdata,#$FF
    rdlong t2,par 'save received byte and inc head
    add t2,rxbuff
    wrbyte rxdata,t2
    sub t2,rxbuff
    add t2,#1
    and t2,#$0F
    wrlong t2,par
    jmp #receive 'byte done, receive next byte
    '
    '
    ' Transmit
    '
    transmit jmpret txcode,rxcode 'run chunk of rx code, then return
    mov t1,par 'check for head <> tail
    add t1,#2 << 2
    rdlong t2,t1
    add t1,#1 << 2
    rdlong t3,t1
    cmp t2,t3 wz
    if_z jmp #transmit
    add t3,txbuff 'get byte and inc tail
    rdbyte txdata,t3
    sub t3,txbuff
    add t3,#1
    and t3,#$0F
    wrlong t3,t1
    or txdata,#$100 'ready byte to transmit
    shl txdata,#2
    or txdata,#1
    mov txbits,#11
    mov txcnt,cnt
    :bit test rxtxmode,#%100 wz 'output bit on tx pin 
    test rxtxmode,#%010 wc 'according to mode
    if_z_and_c xor txdata,#1
    shr txdata,#1 wc
    if_z muxc outa,txmask 
    if_nz muxnc dira,txmask
    add txcnt,bitticks 'ready next cnt
    :wait jmpret txcode,rxcode 'run chunk of rx code, then return
    mov t1,txcnt 'check if bit transmit period done
    sub t1,cnt
    cmps t1,#0 wc
    if_nc jmp #:wait
    djnz txbits,#:bit 'another bit to transmit?
    jmp #transmit 'byte done, transmit next byte
    '
    '
    ' Uninitialized data
    '
    t1 res 1
    t2 res 1
    t3 res 1
    rxtxmode res 1
    bitticks res 1
    rxmask res 1
    rxbuff res 1
    rxdata res 1
    rxbits res 1
    rxcnt res 1
    rxcode res 1
    txmask res 1
    txbuff res 1
    txdata res 1
    txbits res 1
    txcnt res 1
    txcode res 1
    {{
    &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
    &#9474; TERMS OF USE: MIT License &#9474; 
    &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
    &#9474;Permission is hereby granted, free of charge, to any person obtaining a copy of this &#9474;
    &#9474;software and associated documentation files (the "Software"), to deal in the Software &#9474; 
    &#9474;without restriction, including without limitation the rights to use, copy, modify, &#9474;
    &#9474;merge, publish, distribute, sublicense, and/or sell copies of the Software, and to &#9474;
    &#9474;permit persons to whom the Software is furnished to do so, subject to the following &#9474;
    &#9474;conditions: &#9474; &#9474;
    &#9474; &#9474; &#9474;
    &#9474;The above copyright notice and this permission notice shall be included in all copies &#9474;
    &#9474;or substantial portions of the Software. &#9474;
    &#9474; &#9474; &#9474;
    &#9474;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, &#9474;
    &#9474;INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A &#9474;
    &#9474;PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT &#9474;
    &#9474;HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION &#9474;
    &#9474;OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE &#9474;
    &#9474;SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. &#9474;
    &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
    }}
     
    {{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}}
     
    {{
    Flight Control
    Controls Wii Nunchuck, Wii MotionPlus, Digital Compass, ESC outputs, Kalman Filter, and PID loop.
    Uses 3 cogs.
    }}
    CON
    CLOCKS_PER_MICROSECOND = 5*16 ' simple xin*pll / 1_000_000 
    CLOCKS_PER_MILLISECOND = 5000*16 ' simple xin*pll / 1_000
    HzToSec = 1.0 / 80_000_000.0 
    F_Mode = 4.54545454545 ' fast rotation
    G_Mode = 0.111 ' slow rotation for gyro, decreases sample speed
    A_Mode = 0.25 'slow rotation for accel, decreases sample speed
     
    Deg_Sec = 594.7 'the sensetivity of the Wii Motionplus
    aslope = 2.8571 'scaling used for accel, convert to g's
    aoffset = -1000.0 'offset used for accel, convert to g's
    ''/////////////Tune the Following values for the PID loop and the Kalman Filter///////////////////////
    slope = 0 'FLOATING POINT VALUE scale input values to outputs
    offset = 1150
    slopeY = 0.004899 'used to convert the Wii Nunchuck raw values to radians
    slopeX = 0.004800
    slopeZ = 0.004854
    offsetY = -2.521583
    offsetX = -2.445026
    offsetZ = -2.452766
    Accel_Noise = 0.00003 'expected accel noise
    Accel_Trust = 0.00003 'how much to trust the accel
    Gyro_Trust = 0.000001 'how much to trust the gyro 
    ''ALL REMAINING FLOATING POINT VALUES
    Proportional_Gain = 1 'floating point
    Integral_Gain = 0.0 'fixed point
    Derivative_Gain = 0.0 'floating point
    Mtr_A = 0 'ESC pins
    Mtr_B = 1
    Mtr_C = 2
    Mtr_D = 3
    VAR
    long stack[700] 'stack space for PID loop cog
     
    ''/////////////////////////Variables for the Wii sensor measurements////////////////////////////
    long Ay, Ax, Az, GZ 'conditioned accel and gyro data
    long RawAx, RawAy, RawAz 'raw accel and gyro data
    long RawGx, RawGy, RawGz
    long Cgx, Cgy, Cgz, Cax, Cay, Caz, Cap, Car 'inital center values of gyro and accel
    long AccXCen, AccYCen
    ''/////////////////////Variables for the Kalman Filter//////////////////////////
    long xAngleInt, yAngleInt
    long CurTime, PrevTime, DeltaTime, fDelta
    long AccelX, AccelY, fAccelX, fAccelY 
    long GyroX, GyroY, fGyroX, fGyroY
    long GyroXUnb, GyroYUnb, fGyroXUnb, fGyroYUnb
    long xAngle, xBias
    long xP00, xP01, xP10, xP11
    long yAngle, yBias
    long yP00, yP01, yP10, yP11
    long QAngle, QGyro, RAngle
     
    ''/////////////////////////Variables for the PID loop//////////////////////////// 
    long SP[5], b, m, Kp, Ki, Kd 'gains
    long M_A, M_B, M_C, M_D 'motor outputs
    long Auto_Pitch, Auto_Roll 'command given roll and pitch
    long Acc_Pitch, Acc_Roll 'instaneous roll and pitch
    long SP_R, RawCompass, Alt 'rotate, compass, and altitude
    long count
    byte cog1, cog2
     
    OBJ
    IMU : "I2C_Wii"
    f : "F32"
    ESC : "ESC_Spin" 
    PUB Stop 'stop all objects and cogs
    M_A := M_B := M_C := M_D := 1000
    ESC.Stop
    f.Stop
    cogstop(cog1)
    'cogstop(cog2)
     
     
    PUB Init(SCL, SDA, IMU_Mode)
    IMU.init( SCL,SDA,IMU_Mode ) ' Initialize the accelerometer / gyro
    IMU.enable ' Enable the Wii 
    f.start ' Start the F32 Floating Point Math object
     
    KalmanInit( Accel_Trust, Gyro_Trust, Accel_Noise ) 'initialize the kalman filter 
    xAngleInt := yAngleInt := 0
     
    Delay_MS(500) ' Wait 1/2 second before initializing gyros 
    ''/////////Acquire some center values//////////////////////
    IMU.readData 'Must be called before measurements are received
    IMU.readData 'retrieve center values
    Cgx := IMU.rotate_x
    Cgy := IMU.rotate_y
    Cgz := IMU.rotate_z
    Cax := IMU.accelX
    Cay := IMU.accelY
    Caz := IMU.accelZ
    Gyro_Accel_Compass_Calibrate 
    PrevTime := cnt
     
    SP[0] := 0 'initialize set points to zero
    SP[1] := 0
    SP[2] := 0
    SP[3] := 0
    SP[4] := 0
     
    m := slope 'SET CONDITIONS AND GAINS. ONLY M AND KI ARE FLOATING POINT
    b := offset
    Kp := Proportional_Gain
    Ki := Integral_Gain
    Kd := Derivative_Gain
    Delay_MS(3000) 
    cog1 := cognew(Flight_Control, @stack[0]) 'start new cog for PID loop
    return
     
    PUB Motor_Start
    ESC.Start 'start ESC object
    Delay_MS(1000)
     
    '''''''''''''''''''''''INITIALIZE ESC's AND MOTOR's'''''''''''''''''''''' 
    ESC.Servo(Mtr_A,1000) 'initialize ESC's by sending a low pulse to each
    ESC.Servo(Mtr_B,1000)
    ESC.Servo(Mtr_C,1000)
    ESC.Servo(Mtr_D,1000)
    Delay_MS(500)
    return
    PUB Motor_Run
    ESC.Servo(Mtr_A, M_A) 'SEND OUTPUT PULSE TO MOTORS A, B, C, AND D
    ESC.Servo(Mtr_B, M_B) 
    ESC.Servo(Mtr_C, M_C) 
    ESC.Servo(Mtr_D, M_D) 
    PUB PID(Pitch_Orient, Roll_Orient, SP_Pitch, SP_Roll, Rotate_Command, Alt_Command)
    SP[0] := 0 'SAVE SETPOINTS AND FEEDBACK SIGNAL IN SPECIFIED ARRAY SLOTS
    SP[1] := 0 
    SP[2] := 0
    SP[3] := 0 'SP_Pitch
    SP[4] := 0 'SP_Roll
    Auto_Pitch := Pitch_Orient
    Auto_Roll := Roll_Orient
    SP_R := Rotate_Command
    Alt := Alt_Command
    return
     
    PUB Flight_Control | GX_PID, GY_PID, GZ_PID, AccX_PID, AccY_PID, FB[5], Pre_Error[5], e[5], P[5], I[5], D[5], dt[5], pre_dt[5], j
    '''''''''''''''''''''''FLIGHT CONTROL AND PID LOOP''''''''''''''''''''''''''''''''''''''''''''''''''
     
    Update 'first get initial sensor values
    Pre_Error[0] := SP[0] - GyroXUnb 'set first error
    Pre_Error[1] := SP[1] - GyroYUnb
    Pre_Error[2] := SP[2] - GZ
    Pre_Error[3] := SP[3] - Acc_Pitch
    Pre_Error[4] := SP[4] - Acc_Roll 
    I[0] := 0 'initialize integral to zero
    I[1] := 0
    I[2] := 0
    I[3] := 0 
    I[4] := 0 
    Repeat
    Update 'get sensor values
    FB[0] := GyroXUnb 'GX 
    FB[1] := GyroYUnb 'GY 
    FB[2] := 0 'GZ
    FB[3] := Acc_Pitch
    FB[4] := Acc_Roll 
    Repeat j from 0 to 4 
    dt[j] := cnt 'get system counter values 
    dt[j] := f.FSub(f.FFloat(dt[j]), pre_dt[j]) 'get difference of previous counter and current counter
    dt[j] := f.FMul(f.FFloat(dt[j]), HzToSec) 'convert system counts to sec
    e[j] := SP[j] - FB[j] 'error = set point - feedback
    I[j] := I[j] + (f.FRound(f.FMul(f.FFloat(e[j]), dt[j]))) 'integral
    D[j] := f.FRound(f.FDiv(f.FSub(f.FFloat(e[j]), Pre_Error[j]), dt[j])) 'derrivative
    Pre_Error[j] := e[j] 'previous error
    pre_dt[j] := dt[j] 'previous time
    GX_PID := (Kp * e[0]) + (f.FRound(f.FMul(Ki, f.FFloat(I[0])))) + (f.FRound(f.FMul(Kd, f.FFloat(D[0])))) 'multiply by gain values for each
    GY_PID := (Kp * e[1]) + (f.FRound(f.FMul(Ki, f.FFloat(I[1])))) + (f.FRound(f.FMul(Kd, f.FFloat(D[0])))) 'pid loop
    GZ_PID := (Kp * e[2]) + (f.FRound(f.FMul(Ki, f.FFloat(I[2])))) + (f.FRound(f.FMul(Kd, f.FFloat(D[0]))))
    AccX_PID := (Kp * e[3]) + (f.FRound(f.FMul(Ki, f.FFloat(I[3])))) + (f.FRound(f.FMul(Kd, f.FFloat(D[0]))))
    AccY_PID := (Kp * e[4]) + (f.FRound(f.FMul(Ki, f.FFloat(I[4])))) + (f.FRound(f.FMul(Kd, f.FFloat(D[0])))) 
     
    ''/////////////////////////Motor Outputs for a + configuration//////////////////////////////////////////////
    M_A := ((-GY_PID + GZ_PID - AccX_PID - Auto_Pitch + SP_R + Alt + b)#>1100)<#2000 'add all to motor outputs
    M_B := ((GX_PID - GZ_PID - AccY_PID + Auto_Roll - SP_R + Alt + b)#>1100)<#2000 
    M_C := ((GY_PID + GZ_PID + AccX_PID + Auto_Pitch + SP_R + Alt + b)#>1100)<#2000 
    M_D := ((-GX_PID - GZ_PID + AccY_PID - Auto_Roll - SP_R + Alt + b)#>1100)<#2000 
    'Motor_Run 'output to motors
    count ++
     
    PUB Update | radius 
    ''/////////////Read in Wii MotionPlus and Wii Nunchuck Raw Values/////////////////////
     
    IMU.readData
    IMU.readData 
    RawAx := IMU.accelX
    RawAy := IMU.accelY
    RawAz := IMU.accelZ
    RawGx := IMU.rotate_x 
    RawGy := IMU.rotate_y
    RawGz := IMU.rotate_z
    RawCompass := IMU.Digital_Compass
    '''////////////////////////convert gyro data to deg/sec, Make sure to select fast mode or slow mode/////////////////////
     
    GZ := f.FRound(f.FMul(f.FDiv(f.FSub(f.FFloat(Cgz), f.FFloat(RawGz)), f.FDiv(f.FFloat(Cgz), Deg_Sec) ), G_Mode))
    Ax := f.FRound(f.FAdd(f.FMul(f.FFloat(RawAx), aslope), aoffset))
    Ay := f.FRound(f.FAdd(f.FMul(f.FFloat(RawAy), aslope), aoffset))
    Az := f.FRound(f.FAdd(f.FMul(f.FFloat(RawAz), aslope), aoffset))
    radius := ^^((Ax)*(Ax) + (Az)*(Az) + (Ay)*(Ay))
    Acc_Pitch := f.FRound(f.FMul(f.Degrees(f.ACos(f.FDiv(f.FFloat(Ay),f.FFloat(radius)))), A_Mode))-AccXCen
    Acc_Roll := f.FRound(f.FMul(f.Degrees(f.ATan2(f.FFloat(Ax), f.FFloat(Az))), A_Mode))-AccYCen 
    ''///////////////Convert Accelerometer and Gyro readings to radians///////////////////////
    fAccelX := f.Radians(f.FAdd(f.FMul(slopeX, f.FFloat(RawAx)), offsetX)) 
    fAccelY := f.Radians(f.FAdd(f.FMul(slopeY, f.FFloat(RawAy)), offsetY))
    fGyroX := f.Radians(f.FMul(f.FDiv(f.FSub(f.FFloat(Cgx), f.FFloat(RawGx)), f.FDiv(f.FFloat(Cgx), Deg_Sec) ), G_Mode)) 
    fGyroY := f.Radians(f.FMul(f.FDiv(f.FSub(f.FFloat(Cgy), f.FFloat(RawGy)), f.FDiv(f.FFloat(Cgy), Deg_Sec) ), G_Mode)) 
     
    'Compute the time delta
    CurTime := cnt
    DeltaTime := CurTime - PrevTime
    PrevTime := CurTime
    'Convert the delta to seconds 
    fDelta := f.FFloat(DeltaTime)
    fDelta := f.FMul(fDelta, HzToSec) 
    'Run the filter 
    KalmanPredict( fGyroX, fGyroY, fDelta )
    KalmanUpdate( fAccelX, fAccelY )
    'Produce the results 
    xAngleInt := f.FRound(f.Degrees(xAngle)) 
    yAngleInt := f.FRound(f.Degrees(yAngle))
     
    GyroXUnb := f.FRound(f.Degrees(fGyroXUnb)) 
    GyroYUnb := f.FRound(f.Degrees(fGyroYUnb)) 
    return
     
    /
     
    ...........................
     
     
    /
    {{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}}
     
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[URL]http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd[/URL]">
    <html xmlns="[URL]http://www.w3.org/1999/xhtml[/URL]">
    <html>
    <head>
    <style type=text/css>
    DIV {COLOR: #009900; }
    </style>
    <TITLE>Universal Ground Station</TITLE>
    <script type="text/javascript">
    function GetDescriptionFor(e)
    {
    var result, code;
    if ((e.charCode) && (e.keyCode==0))
    {
    result = "charCode: " + e.charCode;
    code = e.charCode;
    } else {
    result = "keyCode: " + e.keyCode;
    code = e.keyCode;
    }
    if (code == 97) setdirection(3)
    else if (code == 98) setdirection(2)
    else if (code == 99) setdirection(1)
    else if (code == 100) setdirection(0)
    else if (code == 101) loadXMLDoc('send.xml')
     
    return;
    }
    function MonitorKeyPress(e)
    {
    if (!e) e=window.event;
    var cell = document.getElementById("td-keypress");
    cell.innerHTML = GetDescriptionFor(e);
    }
    document.onkeypress = MonitorKeyPress;
    var state = 4;
    function ajax_object() {
    if (window.XMLHttpRequest) {
    return new XMLHttpRequest();
    } else if(window.ActiveXObject) {
    return new ActiveXObject("Microsoft.XMLHTTP");
    } else {
    alert("Your Browser does not support AJAX!\nTry Firefox?");
    }
    }
     
    //XmlHttpRequest object
    var directionReq = ajax_object();
    function setdirection(directionstate) {
    state = directionstate;
    //alert("directionstate: " + directionstate);
    if (directionReq.readyState == 4 || directionReq.readyState == 0) {
    //ajax_request('http://192.168.1.120/shp.shp?directionstate=' + directionstate + '&ms=' + new Date().getTime(), null, function(data){ return });
    directionReq.open("GET", 'http://192.168.1.120/shp.shp?directionstate=' + directionstate + '&ms=' + new Date().getTime(), true);
     
    directionReq.onreadystatechange = handledirectionState;
    directionReq.send(null);
     
    } 
    }
    function handledirectionState() {
    if (directionReq.readyState == 4) {
     
    var div = document.getElementById('directionStatusIndicator');
    var str = directionReq.responseText;
    //alert("response: " + directionReq);
    div.innerHTML = str;
    // if ((typeof(str) == 'undefined') || (str == '')) {
    //if (state > '0') {
    // div.innerHTML = "<strong style='color:orange;font-size:26px;'>On</strong>";
    // } else {
    // div.innerHTML = "<strong style='color:gray;font-size:26px;'>Waiting for input</strong>";
    //}
    //} else {
    // div.innerHTML = str;
     
    //var refreshIntervalId = (setInterval("loadXMLDoc('send.xml')",3500));
    //}
    }
    }
     
    var xmlhttp = ajax_object(); 
    function loadXMLDoc(url)
    {
    var txt,x,xx,i, j;
    //ajax_request("send.xml", null, function(data){ return });
    xmlhttp.onreadystatechange=function()
    {
    if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
    txt="<table border='1'><tr><th>Acceleration X</th><th>Acceleration Y</th><th>Acceleration Z</th><th>Roll Degrees</th><th>Pitch Degrees</th><th>Yaw Degrees</th></tr>";
    x=xmlhttp.responseXML.documentElement.getElementsByTagName("DATA");
    for (i=0;i<x.length;i++)
    {
    txt=txt + "<tr>";
    xx=x[i].getElementsByTagName("A_X");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    xx=x[i].getElementsByTagName("A_Y");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    xx=x[i].getElementsByTagName("A_Z");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    xx=x[i].getElementsByTagName("R_D");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    xx=x[i].getElementsByTagName("P_D");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    xx=x[i].getElementsByTagName("Y_D");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    txt=txt + "</tr>";
    }
     
    txt=txt + "</table>"; 
     
    document.getElementById('txtDATAInfo').innerHTML=txt;
     
    txt="<table border='1'><tr><th>Altitude in Inches</th><th>Behavior</th><th>Heading</th><th>Location</th><th>Battery Life</th><th>Sensor Feedback</th></tr>";
    x=xmlhttp.responseXML.documentElement.getElementsByTagName("DATA");
    for (j=0;j<x.length;j++)
    {
     
    txt=txt + "<tr>";
    xx=x[j].getElementsByTagName("A_I");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    xx=x[j].getElementsByTagName("B");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    xx=x[j].getElementsByTagName("H");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    xx=x[j].getElementsByTagName("L");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    xx=x[j].getElementsByTagName("B_L");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    xx=x[j].getElementsByTagName("S_F");
    {
    try
    {
    txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>";
    }
    catch (er)
    {
    txt=txt + "<td> </td>";
    }
    }
    txt=txt + "</tr>";
    }
     
    txt=txt + "</table>";
     
     
    document.getElementById('txtDATA2Info').innerHTML=txt;
    }
    }
    xmlhttp.open("GET",url,true);
    xmlhttp.send();
    //}
    //function begin(a)
    //{
    //state = a
    //if (state == '0') {(setdirection(0));}
    //else if (state == '1') {
    // clearInterval(refreshIntervalId);
    // (setdirection(1));
    // }
    //else if (state == '2') {
    // clearInterval(refreshIntervalId);
    // (setdirection(2));
    // }
    //else if (state == '3') {
    // clearInterval(refreshIntervalId);
    // (setdirection(3));
    // }
    //else {
    //var refreshIntervalId = (setInterval("loadXMLDoc('send.xml')",3500));
    }
    //else {}
    //}
    </script>
    </head>
    <body bgcolor="#000000">
    <div align=center><table border="10"cellpadding="0"cellspacing="1"width="640"height="480"><tr><td bgcolor="white"align="center"valign="middle"><img src="[URL="http://192.168.1.101:8070/video.mjpg%22%3E%3C/td%3E%3C/tr%3E%3C/table%3E%3C/div%3E%3Cbr"]http://192.168.1.101:8070/video.mjpg"></td></tr></table></div><br[/URL]>
    <div align=center> <input type="button" name="Up" value="Up" onclick="setdirection(0)" /><br>
    <input type="button" name="Left" value="Left" onclick="setdirection(1)" />
    <input type="button" name="Right" value="Right" onclick="setdirection(2)" /><br>
    <input type="button" name="Down" value="Down" onclick="setdirection(3)" /><br>
    <div id="directionStatusIndicator"><strong style="color:green">Waiting for input.</strong> </div><br>
    <input type="button" name="Send Data" value="Send Data" onclick="loadXMLDoc('send.xml')" />
     
     
    </div><br> 
    <div align="center" id="txtDATAInfo">
    </div><br>
    <div align="center" id="txtDATA2Info">
    </div>
    </body>
    </html>
     
     
    {{&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&}}
     
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "[URL]http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd[/URL]">
    <html xmlns="[URL]http://www.w3.org/1999/xhtml[/URL]">
    <html>
    <head>
    <style type=text/css>
    DIV {COLOR: #009900; }
    </style>
    <TITLE>Universal Ground Station</TITLE>
    <script language="javascript" src="RXTXajax.js" type="text/javascript"></script>
    <script type="text/javascript">
    var refreshIntervalId = (setInterval("loadXMLDoc('send.xml')",1100));
    var state = 4;
    function ajax_request(url, data, callback)
    {
    var xhrobj = ajax_object();
    xhrobj.open("GET", url);
    xhrobj.setRequestHeader("X-Requested-With", "XMLHttpRequest"); 
     
    xhrobj.onreadystatechange = function()
    {
    if (xhrobj.readyState == 4 && xhrobj.status == 200)
    {
    if (xhrobj.responseText)
    {
    callback(xhrobj.responseText);
    }
    }
    };
    xhrobj.send(data);
    }
     
    function begin(a)
    {
    state = a
    if (state == '0') {(setdirection(0));}
    else if (state == '1') {
    clearInterval(refreshIntervalId);
    (setdirection(1));
    }
    else if (state == '2') {
    clearInterval(refreshIntervalId);
    (setdirection(2));
    }
    else if (state == '3') {
    clearInterval(refreshIntervalId);
    (setdirection(3));
    }
    else {
    (setInterval("loadXMLDoc('send.xml')",1100));
    }
    //else {}
    }
    </script>
    </head>
    <body bgcolor="#000000" onLoad="begin(4);">
    <div align=center><table border="10"cellpadding="0"cellspacing="1"width="640"height="480"><tr><td bgcolor="white"align="center"valign="middle"><img src="[URL="http://192.168.1.101:8070/video.mjpg%22%3E%3C/td%3E%3C/tr%3E%3C/table%3E%3C/div%3E%3Cbr"]http://192.168.1.101:8070/video.mjpg"></td></tr></table></div><br[/URL]>
    <div align=center> <input type="button" name="Up" value="Up" onclick="begin(0)" /><br>
    <input type="button" name="Left" value="Left" onclick="begin(1)" />
    <input type="button" name="Right" value="Right" onclick="begin(2)" /><br>
    <input type="button" name="Down" value="Down" onclick="begin(3)" />
     
    <div id="directionStatusIndicator"><strong style="color:green">Waiting for input.</strong> </div>
    </div><br> 
    <div align="center" id="txtDATAInfo">
    </div><br>
    <div align="center" id="txtDATA2Info">
    </div>
    </body>
    </html>
    

    I will be happy to forward on the rest of our code per request.
  • pthartmannpthartmann Posts: 27
    edited 2011-07-28 14:16
    sample.jpg

    I don't have pictures at work- will have to find more. It's too bad I can't load powerpoint files, or docs.
    499 x 413 - 50K
  • pthartmannpthartmann Posts: 27
    edited 2011-08-03 05:43
    Our code, en mass, is now attached.

    I have also attached our final project we submitted to our university.
    -Albeit this report is geared towards someone unfamiliar with the Spinneret, it still contains the whole scope of what we were able to accomplish.
  • pthartmannpthartmann Posts: 27
    edited 2011-08-03 05:59
    groundstation.jpg
    (Click to enlarge)**
    This is what our Spinneret can host.
    -We have full two way communication via wifi.
    A user can transmit and receive data to and from our U.A.V.
    It also manages our IP video stream.
    Control of the craft can be via computer, remote control, or smart phone.
    -With simple networking we were able to accomplish this both locally and over the internet.

    One of our biggest accomplishments, in conjunction with our Spinneret telemetry, was being able to design and build a high voltage circuit that could capture pulses from a GM tube. This data is also counted within another Propeller μC, transmitted to the Spinneret, and hosted on the webpage.

    **We have since added many more commands(buttons), but I wanted to include this image in order to provide a visual of what our Spinneret is hosting.
    814 x 477 - 82K
  • jkoteckijkotecki Posts: 1
    edited 2011-10-10 18:47
    Is it possible to get the individual code files? I'm not sure where to split the code file you posted.
  • M. K. BorriM. K. Borri Posts: 279
    edited 2011-10-13 11:34
    Woot! This looks sweet. I've been doing this with cell phones, want to share code?
  • john_sjohn_s Posts: 369
    edited 2011-10-13 12:28
    Woot! This looks sweet. I've been doing this with cell phones, want to share code?

    M.K.,

    Go ahead - I'm interested in reading more of your design and code.

    Thanks,
    John
Sign In or Register to comment.