Compare commits
29 Commits
3e94aa1e00
...
main
Author | SHA1 | Date | |
---|---|---|---|
774f7f41d6 | |||
2a585ff318 | |||
e700818a2f | |||
6be2d84f05 | |||
d943d6239e | |||
7a4b75c0a3 | |||
a46ad186db | |||
7b3c164c6d | |||
9da84bb7a8 | |||
1009a14673 | |||
2d1a6ae1f6 | |||
d099aac466 | |||
c4c56efb7a | |||
e975e5a93b | |||
e43a469647 | |||
f156423f8b | |||
f892d78745 | |||
5adf888713 | |||
2dd30990bc | |||
5104f71dc6 | |||
7b81c1dda8 | |||
a1d488be28 | |||
27935b52fb | |||
c3a9ff919d | |||
7cea94942a | |||
66a25a0606 | |||
2440990a6e | |||
b231e7228b | |||
009f4d1861 |
@@ -1,7 +1,7 @@
|
||||
# FR-1 Trace lengths
|
||||
|
||||
## The experiment
|
||||
For the experiment, I used FR-1 that has a thickness of 35µm. On there I will mill 4 trace sizes (0.4 mm, 0,6 mm, 0,8 mm and 1 mm) and they all have the same length of 7.5 cm’s. The traces are seperated by 1 Cm.
|
||||
For the experiment, I used FR-1 that has a thickness of 35µm. On there I will mill 4 trace sizes (0.4 mm, 0,6 mm, 0,8 mm and 1 mm) and they all have the same length of 7.5 cm’s. The traces are separated by 1 Cm.
|
||||
|
||||

|
||||
|
||||
@@ -26,26 +26,29 @@ The capacitance of a PCB trace is the amount of energy it can hold. It works lik
|
||||
The capacitance is determined by the amount of copper is in the trace. So length, width and the thickness. When working with low frequency signals the capacitance is not super important.
|
||||
But when working with higher speed frequencies the capacitance can actually mess up the signal integrity.
|
||||
|
||||
## Heat generation
|
||||
Heat within traces is generated by the current flowing through it. Not the wattage. A good example of the is train lines. These run on 230.000 volts on thin wires while being able to supply multiple trains. The higher the voltage the easier it is the transport a higher amount of wattage.
|
||||
|
||||
## Results
|
||||
|
||||
the power. So I grabbed an old robot with 4 stepper motors attached but I could not get them to draw more than 30 watts. So that is why the maximum in this test is 30 watts. I also had a hard time finding a way to measure the temperature of the traces because we only had a heat gun here and the laser was not the place it was actually measuring. So 0,4 mm is not measured properly.
|
||||
the power. So I grabbed an old robot with 4 stepper motors attached but I could not get them to draw more than 1.6 Amperage. So that is why the maximum in this test is 1.6 Amperage. I also had a hard time finding a way to measure the temperature of the traces because we only had a heat gun here and the laser was not the place it was actually measuring. So 0,4 mm is not measured properly.
|
||||
|
||||
| Trace Width Trace | Resistance | Maximum safe wattage | Burn out wattage |
|
||||
| :---------------- | ---------- | -------------------- | ---------------- |
|
||||
| 0,4mm | 3,1Ω | 15 | 20 |
|
||||
| 0,6mm 2 | 2Ω | 22 | 26 |
|
||||
| 0,8mm | 2Ω | 27 | ?? |
|
||||
| 1mm | 1,85Ω | ?? | ?? |
|
||||
| | | | |
|
||||
| Trace Width Trace | Resistance | Maximum safe Current in Ampere | Burn out Ampere |
|
||||
| :---------------- | ---------- | ------------------------------ | --------------- |
|
||||
| 0,4mm | 3.1Ω | 0.7 | 0.85 |
|
||||
| 0,6mm | 2.7Ω | 0.9 | 1.05 |
|
||||
| 0,8mm | 2Ω | 1.6 | ?? |
|
||||
| 1mm | 1.85Ω | ?? | ?? |
|
||||
| | | | |
|
||||
|
||||
|
||||
* The 0,4 mm trace was the first one I tested. I started with 15 watts for a few seconds. After that I increased the wattage to 20 watts and the trace instantly exploded and burned so I could not get temperature readings there.
|
||||
* The 0,4 mm trace was the first one I tested. I started with 0.7 amps for a few seconds. After that I increased the wattage to 0.85 amps and the trace instantly exploded and burned so I could not get temperature readings there.
|
||||
|
||||
* The 0,6 mm trace it held up well up to 27 watts where it started getting burn marks within a 5-10 seconds. The temperature rose to 50 degrees celsius. At 29 watts the pcb burned out at 68 degrees celsius.
|
||||
* The 0,6 mm trace it held up well up to 0.9 amps where it started getting burn marks within a 5-10 seconds. The temperature rose to 50 degrees celsius. At 1.05 amps the pcb burned out at 68 degrees celsius.
|
||||
|
||||
* When testing the 0,8 mm trace I hit a roof with how much power I could consume with the robot. I could only get up to 30 watts and I did not manage to break the trace after powering it for a few minutes. The maximum I found before it started discoloring was 24 watts at 54 degrees celsius. This was measured after it was left on for 2 minutes.
|
||||
* When testing the 0,8 mm trace I hit a roof with how much power I could consume with the robot. I could only get up to 1.6 amps and I did not manage to break the trace after powering it for a few minutes. The maximum I found before it started discoloring was 24 watts at 54 degrees celsius. This was measured after it was left on for 2 minutes.
|
||||
|
||||
* I could not get to the 1mm trace because the robot could not draw more than 30 watts. So thats why these are not filled in the table.
|
||||
* I could not get to the 1mm trace because the robot could not draw more than 1.6 Amperage. So thats why these are not filled in the table.
|
||||
|
||||
|
||||
## Extra notes on temperature
|
||||
|
@@ -563,4 +563,4 @@ When I changed that connection it worked.
|
||||
|
||||
## The result of the robot
|
||||
|
||||

|
||||
[https://fabacademy.org/2025/labs/waag/img/FINAL_XIAO_DONE.mp4](https://fabacademy.org/2025/labs/waag/img/FINAL_XIAO_DONE.mp4)
|
||||
|
BIN
docs/Assignments/week_15_wildcard_week/image-7.jpg
Normal file
After Width: | Height: | Size: 72 KiB |
@@ -1,5 +1,6 @@
|
||||
# Wildcard week - Compositing
|
||||
|
||||
## Group assignment
|
||||
For this week we started out on compositing. First I didn't really know what it was and a bit confused. But through the day I started picking up more and more and starting to find it more fun.
|
||||
|
||||
In the morning I didn't know anything about compositing aside from Neil's lecture from yesterday. So I started clicking links on the compositing page.
|
||||
@@ -29,4 +30,10 @@ We used little coffee sticks to spread the epoxy on the fabrics.
|
||||
|
||||
After that we put it in the chamber
|
||||

|
||||
And now we wait for 24 hours till it is fully hardened
|
||||
And now we wait for 24 hours till it is fully hardened
|
||||
|
||||
### The result
|
||||
The result
|
||||
|
||||

|
||||
I expected the result to be stiff and more compact like in Neil's compositing lecture from 2016 but then I probably would've needed to use the vacuum bag.
|
BIN
docs/final_project/DroneMain.f3z
Normal file
BIN
docs/final_project/DroneMainBody.stl
Normal file
BIN
docs/final_project/New DroneArm v5.f3d
Normal file
BIN
docs/final_project/PXL_20250528_093723437(1)(1).mp4
Normal file
BIN
docs/final_project/PXL_20250604_121528011(1).mp4
Normal file
BIN
docs/final_project/SidePanel.stl
Normal file
BIN
docs/final_project/arm.stl
Normal file
BIN
docs/final_project/drone_control_board.zip
Normal file
@@ -1,29 +1,29 @@
|
||||
# Idea final project
|
||||
# Final project
|
||||
|
||||
## Introduction
|
||||
I wanted to combine 2 random objects that interest me into one project. A drone and a jumbotron. So im gonna make a drone with multiple screens on it that could for example be used for traffic control or entertainment purposes.
|
||||
I wanted to combine 2 random objects that interest me into one project. A drone and a jumbotron. So im gonna make a drone with multiple screens on it that could for example be used for traffic control or entertainment purposes. There is no real reason why I chose to make this beside it looking and sounding fun to design and build.
|
||||
|
||||
## What does the drone do?
|
||||
The drone needs to be able to fly for at least 20 minutes. It needs 2 screens on both side so it can display images or video. I wanna program the drone myself so I know how drones work and how they keep themselves upright.
|
||||
|
||||
## Drone Requirements
|
||||
### basic requirements
|
||||
* 2 screens
|
||||
* Speakers
|
||||
* 20 min flight time
|
||||
* custom microcontroller pcb that controls the drones and screens
|
||||
* 50 meter range from controller
|
||||
??? Drone Requirements
|
||||
### basic requirements
|
||||
* 2 screens
|
||||
* Speakers
|
||||
* 20 min flight time
|
||||
* custom micro controller pcb that controls the drones and screens
|
||||
* 50 meter range from controller
|
||||
|
||||
### Sensors
|
||||
* GPS
|
||||
* IMU/rotation sensor
|
||||
* Read voltage from battery know battery percentage
|
||||
### Sensors
|
||||
* GPS
|
||||
* IMU/rotation sensor
|
||||
* Read voltage from battery know battery percentage
|
||||
|
||||
### Requirements when far ahead
|
||||
* Docking station
|
||||
* Automatic docking
|
||||
* Automatic pathing
|
||||
* Design own electronic speed controllers
|
||||
### Requirements when far ahead
|
||||
* Docking station
|
||||
* Automatic docking
|
||||
* Automatic pathing
|
||||
* Design own electronic speed controllers
|
||||
|
||||
## Images
|
||||
|
||||
@@ -50,19 +50,19 @@ When designing the drone I wanted to do everything parametric. So im going to tr
|
||||
### Designing the drone parametrically
|
||||
Designing parametrically is very hard because every constraint needs to be perfect. Otherwise when changing lengths the design will fold into itself like this.
|
||||

|
||||
I've spend one day getting the constraints perfect for the sketch so it scales well with everything. And this is the result of that. When I change one parameter in the variable menu the entire body scales with it. This is the final result of my design. I've made the body myself and I've imported the components from grabcad. So I had more time for blender and Onshape and so I could visualize it better.
|
||||
I've spend one day getting the constraints perfect for the sketch so it scales well with everything. And this is the result of that. When I change one parameter in the variable menu the entire body scales with it. This is the final result of my design. I've made the body myself and I've imported the components from GrabCad. So I had more time for blender and Onshape and so I could visualize it better.
|
||||

|
||||
|
||||
### Rendering
|
||||
I've also made a render of the drone to visualize it for other people. Making this was kind of a pain because blender is a gigantic program. You can see my [docs](../Assignments/week_2_computer_aided_design_(cad)/drone_designing.md) to see where I struggled. This is my final result of the render. I've wished it looked nicer but I didn't have enough time to dive deeper into blender.
|
||||
|
||||
<video controls src="../assets/assets_week_2/blender/droneRender.mp4" title="Title"></video>
|
||||
<video controls src="../../assets/assets_week_2/blender/droneRender.mp4" title="Title"></video>
|
||||
|
||||
## Feedback from Bas and Henk
|
||||
I've gotten some feedback on my design and about the use of screens. Maybe I should only use speakers or use LED matrixes. After thinking about it I think im going to give LED matrixes a spin.
|
||||
|
||||
## Drone electronics
|
||||
For the drone I will need a lot of electronics. But I will also need a pcb for inside the drone so I don't have any loose connections. The main board needs a couple of components. Something to step down the voltage from the big battery or I will use two separate batteries one for the motors one for the control board. I will also need a sensor to know the position of the drone and a microcontroller. I also need a lot of power to power the screens so that will also be something interesting.
|
||||
For the drone I will need a lot of electronics. But I will also need a pcb for inside the drone so I don't have any loose connections. The main board needs a couple of components. Something to step down the voltage from the big battery or I will use two separate batteries one for the motors one for the control board. I will also need a sensor to know the position of the drone and a micro controller. I also need a lot of power to power the screens so that will also be something interesting.
|
||||
|
||||
### Components
|
||||
* MCU
|
||||
@@ -74,14 +74,14 @@ For the drone I will need a lot of electronics. But I will also need a pcb for i
|
||||
### Drone remote electronics
|
||||
I'm also building the remote for the drone
|
||||
|
||||
* Potslider for the throttle
|
||||
* Potentiometer slider for the throttle
|
||||
* Joystick for left-right-forward-backwards
|
||||
* battery
|
||||
* screen to see status of drone
|
||||
* LED (mandatory)
|
||||
|
||||
#### Joystick
|
||||
I really wanna use a joystick for this project because having 2 potmeters under each other looks a bit weird.
|
||||
I really wanna use a joystick for this project because having 2 potentiometers under each other looks a bit weird.
|
||||
|
||||
### KiCad
|
||||
The first thing I did was import the design rules for our milling machine. I used the file from the lecture to import it from because there we set everything up together.
|
||||
@@ -143,13 +143,15 @@ For the drone I will be using the [dRehmFlight VTOL program](https://github.com/
|
||||
```mermaid
|
||||
classDiagram
|
||||
namespace Drone {
|
||||
class ESPC6 Thread 1{
|
||||
class ESPC6{
|
||||
+Control Motors
|
||||
+keep drone in the air
|
||||
+Receive communication from controller
|
||||
Flightcontroller()
|
||||
}
|
||||
class MatrixControllerThread2 {
|
||||
class MatrixControllerRP2040 {
|
||||
+Receive communication from ESPC6 for Matrix data
|
||||
ShowMatrix()
|
||||
|
||||
}
|
||||
|
||||
@@ -164,13 +166,15 @@ namespace Controller {
|
||||
}
|
||||
}
|
||||
|
||||
ESPC3 --> ESPC6 Thread 1 : Send data to flight controller using ESPNOW
|
||||
ESPC3 --> ESPC6 : Send data to flight controller using ESPNOW
|
||||
ESPC6 --> MatrixControllerRP2040 : Serial communication
|
||||
|
||||
```
|
||||
Now that I am in week 15 this has changed. The change is on the drone are going to be 2 mcu's. One for the matrixes and one for the flight controller. So they are both separate systems and can't interfere with each other.
|
||||
|
||||

|
||||
|
||||
I've already gotten the motors to spin in [week 10](../Assignments/week_10_output_devices/output_devices.md) using the script in there. Now I also need to modify the original VTOL driver so my ESC's can properly understand it's instructions.
|
||||
I have already gotten the motors to spin in [week 10](../Assignments/week_10_output_devices/output_devices.md) using the script in there. Now I also need to modify the original VTOL driver so my ESC's can properly understand it's instructions.
|
||||
|
||||
There's one specific function I need to change.
|
||||
??? Old code
|
||||
@@ -179,7 +183,7 @@ There's one specific function I need to change.
|
||||
{
|
||||
// DESCRIPTION: Send pulses to motor pins, oneshot125 protocol
|
||||
/*
|
||||
* My crude implimentation of OneShot125 protocol which sends 125 - 250us pulses to the ESCs (mXPin). The pulselengths being
|
||||
* My crude implementation of OneShot125 protocol which sends 125 - 250us pulses to the ESCs (mXPin). The pulse lengths being
|
||||
* sent are mX_command_PWM, computed in scaleCommands(). This may be replaced by something more efficient in the future.
|
||||
*/
|
||||
int wentLow = 0;
|
||||
@@ -243,7 +247,7 @@ There's one specific function I need to change.
|
||||
}
|
||||
}
|
||||
```
|
||||
What this function does is generate PWM values in software which is super inefficient. The esp32C6 according to it's datasheet supports 6 seperate PWM clocks in hardware.
|
||||
What this function does is generate PWM values in software which is super inefficient. The esp32C6 according to it's datasheet supports 6 separate PWM clocks in hardware.
|
||||

|
||||
So I wanna use these to reduce the overhead and so the loop doesn't have to run 20 times a second but faster making the drone respond faster. To use these I need to use this library.
|
||||
Right link: [https://docs.espressif.com/projects/arduino-esp32/en/latest/api/ledc.html](https://docs.espressif.com/projects/arduino-esp32/en/latest/api/ledc.html)
|
||||
@@ -261,12 +265,12 @@ So I really needed 2 functions of the library.
|
||||
## Generative design
|
||||
Online I saw a lot of organic designs that where created by generative design.
|
||||

|
||||
Credits: [formlabs](https://formlabs.com/eu/blog/generative-design/)
|
||||
Credits: [FormLabs](https://formlabs.com/eu/blog/generative-design/)
|
||||
I wanted to do this myself for the drone arms. So I started messing around in the generative design tab of my drone arm model.
|
||||
|
||||

|
||||
I've already designed the base of the arm. What generative design does is either add or remove material based on parameters you give it.
|
||||
I've already prepared my model by created some parts as seperate bodies. So I can preserve them during the generation process.
|
||||
I've already prepared my model by created some parts as separate bodies. So I can preserve them during the generation process.
|
||||
When creating a Study you need to assign which parts to groups.
|
||||
|
||||

|
||||
@@ -284,7 +288,7 @@ So after assigning everything this is my result.
|
||||
After that you can open the Objectives tab to see what you wanna do and set more limits.
|
||||

|
||||
|
||||
The only things I understand from this menu is the Objectives and the Displacement. The objective is straight foward and the Displacement is the amount of millimeters it is allowed to fluctuate from the original design in said direction.
|
||||
The only things I understand from this menu is the Objectives and the Displacement. The objective is straight forward and the Displacement is the amount of millimeters it is allowed to fluctuate from the original design in said direction.
|
||||
|
||||
Now it's time to add the loads on the part. So the amount weight and force the part needs to be able to handle.
|
||||
For that there's a keybind `L`
|
||||
@@ -344,8 +348,13 @@ Now I needed to make the same holes through the case itself in the same location
|
||||
From this tab I can edit the body with all the references in place. So I could use the holes in the drone arm as reference for my cuts for in the drone body.
|
||||

|
||||
|
||||
## 3D printing
|
||||
I chose to fully 3D print my drone due to it's complex shape and time constraints. I still had a lot of work from university to do. We got encouraged to do compositing but that is super hard with the current design. However it would have been better since it is often lighter and more durable while being a lot more eco friendly. For 3D printing I am planning on using PLA because it has a lower carbon footprint than ABS and it releases less toxic fumes.
|
||||
|
||||

|
||||
|
||||
## Burnout!
|
||||
### Part 1
|
||||
One of my motors during testing burned out completely when it was attached to the printed arm. I think the cause for this is overheating due to a propellor that's too big.
|
||||
|
||||

|
||||
@@ -359,6 +368,13 @@ Another thing that also could be the issue is that my ESC was surging above 40 A
|
||||
|
||||
I think the main reason was heat and the coating on the motors evaporating causing a chain reaction.
|
||||
|
||||
### Part 2
|
||||
This time it was completely my fault for not paying attention to the wires when connecting them to the right terminals. I had all other motors taped off like this but I forgot to do that for one motor I was testing.
|
||||
|
||||

|
||||
|
||||
<video controls src="../PXL_20250528_093723437(1)(1).mp4" title="Title"></video>
|
||||
|
||||
## Cables
|
||||
The drone needs lots of cables to get power to the places where they need to be. I first started out by laying out everything that needed to be connected.
|
||||
|
||||
@@ -402,29 +418,118 @@ These are the 2 pcb's I designed I first thought it would be handy to make them
|
||||
## Matrix subsystem
|
||||
|
||||
### Power
|
||||
I tested the power consumption on the matrix by setting the brightness to 100 and the colors to white since white consumes the most amount of power.
|
||||

|
||||
|
||||

|
||||
|
||||
The result of this is that a single matrix consumed 3,8 amps at 5 volt. With 4 ESC's providing a total of 12 amps at 5 volts I should be easily able to power the mcu's and the matrixes.
|
||||
## Assembly
|
||||
Here I started assembly of the drone. I printed the motor arms and screwed all the motors into them and soldered pogo pins to each to connect them to the esc's. Unfortunately I don't know how to connect them yet because the wires aren't labeled. 2 motors need to spin left and the other 2 right for stability.
|
||||

|
||||
I also saw during assembly that the drone body warped a bit but that isn't going to ruin the fun. It will still fly.
|
||||

|
||||

|
||||

|
||||
The weight without the propellors and batteries is 1347 grams. The propellors weight a few grams and each battery weighs 183
|
||||
$$1347 + 183 * 2 = 1713 grams $$
|
||||
|
||||
|
||||
## Kaboom
|
||||
Killed esp C6 due to short in between the PWM output pin of the mcu and the 5v inputs of all electronic speed controllers. The first few tests I was lucky that I didn't plug anything into the shorted connector but when connecting everything the mcu got killed.
|
||||
I accidentally killed an esp C6 due to short in between the PWM output pin of the mcu and the 5v inputs of all electronic speed controllers. That's 5V 12Amps running through the mcu. The first few tests I was lucky that I didn't plug anything into the shorted connector but when connecting everything the mcu got killed. I sadly can't repair the board since there is hot glue everywhere to keep the connectors on the board.
|
||||

|
||||

|
||||

|
||||
|
||||
Later on I realized I also killed my IMU. Because there wasn't any resistance between the 3v3 and the GND
|
||||
|
||||
## Stripped screws
|
||||
Quick tip. When tightening screws do not do it at an angle otherwise you may strip the screw or dislodge the heated insert. Learned that the hard way. ;-;
|
||||
And when doing heated inserts make sure to do a couple of extra wall layers. So the heated insert can latch onto the plastic.
|
||||

|
||||
|
||||
## TODO
|
||||
* [ ] Matrix panel subsystem
|
||||
* [x] Rewrite PWM esc control system in driver
|
||||
* [ ] Does it fly?
|
||||
* [ ] Does it fly? (No it didn't)
|
||||
* [x] Power distribution system (for matrixes and mcu's)
|
||||
* [x] Test physical controller
|
||||
|
||||
## Testing the drone(V1)
|
||||
|
||||
The first test of the drone went horribly wrong. This was a first test with analog control with not stabilization feedback from the IMU to see if it would come off the ground. Sadly it did not go as expected.
|
||||
<video controls src="../PXL_20250604_121528011(1).mp4" title="Title"></video>
|
||||
|
||||
### What went wrong
|
||||
* The drone was not balanced
|
||||
* The drone was a bit too heavy
|
||||
* The drone didn't have good footing to the ground
|
||||
* Motors recalibrating themselves at some startups (Last startup that didn't happen)
|
||||
* The drone arm was too weak to resist the impact of the motors
|
||||
* The batteries weren't fastened inside the drone
|
||||
|
||||
### Improvements for next design
|
||||
* Proper legs so the drone doesn't fall over during takeoff
|
||||
* Lighter design while being as strong as possible with the least amount of material
|
||||
* A power switch because everything instantly starts once it gets power and that could be dangerous with extremely fast spinning propellors.
|
||||
* Fastened batteries
|
||||
|
||||

|
||||
|
||||
### The damage
|
||||

|
||||
One matrix board got a few ripped of leds and capacitors but I think I am able to repair that.
|
||||
|
||||

|
||||
2 ESC's and motors got completely tangled wires. I expect them to still work since they where still spinning after the impact.
|
||||
3/4 arms got ripped off due to the crash.
|
||||
|
||||

|
||||

|
||||
Luckily we placed cardboard under the drone incase something would happen. The propellors completely mauled the cardboard away where it hit.
|
||||
|
||||
Now that the drone is ripped into several pieces I will be abandoning this design and start from scratch on a second design. From this point on I can only go up.
|
||||
|
||||
|
||||
## BOM (bill of materials)
|
||||
| item | price | link |
|
||||
| :---- | ----- | -------- |
|
||||
| item1 | 999 | link.com |
|
||||
|
||||
## Files
|
||||
### Drone
|
||||
#### Parts
|
||||
| item | price | link |
|
||||
| :-------------------- | ------- | -------------------------------------------------------------------------------------------------------- |
|
||||
| 700 grams of filament | € 14 | Any PLA |
|
||||
| Matrix panel * 2 | € 17,78 | [https://aliexpress.com/item/4001296811800.html](https://aliexpress.com/item/4001296811800.html ) |
|
||||
| BLDC ESC * 4 | € 26,76 | [https://aliexpress.com/item/1005006947824850.html](https://aliexpress.com/item/1005006947824850.html ) |
|
||||
| BLDC Motor 1500KV | € 34,39 | [https://aliexpress.com/item/1005006859037930.html](https://aliexpress.com/item/1005006859037930.html) |
|
||||
| 4S Li-Po * 2 | € 15,86 | [https://hobbyking.com/en_us/turnigy-nano-tech-plus-1400mah-4s-70c-lipo-pack-w-xt60.html](https://hobbyking.com/en_us/turnigy-nano-tech-plus-1400mah-4s-70c-lipo-pack-w-xt60.html) |
|
||||
| Propellors 7040 | € 6,99 | [https://aliexpress.com/item/1005008125599756.html](https://aliexpress.com/item/1005008125599756.html) |
|
||||
| XT-60 connectors | € 1,40 | [https://aliexpress.com/item/1005007228544590.html](https://aliexpress.com/item/1005007228544590.html) |
|
||||
| Wiring 26 AWG | € 1,63 | [https://aliexpress.com/item/1005007213319794.html](https://aliexpress.com/item/1005007213319794.html) |
|
||||
| seeed XIAO RP2040 | $ 3,99 | [https://www.seeedstudio.com/XIAO-RP2040-v1-0-p-5026.html](https://www.seeedstudio.com/XIAO-RP2040-v1-0-p-5026.html) |
|
||||
| seeed XIAO ESP-C6 | $ 5,20 | [https://www.seeedstudio.com/Seeed-Studio-XIAO-ESP32C6-p-5884.html](https://www.seeedstudio.com/Seeed-Studio-XIAO-ESP32C6-p-5884.html) |
|
||||
| smd male header pins | € 2,99 | [https://aliexpress.com/item/32796117265.html](https://aliexpress.com/item/32796117265.html) |
|
||||
| M4 screws and nuts | | [https://aliexpress.com/item/4001072025844.html](https://aliexpress.com/item/4001072025844.html) |
|
||||
| M4 Threaded inserts | | [https://aliexpress.com/item/1005003582355741.html](https://aliexpress.com/item/1005003582355741.html) |
|
||||
| | | |
|
||||
|
||||
#### Files
|
||||
* [Drone main body F3Z](DroneMain.f3z)
|
||||
* [Drone arm F3D]("New DroneArm v5.f3d")
|
||||
* [Drone main body STL](DroneMainBody.stl)
|
||||
* [Drone main arm STL](arm.stl)
|
||||
* [Drone side panel STL](SidePanel.stl)
|
||||
* [Drone PCB's KiCad](drone_control_board.zip)
|
||||
* [Drone Software]()
|
||||
* [Matrix Software]()
|
||||
|
||||
### Drone controller
|
||||
#### Parts
|
||||
| item | price | link |
|
||||
| :-------------------- | ------- | ---------------------------------------------------------------------------------- |
|
||||
| XIAO ESP-C3 | $ 4,99 | [https://www.seeedstudio.com/seeed-xiao-esp32c3-p-5431.html](https://www.seeedstudio.com/seeed-xiao-esp32c3-p-5431.html) |
|
||||
| Hall effect joysticks | € 11,99 | [https://aliexpress.com/item/1005007462081151.html](https://aliexpress.com/item/1005007462081151.html) |
|
||||
| Analog Multiplexer IC | $ 0,57 | [https://www.digikey.com/en/products/detail/texas-instruments/CD74HC4067M96/1507236](https://www.digikey.com/en/products/detail/texas-instruments/CD74HC4067M96/1507236) |
|
||||
| | | |
|
||||
|
||||
#### Files
|
||||
* [Handheld controller PCB](Drone%20controller.zip)
|
||||
|
||||
* [Handheld controller pcb](Drone%20controller.zip)
|
||||
* [Drone motor 3D file](https://grabcad.com/library/readytosky-rs2205-1)
|
BIN
docs/final_project/imag1.jpg
Normal file
After Width: | Height: | Size: 130 KiB |
BIN
docs/final_project/imag2.jpg
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
docs/final_project/image-43.jpg
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
docs/final_project/image-44.jpg
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
docs/final_project/image-45.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
docs/final_project/image-46.jpg
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
docs/final_project/image-47.jpg
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
docs/final_project/image-48.jpg
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
docs/final_project/image343.jpg
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
docs/final_project/imaged-1.jpg
Normal file
After Width: | Height: | Size: 86 KiB |
BIN
docs/final_project/imaged-2.jpg
Normal file
After Width: | Height: | Size: 109 KiB |
BIN
docs/final_project/imaged-3.jpg
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
docs/final_project/imaged-4.jpg
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
docs/final_project/imaged.jpg
Normal file
After Width: | Height: | Size: 76 KiB |
@@ -1,7 +0,0 @@
|
||||
# Microcontrollers
|
||||
|
||||
## Microcontroller requirements
|
||||
* 4 channel pwm output for 4 drone motors
|
||||
* 16 bit pwm so all electronic speed controllers can understand
|
||||
* Minimum 2 MB flash size
|
||||
* I2C
|
@@ -3,7 +3,7 @@
|
||||
Welcome to my project!
|
||||
|
||||
## About me
|
||||
Im a Computer Sciences student at Applied University of Amsterdam. With a focus on embedded software development. Im in my second year and im doing the FabAcademy during my internship. Henk is my instructor for the FabAcademy. Im not an offichial student since I'm a intern.
|
||||
Im a Computer Sciences student at Applied University of Amsterdam. With a focus on embedded software development. Im in my second year and im doing the FabAcademy during my internship. Henk is my instructor for the FabAcademy. Im not an official student since I'm a intern.
|
||||
|
||||
### Hobbies
|
||||
I like hanging out with friends. Whenever we're inside we're probably playing mario kart or another party game. And outside we walk through nature or cities and visit random stores that interest us. For example I like visiting second hand stores because they have a lot of random stuff and also old vintage electronics which I like.
|
||||
@@ -12,19 +12,49 @@ I like hanging out with friends. Whenever we're inside we're probably playing ma
|
||||
I listen to a lot of metal, mainly powermetal. I also listen to hardstyle/hardcore.
|
||||
<iframe style="border-radius:12px" src="https://open.spotify.com/embed/track/3pdAe0c6l08B6wMYVghyCs?utm_source=generator" width="100%" height="152" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
|
||||
|
||||
<iframe style="border-radius:12px" src="https://open.spotify.com/embed/track/0CjJJrpnQCpQMsT4nzezII?utm_source=generator" width="100%" height="152" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
|
||||
<iframe style="border-radius:12px" src="https://open.spotify.com/embed/track/2OYtcqflvzQwh3cMPmTHs4?utm_source=generator" width="100%" height="152" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
|
||||
|
||||
<iframe style="border-radius:12px" src="https://open.spotify.com/embed/track/1XyaC0j83whAn7Cpv66Duq?utm_source=generator" width="100%" height="152" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
|
||||
<iframe style="border-radius:12px" src="https://open.spotify.com/embed/track/7CAbF0By0Fpnbiu6Xn5ZF7?utm_source=generator" width="100%" height="152" frameBorder="0" allowfullscreen="" allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
|
||||
|
||||
## Past projects
|
||||
I have build my own powersupply for projects with variable voltage output so I can quickly prototype and test stuff.
|
||||
I have build my own power supply for projects with variable voltage output so I can quickly prototype and test stuff.
|
||||

|
||||
Im also busy with a project with connecting 4 led matrixes together so I can show notifications from my phone for example on it.
|
||||

|
||||
|
||||
|
||||
## This project
|
||||
For this project I wanna build a traffic drone that can be used as temporary traffic signs.
|
||||
For this project I wanna build a traffic drone that can be used as temporary traffic signs or can be used at festivals to quickly display information.
|
||||
|
||||
### What software/hardware am I using
|
||||
For this project I'm using a mix of manjaro and windows. I use Windows for Fusion360 since I can't install it on manjaro and I use manjaro for the rest of my stuff since I daily drive it. I use visual studio code as my editor.
|
||||
For this project I'm using a mix of manjaro and windows. I use Windows for Fusion360 since I can't install it on manjaro and I use manjaro for the rest of my stuff since I daily drive it. I use visual studio code as my editor.
|
||||
|
||||
|
||||
## Schedule
|
||||
|
||||
| Date | Deadline |
|
||||
| ------ | -------------------------------------------------------------------------- |
|
||||
| Jan 29 | principles and practices, presentations, introductions, project management |
|
||||
| Feb 5 | computer-aided design |
|
||||
| Feb 12 | computer-controlled cutting |
|
||||
| Feb 19 | embedded programming |
|
||||
| Feb 26 | 3D scanning and printing |
|
||||
| Mar 5 | electronics design |
|
||||
| Mar 12 | computer-controlled machining |
|
||||
| Mar 19 | electronics production |
|
||||
| Mar 26 | input devices |
|
||||
| Apr 2 | output devices + Intermediate assessment |
|
||||
| Apr 9 | networking and communications |
|
||||
| Apr 16 | mechanical design, machine design |
|
||||
| Apr 30 | molding and casting |
|
||||
| May 7 | interface and application programming |
|
||||
| May 11 | Research paper version 1 |
|
||||
| May 13 | Drone revision 2 done |
|
||||
| May 14 | system integration |
|
||||
| May 28 | applications and implications + Last version research paper |
|
||||
| Jun 5 | Drone flight day |
|
||||
| Jun 6 | Presentation |
|
||||
| Jun 9 | final assignment presentations |
|
||||
| Jun 10 | Portfolio version 1 |
|
||||
| Jun 21 | Portfolio last version |
|
||||
| Jun 24 | Finals Assessment |
|
BIN
docs/presentation.mp4
Normal file
5
src/Fab Matrixes/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
.pio
|
||||
.vscode/.browse.c_cpp.db*
|
||||
.vscode/c_cpp_properties.json
|
||||
.vscode/launch.json
|
||||
.vscode/ipch
|
37
src/Fab Matrixes/include/README
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
This directory is intended for project header files.
|
||||
|
||||
A header file is a file containing C declarations and macro definitions
|
||||
to be shared between several project source files. You request the use of a
|
||||
header file in your project source file (C, C++, etc) located in `src` folder
|
||||
by including it, with the C preprocessing directive `#include'.
|
||||
|
||||
```src/main.c
|
||||
|
||||
#include "header.h"
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Including a header file produces the same results as copying the header file
|
||||
into each source file that needs it. Such copying would be time-consuming
|
||||
and error-prone. With a header file, the related declarations appear
|
||||
in only one place. If they need to be changed, they can be changed in one
|
||||
place, and programs that include the header file will automatically use the
|
||||
new version when next recompiled. The header file eliminates the labor of
|
||||
finding and changing all the copies as well as the risk that a failure to
|
||||
find one copy will result in inconsistencies within a program.
|
||||
|
||||
In C, the convention is to give header files names that end with `.h'.
|
||||
|
||||
Read more about using header files in official GCC documentation:
|
||||
|
||||
* Include Syntax
|
||||
* Include Operation
|
||||
* Once-Only Headers
|
||||
* Computed Includes
|
||||
|
||||
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
46
src/Fab Matrixes/lib/README
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
This directory is intended for project specific (private) libraries.
|
||||
PlatformIO will compile them to static libraries and link into the executable file.
|
||||
|
||||
The source code of each library should be placed in a separate directory
|
||||
("lib/your_library_name/[Code]").
|
||||
|
||||
For example, see the structure of the following example libraries `Foo` and `Bar`:
|
||||
|
||||
|--lib
|
||||
| |
|
||||
| |--Bar
|
||||
| | |--docs
|
||||
| | |--examples
|
||||
| | |--src
|
||||
| | |- Bar.c
|
||||
| | |- Bar.h
|
||||
| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||
| |
|
||||
| |--Foo
|
||||
| | |- Foo.c
|
||||
| | |- Foo.h
|
||||
| |
|
||||
| |- README --> THIS FILE
|
||||
|
|
||||
|- platformio.ini
|
||||
|--src
|
||||
|- main.c
|
||||
|
||||
Example contents of `src/main.c` using Foo and Bar:
|
||||
```
|
||||
#include <Foo.h>
|
||||
#include <Bar.h>
|
||||
|
||||
int main (void)
|
||||
{
|
||||
...
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
The PlatformIO Library Dependency Finder will find automatically dependent
|
||||
libraries by scanning project source files.
|
||||
|
||||
More information about PlatformIO Library Dependency Finder
|
||||
- https://docs.platformio.org/page/librarymanager/ldf.html
|
15
src/Fab Matrixes/platformio.ini
Normal file
@@ -0,0 +1,15 @@
|
||||
; PlatformIO Project Configuration File
|
||||
;
|
||||
; Build options: build flags, source filter
|
||||
; Upload options: custom upload port, speed and extra flags
|
||||
; Library options: dependencies, extra library storages
|
||||
; Advanced options: extra scripting
|
||||
;
|
||||
; Please visit documentation for the other options and examples
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[env:seeed-xiao-rp2040]
|
||||
platform = Seeed Studio
|
||||
board = seeed-xiao-rp2040
|
||||
framework = arduino
|
||||
lib_deps = adafruit/Adafruit NeoMatrix@^1.3.3
|
165
src/Fab Matrixes/src/main.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
// Adafruit_NeoMatrix example for single NeoPixel Shield.
|
||||
// Scrolls 'Howdy' across the matrix in a portrait (vertical) orientation.
|
||||
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Adafruit_NeoMatrix.h>
|
||||
#include <Adafruit_NeoPixel.h>
|
||||
#include "nyan.h"
|
||||
#ifndef PSTR
|
||||
#define PSTR // Make Arduino Due happy
|
||||
#endif
|
||||
|
||||
#define PIN D1
|
||||
|
||||
// MATRIX DECLARATION:
|
||||
// Parameter 1 = width of NeoPixel matrix
|
||||
// Parameter 2 = height of matrix
|
||||
// Parameter 3 = pin number (most are valid)
|
||||
// Parameter 4 = matrix layout flags, add together as needed:
|
||||
// NEO_MATRIX_TOP, NEO_MATRIX_BOTTOM, NEO_MATRIX_LEFT, NEO_MATRIX_RIGHT:
|
||||
// Position of the FIRST LED in the matrix; pick two, e.g.
|
||||
// NEO_MATRIX_TOP + NEO_MATRIX_LEFT for the top-left corner.
|
||||
// NEO_MATRIX_ROWS, NEO_MATRIX_COLUMNS: LEDs are arranged in horizontal
|
||||
// rows or in vertical columns, respectively; pick one or the other.
|
||||
// NEO_MATRIX_PROGRESSIVE, NEO_MATRIX_ZIGZAG: all rows/columns proceed
|
||||
// in the same order, or alternate lines reverse direction; pick one.
|
||||
// See example below for these values in action.
|
||||
// Parameter 5 = pixel type flags, add together as needed:
|
||||
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
|
||||
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
|
||||
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
|
||||
// NEO_GRBW Pixels are wired for GRBW bitstream (RGB+W NeoPixel products)
|
||||
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
|
||||
|
||||
|
||||
// Example for NeoPixel Shield. In this application we'd like to use it
|
||||
// as a 5x8 tall matrix, with the USB port positioned at the top of the
|
||||
// Arduino. When held that way, the first pixel is at the top right, and
|
||||
// lines are arranged in columns, progressive order. The shield uses
|
||||
// 800 KHz (v2) pixels that expect GRB color data.
|
||||
|
||||
void scrollingText(String text, int speed, int color);
|
||||
void playGif(const uint16_t* gifData[], int frameCount, int frameDelay);
|
||||
void displayFullScreenBMP(const uint16_t* bitmap);
|
||||
void bootSequence();
|
||||
void matrixSerialLoop();
|
||||
void matrixSerialSetup();
|
||||
|
||||
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(32, 8, PIN,
|
||||
NEO_MATRIX_BOTTOM + NEO_MATRIX_RIGHT +
|
||||
NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
|
||||
NEO_GRB + NEO_KHZ800);
|
||||
|
||||
const uint16_t colors[] = {
|
||||
matrix.Color(255, 0, 0), matrix.Color(0, 255, 0), matrix.Color(0, 0, 255) }; //RGB
|
||||
|
||||
void setup() {
|
||||
matrix.begin();
|
||||
matrix.setTextWrap(false);
|
||||
matrix.setBrightness(25);
|
||||
matrix.setTextColor(matrix.Color(255, 255, 255));
|
||||
matrixSerialSetup();
|
||||
Serial.begin(9600);
|
||||
|
||||
// bootSequence();
|
||||
}
|
||||
|
||||
int x = matrix.width();
|
||||
int pass = 0;
|
||||
|
||||
void loop() {
|
||||
matrix.fillScreen(0);
|
||||
matrix.setCursor(0, 0);
|
||||
matrix.setTextColor(matrix.Color(255, 0, 255));
|
||||
matrix.print("Outch");
|
||||
matrix.show();
|
||||
}
|
||||
|
||||
void scrollingText(String text, int speed, int color) {
|
||||
// Save original position and color state
|
||||
int textX = matrix.width();
|
||||
int textPass = 0;
|
||||
matrix.setTextColor(color);
|
||||
|
||||
// Calculate the total scroll distance needed for the text to completely scroll through
|
||||
// We want to scroll until the text has fully entered from the right AND fully exited to the left
|
||||
int textWidth = text.length() * 6; // Approx 6 pixels per character
|
||||
int totalScrollDistance = matrix.width() + textWidth;
|
||||
|
||||
// Continue scrolling until the text has completely passed through
|
||||
for (int i = 0; i < totalScrollDistance; i++) {
|
||||
matrix.fillScreen(0);
|
||||
matrix.setCursor(textX, 0);
|
||||
matrix.print(text);
|
||||
matrix.show();
|
||||
|
||||
// Move text position one pixel to the left
|
||||
textX--;
|
||||
|
||||
delay(speed);
|
||||
}
|
||||
}
|
||||
|
||||
void playGif(const uint16_t* gifData[], int frameCount, int frameDelay) {
|
||||
int width = matrix.width(); // 32
|
||||
int height = matrix.height(); // 8
|
||||
|
||||
for (int i = 0; i < frameCount; i++) {
|
||||
matrix.fillScreen(0);
|
||||
|
||||
// Each element in gifData is a pointer to a complete frame
|
||||
// No need for complicated offset calculations
|
||||
const uint16_t* currentFrame = gifData[i];
|
||||
|
||||
// Draw the current frame
|
||||
matrix.drawRGBBitmap(0, 0, currentFrame, width, height);
|
||||
matrix.show();
|
||||
delay(frameDelay);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void displayFullScreenBMP(const uint16_t* bitmap) {
|
||||
int width = matrix.width();
|
||||
int height = matrix.height();
|
||||
matrix.drawRGBBitmap(0, 0, bitmap, width, height);
|
||||
matrix.show();
|
||||
}
|
||||
|
||||
void bootSequence() {
|
||||
//just display something cool on startup. This is hardcoded asf
|
||||
|
||||
matrix.fillScreen(0);
|
||||
matrix.setCursor(5, 0);
|
||||
matrix.setTextColor(matrix.Color(0, 0, 255));
|
||||
matrix.print("Boot");
|
||||
matrix.show();
|
||||
|
||||
delay(5000);
|
||||
|
||||
}
|
||||
|
||||
void matrixSerialSetup(){
|
||||
Serial1.begin(115200);
|
||||
}
|
||||
|
||||
void matrixSerialLoop() {
|
||||
if (Serial1.available()) {
|
||||
String command = Serial1.readStringUntil('\n');
|
||||
command.trim(); // Remove any trailing newline or spaces
|
||||
Serial.println("Received command: " + command);
|
||||
if (command.startsWith("scroll:")) {
|
||||
String text = command.substring(7); // Get the text after "scroll:"
|
||||
scrollingText(text, 50, matrix.Color(255, 0, 255));
|
||||
} else if (command.startsWith("gif:")) {
|
||||
// Handle GIF command
|
||||
// Example: gif:frame1,frame2,frame3
|
||||
// You would need to parse the frames and call playGif()
|
||||
} else if (command.startsWith("bmp:")) {
|
||||
// Handle BMP command
|
||||
// Example: bmp:bitmapData
|
||||
// You would need to parse the bitmap data and call displayFullScreenBMP()
|
||||
}
|
||||
}
|
||||
}
|
216
src/Fab Matrixes/src/nyan.h
Normal file
@@ -0,0 +1,216 @@
|
||||
|
||||
|
||||
|
||||
// 'frame_0_delay-0', 32x8px
|
||||
const uint16_t nyanframe_0_delay_0 [] PROGMEM = {
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0x94b3, 0xf43b, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xf6ae, 0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xf6ae, 0x4a6a, 0x4a6a, 0xf6ae, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0xf780, 0xf780, 0xf780, 0xf780, 0x37e0, 0x37e0, 0x4a6a, 0x6b8d, 0x4a6a, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x45cc, 0x4a6a, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x4154, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0xd512, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x2b5e, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x619f, 0x619f, 0x619f, 0x2b5e, 0x94b3, 0xf6ae, 0xf43b,
|
||||
0xf4dd, 0xf4dd, 0xddf7, 0x94b3, 0x94b3, 0x4a6a, 0x4a6a, 0x4a6a, 0x4a6a, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x6b8d, 0x6b8d, 0x6b8d, 0x94b3,
|
||||
0x94b3, 0x94b3, 0x94b3, 0x4a6a, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c
|
||||
};
|
||||
// 'frame_1_delay-0', 32x8px
|
||||
const uint16_t nyanframe_1_delay_0 [] PROGMEM = {
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xeb42, 0x94b3, 0xf43b, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xddf7, 0x6b8d, 0xddf7, 0xf4dd, 0xd512, 0x0108, 0x4a6a, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xf6ae, 0xf780, 0xf780, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x6b8d, 0x4a6a, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0xf780, 0xf780, 0xf780, 0xf780, 0x37e0, 0x37e0, 0x45cc, 0x6b8d, 0x6b8d, 0xf6ae, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x45cc, 0x6b8d, 0x6b8d, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x5a99, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0xd512, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x619f, 0x619f, 0x619f, 0x2b5e, 0x2b5e, 0x94b3, 0xf6ae, 0xf43b,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x94b3, 0x94b3, 0x6b8d, 0x4a6a, 0x4a6a, 0x4a6a, 0x6b8d, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x5a99, 0x6b8d, 0x6b8d, 0x94b3,
|
||||
0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x4a6a, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c
|
||||
};
|
||||
// 'frame_2_delay-0', 32x8px
|
||||
const uint16_t nyanframe_2_delay_0 [] PROGMEM = {
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0x94b3, 0xddf7, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xddf7, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0x94b3, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xf780, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x6b8d, 0xf4dd, 0xd512, 0x0108, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xf780, 0xf780, 0xf780, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0xf780, 0xf780, 0xf780, 0xf780, 0x37e0, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x6b8d, 0x6b8d, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x45cc, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x5a99, 0x6b8d, 0x6b8d, 0x94b3, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x619f, 0x619f, 0x619f, 0x2b5e, 0x04bf, 0x5a99, 0x6b8d, 0x6b8d, 0x5a99, 0x94b3, 0xf43b, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0xd512, 0x6b8d, 0x94b3, 0x6b8d, 0x6b8d, 0x94b3, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x5a99, 0x717c, 0x717c, 0x6b8d, 0xf6ae, 0xddf7,
|
||||
0xddf7, 0xddf7, 0xddf7, 0xd512, 0x6b8d, 0x94b3, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c
|
||||
};
|
||||
// 'frame_3_delay-0', 32x8px
|
||||
const uint16_t nyanframe_3_delay_0 [] PROGMEM = {
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0x94b3, 0xddf7, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xddf7, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xf6ae, 0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf43b, 0xf43b, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xf780, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x6b8d, 0xf43b, 0xf4dd, 0xd512, 0x4a6a, 0x94b3, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0xf780, 0xf780, 0xf780, 0xf780, 0x37e0, 0x45cc, 0x6b8d, 0x6b8d, 0xd512, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x6b8d, 0x6b8d, 0x6b8d, 0x94b3, 0x94b3, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x6b8d, 0x6b8d, 0x6b8d, 0x4a6a, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x4154, 0x01ac, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x6b8d, 0x6b8d, 0x94b3, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x2b5e, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x619f, 0x619f, 0x619f, 0x2b5e, 0x94b3, 0xf43b, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0xd512, 0xd512, 0x6b8d, 0x6b8d, 0x94b3, 0x6b8d, 0xd512, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x717c, 0x018c, 0x6b8d, 0xf6ae, 0xddf7,
|
||||
0xddf7, 0xddf7, 0xddf7, 0x6b8d, 0x94b3, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c
|
||||
};
|
||||
// 'frame_4_delay-0', 32x8px
|
||||
const uint16_t nyanframe_4_delay_0 [] PROGMEM = {
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0x94b3, 0xf43b, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xf6ae, 0x4a6a, 0x4a6a, 0xf6ae, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xf780, 0xf780, 0xf780, 0xf780, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0xf780, 0xf780, 0x4a6a, 0x6b8d, 0x4a6a, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x4a6a, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x5a99, 0x4154, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x619f, 0x619f, 0x619f, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x94b3, 0xf6ae, 0xf43b,
|
||||
0xf4dd, 0xf4dd, 0xddf7, 0x94b3, 0x94b3, 0x4a6a, 0x4a6a, 0x4a6a, 0x4a6a, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x6b8d, 0x6b8d, 0x6b8d, 0x94b3,
|
||||
0x94b3, 0x94b3, 0x94b3, 0x4a6a, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x94b3
|
||||
};
|
||||
// 'frame_5_delay-0', 32x8px
|
||||
const uint16_t nyanframe_5_delay_0 [] PROGMEM = {
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0x94b3, 0xf43b, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xddf7, 0x6b8d, 0xddf7, 0xf4dd, 0xd512, 0x0108, 0x4a6a, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xf6ae, 0xf780, 0xf780, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x6b8d, 0x4a6a, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xf780, 0xf780, 0xf780, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0xf780, 0xf780, 0x6b8d, 0x6b8d, 0x45cc, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x45cc, 0x6b8d, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x5a99, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0xd512, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x2b5e, 0x619f, 0x619f, 0x2b5e, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x94b3, 0xf6ae, 0xf43b,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x94b3, 0x94b3, 0x6b8d, 0x4a6a, 0x4a6a, 0x4a6a, 0x6b8d, 0x6b8d, 0x018c, 0x01ac, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x5a99, 0x6b8d, 0x6b8d, 0x94b3,
|
||||
0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x4a6a, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x6b8d, 0x018c, 0x018c, 0x018c
|
||||
};
|
||||
// 'frame_6_delay-0', 32x8px
|
||||
const uint16_t nyanframe_6_delay_0 [] PROGMEM = {
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xeb42, 0x94b3, 0xddf7, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xddf7, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0x94b3, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x6b8d,
|
||||
0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xf780, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x6b8d, 0xf4dd, 0xd512, 0x0108, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x6b8d, 0x6b8d,
|
||||
0x37e0, 0x37e0, 0x37e0, 0xf780, 0xf780, 0xf780, 0xf780, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0xf780, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x6b8d, 0x6b8d, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x94b3, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x45cc, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x0108, 0x018c, 0x018c, 0x6b8d, 0x6b8d,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x4154, 0x6b8d, 0x94b3, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x0108, 0x018c, 0x018c, 0x018c, 0x01ac,
|
||||
0x619f, 0x619f, 0x619f, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x619f, 0x5a99, 0x6b8d, 0x6b8d, 0x5a99, 0x94b3, 0xf43b, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0xd512, 0x6b8d, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x5a99, 0x717c, 0x717c, 0x6b8d, 0xf6ae, 0xddf7,
|
||||
0xddf7, 0xddf7, 0xddf7, 0xd512, 0x6b8d, 0x94b3, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c
|
||||
};
|
||||
// 'frame_7_delay-0', 32x8px
|
||||
const uint16_t nyanframe_7_delay_0 [] PROGMEM = {
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0x94b3, 0xddf7, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xf4dd, 0xddf7, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xf780, 0xf780, 0xf780, 0xf780, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf43b, 0xf43b, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x0108, 0x018c, 0x94b3, 0x01ac, 0x018c, 0x018c, 0x018c,
|
||||
0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xf780, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x6b8d, 0xf43b, 0xf4dd, 0xd512, 0x4a6a, 0x94b3, 0x0108, 0x01ac, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xf780, 0xf780, 0xf780, 0xf780, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0xf780, 0xd512, 0x6b8d, 0x6b8d, 0x45cc, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x6b8d, 0x6b8d, 0x6b8d, 0x94b3, 0x94b3, 0x0108, 0x018c, 0x018c, 0x94b3, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x45cc, 0x6b8d, 0x6b8d, 0x4a6a, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x4a6a, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x5a99, 0x5a99, 0x01ac, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x6b8d, 0x6b8d, 0x94b3, 0x6b8d, 0x6b8d, 0x01ac, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x619f, 0x619f, 0x619f, 0x619f, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x94b3, 0xf43b, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0xd512, 0xd512, 0x6b8d, 0x6b8d, 0x94b3, 0x6b8d, 0xd512, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x018c, 0x6b8d, 0xf6ae, 0xddf7,
|
||||
0xddf7, 0xddf7, 0xddf7, 0x6b8d, 0x94b3, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c
|
||||
};
|
||||
// 'frame_8_delay-0', 32x8px
|
||||
const uint16_t nyanframe_8_delay_0 [] PROGMEM = {
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0x94b3, 0xddf7, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf4dd, 0xf43b, 0xf4dd, 0xf4dd, 0xddf7, 0xd512, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0xf6ae, 0xf6ae, 0xf6ae, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xfcc0, 0xf6ae, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd, 0xd512, 0x0108, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xffe0, 0xf6ae, 0xf780, 0xf780, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x0108, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0xf780, 0xf780, 0xf780, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0xf780, 0xf780, 0x6b8d, 0x6b8d, 0x45cc, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x37e0, 0x45cc, 0x6b8d, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x5a99, 0x6b8d, 0x94b3, 0xf4dd, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0x94b3, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x94b3, 0x6b8d, 0x94b3, 0x6b8d, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x619f, 0x619f, 0x619f, 0x2b5e, 0x04bf, 0x04bf, 0x04bf, 0x04bf, 0x2b5e, 0x94b3, 0xf43b, 0xf4dd,
|
||||
0xf4dd, 0xf4dd, 0xf43b, 0x94b3, 0x94b3, 0x4a6a, 0x4a6a, 0x4a6a, 0x4a6a, 0x94b3, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c,
|
||||
0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x619f, 0x717c, 0x717c, 0x717c, 0x6b8d, 0x6b8d, 0xf6ae, 0xddf7,
|
||||
0xddf7, 0xddf7, 0xddf7, 0xd512, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x6b8d, 0x4a6a, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c, 0x018c
|
||||
};
|
||||
|
||||
// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 2448)
|
||||
const int nyanallArray_LEN = 9;
|
||||
const uint16_t* nyanallArray[9] = {
|
||||
nyanframe_0_delay_0,
|
||||
nyanframe_1_delay_0,
|
||||
nyanframe_2_delay_0,
|
||||
nyanframe_3_delay_0,
|
||||
nyanframe_4_delay_0,
|
||||
nyanframe_5_delay_0,
|
||||
nyanframe_6_delay_0,
|
||||
nyanframe_7_delay_0,
|
||||
nyanframe_8_delay_0
|
||||
};
|
||||
|
||||
|
||||
//EEEEEP
|
||||
// 'pixil-frame-0(1)', 32x8px
|
||||
const uint16_t EEP [] PROGMEM = {
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff80, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff80, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0xff80, 0xe8e4, 0xff80, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff80, 0xe8e4, 0xff80, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0xff80, 0xff80, 0xe8e4, 0xff80, 0xff80, 0x0000, 0x0000, 0xe8e4, 0xe8e4, 0xe8e4, 0xe8e4, 0x0000, 0xe8e4,
|
||||
0xe8e4, 0xe8e4, 0xe8e4, 0x0000, 0xe8e4, 0xe8e4, 0xe8e4, 0x0000, 0xff80, 0xff80, 0xe8e4, 0xff80, 0xff80, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0xff80, 0xff80, 0xff80, 0xe8e4, 0xff80, 0xff80, 0xff80, 0x0000, 0xe8e4, 0x0000, 0x0000, 0x0000, 0x0000, 0xe8e4,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0xe8e4, 0x0000, 0x0000, 0xe8e4, 0xff80, 0xff80, 0xe8e4, 0xff80, 0xff80, 0xff80, 0x0000, 0x0000,
|
||||
0x0000, 0xff80, 0xff80, 0xff80, 0xff80, 0xe8e4, 0xff80, 0xff80, 0xff80, 0xff80, 0xe8e4, 0xe8e4, 0xe8e4, 0x0000, 0x0000, 0xe8e4,
|
||||
0xe8e4, 0xe8e4, 0x0000, 0x0000, 0xe8e4, 0xe8e4, 0xe8e4, 0xff80, 0xff80, 0xff80, 0xe8e4, 0xff80, 0xff80, 0xff80, 0xff80, 0x0000,
|
||||
0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xe8e4, 0x0000, 0x0000, 0x0000, 0x0000, 0xe8e4,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0xe8e4, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80,
|
||||
0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xe8e4, 0xff80, 0xff80, 0xff80, 0xff80, 0xe8e4, 0xe8e4, 0xe8e4, 0xe8e4, 0x0000, 0xe8e4,
|
||||
0xe8e4, 0xe8e4, 0xe8e4, 0x0000, 0xe8e4, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xe8e4, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80,
|
||||
0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80, 0xff80
|
||||
};
|
||||
|
||||
// Array of all bitmaps for convenience. (Total bytes used to store images in PROGMEM = 272)
|
||||
const int eepallArray_LEN = 1;
|
||||
const uint16_t* eepallArray[1] = {
|
||||
EEP
|
||||
};
|
11
src/Fab Matrixes/test/README
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
This directory is intended for PlatformIO Test Runner and project tests.
|
||||
|
||||
Unit Testing is a software testing method by which individual units of
|
||||
source code, sets of one or more MCU program modules together with associated
|
||||
control data, usage procedures, and operating procedures, are tested to
|
||||
determine whether they are fit for use. Unit testing finds problems early
|
||||
in the development cycle.
|
||||
|
||||
More information about PlatformIO Unit Testing:
|
||||
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
@@ -12,10 +12,14 @@
|
||||
platform = espressif32
|
||||
board = lolin_c3_mini
|
||||
framework = arduino
|
||||
lib_deps = adafruit/Adafruit SSD1306@^2.5.13
|
||||
lib_deps =
|
||||
adafruit/Adafruit SSD1306@^2.5.13
|
||||
olikraus/U8g2@^2.36.5
|
||||
|
||||
[env:Xiao_c3]
|
||||
board = seeed_xiao_esp32c3
|
||||
framework = arduino
|
||||
platform = espressif32
|
||||
lib_deps = adafruit/Adafruit SSD1306@^2.5.13
|
||||
lib_deps =
|
||||
adafruit/Adafruit SSD1306@^2.5.13
|
||||
olikraus/U8g2@^2.36.5
|
||||
|
@@ -1,19 +1,34 @@
|
||||
#include <Arduino.h>
|
||||
#include <WiFi.h>
|
||||
#include <esp_now.h>
|
||||
#include <U8g2lib.h>
|
||||
|
||||
const int MAXPWMVALUE = 1000;
|
||||
const int MINPWMVALUE = 2000;
|
||||
const uint8_t broadcastAddress[] = {0x8C, 0xBF, 0xEA, 0xCC, 0x8E, 0x5C};
|
||||
// Oled Screen stuff
|
||||
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
|
||||
|
||||
const int MAXPWMVALUE = 2000;
|
||||
const int MINPWMVALUE = 1000;
|
||||
const uint8_t broadcastAddress[] = {0xE4, 0xB3, 0x23, 0xB5, 0x8D, 0xD0};
|
||||
// e4:b3:23:b5:8d:d0
|
||||
//=====================================================================================//
|
||||
// Struct declarations
|
||||
// Struct declarations
|
||||
typedef struct filteredJoystickData
|
||||
{
|
||||
int PWMCH1;
|
||||
int PWMCH2;
|
||||
int PWMCH3;
|
||||
int PWMCH4;
|
||||
int killSwitch = 1; // 1 = throttle cut, 0 = normal operation
|
||||
} filteredJoystickData;
|
||||
filteredJoystickData filteredJoystick; // declare the struct as JoystickData
|
||||
|
||||
typedef struct struct_message
|
||||
{
|
||||
int PWMCH1;
|
||||
int PWMCH2;
|
||||
int PWMCH3;
|
||||
int PWMCH4;
|
||||
int killSwitch = 1; // 1 = throttle cut, 0 = normal operation
|
||||
} struct_message;
|
||||
struct_message JoystickData; // declare the struct as JoystickData
|
||||
|
||||
@@ -25,7 +40,17 @@ struct hardJoystickValues
|
||||
int LXD; // Left joystick X axis down
|
||||
int RXU; // Right joystick X axis up
|
||||
int RXD; // Right joystick X axis down
|
||||
};
|
||||
};
|
||||
|
||||
// uncalibrated offsets these are calibrated in setup() function
|
||||
int offsetPWMCH1 = 0; // Resting value for PWMCH1 (right joystick Y-axis)
|
||||
int offsetPWMCH2 = 0; // Resting value for PWMCH2 (right joystick X-axis)
|
||||
int offsetPWMCH3 = 0; // Resting value for PWMCH3 (left joystick X-axis)
|
||||
int offsetPWMCH4 = 0; // Resting value for PWMCH4 (left joystick Y-axis)
|
||||
// Define deadzone threshold
|
||||
const int deadzone = 200;
|
||||
const int pwmDeadzone = 50; // Much smaller deadzone in PWM space
|
||||
|
||||
|
||||
//=====================================================================================//
|
||||
// declarations
|
||||
@@ -35,48 +60,61 @@ int analogReadMultiPlexer(int addressA, int addressB, int addressC, int addressD
|
||||
void espNow();
|
||||
void readAllMultiPlexer();
|
||||
void espNowLoop();
|
||||
void printPWMValues();
|
||||
void printFilteredPWMValues();
|
||||
void printUnfilteredPWMValues();
|
||||
int digitalReadMultiPlexer(int addressA, int addressB, int addressC, int addressD, int pin);
|
||||
hardJoystickValues GUIParser();
|
||||
void MUXSetup();
|
||||
void killSwitch();
|
||||
void OLEDSetup();
|
||||
void deadZonedJoysticks(); // Apply deadzone to joystick values
|
||||
void calibrateJoystickOffsets();
|
||||
void printOffsets();
|
||||
void debugPWM1();
|
||||
void debugAllChannels();
|
||||
|
||||
|
||||
|
||||
//===========================================================================//
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
OLEDSetup();
|
||||
espNow();
|
||||
MUXSetup(); // Setup the multiplexer
|
||||
Serial.begin(9600);
|
||||
|
||||
calibrateJoystickOffsets();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
//Debugging
|
||||
// Debugging
|
||||
// readAllMultiPlexer();
|
||||
// printPWMValues();
|
||||
|
||||
// printFilteredPWMValues();
|
||||
// printUnfilteredPWMValues();
|
||||
// printOffsets();
|
||||
debugAllChannels();
|
||||
|
||||
// Set values to send
|
||||
JoystickData.PWMCH1 = mapPot(analogReadMultiPlexer(0, 0, 0, 0, A0)); //Right joystick Y
|
||||
JoystickData.PWMCH2 = mapPot(analogReadMultiPlexer(1, 0, 0, 0, A0)); // Right joystick X
|
||||
JoystickData.PWMCH3 = mapPot(analogReadMultiPlexer(0, 0, 0, 1, A0)); // left joystick Y
|
||||
JoystickData.PWMCH4 = mapPot(analogReadMultiPlexer(1, 0, 0, 1, A0)); // left joystick X
|
||||
bool buttonRight = abs(analogReadMultiPlexer(0, 0, 1, 0, A0)/4095); // right button
|
||||
bool buttonLeft = abs(analogReadMultiPlexer(1, 0, 1, 0, A0)/4095); // left button
|
||||
JoystickData.PWMCH1 = analogReadMultiPlexer(0, 0, 0, 0, A0); // Right joystick Y
|
||||
JoystickData.PWMCH2 = analogReadMultiPlexer(1, 0, 0, 0, A0); // Right joystick X
|
||||
JoystickData.PWMCH3 = analogReadMultiPlexer(0, 0, 0, 1, A0); // left joystick Y
|
||||
JoystickData.PWMCH4 = analogReadMultiPlexer(1, 0, 0, 1, A0); // left joystick X
|
||||
|
||||
GUIParser();
|
||||
deadZonedJoysticks(); // Apply deadzone to joystick values
|
||||
|
||||
bool buttonRight = abs(analogReadMultiPlexer(0, 0, 1, 0, A0) / 4095); // right button
|
||||
bool buttonLeft = abs(analogReadMultiPlexer(1, 0, 1, 0, A0) / 4095); // left button
|
||||
|
||||
killSwitch();
|
||||
espNowLoop();
|
||||
delay(10); // delay to avoid hammering the radio and to save power/heat
|
||||
}
|
||||
|
||||
|
||||
int mapPot(int normalizedValue)
|
||||
{
|
||||
return map(normalizedValue, 400, 2500, MINPWMVALUE, MAXPWMVALUE); // map the normalized value to the PWM range
|
||||
return map(normalizedValue, 400, 4095, MINPWMVALUE, MAXPWMVALUE); // map the normalized value to the PWM range
|
||||
}
|
||||
|
||||
//legacy stuff for original potsliders
|
||||
// legacy stuff for original potsliders
|
||||
int normalizePot(int pin, int minValue) // normalize the pot value to a range of 80 to 4095 instead of 0 to 4095 because the potmeter is at lower values not accurate
|
||||
{
|
||||
int pot = analogRead(pin);
|
||||
@@ -100,7 +138,7 @@ int analogReadMultiPlexer(int addressA, int addressB, int addressC, int addressD
|
||||
digitalWrite(D8, addressD);
|
||||
return analogRead(pin);
|
||||
}
|
||||
//For some reason the digitalRead function does not work with the multiplexer. Maybe because the Resistance is too high. Even though analogRead returns 4095
|
||||
// For some reason the digitalRead function does not work with the multiplexer. Maybe because the Resistance is too high. Even though analogRead returns 4095
|
||||
int digitalReadMultiPlexer(int addressA, int addressB, int addressC, int addressD, int pin)
|
||||
{
|
||||
digitalWrite(D3, LOW);
|
||||
@@ -132,9 +170,10 @@ void espNow()
|
||||
}
|
||||
}
|
||||
|
||||
void espNowLoop(){
|
||||
void espNowLoop()
|
||||
{
|
||||
// Send message via ESP-NOW
|
||||
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&JoystickData, sizeof(JoystickData));
|
||||
esp_err_t result = esp_now_send(broadcastAddress, (uint8_t *)&filteredJoystick, sizeof(filteredJoystick));
|
||||
if (result == ESP_OK)
|
||||
{
|
||||
// Serial.println("Sent with success");
|
||||
@@ -155,48 +194,148 @@ void MUXSetup()
|
||||
pinMode(A0, INPUT); // MUX input
|
||||
}
|
||||
// Function to parse joystick data to hard values
|
||||
//If joystick is Up then the value is 1 of var A
|
||||
//If joystick is Down then the value is 1 of var B
|
||||
//If joystick is in the middle A and B are 0
|
||||
hardJoystickValues GUIParser(){
|
||||
// If joystick is Up then the value is 1 of var A
|
||||
// If joystick is Down then the value is 1 of var B
|
||||
// If joystick is in the middle A and B are 0
|
||||
hardJoystickValues GUIParser()
|
||||
{
|
||||
|
||||
// Define joystick offsets (calibrated resting values)
|
||||
const int offsetPWMCH1 = 1090; // Resting value for PWMCH1 (right joystick Y-axis)
|
||||
const int offsetPWMCH2 = 1072; // Resting value for PWMCH2 (right joystick X-axis)
|
||||
const int offsetPWMCH3 = 1043; // Resting value for PWMCH3 (left joystick X-axis)
|
||||
const int offsetPWMCH4 = 1476; // Resting value for PWMCH4 (left joystick Y-axis)
|
||||
|
||||
// Define deadzone threshold
|
||||
const int deadzone = 120;
|
||||
|
||||
// Adjust joystick values by subtracting offsets
|
||||
int adjustedPWMCH1 = JoystickData.PWMCH1 - offsetPWMCH1;
|
||||
int adjustedPWMCH2 = JoystickData.PWMCH2 - offsetPWMCH2;
|
||||
int adjustedPWMCH3 = JoystickData.PWMCH3 - offsetPWMCH3;
|
||||
int adjustedPWMCH4 = JoystickData.PWMCH4 - offsetPWMCH4;
|
||||
|
||||
// Apply deadzone
|
||||
if (abs(adjustedPWMCH1) < deadzone) adjustedPWMCH1 = 0; //abs to avoid negatives
|
||||
if (abs(adjustedPWMCH2) < deadzone) adjustedPWMCH2 = 0;
|
||||
if (abs(adjustedPWMCH3) < deadzone) adjustedPWMCH3 = 0;
|
||||
if (abs(adjustedPWMCH4) < deadzone) adjustedPWMCH4 = 0;
|
||||
// Adjust joystick values by subtracting offsets
|
||||
int adjustedPWMCH1 = JoystickData.PWMCH1 - offsetPWMCH1;
|
||||
int adjustedPWMCH2 = JoystickData.PWMCH2 - offsetPWMCH2;
|
||||
int adjustedPWMCH3 = JoystickData.PWMCH3 - offsetPWMCH3;
|
||||
int adjustedPWMCH4 = JoystickData.PWMCH4 - offsetPWMCH4;
|
||||
|
||||
// Map joystick values to hard values
|
||||
int LXU = 0; // Left joystick X axis up
|
||||
int LXD = 0; // Left joystick X axis down
|
||||
if (adjustedPWMCH1 > 0) {
|
||||
LXU = 1; // Joystick is up
|
||||
} else if (adjustedPWMCH1 < 0) {
|
||||
LXD = 1; // Joystick is down
|
||||
}
|
||||
return {LXU, LXD, 0, 0}; // Return the values as a struct
|
||||
// Apply deadzone
|
||||
if (abs(adjustedPWMCH1) < deadzone)
|
||||
adjustedPWMCH1 = 0; // abs to avoid negatives
|
||||
if (abs(adjustedPWMCH2) < deadzone)
|
||||
adjustedPWMCH2 = 0;
|
||||
if (abs(adjustedPWMCH3) < deadzone)
|
||||
adjustedPWMCH3 = 0;
|
||||
if (abs(adjustedPWMCH4) < deadzone)
|
||||
adjustedPWMCH4 = 0;
|
||||
|
||||
// Map joystick values to hard values
|
||||
int LXU = 0; // Left joystick X axis up
|
||||
int LXD = 0; // Left joystick X axis down
|
||||
if (adjustedPWMCH1 > 0)
|
||||
{
|
||||
LXU = 1; // Joystick is up
|
||||
}
|
||||
else if (adjustedPWMCH1 < 0)
|
||||
{
|
||||
LXD = 1; // Joystick is down
|
||||
}
|
||||
return {LXU, LXD, 0, 0}; // Return the values as a struct
|
||||
}
|
||||
|
||||
void deadZonedJoysticks()
|
||||
{
|
||||
// Convert raw values to PWM first, then check deadzone
|
||||
int pwmCH1 = mapPot(JoystickData.PWMCH1);
|
||||
int pwmCH2 = mapPot(JoystickData.PWMCH2);
|
||||
int pwmCH3 = mapPot(JoystickData.PWMCH3);
|
||||
int pwmCH4 = mapPot(JoystickData.PWMCH4);
|
||||
|
||||
// Convert offsets to PWM too
|
||||
int pwmOffsetCH1 = mapPot(offsetPWMCH1);
|
||||
int pwmOffsetCH2 = mapPot(offsetPWMCH2);
|
||||
int pwmOffsetCH3 = mapPot(offsetPWMCH3);
|
||||
int pwmOffsetCH4 = mapPot(offsetPWMCH4);
|
||||
|
||||
// Calculate adjusted values in PWM space
|
||||
int adjustedPWMCH1 = pwmCH1 - pwmOffsetCH1;
|
||||
int adjustedPWMCH2 = pwmCH2 - pwmOffsetCH2;
|
||||
int adjustedPWMCH3 = pwmCH3 - pwmOffsetCH3;
|
||||
int adjustedPWMCH4 = pwmCH4 - pwmOffsetCH4;
|
||||
|
||||
// // Apply deadzone
|
||||
// if (abs(adjustedPWMCH1) < deadzone)
|
||||
// filteredJoystick.PWMCH1 = 1500; // abs to avoid negatives
|
||||
// else
|
||||
// filteredJoystick.PWMCH1 = JoystickData.PWMCH1 = mapPot(analogReadMultiPlexer(0, 0, 0, 0, A0)); // Right joystick Y
|
||||
// if (abs(adjustedPWMCH2) < deadzone)
|
||||
// filteredJoystick.PWMCH2 = 1500;
|
||||
// else
|
||||
// filteredJoystick.PWMCH2 = JoystickData.PWMCH2 = mapPot(analogReadMultiPlexer(1, 0, 0, 0, A0)); // Right joystick X
|
||||
// if (abs(adjustedPWMCH3) < deadzone)
|
||||
// filteredJoystick.PWMCH3 = 1500;
|
||||
// else
|
||||
// filteredJoystick.PWMCH3 = JoystickData.PWMCH3 = mapPot(analogReadMultiPlexer(0, 0, 0, 1, A0)); // Left joystick Y
|
||||
// if (abs(adjustedPWMCH4) < deadzone)
|
||||
// filteredJoystick.PWMCH4 = 1500;
|
||||
// else
|
||||
// filteredJoystick.PWMCH4 = JoystickData.PWMCH4 = mapPot(analogReadMultiPlexer(1, 0, 0, 1, A0)); // Left joystick X
|
||||
|
||||
// Apply deadzone and set filtered values
|
||||
// This is the same as above but more compact
|
||||
|
||||
filteredJoystick.PWMCH1 = (abs(adjustedPWMCH1) < pwmDeadzone) ? 1500 : pwmCH1;
|
||||
filteredJoystick.PWMCH2 = (abs(adjustedPWMCH2) < pwmDeadzone) ? 1500 : pwmCH2;
|
||||
filteredJoystick.PWMCH3 = (abs(adjustedPWMCH3) < pwmDeadzone) ? 1500 : pwmCH3;
|
||||
filteredJoystick.PWMCH4 = (abs(adjustedPWMCH4) < pwmDeadzone) ? 1500 : pwmCH4;
|
||||
|
||||
// Copy kill switch state
|
||||
filteredJoystick.killSwitch = JoystickData.killSwitch;
|
||||
}
|
||||
|
||||
void calibrateJoystickOffsets()
|
||||
{
|
||||
delay(5000); // Wait for everything to stabilize
|
||||
|
||||
long sum[4] = {0, 0, 0, 0};
|
||||
const int samples = 500;
|
||||
|
||||
for (int i = 0; i < samples; i++)
|
||||
{
|
||||
sum[0] += analogReadMultiPlexer(0, 0, 0, 0, A0);
|
||||
sum[1] += analogReadMultiPlexer(1, 0, 0, 0, A0);
|
||||
sum[2] += analogReadMultiPlexer(0, 0, 0, 1, A0);
|
||||
sum[3] += analogReadMultiPlexer(1, 0, 0, 1, A0);
|
||||
delay(10); // Longer delay for stability
|
||||
}
|
||||
|
||||
offsetPWMCH1 = sum[0] / samples;
|
||||
offsetPWMCH2 = sum[1] / samples;
|
||||
offsetPWMCH3 = sum[2] / samples;
|
||||
offsetPWMCH4 = sum[3] / samples + 1000; // Hardware quirk of the controller
|
||||
}
|
||||
|
||||
void killSwitch()
|
||||
{
|
||||
// Set the kill switch to 1 to stop the motors
|
||||
if (abs(analogReadMultiPlexer(0, 0, 1, 0, A0) / 4095) == 1) // Right button
|
||||
{
|
||||
JoystickData.killSwitch = 1; // Activate kill switch
|
||||
Serial.println("Kill switch activated, motors stopped.");
|
||||
u8g2.clearBuffer();
|
||||
u8g2.drawStr(25, 15, "Kill Switch: ON");
|
||||
}
|
||||
else if (abs(analogReadMultiPlexer(1, 0, 1, 0, A0) / 4095) == 1) // Left button
|
||||
{
|
||||
JoystickData.killSwitch = 0; // Deactivate kill switch
|
||||
Serial.println("Kill switch deactivated, motors can run.");
|
||||
u8g2.clearBuffer();
|
||||
u8g2.drawStr(25, 15, "Kill Switch: OFF");
|
||||
}
|
||||
u8g2.sendBuffer();
|
||||
}
|
||||
|
||||
void OLEDSetup()
|
||||
{
|
||||
u8g2.begin();
|
||||
u8g2.clearBuffer();
|
||||
u8g2.sendBuffer();
|
||||
u8g2.setFont(u8g2_font_6x10_tf); // Use a different, simpler font
|
||||
u8g2.clearBuffer();
|
||||
u8g2.drawStr(25, 15, "Kill Switch: ON");
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// Debugging Functions //
|
||||
/////////////////////////////////////////////
|
||||
|
||||
|
||||
void readAllMultiPlexer()
|
||||
{
|
||||
// we counting in binary
|
||||
@@ -234,8 +373,19 @@ void readAllMultiPlexer()
|
||||
Serial.println(analogReadMultiPlexer(1, 1, 1, 1, A0));
|
||||
}
|
||||
|
||||
void printFilteredPWMValues()
|
||||
{
|
||||
Serial.print("PWMCH1: ");
|
||||
Serial.print(filteredJoystick.PWMCH1);
|
||||
Serial.print(" PWMCH2: ");
|
||||
Serial.print(filteredJoystick.PWMCH2);
|
||||
Serial.print(" PWMCH3: ");
|
||||
Serial.print(filteredJoystick.PWMCH3);
|
||||
Serial.print(" PWMCH4: ");
|
||||
Serial.println(filteredJoystick.PWMCH4);
|
||||
}
|
||||
|
||||
void printPWMValues()
|
||||
void printUnfilteredPWMValues()
|
||||
{
|
||||
Serial.print("PWMCH1: ");
|
||||
Serial.print(JoystickData.PWMCH1);
|
||||
@@ -245,4 +395,75 @@ void printPWMValues()
|
||||
Serial.print(JoystickData.PWMCH3);
|
||||
Serial.print(" PWMCH4: ");
|
||||
Serial.println(JoystickData.PWMCH4);
|
||||
}
|
||||
|
||||
void printOffsets()
|
||||
{
|
||||
Serial.print("offsetPWMCH1: ");
|
||||
Serial.print(offsetPWMCH1);
|
||||
Serial.print(" offsetPWMCH2: ");
|
||||
Serial.print(offsetPWMCH2);
|
||||
Serial.print(" offsetPWMCH3: ");
|
||||
Serial.print(offsetPWMCH3);
|
||||
Serial.print(" offsetPWMCH4: ");
|
||||
Serial.println(offsetPWMCH4);
|
||||
}
|
||||
|
||||
void debugAllChannels() {
|
||||
// Raw values
|
||||
Serial.print("Raw: [");
|
||||
Serial.print(JoystickData.PWMCH1); Serial.print(",");
|
||||
Serial.print(JoystickData.PWMCH2); Serial.print(",");
|
||||
Serial.print(JoystickData.PWMCH3); Serial.print(",");
|
||||
Serial.print(JoystickData.PWMCH4); Serial.print("] ");
|
||||
|
||||
// Offsets
|
||||
Serial.print("Offsets: [");
|
||||
Serial.print(offsetPWMCH1); Serial.print(",");
|
||||
Serial.print(offsetPWMCH2); Serial.print(",");
|
||||
Serial.print(offsetPWMCH3); Serial.print(",");
|
||||
Serial.print(offsetPWMCH4); Serial.print("] ");
|
||||
|
||||
// PWM converted values
|
||||
int pwmCH1 = mapPot(JoystickData.PWMCH1);
|
||||
int pwmCH2 = mapPot(JoystickData.PWMCH2);
|
||||
int pwmCH3 = mapPot(JoystickData.PWMCH3);
|
||||
int pwmCH4 = mapPot(JoystickData.PWMCH4);
|
||||
|
||||
int pwmOffsetCH1 = mapPot(offsetPWMCH1);
|
||||
int pwmOffsetCH2 = mapPot(offsetPWMCH2);
|
||||
int pwmOffsetCH3 = mapPot(offsetPWMCH3);
|
||||
int pwmOffsetCH4 = mapPot(offsetPWMCH4);
|
||||
|
||||
Serial.print("PWM: [");
|
||||
Serial.print(pwmCH1); Serial.print(",");
|
||||
Serial.print(pwmCH2); Serial.print(",");
|
||||
Serial.print(pwmCH3); Serial.print(",");
|
||||
Serial.print(pwmCH4); Serial.print("] ");
|
||||
|
||||
// Adjusted values
|
||||
int adjustedPWMCH1 = pwmCH1 - pwmOffsetCH1;
|
||||
int adjustedPWMCH2 = pwmCH2 - pwmOffsetCH2;
|
||||
int adjustedPWMCH3 = pwmCH3 - pwmOffsetCH3;
|
||||
int adjustedPWMCH4 = pwmCH4 - pwmOffsetCH4;
|
||||
|
||||
Serial.print("Adj: [");
|
||||
Serial.print(adjustedPWMCH1); Serial.print(",");
|
||||
Serial.print(adjustedPWMCH2); Serial.print(",");
|
||||
Serial.print(adjustedPWMCH3); Serial.print(",");
|
||||
Serial.print(adjustedPWMCH4); Serial.print("] ");
|
||||
|
||||
// Deadzone status
|
||||
Serial.print("InDZ: [");
|
||||
Serial.print(abs(adjustedPWMCH1) < pwmDeadzone ? "Y" : "N"); Serial.print(",");
|
||||
Serial.print(abs(adjustedPWMCH2) < pwmDeadzone ? "Y" : "N"); Serial.print(",");
|
||||
Serial.print(abs(adjustedPWMCH3) < pwmDeadzone ? "Y" : "N"); Serial.print(",");
|
||||
Serial.print(abs(adjustedPWMCH4) < pwmDeadzone ? "Y" : "N"); Serial.print("] ");
|
||||
|
||||
// Final filtered values
|
||||
Serial.print("Final: [");
|
||||
Serial.print(filteredJoystick.PWMCH1); Serial.print(",");
|
||||
Serial.print(filteredJoystick.PWMCH2); Serial.print(",");
|
||||
Serial.print(filteredJoystick.PWMCH3); Serial.print(",");
|
||||
Serial.print(filteredJoystick.PWMCH4); Serial.println("]");
|
||||
}
|
@@ -19,4 +19,5 @@ lib_deps =
|
||||
paulstoffregen/PWMServo@^2.1
|
||||
sparkfun/SparkFun BNO080 Cortex Based IMU@^1.1.12
|
||||
madhephaestus/ESP32Servo@^3.0.6
|
||||
build_flags = -DCORE_DEBUG_LEVEL=5
|
||||
plerup/EspSoftwareSerial@^8.2.0
|
||||
build_flags = -DCORE_DEBUG_LEVEL=5
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include <esp_now.h>
|
||||
#include <WiFi.h>
|
||||
#include <driver/ledc.h>
|
||||
#include <softwareSerial.h>
|
||||
// Arduino/Teensy Flight Controller - dRehmFlight
|
||||
// Author: Nicholas Rehm
|
||||
// Project Start: 1/6/2020
|
||||
@@ -27,7 +28,6 @@ http://www.bolderflight.com
|
||||
Thank you to:
|
||||
RcGroups 'jihlein' - IMU implementation overhaul + SBUS implementation.
|
||||
Everyone that sends me pictures and videos of your flying creations! -Nick
|
||||
|
||||
*/
|
||||
|
||||
//========================================================================================================================//
|
||||
@@ -157,10 +157,8 @@ BNO080 myIMU;
|
||||
#define ACCEL_SCALE_FACTOR 1.0 // BNO085 gives values already in g's when converted in getIMUdata
|
||||
#endif
|
||||
|
||||
#define LEDC_FREQ 500 // PWM frequency in Hz for OneShot125 (can be higher)
|
||||
#define LEDC_RESOLUTION 13 // 13-bit resolution gives values from 0-8191
|
||||
#define LEDC_TIMER LEDC_TIMER_0 // Use timer 0
|
||||
#define LEDC_MODE LEDC_HIGH_SPEED_MODE // Use high-speed mode
|
||||
#define LEDC_FREQ 50
|
||||
#define LEDC_RESOLUTION 16
|
||||
|
||||
|
||||
//========================================================================================================================//
|
||||
@@ -176,9 +174,9 @@ unsigned long channel_5_fs = 2000; // gear, greater than 1500 = throttle cut
|
||||
unsigned long channel_6_fs = 2000; // aux1
|
||||
|
||||
// Filter parameters - Defaults tuned for 2kHz loop rate; Do not touch unless you know what you are doing:
|
||||
float B_madgwick = 0.04; // Madgwick filter parameter
|
||||
float B_accel = 0.14; // Accelerometer LP filter paramter, (MPU6050 default: 0.14. MPU9250 default: 0.2)
|
||||
float B_gyro = 0.1; // Gyro LP filter paramter, (MPU6050 default: 0.1. MPU9250 default: 0.17)
|
||||
float B_madgwick = 0.00001; // Madgwick filter parameter
|
||||
float B_accel = 0.00001; // Accelerometer LP filter paramter, (MPU6050 default: 0.14. MPU9250 default: 0.2)
|
||||
float B_gyro = 0.0001; // Gyro LP filter paramter, (MPU6050 default: 0.1. MPU9250 default: 0.17)
|
||||
float B_mag = 1.0; // Magnetometer LP filter parameter
|
||||
|
||||
// Magnetometer calibration parameters - if using MPU9250, uncomment calibrateMagnetometer() in void setup() to get these values, else just ignore these
|
||||
@@ -190,33 +188,33 @@ float MagScaleY = 1.0;
|
||||
float MagScaleZ = 1.0;
|
||||
|
||||
// IMU calibration parameters - calibrate IMU using calculate_IMU_error() in the void setup() to get these values, then comment out calculate_IMU_error()
|
||||
float AccErrorX = 0.0;
|
||||
float AccErrorY = 0.0;
|
||||
float AccErrorZ = 0.0;
|
||||
float GyroErrorX = 0.0;
|
||||
float GyroErrorY = 0.0;
|
||||
float GyroErrorZ = 0.0;
|
||||
float AccErrorX = 0.07;
|
||||
float AccErrorY = -0.01;
|
||||
float AccErrorZ = -0.01;
|
||||
float GyroErrorX = -0.00;
|
||||
float GyroErrorY = 0.00;
|
||||
float GyroErrorZ = 0.00;
|
||||
|
||||
// Controller parameters (take note of defaults before modifying!):
|
||||
float i_limit = 25.0; // Integrator saturation level, mostly for safety (default 25.0)
|
||||
float maxRoll = 30.0; // Max roll angle in degrees for angle mode (maximum ~70 degrees), deg/sec for rate mode
|
||||
float maxPitch = 30.0; // Max pitch angle in degrees for angle mode (maximum ~70 degrees), deg/sec for rate mode
|
||||
float maxRoll = 15.0; // Max roll angle in degrees for angle mode (maximum ~70 degrees), deg/sec for rate mode
|
||||
float maxPitch = 15.0; // Max pitch angle in degrees for angle mode (maximum ~70 degrees), deg/sec for rate mode
|
||||
float maxYaw = 160.0; // Max yaw rate in deg/sec
|
||||
|
||||
float Kp_roll_angle = 0.2; // Roll P-gain - angle mode
|
||||
float Ki_roll_angle = 0.3; // Roll I-gain - angle mode
|
||||
float Kd_roll_angle = 0.05; // Roll D-gain - angle mode (has no effect on controlANGLE2)
|
||||
float Kp_roll_angle = 2.0; // Roll P-gain - angle mode
|
||||
float Ki_roll_angle = 0.5; // Roll I-gain - angle mode
|
||||
float Kd_roll_angle = 0.1; // Roll D-gain - angle mode (has no effect on controlANGLE2)
|
||||
float B_loop_roll = 0.9; // Roll damping term for controlANGLE2(), lower is more damping (must be between 0 to 1)
|
||||
float Kp_pitch_angle = 0.2; // Pitch P-gain - angle mode
|
||||
float Ki_pitch_angle = 0.3; // Pitch I-gain - angle mode
|
||||
float Kd_pitch_angle = 0.05; // Pitch D-gain - angle mode (has no effect on controlANGLE2)
|
||||
float Kp_pitch_angle = 2.0; // Pitch P-gain - angle mode
|
||||
float Ki_pitch_angle = 0.5; // Pitch I-gain - angle mode
|
||||
float Kd_pitch_angle = 0.1; // Pitch D-gain - angle mode (has no effect on controlANGLE2)
|
||||
float B_loop_pitch = 0.9; // Pitch damping term for controlANGLE2(), lower is more damping (must be between 0 to 1)
|
||||
|
||||
float Kp_roll_rate = 0.15; // Roll P-gain - rate mode
|
||||
float Ki_roll_rate = 0.2; // Roll I-gain - rate mode
|
||||
float Kp_roll_rate = 0.015; // Roll P-gain - rate mode
|
||||
float Ki_roll_rate = 0.02; // Roll I-gain - rate mode
|
||||
float Kd_roll_rate = 0.0002; // Roll D-gain - rate mode (be careful when increasing too high, motors will begin to overheat!)
|
||||
float Kp_pitch_rate = 0.15; // Pitch P-gain - rate mode
|
||||
float Ki_pitch_rate = 0.2; // Pitch I-gain - rate mode
|
||||
float Kp_pitch_rate = 0.015; // Pitch P-gain - rate mode
|
||||
float Ki_pitch_rate = 0.02; // Pitch I-gain - rate mode
|
||||
float Kd_pitch_rate = 0.0002; // Pitch D-gain - rate mode (be careful when increasing too high, motors will begin to overheat!)
|
||||
|
||||
float Kp_yaw = 0.3; // Yaw P-gain
|
||||
@@ -233,16 +231,16 @@ float Kd_yaw = 0.00015; // Yaw D-gain (be careful when increasing too high, moto
|
||||
const int ch1Pin = 15; // throttle
|
||||
const int ch2Pin = 16; // ail
|
||||
const int ch3Pin = 17; // ele
|
||||
const int ch4Pin = 20; // rudd
|
||||
const int ch5Pin = D7; // gear (throttle cut)
|
||||
const int ch6Pin = D7; // aux1 (free aux channel)
|
||||
const int PPM_Pin = D7;
|
||||
const int ch4Pin = 17; // rudd
|
||||
const int ch5Pin = 17; // gear (throttle cut)
|
||||
const int ch6Pin = 17; // aux1 (free aux channel)
|
||||
const int PPM_Pin = 17;
|
||||
// OneShot125 ESC pin outputs:
|
||||
const int m1Pin = D0;
|
||||
const int m2Pin = D1;
|
||||
const int m3Pin = D2;
|
||||
const int m4Pin = D3;
|
||||
const int m5Pin = D7;
|
||||
const int m1Pin = D9; // Front Right CW (top right motor)
|
||||
const int m2Pin = D10; // Front Left CCW (top left motor)
|
||||
const int m3Pin = D3; // Back Right CCW (bottom right motor)
|
||||
const int m4Pin = D2; // Back Left CW (bottom left motor)
|
||||
const int m5Pin = D7; //for some readon D9 doesnt work
|
||||
const int m6Pin = D8;
|
||||
|
||||
|
||||
@@ -278,8 +276,9 @@ typedef struct struct_message
|
||||
int PWMCH2 = 1500;
|
||||
int PWMCH3 = 1500;
|
||||
int PWMCH4 = 1500;
|
||||
int killSwitch = 1; // 1 = throttle cut, 0 = normal operation
|
||||
} struct_message;
|
||||
struct_message myData; //Initialise struct as myData
|
||||
struct_message ControllerData; //Initialise struct as ControllerData
|
||||
#endif
|
||||
|
||||
// IMU:
|
||||
@@ -321,8 +320,6 @@ void IMUinit();
|
||||
void getIMUdata();
|
||||
void calculate_IMU_error();
|
||||
void calibrateAttitude();
|
||||
void Madgwick(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float invSampleFreq);
|
||||
void Madgwick6DOF(float gx, float gy, float gz, float ax, float ay, float az, float invSampleFreq);
|
||||
void getDesState();
|
||||
void controlANGLE();
|
||||
void controlANGLE2();
|
||||
@@ -331,6 +328,8 @@ void scaleCommands();
|
||||
void getCommands();
|
||||
void failSafe();
|
||||
void commandMotors();
|
||||
void Madgwick(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float invSampleFreq);
|
||||
void Madgwick6DOF(float gx, float gy, float gz, float ax, float ay, float az, float invSampleFreq);
|
||||
void armMotors();
|
||||
void calibrateESCs();
|
||||
float floatFaderLinear(float param, float param_min, float param_max, float fadeTime, int state, int loopFreq);
|
||||
@@ -356,6 +355,12 @@ void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len);
|
||||
void ESPNowSetup();
|
||||
void printDebugInfo();
|
||||
void setupMotorPWM();
|
||||
int pulseWidthToDutyCycle(int pulseWidth);
|
||||
void setupMatrixSerial();
|
||||
void printPitchInfo();
|
||||
void printAttitudeDebug();
|
||||
void testIndividualMotors();
|
||||
void armMotors();
|
||||
//========================================================================================================================//
|
||||
// VOID SETUP //
|
||||
//========================================================================================================================//
|
||||
@@ -365,14 +370,15 @@ void setup()
|
||||
Serial.begin(9600); // USB serial
|
||||
Serial.println("Serial started");
|
||||
|
||||
Serial.println("Initiating ESC");
|
||||
setupMotorPWM();
|
||||
|
||||
// Initialize IMU communication
|
||||
Serial.println("Initiating IMU");
|
||||
IMUinit();
|
||||
|
||||
Serial.println("Initiating ESC");
|
||||
setupMotorPWM();
|
||||
Serial.println("Attach servos");
|
||||
|
||||
setupMatrixSerial();
|
||||
// servo7.attach(servo7Pin, 900, 2100);
|
||||
|
||||
// Set built in LED to turn on to signal startup
|
||||
@@ -406,7 +412,7 @@ void setup()
|
||||
|
||||
delay(5);
|
||||
|
||||
// calibrateESCs(); //PROPS OFF. Uncomment this to calibrate your ESCs by setting throttle stick to max, powering on, and lowering throttle to zero after the beeps
|
||||
calibrateESCs(); //PROPS OFF. Uncomment this to calibrate your ESCs by setting throttle stick to max, powering on, and lowering throttle to zero after the beeps
|
||||
// Code will not proceed past here if this function is uncommented!
|
||||
|
||||
// Arm OneShot125 motors
|
||||
@@ -425,6 +431,8 @@ void setup()
|
||||
|
||||
// If using MPU9250 IMU, uncomment for one-time magnetometer calibration (may need to repeat for new locations)
|
||||
// calibrateMagnetometer(); //Generates magentometer error and scale factors to be pasted in user-specified variables section
|
||||
// armMotors();
|
||||
// testIndividualMotors(); // Test individual motors by commanding them to 1200us pulse length for 1 second each
|
||||
}
|
||||
|
||||
//========================================================================================================================//
|
||||
@@ -442,15 +450,17 @@ void loop()
|
||||
|
||||
// Print data at 100hz (uncomment one at a time for troubleshooting) - SELECT ONE:
|
||||
// printRadioData(); //Prints radio pwm values (expected: 1000 to 2000)
|
||||
printDesiredState(); //Prints desired vehicle state commanded in either degrees or deg/sec (expected: +/- maxAXIS for roll, pitch, yaw; 0 to 1 for throttle)
|
||||
// printDesiredState(); //Prints desired vehicle state commanded in either degrees or deg/sec (expected: +/- maxAXIS for roll, pitch, yaw; 0 to 1 for throttle)
|
||||
// printGyroData(); //Prints filtered gyro data direct from IMU (expected: ~ -250 to 250, 0 at rest)
|
||||
// printAccelData(); // Prints filtered accelerometer data direct from IMU (expected: ~ -2 to 2; x,y 0 when level, z 1 when level)
|
||||
// printMagData(); //Prints filtered magnetometer data direct from IMU (expected: ~ -300 to 300)
|
||||
// printRollPitchYaw(); //Prints roll, pitch, and yaw angles in degrees from Madgwick filter (expected: degrees, 0 when level)
|
||||
// printPIDoutput(); //Prints computed stabilized PID variables from controller and desired setpoint (expected: ~ -1 to 1)
|
||||
// printMotorCommands(); //Prints the values being written to the motors (expected: 120 to 250)
|
||||
printMotorCommands(); //Prints the values being written to the motors (expected: 120 to 250)
|
||||
// printLoopRate(); //Prints the time between loops in microseconds (expected: microseconds between loop iterations)
|
||||
// printDebugInfo(); //PWM 1 and 5 + Armed condition
|
||||
// printDebugInfo(); //PWM 1 and 5 + Armed conditio
|
||||
// printPitchInfo();
|
||||
// printAttitudeDebug();
|
||||
|
||||
// Get arming status
|
||||
armedStatus(); // Check if the throttle cut is off and throttle is low.
|
||||
@@ -503,16 +513,16 @@ void controlMixer()
|
||||
*roll_passthru, pitch_passthru, yaw_passthru - direct unstabilized command passthrough
|
||||
*channel_6_pwm - free auxillary channel, can be used to toggle things with an 'if' statement
|
||||
*/
|
||||
|
||||
// Quad mixing - EXAMPLE
|
||||
m1_command_scaled = thro_des - pitch_PID + roll_PID + yaw_PID; // Front Left
|
||||
m2_command_scaled = thro_des - pitch_PID - roll_PID - yaw_PID; // Front Right
|
||||
m3_command_scaled = thro_des + pitch_PID - roll_PID + yaw_PID; // Back Right
|
||||
m4_command_scaled = thro_des + pitch_PID + roll_PID - yaw_PID; // Back Left
|
||||
|
||||
// Quad mixer with pitch bias because the sensor isnt flat on the board
|
||||
m1_command_scaled = thro_des + roll_PID - pitch_PID + yaw_PID; // Front Right CW
|
||||
m2_command_scaled = thro_des - roll_PID - pitch_PID - yaw_PID; // Back Left CW
|
||||
m3_command_scaled = thro_des + roll_PID + pitch_PID - yaw_PID; // Back Right CCW
|
||||
m4_command_scaled = thro_des - roll_PID + pitch_PID + yaw_PID; // Front Left CCW
|
||||
|
||||
m5_command_scaled = 0;
|
||||
m6_command_scaled = 0;
|
||||
|
||||
// 0.5 is centered servo, 0.0 is zero throttle if connecting to ESC for conventional PWM, 1.0 is max throttle
|
||||
|
||||
s1_command_scaled = 0;
|
||||
s2_command_scaled = 0;
|
||||
s3_command_scaled = 0;
|
||||
@@ -590,7 +600,7 @@ void IMUinit()
|
||||
}
|
||||
Wire.setClock(400000); // Increase I2C data rate to 400kHz
|
||||
Serial.println("IMU initialized");
|
||||
myIMU.enableGyro(50);
|
||||
myIMU.enableGyro(50); //for some reason when enabling things too fast the BNO085 will not respond
|
||||
delay(100);
|
||||
myIMU.enableAccelerometer(50);
|
||||
delay(100);
|
||||
@@ -620,7 +630,6 @@ void getIMUdata()
|
||||
mpu9250.getMotion9(&AcX, &AcY, &AcZ, &GyX, &GyY, &GyZ, &MgX, &MgY, &MgZ);
|
||||
#elif defined USE_BNO085_I2C
|
||||
// Keep waiting until data becomes available
|
||||
// Wait for data with timeout
|
||||
unsigned long startTime = millis();
|
||||
while (!myIMU.dataAvailable()) {
|
||||
delay(1);
|
||||
@@ -630,18 +639,16 @@ void getIMUdata()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get quaternion directly from BNO085
|
||||
q0 = myIMU.getQuatReal(); // w component
|
||||
q1 = myIMU.getQuatI(); // x component
|
||||
q2 = myIMU.getQuatJ(); // y component
|
||||
q3 = myIMU.getQuatK(); // z component
|
||||
|
||||
// Calculate Euler angles from quaternion
|
||||
roll_IMU = atan2(2.0f * (q0*q1 + q2*q3), 1.0f - 2.0f * (q1*q1 + q2*q2)) * 57.29577951f;
|
||||
pitch_IMU = asin(2.0f * (q0*q2 - q3*q1)) * 57.29577951f;
|
||||
yaw_IMU = atan2(2.0f * (q0*q3 + q1*q2), 1.0f - 2.0f * (q2*q2 + q3*q3)) * 57.29577951f;
|
||||
|
||||
// Get quaternion directly from BNO085
|
||||
q0 = myIMU.getQuatReal(); // w component
|
||||
q1 = myIMU.getQuatI(); // x component
|
||||
q2 = myIMU.getQuatJ(); // y component
|
||||
q3 = myIMU.getQuatK(); // z component
|
||||
|
||||
// CORRECTED Euler angle calculation for BNO085
|
||||
roll_IMU = atan2(2.0f * (q0*q1 + q2*q3), 1.0f - 2.0f * (q1*q1 + q2*q2)) * 57.29577951f;
|
||||
pitch_IMU = asin(constrain(2.0f * (q0*q2 - q3*q1), -0.999999, 0.999999)) * 57.29577951f;
|
||||
yaw_IMU = atan2(2.0f * (q0*q3 + q1*q2), 1.0f - 2.0f * (q2*q2 + q3*q3)) * 57.29577951f;
|
||||
|
||||
// Get raw sensor data for PID controller
|
||||
GyroX = myIMU.getGyroX() * 57.29577951f; // Convert rad/s to deg/s
|
||||
@@ -660,7 +667,7 @@ void getIMUdata()
|
||||
GyroY_prev = GyroY;
|
||||
GyroZ_prev = GyroZ;
|
||||
|
||||
// Get accelerometer data (still useful for PID control)
|
||||
// Get accelerometer data (still useful for validation)
|
||||
AccX = myIMU.getAccelX() / 9.80665f; // Convert m/s² to g
|
||||
AccY = myIMU.getAccelY() / 9.80665f;
|
||||
AccZ = myIMU.getAccelZ() / 9.80665f;
|
||||
@@ -677,7 +684,6 @@ void getIMUdata()
|
||||
AccY_prev = AccY;
|
||||
AccZ_prev = AccZ;
|
||||
|
||||
|
||||
// Return to avoid executing code for other IMUs
|
||||
return;
|
||||
#endif
|
||||
@@ -740,45 +746,54 @@ void calculate_IMU_error()
|
||||
* measurement.
|
||||
*/
|
||||
int16_t AcX, AcY, AcZ, GyX, GyY, GyZ, MgX, MgY, MgZ;
|
||||
AccErrorX = 0.0;
|
||||
AccErrorY = 0.0;
|
||||
AccErrorZ = 0.0;
|
||||
GyroErrorX = 0.0;
|
||||
GyroErrorY = 0.0;
|
||||
GyroErrorZ = 0.0;
|
||||
// DESCRIPTION: Computes IMU accelerometer and gyro error on startup
|
||||
AccErrorX = 0.0;
|
||||
AccErrorY = 0.0;
|
||||
AccErrorZ = 0.0;
|
||||
GyroErrorX = 0.0;
|
||||
GyroErrorY = 0.0;
|
||||
GyroErrorZ = 0.0;
|
||||
|
||||
// Read IMU values 12000 times
|
||||
int c = 0;
|
||||
while (c < 12000)
|
||||
{
|
||||
#if defined USE_MPU6050_I2C
|
||||
Serial.println("Starting IMU calibration...");
|
||||
|
||||
// Read IMU values 2000 times (reduced from 12000 for faster calibration)
|
||||
int c = 0;
|
||||
while (c < 2000)
|
||||
{
|
||||
#if defined USE_BNO085_I2C
|
||||
unsigned long startTime = millis();
|
||||
while (!myIMU.dataAvailable())
|
||||
{
|
||||
delay(1);
|
||||
if (millis() - startTime > 100) // Reduced timeout
|
||||
{
|
||||
Serial.print("BNO085 timeout at iteration: ");
|
||||
Serial.println(c);
|
||||
return; // Exit if timeout
|
||||
}
|
||||
}
|
||||
|
||||
// Get raw sensor data - BNO085 already gives calibrated values
|
||||
float tempGyroX = myIMU.getGyroX() * 57.29577951f; // Convert rad/s to deg/s
|
||||
float tempGyroY = myIMU.getGyroY() * 57.29577951f;
|
||||
float tempGyroZ = myIMU.getGyroZ() * 57.29577951f;
|
||||
|
||||
float tempAccX = myIMU.getAccelX() / 9.80665f; // Convert m/s² to g
|
||||
float tempAccY = myIMU.getAccelY() / 9.80665f;
|
||||
float tempAccZ = myIMU.getAccelZ() / 9.80665f;
|
||||
|
||||
// Sum all readings
|
||||
AccErrorX += tempAccX;
|
||||
AccErrorY += tempAccY;
|
||||
AccErrorZ += tempAccZ;
|
||||
GyroErrorX += tempGyroX;
|
||||
GyroErrorY += tempGyroY;
|
||||
GyroErrorZ += tempGyroZ;
|
||||
|
||||
#elif defined USE_MPU6050_I2C
|
||||
mpu6050.getMotion6(&AcX, &AcY, &AcZ, &GyX, &GyY, &GyZ);
|
||||
#elif defined USE_MPU9250_SPI
|
||||
mpu9250.getMotion9(&AcX, &AcY, &AcZ, &GyX, &GyY, &GyZ, &MgX, &MgY, &MgZ);
|
||||
#elif defined USE_BNO085_I2C
|
||||
|
||||
while (!myIMU.dataAvailable())
|
||||
{
|
||||
delay(1); // Small delay to avoid hammering the I2C bus too hard
|
||||
}
|
||||
|
||||
// bno magnetometer
|
||||
MgX = myIMU.getMagX();
|
||||
MgY = myIMU.getMagY();
|
||||
MgZ = myIMU.getMagZ();
|
||||
|
||||
// convert radians to degrees
|
||||
GyX = myIMU.getGyroX() * 180 / PI;
|
||||
GyY = myIMU.getGyroY() * 180 / PI;
|
||||
GyZ = myIMU.getGyroZ() * 180 / PI;
|
||||
|
||||
// Acelleration (dont know if I need linear or normal. One way to find out)
|
||||
AcX = myIMU.getAccelX() / 9.80665; // m/s² to g
|
||||
AcY = myIMU.getAccelY() / 9.80665; // m/s² to g
|
||||
AcZ = myIMU.getAccelZ() / 9.80665; // m/s² to g
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
AccX = AcX / ACCEL_SCALE_FACTOR;
|
||||
@@ -1352,17 +1367,17 @@ void getCommands()
|
||||
}
|
||||
|
||||
#elif defined USE_ESPNow
|
||||
channel_1_pwm = myData.PWMCH1;
|
||||
channel_2_pwm = myData.PWMCH2;
|
||||
channel_3_pwm = myData.PWMCH3;
|
||||
channel_4_pwm = myData.PWMCH4;
|
||||
channel_1_pwm = ControllerData.PWMCH1;
|
||||
channel_2_pwm = ControllerData.PWMCH2;
|
||||
channel_3_pwm = ControllerData.PWMCH3;
|
||||
channel_4_pwm = ControllerData.PWMCH4;
|
||||
channel_5_pwm = 1000; // Temporary always armed
|
||||
// channel_6_pwm = getRadioPWM(6);
|
||||
|
||||
#endif
|
||||
|
||||
// Low-pass the critical commands and update previous values
|
||||
float b = 0.7; // Lower=slower, higher=noiser
|
||||
float b = 0.5; // Lower=slower, higher=noiser
|
||||
channel_1_pwm = (1.0 - b) * channel_1_pwm_prev + b * channel_1_pwm;
|
||||
channel_2_pwm = (1.0 - b) * channel_2_pwm_prev + b * channel_2_pwm;
|
||||
channel_3_pwm = (1.0 - b) * channel_3_pwm_prev + b * channel_3_pwm;
|
||||
@@ -1428,29 +1443,24 @@ void commandMotors() {
|
||||
uint32_t m4_duty = map(m4_command_PWM, 125, 250, 1100, 2000);
|
||||
uint32_t m5_duty = map(m5_command_PWM, 125, 250, 1100, 2000);
|
||||
uint32_t m6_duty = map(m6_command_PWM, 125, 250, 1100, 2000);
|
||||
|
||||
// Write the duty cycle values directly to each pin
|
||||
ledcWrite(m1Pin, m1_duty);
|
||||
ledcWrite(m2Pin, m2_duty);
|
||||
ledcWrite(m3Pin, m3_duty);
|
||||
ledcWrite(m4Pin, m4_duty);
|
||||
ledcWrite(m5Pin, m5_duty);
|
||||
ledcWrite(m6Pin, m6_duty);
|
||||
}
|
||||
|
||||
void armMotors()
|
||||
{
|
||||
// DESCRIPTION: Sends many command pulses to the motors, to be used to arm motors in the void setup()
|
||||
/*
|
||||
* Loops over the commandMotors() function 50 times with a delay in between, simulating how the commandMotors()
|
||||
* function is used in the main loop. Ensures motors arm within the void setup() where there are some delays
|
||||
* for other processes that sometimes prevent motors from arming.
|
||||
*/
|
||||
for (int i = 0; i <= 50; i++)
|
||||
{
|
||||
commandMotors();
|
||||
delay(2);
|
||||
}
|
||||
//debug shizzle
|
||||
// Serial.print("m1_duty: ");
|
||||
// Serial.println(m1_duty);
|
||||
// Serial.print("m2_duty: ");
|
||||
// Serial.println(m2_duty);
|
||||
// Serial.print("m3_duty: ");
|
||||
// Serial.println(m3_duty);
|
||||
// Serial.print("m4_duty: ");
|
||||
// Serial.println(m4_duty);
|
||||
|
||||
// Write the duty cycle values directly to each pin
|
||||
ledcWrite(m1Pin, pulseWidthToDutyCycle(m1_duty));
|
||||
ledcWrite(m2Pin, pulseWidthToDutyCycle(m2_duty));
|
||||
ledcWrite(m3Pin, pulseWidthToDutyCycle(m3_duty));
|
||||
ledcWrite(m4Pin, pulseWidthToDutyCycle(m4_duty));
|
||||
ledcWrite(m5Pin, pulseWidthToDutyCycle(m5_duty));
|
||||
ledcWrite(m6Pin, pulseWidthToDutyCycle(m6_duty));
|
||||
}
|
||||
|
||||
void calibrateESCs()
|
||||
@@ -1463,9 +1473,7 @@ void calibrateESCs()
|
||||
*/
|
||||
while (true)
|
||||
{
|
||||
prev_time = current_time;
|
||||
current_time = micros();
|
||||
dt = (current_time - prev_time) / 1000000.0;
|
||||
|
||||
|
||||
// digitalWrite(13, HIGH); // LED on to indicate we are not in main loop
|
||||
|
||||
@@ -1473,7 +1481,6 @@ void calibrateESCs()
|
||||
failSafe(); // Prevent failures in event of bad receiver connection, defaults to failsafe values assigned in setup
|
||||
getDesState(); // Convert raw commands to normalized values based on saturated control limits
|
||||
getIMUdata(); // Pulls raw gyro, accelerometer, and magnetometer data from IMU and LP filters to remove noise
|
||||
Madgwick(GyroX, -GyroY, -GyroZ, -AccX, AccY, AccZ, MagY, -MagX, MagZ, dt); // Updates roll_IMU, pitch_IMU, and yaw_IMU (degrees)
|
||||
getDesState(); // Convert raw commands to normalized values based on saturated control limits
|
||||
|
||||
m1_command_scaled = thro_des;
|
||||
@@ -1610,6 +1617,12 @@ void setupMotorPWM() {
|
||||
ledcAttach(m6Pin, LEDC_FREQ, LEDC_RESOLUTION);
|
||||
}
|
||||
|
||||
// Function to convert pulse width to LEDC duty cycle
|
||||
int pulseWidthToDutyCycle(int pulseWidth) {
|
||||
int maxDuty = (1 << LEDC_RESOLUTION) - 1;
|
||||
return (pulseWidth * maxDuty) / 20000; // Convert to 16-bit duty cycle
|
||||
}
|
||||
|
||||
void calibrateMagnetometer()
|
||||
{
|
||||
#if defined USE_MPU9250_SPI
|
||||
@@ -1872,6 +1885,88 @@ void printDebugInfo() {
|
||||
}
|
||||
}
|
||||
|
||||
void printPitchInfo() {
|
||||
if (current_time - print_counter > 10000) {
|
||||
print_counter = micros();
|
||||
Serial.print("pitch_IMU:");
|
||||
Serial.print(pitch_IMU);
|
||||
Serial.print(" pitch_PID:");
|
||||
Serial.print(pitch_PID);
|
||||
Serial.print(" pitch_des:");
|
||||
Serial.print(pitch_des);
|
||||
Serial.print(" error_pitch:");
|
||||
Serial.println(error_pitch);
|
||||
}
|
||||
}
|
||||
|
||||
void printAttitudeDebug() {
|
||||
if (current_time - print_counter > 10000) {
|
||||
print_counter = micros();
|
||||
Serial.print("q0:");
|
||||
Serial.print(q0, 4);
|
||||
Serial.print(" q1:");
|
||||
Serial.print(q1, 4);
|
||||
Serial.print(" q2:");
|
||||
Serial.print(q2, 4);
|
||||
Serial.print(" q3:");
|
||||
Serial.print(q3, 4);
|
||||
Serial.print(" roll:");
|
||||
Serial.print(roll_IMU);
|
||||
Serial.print(" pitch:");
|
||||
Serial.println(pitch_IMU);
|
||||
}
|
||||
}
|
||||
|
||||
void testIndividualMotors() {
|
||||
Serial.println("=== MOTOR PIN TEST - PROPS OFF! ===");
|
||||
|
||||
// Test each pin individually for 3 seconds
|
||||
Serial.println("Testing D3 (m1Pin) for 3 seconds...");
|
||||
ledcWrite(D3, pulseWidthToDutyCycle(1300));
|
||||
delay(3000);
|
||||
ledcWrite(D3, pulseWidthToDutyCycle(1000));
|
||||
delay(2000);
|
||||
|
||||
Serial.println("Testing D2 (m2Pin) for 3 seconds...");
|
||||
ledcWrite(D2, pulseWidthToDutyCycle(1300));
|
||||
delay(3000);
|
||||
ledcWrite(D2, pulseWidthToDutyCycle(1000));
|
||||
delay(2000);
|
||||
|
||||
Serial.println("Testing D10 (m3Pin) for 3 seconds...");
|
||||
ledcWrite(D10, pulseWidthToDutyCycle(1300));
|
||||
delay(3000);
|
||||
ledcWrite(D10, pulseWidthToDutyCycle(1000));
|
||||
delay(2000);
|
||||
|
||||
Serial.println("Testing D9 (m4Pin) for 3 seconds...");
|
||||
ledcWrite(D9, pulseWidthToDutyCycle(1300));
|
||||
delay(3000);
|
||||
ledcWrite(D9, pulseWidthToDutyCycle(1000));
|
||||
delay(2000);
|
||||
|
||||
Serial.println("Test complete! Note which physical motor each pin controls.");
|
||||
|
||||
while(true) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void armMotors(){
|
||||
|
||||
|
||||
ledcWrite(m1Pin, pulseWidthToDutyCycle(2000)); // maximum pulse width (2000μs)
|
||||
ledcWrite(m2Pin, pulseWidthToDutyCycle(2000));
|
||||
ledcWrite(m3Pin, pulseWidthToDutyCycle(2000));
|
||||
ledcWrite(m4Pin, pulseWidthToDutyCycle(2000));
|
||||
|
||||
delay(9000);
|
||||
ledcWrite(m1Pin, pulseWidthToDutyCycle(1000)); // Minimum pulse width (1000μs)
|
||||
ledcWrite(m2Pin, pulseWidthToDutyCycle(1000));
|
||||
ledcWrite(m3Pin, pulseWidthToDutyCycle(1000));
|
||||
ledcWrite(m4Pin, pulseWidthToDutyCycle(1000));
|
||||
}
|
||||
|
||||
//=========================================================================================//
|
||||
|
||||
// HELPER FUNCTIONS
|
||||
@@ -1899,6 +1994,8 @@ float invSqrt(float x)
|
||||
return 1.0 / sqrtf(x); // Teensy is fast enough to just take the compute penalty lol suck it arduino nano
|
||||
}
|
||||
|
||||
|
||||
/// ESP stuff lol
|
||||
void ESPNowSetup()
|
||||
{
|
||||
WiFi.mode(WIFI_STA);
|
||||
@@ -1914,5 +2011,46 @@ void ESPNowSetup()
|
||||
|
||||
void OnDataRecv(const uint8_t *mac, const uint8_t *incomingData, int len)
|
||||
{
|
||||
memcpy(&myData, incomingData, sizeof(myData));
|
||||
// Create a temporary struct to check the data first
|
||||
struct_message tempData;
|
||||
memcpy(&tempData, incomingData, sizeof(tempData));
|
||||
|
||||
// Always update the kill switch state first
|
||||
ControllerData.killSwitch = tempData.killSwitch;
|
||||
|
||||
if (tempData.killSwitch == 1)
|
||||
{
|
||||
// Keep channel values at safe defaults when kill switch is active
|
||||
ControllerData.PWMCH1 = 1000; // Min throttle
|
||||
ControllerData.PWMCH2 = 1500; // Center stick
|
||||
ControllerData.PWMCH3 = 1500; // Center stick
|
||||
ControllerData.PWMCH4 = 1500; // Center stick
|
||||
|
||||
// Immediately disable motors
|
||||
ledcWrite(m1Pin, pulseWidthToDutyCycle(1000)); // Minimum pulse width (1000μs)
|
||||
ledcWrite(m2Pin, pulseWidthToDutyCycle(1000));
|
||||
ledcWrite(m3Pin, pulseWidthToDutyCycle(1000));
|
||||
ledcWrite(m4Pin, pulseWidthToDutyCycle(1000));
|
||||
|
||||
Serial.println("Kill switch activated. Motors disabled.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only copy control channel data if kill switch is not active
|
||||
ControllerData.PWMCH1 = tempData.PWMCH1;
|
||||
ControllerData.PWMCH2 = tempData.PWMCH2;
|
||||
ControllerData.PWMCH3 = tempData.PWMCH3;
|
||||
ControllerData.PWMCH4 = tempData.PWMCH4;
|
||||
}
|
||||
|
||||
// For debugging - uncomment if needed
|
||||
// Serial.print("KillSwitch: ");
|
||||
// Serial.println(ControllerData.killSwitch);
|
||||
}
|
||||
|
||||
void setupMatrixSerial()
|
||||
{
|
||||
SoftwareSerial MatrixSerial(D0, D8); //RX is unconnected
|
||||
MatrixSerial.begin(115200);
|
||||
MatrixSerial.println("Hi");
|
||||
}
|
@@ -1,24 +0,0 @@
|
||||
void setup() {
|
||||
pinMode(PIN_LED_R, OUTPUT);
|
||||
pinMode(PIN_LED_G, OUTPUT);
|
||||
pinMode(PIN_LED_B, OUTPUT);
|
||||
|
||||
Serial.begin(9600);
|
||||
|
||||
digitalWrite(PIN_LED_R, HIGH);
|
||||
digitalWrite(PIN_LED_G, HIGH);
|
||||
digitalWrite(PIN_LED_B, HIGH);
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
digitalWrite(LED_BUILTIN, 0); //Turn the led on RP2040 is reversed
|
||||
|
||||
Serial.println("on");
|
||||
delay(10);
|
||||
digitalWrite(LED_BUILTIN, 1);
|
||||
Serial.println("off");
|
||||
delay(10);
|
||||
|
||||
}
|