Power_Broker

Active member
Also, I've been starting to get serious about learning how to develop a UAV autopilot. To help do this, I wan't to use the game War Thunder (as mentioned in post 1). Basically, I want to use War Thunder as a plane simulator with the "plane" being controlled by an Arduino running autopilot control code. This way, I can rapidly develop autopilot algorithms without worrying about crashing my plane :D


To start things off, I've written a Python module that will get plane telemetry "real-time" during the virtual plane's flight. Here is a link to the Python module.

Next, I wrote a Python script that imported said module, queries for plane telemetry, logs the telemetry data, then reports the data to the Arduino running the Autopilot for feedback. Here is a link to the Python script.

Lastly, I need to write the Arduino code to accept the telemetry feedback, compute control outputs, and then feed those outputs to War Thunder as a "HID joystick" device. As part of the Arduino code, I'm in the process of developing a custom PID library. Here is a link to the PID library.
 

Power_Broker

Active member
The other day I was thinking about how the ArdUAV library works and I realized I need to overhaul it. The reasons why it needs an overhaul:
  • The sub-libraries (i.e. the gps library, serial transfer library, etc) are not independent. They can not be run independent of ArdUAV and therefore can not be used "standalone". This is mainly due to the library written in (more or less) spaghetti code.
  • Many of the sub-libraries use serialEvent() calls. This complicates the code and is the main reason why I originally wrote the code like spaghetti (as noted above)
  • Many of the sub-libraries use "String" objects. This causes large processing overhead and (potentially) memory errors in microcontrollers like the one I'm using
In order to overhaul the ArdUAV library, I'm rewriting the sub-libraries one at a time. After being rewritten, each of the sub-libraries will be optimized and able to be used "standalone". This will greatly simplify ArdUAV, provide easier readability/maintainability, and will decrease the amount of memory/processing power required by the library.

The first library to be rewritten was the GPS library and it was finished today. Here is a link to the new GPS library: https://github.com/PowerBroker2/NEO-6M_GPS

Next, I'll tackle the serial transfer library (AirComms.h)...
 

clolsonus

Well-known member
If any of this helps, or gives you ideas, I recently developed a simple and lean proto-messaging system that runs on arduino, linux (raspberry pi, etc.), and python. If you've seen mavlink, it's sort of a mavlink-lite without requiring you to also generate 80 pages of source code to support every message for the entire spec.

The idea is you define your communication messages with a simple json format. From there a script generates both C++ code and python code to read/parse and also generate the messages. It's intended for communicating over limited bandwidth pipes (like serial ports) where packing efficiency makes a big difference. I also use it to log my important data messages to a file on board.

It supports packing floating point numbers into smaller signed or unsigned integers with some scaling value. For example if you estimate wind speed on board and want to send it to the ground, wind speed might be a 4-byte float. But (for example) lets' say I never expect wind speed to be > 60 kts and I'm ok with a resolution of 0.25 kts. Then I can multiply the wind by 4 and store it as a 1-byte unsigned int. On the receiving side I divide by 4 and tada I have my original wind speed back (within 0.25 kts.) This is all handled by the autogenerated code.

I also have low level packet transport code (2 start bytes, a message id, a message len, and 2 checksum bytes) you are welcome to use.

In addition, the message system supports including arrays in the structure like "float orientation[9]" and very recently I introduced the ability to efficiently pack and transmit variable length character strings. This is great for ascii messages or commands or things that don't quite fit as a number. (Requires std::string to be installed if you use this feature on arduino, but only if you use strings in your structures.)

All this is licensed with the MIT open-source license.

When I introduced this system into my own code, it really helped clean up a number of items. Because the message structures and pack/unpack code is auto-generated, you can just change the message json structure and the tricky code bits are all updated by the auto-code generator. It really helps reduce the chance of messaging errors because you changed once side and didn't quite get the other side exactly right.

The low level (packed) messages are compatible between python and C++, so for myself, my airplane generates messages in C++ and sends them to the ground, and my python link script on the ground reads/parses/extracts them and relays the data to my instrument panel and map.

In case it helps anyone, examples and autogenerator script is here:

https://github.com/AuraUAS/aura-core/tree/master/tools/messages

The low level serial packet transmit/receive/validate code I use is here (linux version):

https://github.com/AuraUAS/aura-core/blob/master/src/comms/serial_link.cxx
https://github.com/AuraUAS/aura-core/blob/master/src/comms/serial_link.hxx

And I have a compatible arduino version that uses "Serial" here:

https://github.com/AuraUAS/aura-sensors/tree/master/src/serial_link
 

Power_Broker

Active member
I was able to successfully update the AirComms library by creating a standalone library named SerialTransfer.

This is a simple, non-blocking library that quickly, easily, and reliably transfers up to 255 bytes of payload data from one Arduino to another via UART. In my case, I'll be sending data between the ground station and the flight controller.

I am currently trying to mesh the new GPS and transfer libraries into ArdUAV in addition to basic optimizations throughout the other parts of the library.
 

Power_Broker

Active member
Here is a sort of "packet anatomy" as defined by the library:

Code:
01111110 00000000 00000000 00000000 ... 00000000 10000001
|      | |      | |      | |      | | | |      | |______|__Stop byte
|      | |      | |      | |      | | | |______|___________8-bit CRC
|      | |      | |      | |      | |_|____________________Rest of payload
|      | |      | |      | |______|________________________2nd payload byte
|      | |      | |______|_________________________________1st payload byte
|      | |______|__________________________________________# of payload bytes
|______|___________________________________________________Start byte (constant)
 

Power_Broker

Active member
I finished refactoring most of the library (yay!) and verified it is working/stable for full manual control. Here is the link: ArdUAV GitHub

The few things I need to still update:
- Parse and convert the UTC date pieces from the "ddmmyy" string in the GPS library
- Determine loss link condition for both the hand controller and flight controller
- Basic pitch/roll limiter

Lastly, I did have a surprise test flight with the legacy code yesterday. Due to early rotation on takeoff roll, it crashed before it got off the ground :confused:. None of it was videotaped, but damage was minimal - should have another test flight soon!
 

Power_Broker

Active member
Did some hardware work this past weekend.

I added a panel mount barrel jack, new power switch (DPDT), and a panel mount USB connector. The barrel jack allows me to easily plug in power from a 12V wall power supply during avionics testing on the bench (I've depleted way too many batteries during bench testing, lol). The new power switch (previously a single "on-off" switch) has 3 positions: battery power, wall power (i.e. "shore power"), and off. Lastly, the panel mount USB connector gives me super easy access to the flight controller's USB port - making the process of debugging/firmware updating faster and easier.

Here are some pics:

IMG_5127.jpg


IMG_5126.jpg


IMG_5128.jpg


IMG_5143.jpg


IMG_5144.jpg


IMG_5147.jpg


IMG_5146.jpg
 

Power_Broker

Active member
My apologies for not posting a lot lately, but I've been having trouble creating my War Thunder based PilSim.

Basically, the software running on my PC that gathers the localhost game data to be sent to the Teensy is giving me trouble - my Python code is too slow and my C++ app randomly crashes. At this point I'm going to just skip the PilSim portion of the project and do all of my testing in well controlled test flights and pray I don't crash my plane too badly doing it.

I still have some small improvements to do for the ArdUAV library, so I'll be working on those plus adding a very basic "straight and level" autopilot to the code.

Something important to note here: ArdUAV is not meant to be like ArduPilot/Pixhawk. ArdUAV is designed to be an easy to implement RC airplane code "framework" that takes care of the basic necessities while giving you the flexibility to add and experiment with your own features. That means anything that does NOT deal with basic manual flight, telemetry, or sensors/sensor fusion will NOT be implemented in the ArdUAV library. For this reason, I'm going to develop my basic "straight and level" autopilot at the sketch level and not in the library.

Might have a test flight in 2-3 weeks if I'm lucky :)
 

MechWrench

New member
Here is a sort of "packet anatomy" as defined by the library:

Code:
01111110 00000000 00000000 00000000 ... 00000000 10000001
|      | |      | |      | |      | | | |      | |______|__Stop byte
|      | |      | |      | |      | | | |______|___________8-bit CRC
|      | |      | |      | |      | |_|____________________Rest of payload
|      | |      | |      | |______|________________________2nd payload byte
|      | |      | |______|_________________________________1st payload byte
|      | |______|__________________________________________# of payload bytes
|______|___________________________________________________Start byte (constant)

You might be better off encoding and decoding your packets into a COBS framework - https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing. Otherwise, you're going to deal with occasional payload or CRC bytes that might be (in the event of loss of packet sync) mistaken for the constant you've chosen for the packet start byte.
 

Power_Broker

Active member
You might be better off encoding and decoding your packets into a COBS framework - https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing. Otherwise, you're going to deal with occasional payload or CRC bytes that might be (in the event of loss of packet sync) mistaken for the constant you've chosen for the packet start byte.

Very true, I've seen that before. I once had a workaround for such a scenario that didn't involve encoding the entire packet - maybe if I can remember what I did I'll incorporate it. Might even try the whole COBS encoding...

Thanks for the tip!!
 

Power_Broker

Active member
You might be better off encoding and decoding your packets into a COBS framework - https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing. Otherwise, you're going to deal with occasional payload or CRC bytes that might be (in the event of loss of packet sync) mistaken for the constant you've chosen for the packet start byte.

I've done some thought on this and I've decided to do COBS, but with a twist: COBS will be implemented for the packet start byte, not the end byte.

Here is the packet anatomy I'm envisioning:

Code:
01111110 11111111 00000000 00000000 00000000 ... 00000000 10000001
|      | |      | |      | |      | |      | | | |      | |______|__Stop byte
|      | |      | |      | |      | |      | | | |______|___________8-bit CRC
|      | |      | |      | |      | |      | |_|____________________Rest of payload
|      | |      | |      | |      | |______|________________________2nd payload byte
|      | |      | |      | |______|_________________________________1st payload byte
|      | |      | |______|__________________________________________# of payload bytes
|      | |______|___________________________________________________Initial COBS Overhead Byte
|______|____________________________________________________________Start byte (constant)

The Initial COBS Overhead Byte will be the number of bytes to its right that is equal to the Start Byte. This process continues until reaching the last byte in the packet that originally held the same value as the Start Byte. This last byte will always be 0 since there are no other bytes to encode/stuff.

Once I'm done adding this to the library/validating it, I'll let y'all know.
 

Power_Broker

Active member
Finished adding the COBS encoding/decoding!

Next I'll be working on polishing up the GPS library. More specifically, I'll be working on making functions that convert DD to DM and vice versa.
 

Power_Broker

Active member
Updated the GPS library to expose lat/lon coordinates in "Degrees Decimal", implement parsing of individual pieces of the UTC timestamp (i.e. you can now determine the year, month, day, hour, minute, and second the previous GPS data was determined), and made some bug fixes.
 

Power_Broker

Active member
Since the last update, I've flown 2 test flights.

The first test flight ended in a catastrophic crash after the plane lost connection to the hand controller. This loss of connection was most likely due to a power-cycle due vibrations during the flight mixed with bad wiring/soldering.

After the first test flight, I redesigned the plane's frame, built the fuselage from scratch, reinstalled all of the electronics, and revamped the power wiring. Once the new plane was complete, I took it out for it's maiden voyage. The flight was very successful despite it being very tail heavy and sustained minimal damage on landing. Basically, I landed it in the grass and the prop happened to dig into the ground - causing the nose to nearly rip off, but was easily fixed by hot glue.

In addition to fixing the physical plane, I also added a second Teensy microcontroller to the plane as an SD card telemetry datalogger. It receives telemetry data from the main flight controller and simply logs it on the SD card. Simple but powerful.

I'll be flying soon (weather permitting) and should have a video/quality pics, but till then, here are some pics of the current plane on the ground:

IMG_5478.jpg


IMG_5470.jpg


IMG_5471.jpg


IMG_5474.jpg


IMG_5475.jpg


IMG_5476.jpg


IMG_5477.jpg


IMG_5472.jpg


IMG_5473.jpg
 

Power_Broker

Active member
Last night I updated the Python Configuration GUI for the ArdUAV library to support the saving/loading of config files in JSON format. Now you can save configurations for different planes with the same library without having to manually change settings!

Example config.json:
JSON:
{"GSTools": {"AileronSettings": {"AileronAnalogPin": "A0",
                                 "AileronReverse": "1",
                                 "MaxAileronADCValue": "41920",
                                 "MaxAileronServoValue": "2000",
                                 "MinAileronADCValue": "25190",
                                 "MinAileronServoValue": "1000"},
             "ElevatorSettings": {"ElevatorAnalogPin": "A17",
                                  "ElevatorReverse": "1",
                                  "MaxElevatorADCValue": "41220",
                                  "MaxElevatorServoValue": "2000",
                                  "MinElevatorADCValue": "24030",
                                  "MinElevatorServoValue": "1000"},
             "RudderSettings": {"MaxRudderADCValue": "41740",
                                "MaxRudderServoValue": "2000",
                                "MinRudderADCValue": "23300",
                                "MinRudderServoValue": "1000",
                                "RudderAnalogPin": "A2",
                                "RudderReverse": "0"},
             "SerialSettings": {"GS_CommandPortNumber": "4",
                                "GS_DebugPortNumber": "0",
                                "GS_TelemetryPortNumber": "3"},
             "ThrottleSettings": {"MaxThrottleADCValue": "41060",
                                  "MinThrottleADCValue": "24130",
                                  "ThrottleAnalogPin": "A3",
                                  "ThrottleReverse": "1"}},
 "IFCTools": {"AutopilotSettings": {"MaxPitchDownAngle": "25",
                                    "MaxPitchUpAngle": "-45",
                                    "MaxRollLeftAngle": "50",
                                    "MaxRollRightAngle": "-50",
                                    "UnsafePitchDownAngle": "10",
                                    "UnsafePitchUpAngle": "-30",
                                    "UnsafeRollLeftAngle": "35",
                                    "UnsafeRollRightAngle": "-35"},
              "ControlSurfaceSettings": {"ElevatorPin": "26",
                                         "LeftAileronPin": "25",
                                         "RightAileronPin": "24",
                                         "RudderPin": "39",
                                         "ThrottlePin": "2"},
              "OtherSettings": {"LiDARFixedMount": "1",
                                "PitotTubeAnalogPin": "A9"},
              "SerialSettings": {"IFC_CommandPortNumber": "4",
                                 "IFC_DebugPortNumber": "0",
                                 "IFC_GPSPortNumber": "1",
                                 "IFC_TelemetryPortNumber": "3"}},
 "SharedTools": {"AileronSettings": {"MaxAileronValue": "50",
                                     "MinAileronValue": "1000"},
                 "ElevatorSettings": {"MaxElevatorValue": "2000",
                                      "MinElevatorValue": "2000"},
                 "Reporting_TimeoutSettings": {"CommandReportingRate": "20.00",
                                               "LossLinkTimeout": "1000",
                                               "TelemetryReportingRate": "10.00"},
                 "RudderSettings": {"MaxRudderValue": "1000",
                                    "MinRudderValue": "1000"},
                 "SerialSettings": {"Command_Port_Baud": "115200",
                                    "Debug_Port_Baud": "115200",
                                    "GPS_Port_Baud": "9600",
                                    "Telemetry_Port_Baud": "9600"},
                 "ThrottleSettings": {"MaxThrottleValue": "170",
                                      "MinThrottleValue": "180"}}}
 

clolsonus

Well-known member
So now you should potentially be able to do things like elevon or ruddervator or flaperon mixing right on board the airplane, right? Also things like scheduling elevator with throttle or flap changes, and all kinds of exotic mixing or control allocation modes. I really like the idea of being able to do all the fancy stuff right on the airplane (with a json config file to set it up) versus needing fancier and fancier transmitters to do all these things.

One nifty side effect is if you do simple gyro stabilization, you could apply the control allocation after the stabilization step so you can gyro stabilize all kinds of fancy aircraft configurations ... something you can't do from a transmitter, no matter how fancy schmancy it is.

The down side is setting a configuration and mixing modes through a json file on their PC may be harder for people who are used to doing this stuff with obscure button/dial presses on a 120x80 pixel monochrome screen. :)
 

Power_Broker

Active member
So now you should potentially be able to do things like elevon or ruddervator or flaperon mixing right on board the airplane, right? Also things like scheduling elevator with throttle or flap changes, and all kinds of exotic mixing or control allocation modes. I really like the idea of being able to do all the fancy stuff right on the airplane (with a json config file to set it up) versus needing fancier and fancier transmitters to do all these things.

Ooo, that's a really good idea. I haven't thought of that, but I probably will add stuff like that later.

One nifty side effect is if you do simple gyro stabilization, you could apply the control allocation after the stabilization step so you can gyro stabilize all kinds of fancy aircraft configurations ... something you can't do from a transmitter, no matter how fancy schmancy it is.

I'm trying to keep the ArdUAV library more on the basic side, but I might be able to add something like this. That's the cool thing about open source controllers - nearly anything is possible! :cool:

The down side is setting a configuration and mixing modes through a json file on their PC may be harder for people who are used to doing this stuff with obscure button/dial presses on a 120x80 pixel monochrome screen. :)

The cool thing about the config files is that they are generated by the Python GUI! No need to mess with the individual settings in notepad - you can just use the buttons/text fields in the GUI to change, update, or create/save the JSON.