Englebert
2 years ago
92 changed files with 4325 additions and 0 deletions
-
11LITTLEFS.patch
-
128README.md
-
7bert2M_fat12M_16MB.csv
-
39include/README
-
46lib/README
-
307openscad/AlienCase.scad
-
722openscad/BoxMode.scad
-
274openscad/Case.scad
-
BINopenscad/Case.stl
-
300openscad/case02.scad
-
BINopenscad/gimbal_ring.stl
-
BINopenscad/gimbalbox-test-001.stl
-
BINopenscad/rc_controller-body.stl
-
BINopenscad/rc_controller-gimbal.stl
-
BINopenscad/rc_controller-m5backcover.stl
-
BINopenscad/rc_controller_body.stl
-
21platformio.ini
-
17sounds/AudaCity.aup
-
BINsounds/AudaCity_data/eff/d1f/eff1f356.au
-
10sounds/README.md
-
BINsounds/bluetooth.mp3
-
BINsounds/bluetooth.raw
-
BINsounds/pitchdecrease.mp3
-
BINsounds/pitchdecrease.raw
-
BINsounds/pitchincrease.mp3
-
BINsounds/pitchincrease.raw
-
BINsounds/poweroff.mp3
-
BINsounds/poweroff.raw
-
BINsounds/rccontroller.mp3
-
BINsounds/rccontroller.raw
-
BINsounds/reboot.mp3
-
BINsounds/reboot.raw
-
BINsounds/reset.mp3
-
BINsounds/reset.raw
-
BINsounds/rfscanners.mp3
-
BINsounds/rfscanners.raw
-
BINsounds/rfsettings.mp3
-
BINsounds/rfsettings.raw
-
BINsounds/rolldecrease.mp3
-
BINsounds/rolldecrease.raw
-
BINsounds/rollincrease.mp3
-
BINsounds/rollincrease.raw
-
BINsounds/rxbinding.mp3
-
BINsounds/rxbinding.raw
-
BINsounds/savesettings.mp3
-
BINsounds/savesettings.raw
-
BINsounds/stickcalibration.mp3
-
BINsounds/stickcalibration.raw
-
BINsounds/stickpositions.mp3
-
BINsounds/stickpositions.raw
-
BINsounds/systemrebooting.mp3
-
BINsounds/systemrebooting.raw
-
BINsounds/systemshuttingdown.mp3
-
BINsounds/systemshuttingdown.raw
-
BINsounds/throttledecrease.mp3
-
BINsounds/throttledecrease.raw
-
BINsounds/throttleincrease.mp3
-
BINsounds/throttleincrease.raw
-
BINsounds/trims.mp3
-
BINsounds/trims.raw
-
15sounds/upload.py
-
BINsounds/welcome.mp3
-
BINsounds/welcome.raw
-
BINsounds/wifi.mp3
-
BINsounds/wifi.raw
-
BINsounds/yawdecrease.mp3
-
BINsounds/yawdecrease.raw
-
BINsounds/yawincrease.mp3
-
BINsounds/yawincrease.raw
-
131src/BLE.cpp
-
45src/BLE.h
-
241src/BLEGamepad.cpp
-
67src/BLEGamepad.h
-
33src/CustomWiFi.cpp
-
24src/CustomWiFi.h
-
5src/GimbalGraph.cpp
-
14src/GimbalGraph.h
-
320src/Inputs.cpp
-
88src/Inputs.h
-
302src/Screen.cpp
-
70src/Screen.h
-
228src/StickCalibration.cpp
-
41src/StickCalibration.h
-
40src/StickPositions.cpp
-
21src/StickPositions.h
-
220src/Storage.cpp
-
42src/Storage.h
-
225src/Web.cpp
-
23src/Web.h
-
207src/main.cpp
-
30src/main.h
-
11test/README
@ -0,0 +1,11 @@ |
|||
--- /tmp/LITTLEFS.cpp 2023-03-17 11:31:01.330618361 +0800
|
|||
+++ .pio/libdeps/m5stack-core2/LittleFS_esp32/src/LITTLEFS.cpp 2023-03-17 11:31:12.126579098 +0800
|
|||
@@ -41,7 +41,7 @@
|
|||
|
|||
bool LITTLEFSImpl::exists(const char* path) |
|||
{ |
|||
- File f = open(path, "r");
|
|||
+ File f = open(path, "r", false);
|
|||
return (f == true); |
|||
} |
|||
|
@ -0,0 +1,128 @@ |
|||
### OBJECTIVE |
|||
1. Creating a controller running for RubyFPV [ Using it on Ground Station ] |
|||
2. Long Range via 5.8GHz [ Using it on ground station ] |
|||
3. Swappable 18650 [ NO NEED. As I had upgraded to 700mAH ] |
|||
4. High Power. Running on at least 2S2P (7.4v) for supplying 5V. [ NO NEED! As I am only using it and broadcast via ESPNow to Ground Station. ] |
|||
5. Having another M5Core2 as touch switches and interfacing with the gimbala. |
|||
6. Having a 800x480 pixel as FPV screen [ No NEED. Because using it at ground station ] |
|||
7. Extendable USB for the antennas sticks. [ No NEED. Using on grond station ] |
|||
|
|||
|
|||
### FEATURES |
|||
SILENT MODE: |
|||
During power up, push up the PITCH stick to the maximum then press power ON. |
|||
|
|||
### LAYOUT DIAGRAM ### |
|||
|
|||
|
|||
M5Core2 Pinout: |
|||
+---------------+ +-------+-------+ |
|||
| GND | | ADC | G35 | |
|||
+---------------+ +-------+-------+ |
|||
| GND | | ADC | G36 | |
|||
+---------------+ +-------+-------+ |
|||
| GND | | RST | EN | |
|||
+-------+-------+ +-------+-------+ |
|||
| G23 | MOSI | | DAC | G25 | |
|||
+-------+-------+ +-------+-------+ |
|||
| G38 | MISO | | DAC | G26 | |
|||
+-------+-------+ +-------+-------+ |
|||
| G18 | SCK | | 3.3V | |
|||
+-------+-------+ +-------+-------+ |
|||
| G3 | RXD0 | | TXD0 | G1 | |
|||
+-------+-------+ +-------+-------+ |
|||
| G13 | RXD2 | | TXD2 | G14 | |
|||
+-------+-------+ +-------+-------+ |
|||
| G21 | iSDA | | iSCL | G22 | |
|||
+-------+-------+ +-------+-------+ |
|||
| G32 | pSDA | | pSCL | G33 | |
|||
+-------+-------+ +-------+-------+ |
|||
| G27 | GPIO | | GPIO | G19 | |
|||
+-------+-------+ +-------+-------+ |
|||
| G2 | I2SOUT| | I2SCK | G0 | |
|||
+-------+-------+ +-------+-------+ |
|||
| NC | | PDMDT | G34 | |
|||
+---------------+ +-------+-------+ |
|||
| NC | | 5V | |
|||
+---------------+ +-------+-------+ |
|||
| NC | | BAT | |
|||
+---------------+ +-------+-------+ |
|||
|
|||
|
|||
ESP32 Dev Pinout: |
|||
+-----------------------+ |
|||
| O | USB | O | |
|||
| ------- | |
|||
3V3 | [ ] [ ] | VIN |
|||
GND | [ ] [ ] | GND |
|||
Touch3 / HSPI_CS0 / ADC2_3 / GPIO15 | [ ] [ ] | GPIO13 / ADC2_4 / HSPI_ID / Touch4 |
|||
CS / Touch2 / HSPI_WP / ADC2_2 / GPIO2 | [ ] [ ] | GPIO12 / ADC2_5 / HSPI_Q / Touch5 |
|||
Touch0 / HSPI_HD / ADC2_0 / GPIO4 | [ ] [ ] | GPIO14 / ADC2_6 / HSPI_CLK / Touch6 |
|||
U2_RXD / GPIO16 | [ ] [ ] | GPIO27 / ADC2_7 / Touch7 |
|||
U2_TXD / GPIO17 | [ ] [ ] | GPIO26 / ADC2_9 / DAC2 |
|||
V_SPI_CS0 / GPIO5 | [ ] ___________ [ ] | GPIO25 / ADC2_8 / DAC1 |
|||
SCK / V_SPI_CLK / GPIO18 | [ ] | | [ ] | GPIO33 / ADC1_5 / Touch8 / XTAL32 |
|||
U0_CTS / MISO / V_SPI_Q / GPIO19 | [ ] | | [ ] | GPIO32 / ADC1_4 / Touch9 / XTAL32 |
|||
SDA / V_SPI_HD / GPIO21 | [ ] | | [ ] | GPIO35 / ADC1_7 |
|||
CLK2 / U0_RXD / GPIO3 | [ ] | | [ ] | GPIO34 / ADC1_6 |
|||
CLK3 / U0_TXD / GPIO1 | [ ] | | [ ] | GPIO39 / ADC1_3 / SensVN |
|||
SCL / U0_RTS / V_SPI_WP / GPIO22 | [ ] | | [ ] | GPIO36 / ADC1_0 / SensVP |
|||
MOSI / V_SPI_WP / GPIO23 | [ ] |___________| [ ] | EN |
|||
| | |
|||
| | | ____ ____ | | |
|||
| | | | | | | | | |
|||
| |__|__| |__| |__| | |
|||
| O O | |
|||
+-----------------------+ |
|||
|
|||
# REF: |
|||
Document on How to Use: https://github.com/m5stack/m5-docs/blob/master/docs/en/api/lcd.md |
|||
Pinout and labels - https://docs.m5stack.com/en/core/core2_for_aws |
|||
MP3 Sound generator - https://ttsmp3.com/ |
|||
|
|||
GPIO35 - Throttle |
|||
GPIO36 - Yaw |
|||
GPIO32 - Pitch |
|||
GPIO33 - Roll |
|||
|
|||
### FLOW |
|||
|
|||
### HOW TO COMPILE |
|||
After pio run you will hit into issues. Use the below way to patch the downloaded libraries. |
|||
|
|||
1. Run the below commands: |
|||
patch .pio/libdeps/m5stack-core2/LittleFS_esp32/src/LITTLEFS.cpp < LITTLEFS.patch |
|||
patching file .pio/libdeps/m5stack-core2/LittleFS_esp32/src/LITTLEFS.cpp |
|||
|
|||
### HOW TO PUSH FILES |
|||
1. For all the voice files, go to sound folder and issue the commands below once connected to M5Core2TX WiFi: |
|||
for i in $(ls *.raw); do echo "Pushing [$i]..."; ./upload.py http://192.168.1.1/webupload $i; done |
|||
Pushing [bluetooth.raw]... |
|||
Pushing [pitchdecrease.raw]... |
|||
Pushing [pitchincrease.raw]... |
|||
Pushing [poweroff.raw]... |
|||
Pushing [rccontroller.raw]... |
|||
Pushing [reboot.raw]... |
|||
Pushing [reset.raw]... |
|||
Pushing [rfscanners.raw]... |
|||
Pushing [rfsettings.raw]... |
|||
Pushing [rolldecrease.raw]... |
|||
Pushing [rollincrease.raw]... |
|||
Pushing [rxbinding.raw]... |
|||
Pushing [savesettings.raw]... |
|||
Pushing [stickcalibration.raw]... |
|||
Pushing [stickpositions.raw]... |
|||
Pushing [systemrebooting.raw]... |
|||
Pushing [systemshuttingdown.raw]... |
|||
Pushing [throttledecrease.raw]... |
|||
Pushing [throttleincrease.raw]... |
|||
Pushing [trims.raw]... |
|||
Pushing [welcome.raw]... |
|||
Pushing [wifi.raw]... |
|||
Pushing [yawdecrease.raw]... |
|||
Pushing [yawincrease.raw]... |
|||
|
|||
|
|||
### HOW TO DELETE THE FILE |
|||
To delete it is easy: |
|||
curl -s http://192.168.1.1/erase?filename=the_filename |
@ -0,0 +1,7 @@ |
|||
# Name, Type, SubType, Offset, Size, Flags |
|||
nvs, data, nvs, 0x9000, 0x5000, |
|||
otadata, data, ota, 0xe000, 0x2000, |
|||
app0, app, ota_0, 0x10000, 0x200000, |
|||
app1, app, ota_1, 0x210000,0x200000, |
|||
spiffs, data, spiffs, 0x410000,0xbf0000, |
|||
# 12MB LITTLEFS DiskSpace, 2MB ROM with 2MB OTA |
@ -0,0 +1,39 @@ |
|||
|
|||
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 usual convention is to give header files names that end with `.h'. |
|||
It is most portable to use only letters, digits, dashes, and underscores in |
|||
header file names, and at most one dot. |
|||
|
|||
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 |
@ -0,0 +1,46 @@ |
|||
|
|||
This directory is intended for project specific (private) libraries. |
|||
PlatformIO will compile them to static libraries and link into executable file. |
|||
|
|||
The source code of each library should be placed in a an own separate directory |
|||
("lib/your_library_name/[here are source files]"). |
|||
|
|||
For example, see a structure of the following two libraries `Foo` and `Bar`: |
|||
|
|||
|--lib |
|||
| | |
|||
| |--Bar |
|||
| | |--docs |
|||
| | |--examples |
|||
| | |--src |
|||
| | |- Bar.c |
|||
| | |- Bar.h |
|||
| | |- library.json (optional, 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 |
|||
|
|||
and a contents of `src/main.c`: |
|||
``` |
|||
#include <Foo.h> |
|||
#include <Bar.h> |
|||
|
|||
int main (void) |
|||
{ |
|||
... |
|||
} |
|||
|
|||
``` |
|||
|
|||
PlatformIO Library Dependency Finder will find automatically dependent |
|||
libraries scanning project source files. |
|||
|
|||
More information about PlatformIO Library Dependency Finder |
|||
- https://docs.platformio.org/page/librarymanager/ldf.html |
@ -0,0 +1,307 @@ |
|||
round_quality = 100; |
|||
|
|||
// Main Screen Design |
|||
translate([0,0,-6]) { |
|||
pi_screen(); |
|||
pi_screen_cover(); |
|||
} |
|||
|
|||
translate([34,0,0]) |
|||
gimbal(); |
|||
translate([236,0,0]) |
|||
gimbal(); |
|||
|
|||
// Tops Left Buttons |
|||
left_buttons(); |
|||
right_buttons(); |
|||
|
|||
|
|||
// Joining - Holders |
|||
// main_backbone(); |
|||
|
|||
|
|||
/**** MODULES ****/ |
|||
module left_buttons() { |
|||
translate([-101,42.5,-6]) { |
|||
difference() { |
|||
cube([70,15,2], center=true); |
|||
translate([-33,5,0]) |
|||
rotate([0,0,45]) |
|||
cube([45,15,3], center=true); |
|||
} |
|||
translate([-26.52,-.5,-19]) |
|||
rotate([0,0,45]) |
|||
cube([22,2,40], center=true); |
|||
translate([7.5,7,-19]) |
|||
rotate([0,0,0]) |
|||
cube([54,2,40], center=true); |
|||
translate([34,0,-19]) |
|||
rotate([0,0,90]) |
|||
cube([16,2,40], center=true); |
|||
} |
|||
} |
|||
|
|||
module right_buttons() { |
|||
translate([101,42.5,-6]) { |
|||
difference() { |
|||
cube([70,15,2], center=true); |
|||
translate([43.5,5,0]) |
|||
rotate([0,0,45]) |
|||
cube([30,45,3], center=true); |
|||
} |
|||
translate([26.5,-.5,-19]) |
|||
rotate([0,0,-45]) |
|||
cube([22,2,40], center=true); |
|||
translate([-7.5,7,-19]) |
|||
rotate([0,0,0]) |
|||
cube([54,2,40], center=true); |
|||
translate([-34,0,-19]) |
|||
rotate([0,0,90]) |
|||
cube([16,2,40], center=true); |
|||
} |
|||
} |
|||
|
|||
|
|||
module gimbal() { |
|||
translate([-90,25,0]) { |
|||
/** |
|||
translate([-45,-25,-30]) |
|||
cylinder($fn=round_quality, h=50, d=90, center=true); |
|||
**/ |
|||
translate([-45,-25,-25]) { |
|||
difference() { |
|||
cube([70,70,40], center=true); |
|||
|
|||
// The center hole |
|||
translate([0,0,-2]) |
|||
cube([66,66,40], center=true); |
|||
|
|||
|
|||
// The Gimbal hole |
|||
translate([0,0,0]) |
|||
cylinder($fn=round_quality, h=60, d=50, center=true); |
|||
} |
|||
} |
|||
translate([2,0,-26]) |
|||
gimbal_ring(); |
|||
} |
|||
} |
|||
|
|||
module main_backbone() { |
|||
translate([0,0,-34]) |
|||
cube([250,20,10], center=true); |
|||
} |
|||
|
|||
module pi_screen_cover() { |
|||
translate([0,0,-30]) { |
|||
difference() { |
|||
cube([132,100,2], center=true); |
|||
|
|||
translate([0,-50,0]) |
|||
cube([100,10,3], center=true); |
|||
translate([0,50,0]) |
|||
cube([100,10,3], center=true); |
|||
|
|||
translate([-50,52,0]) |
|||
rotate([0,0,45]) |
|||
cube([10,10,3], center=true); |
|||
translate([-50,-52,0]) |
|||
rotate([0,0,45]) |
|||
cube([10,10,3], center=true); |
|||
translate([50,-52,0]) |
|||
rotate([0,0,45]) |
|||
cube([10,10,3], center=true); |
|||
translate([50,52,0]) |
|||
rotate([0,0,45]) |
|||
cube([10,10,3], center=true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 121mm x 76mm (hole) |
|||
module pi_screen() { |
|||
difference() { |
|||
translate([0,0,0]) { |
|||
cube([130,96,2], center=true); |
|||
|
|||
// Holders |
|||
translate([-60,43,-2]) |
|||
cube([10,10,2], center=true); |
|||
translate([60,43,-2]) |
|||
cube([10,10,2], center=true); |
|||
translate([-60,-43,-2]) |
|||
cube([10,10,2], center=true); |
|||
translate([60,-43,-2]) |
|||
cube([10,10,2], center=true); |
|||
} |
|||
translate([0,0,0]) |
|||
cube([121,76,3], center=true); |
|||
|
|||
// Top and Bottom Cut |
|||
translate([0,53,0]) |
|||
cube([100,20,3], center=true); |
|||
translate([0,-53,0]) |
|||
cube([100,20,3], center=true); |
|||
|
|||
// 45 degree cut |
|||
translate([-50,50,0]) |
|||
rotate([0,0,45]) |
|||
cube([10,10,3], center=true); |
|||
translate([50,50,0]) |
|||
rotate([0,0,45]) |
|||
cube([10,10,3], center=true); |
|||
translate([-50,-50,0]) |
|||
rotate([0,0,45]) |
|||
cube([10,10,3], center=true); |
|||
translate([50,-50,0]) |
|||
rotate([0,0,45]) |
|||
cube([10,10,3], center=true); |
|||
} |
|||
|
|||
// Sides |
|||
translate([-65,0,-14]) |
|||
rotate([90,0,90]) |
|||
cube([96,30,2], center=true); |
|||
translate([65,0,-14]) |
|||
rotate([90,0,90]) |
|||
difference() { |
|||
cube([96,30,2], center=true); |
|||
|
|||
// USB Hole |
|||
translate([10,0,0]) |
|||
cube([50,16,3], center=true); |
|||
} |
|||
translate([0,44,-14]) |
|||
rotate([90,0,0]) |
|||
cube([100,30,2], center=true); |
|||
translate([0,-44,-14]) |
|||
rotate([90,0,0]) |
|||
difference() { |
|||
cube([100,30,2], center=true); |
|||
|
|||
translate([40,0,0]) |
|||
cube([10,10,5], center=true); |
|||
translate([15,0,0]) |
|||
cube([10,10,5], center=true); |
|||
translate([-15,0,0]) |
|||
cube([10,10,5], center=true); |
|||
translate([-40,0,0]) |
|||
cube([10,10,5], center=true); |
|||
} |
|||
|
|||
// Sides top and bottom |
|||
translate([-60.5,-48.9,-14]) |
|||
rotate([90,0,0]) |
|||
cube([11,30,2], center=true); |
|||
translate([60.5,-48.9,-14]) |
|||
rotate([90,0,0]) |
|||
cube([11,30,2], center=true); |
|||
translate([60.5,48.9,-14]) |
|||
rotate([90,0,0]) |
|||
cube([11,30,2], center=true); |
|||
translate([-60.5,48.9,-14]) |
|||
rotate([90,0,0]) |
|||
cube([11,30,2], center=true); |
|||
|
|||
// Sides 45 degree top and bottom |
|||
translate([-52.5,46,-14]) |
|||
rotate([90,0,-45]) |
|||
cube([9,30,2], center=true); |
|||
translate([52.5,46,-14]) |
|||
rotate([90,0,45]) |
|||
cube([9,30,2], center=true); |
|||
translate([-52.5,-46,-14]) |
|||
rotate([90,0,45]) |
|||
cube([9,30,2], center=true); |
|||
translate([52.5,-46,-14]) |
|||
rotate([90,0,-45]) |
|||
cube([9,30,2], center=true); |
|||
} |
|||
|
|||
|
|||
module gimbal_ring_full() { |
|||
translate([-47,-25,23.5]) |
|||
cylinder(h=5, d=64.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-71,-49,23.5]) |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
|
|||
translate([-23,-49,23.5]) |
|||
mirror([-1,0,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
|||
translate([-71,-0.8,23.5]) |
|||
mirror([0,-1,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
|||
translate([-22.5,-0.5,23.5]) |
|||
mirror([-1,-1,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
module gimbal_ring() { |
|||
difference() { |
|||
gimbal_ring_full(); |
|||
|
|||
translate([-47,-25,23.5]) |
|||
cylinder(h=6, d1=47.5, d2=57.5, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,722 @@ |
|||
round_quality=500; |
|||
draft=true; |
|||
show_gimbal=false; |
|||
show_body=true; |
|||
show_all=false; |
|||
show_m5_back=true; |
|||
|
|||
if(show_body) { |
|||
controller(); |
|||
} |
|||
|
|||
if(show_m5_back) { |
|||
m5_back(); |
|||
} |
|||
|
|||
if(show_gimbal) { |
|||
translate([0,-41,-8.5]) |
|||
gimbal_ring(); |
|||
|
|||
if(show_all) { |
|||
translate([0,91,-8.5]) |
|||
gimbal_ring(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
// Ruler |
|||
translate([-50,-80,0]) |
|||
rotate([0,0,45]) |
|||
cube([200,2,2], center=true); |
|||
|
|||
translate([-50,-89.5,13]) |
|||
rotate([0,0,0]) |
|||
cube([100,1,1], center=true); |
|||
translate([-70.5,-70,13]) |
|||
rotate([0,0,0]) |
|||
cube([1,100,1], center=true); |
|||
translate([-50,-42.5,13]) |
|||
rotate([0,0,0]) |
|||
cube([100,1,1], center=true); |
|||
translate([-23.5,-70,13]) |
|||
rotate([0,0,0]) |
|||
cube([1,100,1], center=true); |
|||
**/ |
|||
|
|||
/******* Modules *******/ |
|||
module m5_back() { |
|||
translate([-95,0,-6]) { |
|||
difference() { |
|||
roundedcube([59.6,59.6,17.5], center = true, 0.5); |
|||
translate([0,0,1]) |
|||
roundedcube([56.8,56.8,17.5], center = true, 0.5); |
|||
translate([26,0,-1.5]) |
|||
cube([10,11,10], center=true); |
|||
translate([26,0,5]) |
|||
cube([10,11,10], center=true); |
|||
translate([3.5,-25,5]) |
|||
cube([50,11,10], center=true); |
|||
translate([3.5,25,5]) |
|||
cube([50,11,10], center=true); |
|||
translate([25.5,23,5]) |
|||
cube([10,5,20], center=true); |
|||
translate([25.5,-23,5]) |
|||
cube([10,5,20], center=true); |
|||
} |
|||
|
|||
translate([-28,0,7]) { |
|||
difference() { |
|||
cube([2,9,2], center=true); |
|||
} |
|||
} |
|||
translate([28,-15,7]) { |
|||
difference() { |
|||
cube([2,9,2], center=true); |
|||
} |
|||
} |
|||
translate([28,15,7]) { |
|||
difference() { |
|||
cube([2,9,2], center=true); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
module holder_base() { |
|||
cylinder(h=8, d=11, $fn=round_quality); |
|||
translate([-6,0,4]) |
|||
cube([11,11,5], center=true); |
|||
} |
|||
|
|||
module gimbal_top() { |
|||
translate([-47,-66,10.5]) { |
|||
difference() { |
|||
cube([63,63,3], center=true); |
|||
cylinder(h=5, d=48.2, center=true, $fn=round_quality); |
|||
|
|||
translate([-26.7,-26.7,0]) |
|||
cylinder(h=5, d=5.6, center=true, $fn=round_quality); |
|||
|
|||
translate([26.7,-26.7,0]) |
|||
cylinder(h=5, d=5.6, center=true, $fn=round_quality); |
|||
|
|||
translate([26.7,26.7,0]) |
|||
cylinder(h=5, d=5.6, center=true, $fn=round_quality); |
|||
|
|||
translate([-26.7,26.7,0]) |
|||
cylinder(h=5, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
} |
|||
|
|||
module controller() { |
|||
difference() { |
|||
controller_base(); |
|||
|
|||
translate([-37,0,-9]) |
|||
cube([6,77.8,4], center=true); |
|||
|
|||
translate([-55,0,-9]) |
|||
rotate([0,0,90]) |
|||
cube([6,37.8,4], center=true); |
|||
} |
|||
} |
|||
|
|||
module controller_base() { |
|||
rotate([180,0,270]) |
|||
m5_body(); |
|||
translate([0,-14-30,0]) |
|||
gimbal_box(); |
|||
translate([0,58+30,0]) |
|||
gimbal_box(); |
|||
translate([-25,0,-5]) { |
|||
difference() { |
|||
holder_base(); |
|||
translate([0,0,-1]) |
|||
cylinder(h=11, d=6.5, $fn=round_quality); |
|||
translate([-15,0,5]) |
|||
cube([12,12,12], center=true); |
|||
} |
|||
} |
|||
|
|||
joints(); |
|||
|
|||
gimbal_top(); |
|||
translate([0,132,0]) |
|||
gimbal_top(); |
|||
|
|||
} |
|||
|
|||
module bracket() { |
|||
translate([-100,-48,1]) |
|||
cube([5,60,10], center=true); |
|||
} |
|||
|
|||
module joints() { |
|||
translate([-72,-30,0]) |
|||
cube([10,5,10], center=true); |
|||
translate([-72,30,0]) |
|||
cube([10,5,10], center=true); |
|||
|
|||
translate([-47,0,0]) |
|||
difference() { |
|||
cube([30,65,10], center=true); |
|||
|
|||
for(i = [0:10]) |
|||
translate([0,-40+i*10,0]) |
|||
rotate([0,0,45]) |
|||
cube([30,5,20], center=true); |
|||
} |
|||
|
|||
translate([-64,0,0]) |
|||
cube([6,10,10], center=true); |
|||
|
|||
translate([-49.9,0,-8]) |
|||
difference() { |
|||
cube([35.8,10,8], center=true); |
|||
cube([45.8,6,4], center=true); |
|||
} |
|||
|
|||
translate([-37,0,-8]) { |
|||
difference() { |
|||
cube([10,65.8,8], center=true); |
|||
cube([6,67.8,4], center=true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
module antenna() { |
|||
translate([80,0,0]) { |
|||
cube([30,20,8], center=true); |
|||
|
|||
translate([25,65,0]) |
|||
rotate([90,0,0]) |
|||
cylinder(h=150, d=15, center=true, $fn=round_quality); |
|||
} |
|||
translate([80,-25,0]) { |
|||
cube([30,20,8], center=true); |
|||
|
|||
translate([92,0,0]) |
|||
rotate([0,90,0]) |
|||
cylinder(h=150, d=15, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
|||
module center_switch() { |
|||
translate([0,-100,18]) |
|||
cube([20,63,2], center=true); |
|||
translate([0,-131,0]) |
|||
cube([20,2,40], center=true); |
|||
} |
|||
|
|||
module esp32_box() { |
|||
translate([0,-8,-2.5]) { |
|||
cube([105,40,35], center=true); |
|||
} |
|||
} |
|||
|
|||
|
|||
module box_side() { |
|||
translate([0,-50,20]) { |
|||
difference() { |
|||
cube([140,25,80], center=true); |
|||
|
|||
translate([15,20,0]) |
|||
rotate([0,0,180]) |
|||
trapezium_b(); |
|||
translate([-15,20,0]) |
|||
rotate([0,0,180]) |
|||
trapezium_b(); |
|||
translate([45,20,0]) |
|||
rotate([0,0,180]) |
|||
trapezium_b(); |
|||
translate([-45,20,0]) |
|||
rotate([0,0,180]) |
|||
trapezium_b(); |
|||
|
|||
// Corner cut |
|||
translate([55,0,53]) |
|||
rotate([0,45,0]) |
|||
cube([50,35,5], center=true); |
|||
translate([-55,0,53]) |
|||
rotate([0,-45,0]) |
|||
cube([50,35,5], center=true); |
|||
translate([55,0,-53]) |
|||
rotate([0,-45,0]) |
|||
cube([50,35,5], center=true); |
|||
translate([-55,0,-53]) |
|||
rotate([0,45,0]) |
|||
cube([50,35,5], center=true); |
|||
|
|||
// Frame Pattern |
|||
translate([-15,0,48]) |
|||
rotate([90,180,0]) |
|||
trapezium_a(); |
|||
translate([15,0,48]) |
|||
rotate([90,180,0]) |
|||
trapezium_a(); |
|||
translate([-45,0,48]) |
|||
rotate([90,180,0]) |
|||
trapezium_a(); |
|||
translate([45,0,48]) |
|||
rotate([90,180,0]) |
|||
trapezium_a(); |
|||
|
|||
translate([45,0,-48]) |
|||
rotate([90,0,0]) |
|||
trapezium_a(); |
|||
translate([-45,0,-48]) |
|||
rotate([90,0,0]) |
|||
trapezium_a(); |
|||
translate([15,0,-48]) |
|||
rotate([90,0,0]) |
|||
trapezium_a(); |
|||
translate([-15,0,-48]) |
|||
rotate([90,0,0]) |
|||
trapezium_a(); |
|||
|
|||
// M5Core2 |
|||
translate([0,-4,0]) |
|||
rotate([90,0,0]) |
|||
m5core2(); |
|||
|
|||
// Upper Layer |
|||
translate([0,-11,0]) |
|||
rotate([90,0,0]) |
|||
cube([130,70,5], center=true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
module m5core2() { |
|||
cube([54.2,54.2,16.2], center=true); |
|||
} |
|||
|
|||
|
|||
module gimbal_box() { |
|||
// translate([0,3,-1]) |
|||
// gimbal_ring(); |
|||
|
|||
difference() { |
|||
translate([-47,-22,0]) |
|||
cube([70,70,24], center=true); |
|||
|
|||
// Corner cut |
|||
translate([-86,-52,0]) |
|||
rotate([0,90,45]) |
|||
cube([50,35,5], center=true); |
|||
translate([-8,-52,0]) |
|||
rotate([0,90,-45]) |
|||
cube([50,35,5], center=true); |
|||
translate([-86,8,0]) |
|||
rotate([0,90,-45]) |
|||
cube([50,35,5], center=true); |
|||
translate([-8,8,0]) |
|||
rotate([0,90,45]) |
|||
cube([50,35,5], center=true); |
|||
|
|||
// Top corners |
|||
translate([-50,-60,15]) |
|||
rotate([45,0,0]) |
|||
cube([100,10,10], center=true); |
|||
translate([-50,15.5,15]) |
|||
rotate([45,0,0]) |
|||
cube([100,10,10], center=true); |
|||
translate([-9.5,-25.5,15]) |
|||
rotate([45,0,90]) |
|||
cube([100,10,10], center=true); |
|||
translate([-84.5,-25.5,15]) |
|||
rotate([45,0,90]) |
|||
cube([100,10,10], center=true); |
|||
|
|||
// Latch |
|||
translate([-75,0,-7]) |
|||
cube([10,10,4], center=true); |
|||
translate([-75,-43,-7]) |
|||
cube([10,10,4], center=true); |
|||
translate([-18.5,0,-7]) |
|||
cube([10,10,4], center=true); |
|||
translate([-18.5,-43,-7]) |
|||
cube([10,10,4], center=true); |
|||
|
|||
// Internal |
|||
translate([-47,-22,7]) |
|||
cube([63,63,50], center=true); |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
module base_box() { |
|||
difference() { |
|||
translate([0,0,0]) |
|||
cube([140,105,20], center=true); |
|||
|
|||
// For display to push in |
|||
translate([0,0,3]) |
|||
cube([121,96,25], center=true); |
|||
|
|||
// Corner cut |
|||
translate([-69,-51,0]) |
|||
rotate([0,90,45]) |
|||
cube([50,15,5], center=true); |
|||
translate([69,-51,0]) |
|||
rotate([0,90,-45]) |
|||
cube([50,15,5], center=true); |
|||
translate([-69,51,0]) |
|||
rotate([0,90,-45]) |
|||
cube([50,15,5], center=true); |
|||
translate([69,51,0]) |
|||
rotate([0,90,45]) |
|||
cube([50,15,5], center=true); |
|||
|
|||
// Frame Pattern |
|||
/** |
|||
translate([-45,60,0]) |
|||
rotate([0,0,180]) |
|||
trapezium_a(); |
|||
translate([-15,60,0]) |
|||
rotate([0,0,180]) |
|||
trapezium_a(); |
|||
translate([15,60,0]) |
|||
rotate([0,0,180]) |
|||
trapezium_a(); |
|||
translate([45,60,0]) |
|||
rotate([0,0,180]) |
|||
trapezium_a(); |
|||
translate([-77,25,0]) |
|||
rotate([0,0,-90]) |
|||
trapezium_a(); |
|||
translate([-77,-2,0]) |
|||
rotate([0,0,-90]) |
|||
trapezium_a(); |
|||
translate([-77,-28,0]) |
|||
rotate([0,0,-90]) |
|||
trapezium_a(); |
|||
translate([77,25,0]) |
|||
rotate([0,0,90]) |
|||
trapezium_a(); |
|||
translate([77,-2,0]) |
|||
rotate([0,0,90]) |
|||
trapezium_a(); |
|||
translate([77,-28,0]) |
|||
rotate([0,0,90]) |
|||
trapezium_a(); |
|||
**/ |
|||
|
|||
// USB Ports |
|||
translate([70,3,0]) |
|||
rotate([90,0,90]) |
|||
cube([50,16,30], center=true); |
|||
} |
|||
} |
|||
|
|||
|
|||
module trapezium_a() { |
|||
difference() { |
|||
cube([20,20,25], center=true); |
|||
|
|||
translate([-10,7,0]) |
|||
rotate([90,0,45]) |
|||
cube([20,26,5], center=true); |
|||
|
|||
translate([10,7,0]) |
|||
rotate([90,0,-45]) |
|||
cube([20,26,5], center=true); |
|||
} |
|||
} |
|||
|
|||
|
|||
module trapezium_b() { |
|||
difference() { |
|||
cube([20,20,125], center=true); |
|||
|
|||
translate([-10,7,0]) |
|||
rotate([90,0,45]) |
|||
cube([20,126,5], center=true); |
|||
|
|||
translate([10,7,0]) |
|||
rotate([90,0,-45]) |
|||
cube([20,126,5], center=true); |
|||
} |
|||
} |
|||
|
|||
|
|||
module gimbal_ring_base() { |
|||
translate([-47,-25,23.5]) |
|||
cylinder(h=5, d=64.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-71,-49,23.5]) |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
|
|||
translate([-23,-49,23.5]) |
|||
mirror([-1,0,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
|||
translate([-71,-0.8,23.5]) |
|||
mirror([0,-1,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
|||
translate([-22.5,-0.5,23.5]) |
|||
mirror([-1,-1,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
module gimbal_ring() { |
|||
difference() { |
|||
gimbal_ring_base(); |
|||
|
|||
translate([-47,-25,23.5]) |
|||
cylinder(h=6, d1=47.5, d2=57.5, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
module m5_body() { |
|||
translate([0,95,-1]) { |
|||
difference() { |
|||
if(!draft) { |
|||
rounded_corner_cube(8, [56.6,56.6,17.5]); |
|||
} else { |
|||
roundedcube([56.6,56.6,17.5], center = true, 0.5); |
|||
} |
|||
if(!draft) { |
|||
rounded_corner_cube(8, [50.6,50.6,17.5]); |
|||
} else { |
|||
roundedcube([50.6,50.6,19.5], center = true, 0.5); |
|||
} |
|||
translate([0,0,1]) |
|||
if(!draft) { |
|||
rounded_corner_cube(8, [54.6,54.6,17.5]); |
|||
} else { |
|||
roundedcube([54.6,54.6,17.5], center = true); |
|||
} |
|||
|
|||
// Power Button |
|||
translate([25,13,0]) |
|||
rotate([90,90,90]) |
|||
cylinder(h=10, d=8, center=true, $fn=round_quality); |
|||
|
|||
// Charging Port |
|||
translate([25,-1,0]) |
|||
rotate([0,90,90]) |
|||
cube([4,18.5,10], center=true); |
|||
|
|||
// Speaker Holes |
|||
translate([-25,11,0]) |
|||
rotate([90,0,90]) |
|||
cylinder(h=10, d=3, center=true, $fn=round_quality); |
|||
translate([-25,5,0]) |
|||
rotate([90,0,90]) |
|||
cylinder(h=10, d=3, center=true, $fn=round_quality); |
|||
translate([-25,-1,0]) |
|||
rotate([90,0,90]) |
|||
cylinder(h=10, d=3, center=true, $fn=round_quality); |
|||
translate([-25,-7,0]) |
|||
rotate([90,0,90]) |
|||
cylinder(h=10, d=3, center=true, $fn=round_quality); |
|||
translate([-25,-13,0]) |
|||
rotate([90,0,90]) |
|||
cylinder(h=10, d=3, center=true, $fn=round_quality); |
|||
|
|||
// Latches |
|||
translate([0,25,0]) |
|||
cube([10,10,3], center=true); |
|||
translate([-15,-25,0]) |
|||
cube([10,10,3], center=true); |
|||
translate([15,-25,0]) |
|||
cube([10,10,3], center=true); |
|||
} |
|||
} |
|||
} |
|||
|
|||
module rounded_corner_cube(diameter, dimension) { |
|||
cube_length = dimension[0]; |
|||
cube_width = dimension[1]; |
|||
cube_thickness = dimension[2]; |
|||
|
|||
difference() { |
|||
cube(dimension, center=true); |
|||
translate([0,0,0]) |
|||
cube_corners(diameter, dimension); |
|||
} |
|||
} |
|||
|
|||
module cube_corners(diameter, dimension) { |
|||
cube_length = dimension[0]; |
|||
cube_width = dimension[1]; |
|||
cube_thickness = dimension[2] + 0.1; |
|||
|
|||
translate([0.1,0.1,0]) { |
|||
difference() { |
|||
translate([0,0,0]) |
|||
cube([cube_length, cube_width, cube_thickness], center=true); |
|||
|
|||
translate([cube_length/2-(diameter/2), cube_width/2-(diameter/2), 0]) |
|||
cylinder(h = cube_thickness+0.1, d = diameter, center = true, $fn=round_quality); |
|||
|
|||
translate([-(diameter/2),0,0]) |
|||
cube([cube_length+0.1, cube_width+0.1, cube_thickness+0.1], center=true); |
|||
|
|||
translate([0,-(diameter/2),0]) |
|||
cube([cube_length+0.1, cube_width, cube_thickness+0.1], center=true); |
|||
} |
|||
} |
|||
|
|||
translate([0.1,-0.1,0]) { |
|||
difference() { |
|||
translate([0,0,0]) |
|||
cube([cube_length, cube_width, cube_thickness], center=true); |
|||
|
|||
translate([cube_length/2-(diameter/2), -(cube_width/2-(diameter/2)), 0]) |
|||
cylinder(h = cube_thickness+0.1, d = diameter, center = true, $fn=round_quality); |
|||
|
|||
translate([-(diameter/2),0,0]) |
|||
cube([cube_length+0.1, cube_width+0.1, cube_thickness+0.1], center=true); |
|||
|
|||
translate([0,(diameter/2),0]) |
|||
cube([cube_length+0.1, cube_width, cube_thickness+0.1], center=true); |
|||
} |
|||
} |
|||
|
|||
translate([-0.1,0.1,0]) { |
|||
difference() { |
|||
translate([0,0,0]) |
|||
cube([cube_length, cube_width, cube_thickness], center=true); |
|||
|
|||
translate([-(cube_length/2-(diameter/2)), cube_width/2-(diameter/2), 0]) |
|||
cylinder(h = cube_thickness+0.1, d = diameter, center = true, $fn=round_quality); |
|||
translate([(diameter/2),0,0]) |
|||
cube([cube_length+0.1, cube_width+0.1, cube_thickness+0.1], center=true); |
|||
translate([0,-(diameter/2),0]) |
|||
cube([cube_length+0.1, cube_width, cube_thickness+0.1], center=true); |
|||
} |
|||
} |
|||
|
|||
translate([-0.1,-0.1,0]) { |
|||
difference() { |
|||
translate([0,0,0]) |
|||
cube([cube_length, cube_width, cube_thickness], center=true); |
|||
|
|||
translate([-(cube_length/2-(diameter/2)), -(cube_width/2-(diameter/2)), 0]) |
|||
cylinder(h = cube_thickness+0.1, d = diameter, center = true, $fn=round_quality); |
|||
|
|||
translate([(diameter/2),0,0]) |
|||
cube([cube_length+0.1, cube_width+0.1, cube_thickness+0.1], center=true); |
|||
|
|||
translate([0,(diameter/2),0]) |
|||
cube([cube_length+0.1, cube_width, cube_thickness+0.1], center=true); |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
module roundedcube(size = [1, 1, 1], center = false, radius = 0.5, apply_to = "all") { |
|||
// If single value, convert to [x, y, z] vector |
|||
size = (size[0] == undef) ? [size, size, size] : size; |
|||
|
|||
translate_min = radius; |
|||
translate_xmax = size[0] - radius; |
|||
translate_ymax = size[1] - radius; |
|||
translate_zmax = size[2] - radius; |
|||
|
|||
diameter = radius * 2; |
|||
|
|||
obj_translate = (center == false) ? |
|||
[0, 0, 0] : [ |
|||
-(size[0] / 2), |
|||
-(size[1] / 2), |
|||
-(size[2] / 2) |
|||
]; |
|||
|
|||
translate(v = obj_translate) { |
|||
hull() { |
|||
for (translate_x = [translate_min, translate_xmax]) { |
|||
x_at = (translate_x == translate_min) ? "min" : "max"; |
|||
for (translate_y = [translate_min, translate_ymax]) { |
|||
y_at = (translate_y == translate_min) ? "min" : "max"; |
|||
for (translate_z = [translate_min, translate_zmax]) { |
|||
z_at = (translate_z == translate_min) ? "min" : "max"; |
|||
|
|||
translate(v = [translate_x, translate_y, translate_z]) |
|||
if ( |
|||
(apply_to == "all") || |
|||
(apply_to == "xmin" && x_at == "min") || (apply_to == "xmax" && x_at == "max") || |
|||
(apply_to == "ymin" && y_at == "min") || (apply_to == "ymax" && y_at == "max") || |
|||
(apply_to == "zmin" && z_at == "min") || (apply_to == "zmax" && z_at == "max") |
|||
) { |
|||
sphere(r = radius); |
|||
} else { |
|||
rotate = |
|||
(apply_to == "xmin" || apply_to == "xmax" || apply_to == "x") ? [0, 90, 0] : ( |
|||
(apply_to == "ymin" || apply_to == "ymax" || apply_to == "y") ? [90, 90, 0] : |
|||
[0, 0, 0] |
|||
); |
|||
rotate(a = rotate) |
|||
cylinder(h = diameter, r = radius, center = true); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,274 @@ |
|||
round_quality = 100; |
|||
show_faceplate = true; |
|||
|
|||
if(show_faceplate) { |
|||
// Basic structures |
|||
translate([-30,-80,0]) |
|||
gimbal_unit_face(); |
|||
translate([0,0,0]) |
|||
pi_screen(); |
|||
translate([100,0,0]) |
|||
m5stack(); |
|||
translate([95,-80,0]) |
|||
gimbal_unit_face(); |
|||
|
|||
// Spacers |
|||
difference() { |
|||
spacers(); |
|||
// Power Switch 1 |
|||
translate([20,-80,0]) |
|||
cube([11,16,5], center=true); |
|||
// Power Switch 2 |
|||
translate([45,-80,0]) |
|||
cube([11,16,5], center=true); |
|||
// Screw Holes |
|||
translate([-70,38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([-70,-38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([-70,-110,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([70,38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([70,-38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([133,-110,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([133,-38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([133,38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([33,-110,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
} |
|||
|
|||
// Ruler |
|||
// translate([30,-48,0]) |
|||
// cube([360,2,2], center=true); |
|||
|
|||
} // End of show_faceplate |
|||
|
|||
|
|||
// Gimbal Decor |
|||
rotate([0,180,0]) |
|||
translate([77,-55,-20]) |
|||
gimbal_ring(); |
|||
rotate([0,180,0]) |
|||
translate([-47,-55,-20]) |
|||
gimbal_ring(); |
|||
|
|||
|
|||
/** |
|||
translate([-125,-8,0]) |
|||
gimbal_unit_face(); |
|||
translate([125,-8,0]) |
|||
gimbal_unit_face(); |
|||
translate([0,0,0]) |
|||
pi_screen(); |
|||
translate([0,-72,0]) |
|||
m5stack(); |
|||
|
|||
// Bridges |
|||
translate([0,0,0]) |
|||
face_bridges(); |
|||
**/ |
|||
|
|||
module spacers() { |
|||
translate([-70,0,0]) |
|||
cube([10,86,2], center=true); |
|||
translate([68,0,0]) |
|||
cube([10,86,2], center=true); |
|||
translate([132,0,0]) |
|||
cube([10,86,2], center=true); |
|||
translate([100,35,0]) |
|||
cube([55,16,2], center=true); |
|||
translate([100,-35,0]) |
|||
cube([55,16,2], center=true); |
|||
translate([31,-44,0]) |
|||
cube([212,5,2], center=true); |
|||
translate([30,-80,0]) |
|||
cube([60,70,2], center=true); |
|||
translate([-70,-80,0]) |
|||
cube([10,70,2], center=true); |
|||
translate([132,-80,0]) |
|||
cube([10,70,2], center=true); |
|||
} |
|||
|
|||
|
|||
module face_bridges() { |
|||
translate([-80,0,0]) |
|||
cube([30,86,2], center=true); |
|||
translate([80,0,0]) |
|||
cube([30,86,2], center=true); |
|||
translate([0,48,0]) |
|||
cube([190,10,2], center=true); |
|||
translate([0,-48,0]) |
|||
difference() { |
|||
cube([320,10,2], center=true); |
|||
translate([0,-25,1]) |
|||
m5stack_solid(); |
|||
} |
|||
} |
|||
|
|||
|
|||
module m5stack_solid() { |
|||
cube([55,55,10], center=true); |
|||
} |
|||
|
|||
|
|||
module m5stack() { |
|||
difference() { |
|||
translate([0,0,0]) |
|||
cube([55,55,2], center=true); |
|||
translate([0,0,0]) |
|||
cube([50,50,3], center=true); |
|||
} |
|||
} |
|||
|
|||
|
|||
// 121mm x 76mm (hole) |
|||
module pi_screen() { |
|||
difference() { |
|||
translate([0,0,0]) |
|||
cube([130,86,2], center=true); |
|||
translate([0,0,0]) |
|||
cube([121,76,3], center=true); |
|||
} |
|||
} |
|||
|
|||
|
|||
module gimbal_unit_face() { |
|||
translate([0,0,0]) { |
|||
// Top |
|||
difference() { |
|||
translate([0,0,0]) |
|||
rotate([0,0,0]) |
|||
cube([70,70,2], center=true); |
|||
|
|||
// Frsky M9 Gimbal |
|||
translate([0,0,0]) |
|||
rotate([0,0,0]) |
|||
cylinder($fn=round_quality, h=5,d=49, center=true); |
|||
|
|||
// Frsky M9 Screen Top Left |
|||
translate([-27.25,27.25,0]) |
|||
rotate([0,0,0]) |
|||
cylinder($fn=round_quality, h=5,d=5.6, center=true); |
|||
|
|||
// Frsky M9 Screen Top Right |
|||
translate([27.25,27.25,0]) |
|||
rotate([0,0,0]) |
|||
cylinder($fn=round_quality, h=5,d=5.6, center=true); |
|||
|
|||
// Frsky M9 Screen Bottom Left |
|||
translate([-27.25,-27.25,0]) |
|||
rotate([0,0,0]) |
|||
cylinder($fn=round_quality, h=5,d=5.6, center=true); |
|||
|
|||
// Frsky M9 Screen Bottom Right |
|||
translate([27.25,-27.25,0]) |
|||
rotate([0,0,0]) |
|||
cylinder($fn=round_quality, h=5,d=5.6, center=true); |
|||
/* |
|||
translate([0,0,0]) |
|||
rotate([0,0,0]) |
|||
cube([77,51,4], center=true); |
|||
*/ |
|||
} |
|||
} |
|||
|
|||
// Just a ruler |
|||
/*** |
|||
translate([-13.9,-44.4]) |
|||
cube([100,10,10], center=true); |
|||
***/ |
|||
|
|||
} |
|||
|
|||
|
|||
module gimbal_ring_full() { |
|||
translate([-47,-25,23.5]) |
|||
cylinder(h=5, d=64.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-71,-49,23.5]) |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
|
|||
translate([-23,-49,23.5]) |
|||
mirror([-1,0,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
|||
translate([-71,-0.8,23.5]) |
|||
mirror([0,-1,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
|||
translate([-22.5,-0.5,23.5]) |
|||
mirror([-1,-1,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
module gimbal_ring() { |
|||
difference() { |
|||
gimbal_ring_full(); |
|||
|
|||
translate([-47,-25,23.5]) |
|||
cylinder(h=6, d1=47.5, d2=57.5, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,300 @@ |
|||
round_quality = 100; |
|||
show_faceplate = true; |
|||
|
|||
if(show_faceplate) { |
|||
// Basic structures |
|||
translate([-30,-80,0]) |
|||
gimbal_unit_face(); |
|||
translate([0,0,0]) |
|||
pi_screen(); |
|||
translate([100,0,0]) |
|||
m5stack(); |
|||
translate([95,-80,0]) |
|||
gimbal_unit_face(); |
|||
|
|||
// Spacers |
|||
difference() { |
|||
spacers(); |
|||
// Power Switch 1 |
|||
translate([20,-80,0]) |
|||
cube([11,16,5], center=true); |
|||
// Power Switch 2 |
|||
translate([45,-80,0]) |
|||
cube([11,16,5], center=true); |
|||
// Screw Holes |
|||
translate([-70,38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([-70,-38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([-70,-110,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([70,38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([70,-38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([133,-110,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([133,-38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([133,38,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
translate([33,-110,0]) |
|||
cylinder($fn=round_quality, h=5, d=5, center=true); |
|||
} |
|||
|
|||
// Ruler |
|||
// translate([30,-48,0]) |
|||
// cube([360,2,2], center=true); |
|||
|
|||
// Display Decor |
|||
translate([0,0,3]) |
|||
difference() { |
|||
cube([130,86,5], center=true); |
|||
cube([121,76,8], center=true); |
|||
} |
|||
|
|||
// M5Stack Decor |
|||
translate([100,0,3]) |
|||
difference() { |
|||
translate([0,0,0]) |
|||
cube([55,55,5], center=true); |
|||
translate([0,0,0]) |
|||
cube([50,50,8], center=true); |
|||
} |
|||
|
|||
// Pi Screen and M5Stack Links |
|||
translate([0,0,3]) { |
|||
translate([70,0,0]) |
|||
cube([10,5,2], center=true); |
|||
translate([70,10,0]) |
|||
cube([10,5,2], center=true); |
|||
translate([70,20,0]) |
|||
cube([10,5,2], center=true); |
|||
translate([70,-20,0]) |
|||
cube([10,5,2], center=true); |
|||
translate([70,-10,0]) |
|||
cube([10,5,2], center=true); |
|||
} |
|||
} // End of show_faceplate |
|||
|
|||
// Gimbal Decor |
|||
translate([17,-55,-20]) |
|||
gimbal_ring(); |
|||
translate([143,-55,-20]) |
|||
gimbal_ring(); |
|||
|
|||
|
|||
/** |
|||
translate([-125,-8,0]) |
|||
gimbal_unit_face(); |
|||
translate([125,-8,0]) |
|||
gimbal_unit_face(); |
|||
translate([0,0,0]) |
|||
pi_screen(); |
|||
translate([0,-72,0]) |
|||
m5stack(); |
|||
|
|||
// Bridges |
|||
translate([0,0,0]) |
|||
face_bridges(); |
|||
**/ |
|||
|
|||
module spacers() { |
|||
translate([-70,0,0]) |
|||
cube([10,86,2], center=true); |
|||
translate([68,0,0]) |
|||
cube([10,86,2], center=true); |
|||
translate([132,0,0]) |
|||
cube([10,86,2], center=true); |
|||
translate([100,35,0]) |
|||
cube([55,16,2], center=true); |
|||
translate([100,-35,0]) |
|||
cube([55,16,2], center=true); |
|||
translate([31,-44,0]) |
|||
cube([212,5,2], center=true); |
|||
translate([30,-80,0]) |
|||
cube([60,70,2], center=true); |
|||
translate([-70,-80,0]) |
|||
cube([10,70,2], center=true); |
|||
translate([132,-80,0]) |
|||
cube([10,70,2], center=true); |
|||
} |
|||
|
|||
|
|||
module face_bridges() { |
|||
translate([-80,0,0]) |
|||
cube([30,86,2], center=true); |
|||
translate([80,0,0]) |
|||
cube([30,86,2], center=true); |
|||
translate([0,48,0]) |
|||
cube([190,10,2], center=true); |
|||
translate([0,-48,0]) |
|||
difference() { |
|||
cube([320,10,2], center=true); |
|||
translate([0,-25,1]) |
|||
m5stack_solid(); |
|||
} |
|||
} |
|||
|
|||
|
|||
module m5stack_solid() { |
|||
cube([55,55,10], center=true); |
|||
} |
|||
|
|||
|
|||
module m5stack() { |
|||
difference() { |
|||
translate([0,0,0]) |
|||
cube([55,55,2], center=true); |
|||
translate([0,0,0]) |
|||
cube([50,50,3], center=true); |
|||
} |
|||
} |
|||
|
|||
|
|||
// 121mm x 76mm (hole) |
|||
module pi_screen() { |
|||
difference() { |
|||
translate([0,0,0]) |
|||
cube([130,86,2], center=true); |
|||
translate([0,0,0]) |
|||
cube([121,76,3], center=true); |
|||
} |
|||
} |
|||
|
|||
|
|||
module gimbal_unit_face() { |
|||
translate([0,0,0]) { |
|||
// Top |
|||
difference() { |
|||
translate([0,0,0]) |
|||
rotate([0,0,0]) |
|||
cube([70,70,2], center=true); |
|||
|
|||
// Frsky M9 Gimbal |
|||
translate([0,0,0]) |
|||
rotate([0,0,0]) |
|||
cylinder($fn=round_quality, h=5,d=49, center=true); |
|||
|
|||
// Frsky M9 Screen Top Left |
|||
translate([-27.25,27.25,0]) |
|||
rotate([0,0,0]) |
|||
cylinder($fn=round_quality, h=5,d=5.6, center=true); |
|||
|
|||
// Frsky M9 Screen Top Right |
|||
translate([27.25,27.25,0]) |
|||
rotate([0,0,0]) |
|||
cylinder($fn=round_quality, h=5,d=5.6, center=true); |
|||
|
|||
// Frsky M9 Screen Bottom Left |
|||
translate([-27.25,-27.25,0]) |
|||
rotate([0,0,0]) |
|||
cylinder($fn=round_quality, h=5,d=5.6, center=true); |
|||
|
|||
// Frsky M9 Screen Bottom Right |
|||
translate([27.25,-27.25,0]) |
|||
rotate([0,0,0]) |
|||
cylinder($fn=round_quality, h=5,d=5.6, center=true); |
|||
/* |
|||
translate([0,0,0]) |
|||
rotate([0,0,0]) |
|||
cube([77,51,4], center=true); |
|||
*/ |
|||
} |
|||
} |
|||
|
|||
// Just a ruler |
|||
/*** |
|||
translate([-13.9,-44.4]) |
|||
cube([100,10,10], center=true); |
|||
***/ |
|||
|
|||
} |
|||
|
|||
|
|||
module gimbal_ring_full() { |
|||
translate([-47,-25,23.5]) |
|||
cylinder(h=5, d=64.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-71,-49,23.5]) |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
|
|||
translate([-23,-49,23.5]) |
|||
mirror([-1,0,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
|||
translate([-71,-0.8,23.5]) |
|||
mirror([0,-1,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
|||
translate([-22.5,-0.5,23.5]) |
|||
mirror([-1,-1,0]) { |
|||
rotate([0,0,45]) |
|||
difference() { |
|||
cube([15,10,5], center=true); |
|||
|
|||
translate([-9,0,2]) |
|||
rotate([0,-15,0]) |
|||
cube([25,15,5], center=true); |
|||
|
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=10, d=3.5, center=true, $fn=round_quality); |
|||
|
|||
translate([-4,0,0]) |
|||
cylinder(h=3, d=5.6, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
module gimbal_ring() { |
|||
difference() { |
|||
gimbal_ring_full(); |
|||
|
|||
translate([-47,-25,23.5]) |
|||
cylinder(h=6, d1=47.5, d2=57.5, center=true, $fn=round_quality); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,21 @@ |
|||
; 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:m5stack-core2] |
|||
platform = espressif32 |
|||
board = m5stack-core2 |
|||
framework = arduino |
|||
board_build.mcu = esp32 |
|||
board_build.f_cpu = 240000000L |
|||
board_build.partitions = bert2M_fat12M_16MB.csv |
|||
lib_deps = |
|||
m5stack/M5Core2 |
|||
lorol/LittleFS_esp32 |
|||
lemmingdev/ESP32-BLE-Gamepad@^0.3.4 |
@ -0,0 +1,17 @@ |
|||
<?xml version="1.0" standalone="no" ?> |
|||
<!DOCTYPE project PUBLIC "-//audacityproject-1.3.0//DTD//EN" "http://audacity.sourceforge.net/xml/audacityproject-1.3.0.dtd" > |
|||
<project xmlns="http://audacity.sourceforge.net/xml/" projname="AudaCity_data" version="1.3.0" audacityversion="2.4.2" sel0="0.0000000000" sel1="0.0000000000" vpos="0" h="0.0000000000" zoom="934.0723511654" rate="22050.0" snapto="off" selectionformat="hh:mm:ss + milliseconds" frequencyformat="Hz" bandwidthformat="octaves"> |
|||
<tags> |
|||
<tag name="Software" value="Lavf58.76.100"/> |
|||
</tags> |
|||
<wavetrack name="savesettings" isSelected="1" height="150" minimized="0" channel="2" linked="0" mute="0" solo="0" rate="22050" gain="1.0" pan="0.0" colorindex="0"> |
|||
<waveclip offset="0.00000000" colorindex="0"> |
|||
<sequence maxsamples="262144" sampleformat="262159" numsamples="20207"> |
|||
<waveblock start="0"> |
|||
<simpleblockfile filename="eff1f356.au" len="20207" min="-0.344003" max="0.329232" rms="0.068922"/> |
|||
</waveblock> |
|||
</sequence> |
|||
<envelope numpoints="0"/> |
|||
</waveclip> |
|||
</wavetrack> |
|||
</project> |
@ -0,0 +1,10 @@ |
|||
### How to upload |
|||
This is to upload sounds to the device. In order to speak correctly. |
|||
Below is the command to push the files to the M5Core2 once is connected to the device via WiFi. |
|||
|
|||
for i in $(ls *.mp3); do echo "Pushing [$i]...."; ./upload.py http://192.168.1.1/webupload $i; done |
|||
|
|||
|
|||
### How to delete |
|||
To delete it is easy: |
|||
curl -s http://192.168.1.1/erase?filename=the_filename |
@ -0,0 +1,15 @@ |
|||
#!/usr/bin/python3 |
|||
import requests |
|||
import sys |
|||
|
|||
### print (sys.argv) |
|||
### print(len(sys.argv)) |
|||
if len(sys.argv) < 3: |
|||
print("Invalid parameters\n") |
|||
exit |
|||
|
|||
filename = sys.argv[2] |
|||
url = sys.argv[1] |
|||
|
|||
files = {'file': open(filename, 'rb')} |
|||
final_resp = requests.post(url, files=files) |
@ -0,0 +1,131 @@ |
|||
#include "BLE.h"
|
|||
|
|||
BleGamepad bleGamepad; |
|||
|
|||
BLE::BLE(void) { |
|||
updated = true; |
|||
} |
|||
|
|||
void BLE::begin(void) { |
|||
updated = true; |
|||
ble_begin = true; |
|||
} |
|||
|
|||
|
|||
void BLE::end(void) { |
|||
ble_begin = false; |
|||
ble_started = false; |
|||
|
|||
Serial.printf("BLEGamepad is disconnected"); |
|||
bleGamepad.end(); |
|||
} |
|||
|
|||
|
|||
void BLE::update(void) { |
|||
// Starts to connect when receive this...
|
|||
if(ble_begin) { |
|||
if(!ble_started) { |
|||
Serial.printf("Starting BLEGamepad."); |
|||
bleGamepad.begin(); |
|||
Serial.printf("BLEGamepad OK"); |
|||
ble_started = true; |
|||
} |
|||
|
|||
|
|||
if(bleGamepad.isConnected()) { |
|||
if((uint32_t)(millis() - last_updated) > 1) { |
|||
int16_t throttle = map(inputs.throttle, 0, 4096, -32767, 32767); // Throttle
|
|||
int16_t yaw = map(inputs.yaw, 0, 4096, -32767, 32767); // Yaw
|
|||
int16_t pitch = map(inputs.pitch, 0, 4096, -32767, 32767); // Pitch
|
|||
int16_t roll = map(inputs.roll, 0, 4096, -32767, 32767); // Roll
|
|||
|
|||
bleGamepad.setAxes(yaw, throttle, roll, pitch, 0, 0, DPAD_CENTERED); |
|||
|
|||
uint32_t m = millis(); |
|||
if(button1) { |
|||
if((uint32_t)(m - last_button1) > 100) { |
|||
bleGamepad.release(BUTTON_1); |
|||
button1 = false; |
|||
button1_sent = false; |
|||
} else { |
|||
// Only sent once and delay for 100ms
|
|||
if(!button1_sent) { |
|||
bleGamepad.press(BUTTON_1); |
|||
button1_sent = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if(button2) { |
|||
if((uint32_t)(m - last_button2) > 100) { |
|||
bleGamepad.release(BUTTON_2); |
|||
button2 = false; |
|||
button2_sent = false; |
|||
} else { |
|||
// Only sent once and delay for 100ms
|
|||
if(!button2_sent) { |
|||
bleGamepad.press(BUTTON_2); |
|||
button2_sent = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if(button3) { |
|||
if((uint32_t)(m - last_button3) > 100) { |
|||
bleGamepad.release(BUTTON_3); |
|||
button3 = false; |
|||
button3_sent = false; |
|||
} else { |
|||
// Only sent once and delay for 100ms
|
|||
if(!button3_sent) { |
|||
bleGamepad.press(BUTTON_3); |
|||
button3_sent = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if(button4) { |
|||
if((uint32_t)(m - last_button4) > 100) { |
|||
bleGamepad.release(BUTTON_4); |
|||
button4 = false; |
|||
button4_sent = false; |
|||
} else { |
|||
// Only sent once and delay for 100ms
|
|||
if(!button4_sent) { |
|||
bleGamepad.press(BUTTON_4); |
|||
button4_sent = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if(button5) { |
|||
if((uint32_t)(m - last_button5) > 100) { |
|||
bleGamepad.release(BUTTON_5); |
|||
button5 = false; |
|||
button5_sent = false; |
|||
} else { |
|||
// Only sent once and delay for 100ms
|
|||
if(!button5_sent) { |
|||
bleGamepad.press(BUTTON_5); |
|||
button5_sent = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
sent_counter_raw++; |
|||
|
|||
last_updated = millis(); |
|||
// Serial.printf("Sent\n");
|
|||
} |
|||
} |
|||
} |
|||
|
|||
// Reset the profiler when hits 1 second mark
|
|||
if((uint32_t)(millis() - last_seconds) > 1000) { |
|||
sent_counter = sent_counter_raw; |
|||
sent_counter_raw = 0; |
|||
last_seconds = millis(); |
|||
} |
|||
} |
|||
|
|||
BLE ble; |
@ -0,0 +1,45 @@ |
|||
#ifndef _BLE_H |
|||
#define _BLE_H |
|||
|
|||
#include <Arduino.h> |
|||
#include <M5Core2.h> |
|||
#include <BleGamepad.h> |
|||
#include "Inputs.h" |
|||
|
|||
class BLE { |
|||
public: |
|||
bool ble_begin = false; |
|||
bool updated = false; |
|||
bool ble_started = false; |
|||
int16_t sent_counter_raw = 0; |
|||
int16_t sent_counter = 0; |
|||
|
|||
bool button1 = false; |
|||
bool button2 = false; |
|||
bool button3 = false; |
|||
bool button4 = false; |
|||
bool button5 = false; |
|||
bool button1_sent = false; |
|||
bool button2_sent = false; |
|||
bool button3_sent = false; |
|||
bool button4_sent = false; |
|||
bool button5_sent = false; |
|||
uint32_t last_button1 = 0; |
|||
uint32_t last_button2 = 0; |
|||
uint32_t last_button3 = 0; |
|||
uint32_t last_button4 = 0; |
|||
uint32_t last_button5 = 0; |
|||
|
|||
|
|||
BLE(void); |
|||
void begin(void); |
|||
void end(void); |
|||
void update(void); |
|||
|
|||
private: |
|||
uint32_t last_updated = 0; |
|||
uint32_t last_seconds = 0; |
|||
}; |
|||
|
|||
extern BLE ble; |
|||
#endif |
@ -0,0 +1,241 @@ |
|||
#include "BLEGamepad.h"
|
|||
|
|||
BLEGamepad::BLEGamepad(void) { |
|||
updated = true; |
|||
} |
|||
|
|||
void BLEGamepad::begin(void) { |
|||
updated = true; |
|||
} |
|||
|
|||
void BLEGamepad::draw_gimbal(TFT_eSprite *m, uint16_t x, uint16_t y) { |
|||
// Top
|
|||
m->drawFastHLine(x, y, 90, TFT_DARKGREEN); |
|||
// Bottom
|
|||
m->drawFastHLine(x, y + 89, 90, TFT_DARKGREEN); |
|||
// Left
|
|||
m->drawFastVLine(x, y, 90, TFT_DARKGREEN); |
|||
// Right
|
|||
m->drawFastVLine(x + 89, y, 90, TFT_DARKGREEN); |
|||
|
|||
// Middle dotted lines
|
|||
for(uint16_t x1 = x; x1 < x + 90; x1 += 10) { |
|||
m->drawFastHLine(x1, y + 45, 5, TFT_DARKGREEN); |
|||
} |
|||
for(uint16_t y1 = y; y1 < y + 90; y1 += 10) { |
|||
m->drawFastVLine(x + 45, y1, 5, TFT_DARKGREEN); |
|||
} |
|||
} |
|||
|
|||
void BLEGamepad::show(TFT_eSprite *m) { |
|||
if(!ble_begin) { |
|||
ble_begin = true; |
|||
ble.begin(); |
|||
} |
|||
|
|||
if((uint32_t) (millis() - last_updated) > 50) { |
|||
updated = true; |
|||
} |
|||
|
|||
|
|||
if((uint32_t) (millis() - last_voltage_read) > 500) { |
|||
voltage = M5.Axp.GetBatVoltage(); |
|||
current = M5.Axp.GetBatCurrent(); |
|||
|
|||
last_voltage_read = millis(); |
|||
} |
|||
|
|||
if(updated) { |
|||
m->fillRect(0, 0, 320, 240, BLACK); |
|||
m->setTextColor(WHITE, BLACK); |
|||
m->setTextSize(2); |
|||
m->setCursor(0, 0); |
|||
m->print("Packet(s) Sent: "); |
|||
m->print(String(ble.sent_counter)); |
|||
|
|||
m->setCursor(0, 17); |
|||
m->print("Bat: "); |
|||
m->print(String(voltage) + "V " + String(current) + "mA"); |
|||
|
|||
// Draw the gimbal box
|
|||
draw_gimbal(m, 69, 150); |
|||
draw_gimbal(m, 161, 150); |
|||
// Show the position of the left and right gimbal
|
|||
// Convert the throttle and raw to x and y
|
|||
// Temporary assume 4096 = max
|
|||
uint16_t left_gimbal_y = map(inputs.throttle, stickcalibration.throttle_min, stickcalibration.throttle_max, 90, 0); |
|||
uint16_t left_gimbal_x = map(inputs.yaw, stickcalibration.yaw_min, stickcalibration.yaw_max, 90, 0); |
|||
m->fillCircle(left_gimbal_x + 69, left_gimbal_y + 150, 5, TFT_YELLOW); |
|||
uint16_t right_gimbal_y = map(inputs.pitch, stickcalibration.pitch_min, stickcalibration.pitch_max, 0, 90); |
|||
uint16_t right_gimbal_x = map(inputs.roll, stickcalibration.roll_min, stickcalibration.roll_max, 0, 90); |
|||
m->fillCircle(right_gimbal_x + 161, right_gimbal_y + 150, 5, TFT_YELLOW); |
|||
|
|||
// Draw the switches
|
|||
uint32_t last_millis = millis(); |
|||
if((uint32_t)(last_millis - last_button1) > 500) { |
|||
m->drawRect(0, 40, 60, 60, TFT_BLUE); // Button 1
|
|||
} else { |
|||
m->drawRect(0, 40, 60, 60, TFT_BLUE); // Button 1
|
|||
m->fillRect(1, 41, 60, 60, TFT_BLUE); |
|||
} |
|||
|
|||
if((uint32_t)(last_millis - last_button2) > 500) { |
|||
m->drawRect(65, 40, 60, 60, TFT_BLUE); // Button 2
|
|||
} else { |
|||
m->drawRect(65, 40, 60, 60, TFT_BLUE); // Button 2
|
|||
m->fillRect(66, 41, 60, 60, TFT_BLUE); |
|||
} |
|||
|
|||
if((uint32_t)(last_millis - last_button3) > 500) { |
|||
m->drawRect(130, 40, 60, 60, TFT_BLUE); // Button 3
|
|||
} else { |
|||
m->drawRect(130, 40, 60, 60, TFT_BLUE); // Button 3
|
|||
m->fillRect(131, 41, 60, 60, TFT_BLUE); |
|||
} |
|||
|
|||
if((uint32_t)(last_millis - last_button4) > 500) { |
|||
m->drawRect(195, 40, 60, 60, TFT_BLUE); // Button 4
|
|||
} else { |
|||
m->drawRect(195, 40, 60, 60, TFT_BLUE); // Button 4
|
|||
m->fillRect(196, 41, 60, 60, TFT_BLUE); |
|||
} |
|||
|
|||
if((uint32_t)(last_millis - last_button5) > 500) { |
|||
m->drawRect(260, 40, 60, 60, TFT_BLUE); // Button 5
|
|||
} else { |
|||
m->drawRect(260, 40, 60, 60, TFT_BLUE); // Button 5
|
|||
m->fillRect(261, 41, 60, 60, TFT_BLUE); |
|||
} |
|||
|
|||
m->setCursor(90, 130); |
|||
m->print("BLE GAMEPAD"); |
|||
|
|||
m->pushSprite(0,0); |
|||
|
|||
updated = false; |
|||
last_updated = millis(); |
|||
} |
|||
|
|||
|
|||
// Key Input Handler
|
|||
if(M5.BtnC.wasPressed()) { |
|||
if(ble_begin) { |
|||
ble_begin = false; |
|||
ble.end(); |
|||
} |
|||
|
|||
screen.current_screen = SCREEN_MENU; |
|||
screen.updated = true; |
|||
} |
|||
|
|||
// Temporary connect from here...
|
|||
if(M5.BtnA.wasPressed()) { |
|||
} |
|||
|
|||
// Handling inputs
|
|||
Event &e = M5.Buttons.event; |
|||
coordinate = M5.Touch.getPressPoint(); |
|||
|
|||
// Making sure not retrigger...
|
|||
if((uint32_t)(millis() - touch_time) > 100) { |
|||
if(e & (E_TOUCH)) { |
|||
Serial.printf("E_TOUCH X:%d, Y:%d\r\n", e.to.x, e.to.y); |
|||
|
|||
// Determine touched positions:
|
|||
// Left gimbal:
|
|||
// - Top
|
|||
// - MinX: 110
|
|||
// - MinY: 150
|
|||
// - MaxX: 120
|
|||
// - MaxY: 180
|
|||
// - Bottom
|
|||
// - MinX: 110
|
|||
// - MinY: 200
|
|||
// - MaxX: 120
|
|||
// - MaxY: 220
|
|||
// - Left
|
|||
// - MinX: 80
|
|||
// - MinY: 180
|
|||
// - MaxX: 105
|
|||
// - MaxY: 210
|
|||
// - Right
|
|||
// - MinX: 110
|
|||
// - MinY: 150
|
|||
// - MaxX: 160
|
|||
// - MaxY: 190
|
|||
|
|||
if(e.to.x >= 110 && e.to.x <= 120 && e.to.y >= 140 && e.to.y <= 180) { |
|||
// Minus throttle
|
|||
// Serial.printf("Left: TOP\n");
|
|||
inputs.trim_throttle-=20; |
|||
//// speak.speak_trim_word = TRIM_THROTTLE_INCREASE;
|
|||
//// speak.speak_now_trim = true;
|
|||
} else if(e.to.x >= 110 && e.to.x <= 170 && e.to.y >= 200 && e.to.y <= 220) { |
|||
// Add throttle
|
|||
// Serial.printf("Left: BOTTOM\n");
|
|||
inputs.trim_throttle+=20; |
|||
//// speak.speak_trim_word = TRIM_THROTTLE_DECREASE;
|
|||
//// speak.speak_now_trim = true;
|
|||
} else if(e.to.x >= 60 && e.to.x <= 105 && e.to.y >= 180 && e.to.y <= 210) { |
|||
// Minus Yaw
|
|||
// Serial.printf("Left: LEFT\n");
|
|||
inputs.trim_yaw-=20; |
|||
//// speak.speak_trim_word = TRIM_YAW_DECREASE;
|
|||
//// speak.speak_now_trim = true;
|
|||
} else if(e.to.x >= 110 && e.to.x <= 160 && e.to.y >= 150 && e.to.y <= 190) { |
|||
// Add Yaw
|
|||
// Serial.printf("Left: RIGHT\n");
|
|||
inputs.trim_yaw+=20; |
|||
//// speak.speak_trim_word = TRIM_YAW_INCREASE;
|
|||
//// speak.speak_now_trim = true;
|
|||
} else if(e.to.x >= 250 && e.to.x <= 230 && e.to.y >= 187 && e.to.y <= 197) { |
|||
// Add Row
|
|||
inputs.trim_roll+=20; |
|||
//// speak.speak_trim_word = TRIM_ROLL_INCREASE;
|
|||
//// speak.speak_now_trim = true;
|
|||
} else if(e.to.x >= 180 && e.to.x <= 175 && e.to.y >= 187 && e.to.y <= 197) { |
|||
// Minus Row
|
|||
inputs.trim_roll+=20; |
|||
//// speak.speak_trim_word = TRIM_ROLL_DECREASE;
|
|||
//// speak.speak_now_trim = true;
|
|||
// TODO: Continue the rest.....
|
|||
} else if(e.to.x >= 0 && e.to.x <= 60 && e.to.y >= 40 && e.to.y <= 100) { |
|||
// Button 1
|
|||
ble.last_button1 = millis(); // Setting the time so the system knows how long to trigger
|
|||
last_button1 = ble.last_button1; // For remembering last press on UI
|
|||
ble.button1 = true; // Send to BLE let the process to run through
|
|||
} else if(e.to.x >= 65 && e.to.x <= 125 && e.to.y >= 40 && e.to.y <= 100) { |
|||
// Button 2
|
|||
ble.last_button2 = millis(); // Setting the time so the system knows how long to trigger
|
|||
last_button2 = ble.last_button2; // For remembering last press on UI
|
|||
ble.button2 = true; // Send to BLE let the process to run through
|
|||
} else if(e.to.x >= 130 && e.to.x <= 190 && e.to.y >= 40 && e.to.y <= 100) { |
|||
// Button 3
|
|||
ble.last_button3 = millis(); // Setting the time so the system knows how long to trigger
|
|||
last_button3 = ble.last_button3; // For remembering last press on UI
|
|||
ble.button3 = true; // Send to BLE let the process to run through
|
|||
} else if(e.to.x >= 195 && e.to.x <= 255 && e.to.y >= 40 && e.to.y <= 100) { |
|||
// Button 4
|
|||
ble.last_button4 = millis(); // Setting the time so the system knows how long to trigger
|
|||
last_button4 = ble.last_button4; // For remembering last press on UI
|
|||
ble.button4 = true; // Send to BLE let the process to run through
|
|||
} else if(e.to.x >= 260 && e.to.x <= 310 && e.to.y >= 40 && e.to.y <= 100) { |
|||
// Button 5
|
|||
ble.last_button5 = millis(); // Setting the time so the system knows how long to trigger
|
|||
last_button5 = ble.last_button5; // For remembering last press on UI
|
|||
ble.button5 = true; // Send to BLE let the process to run through
|
|||
} |
|||
touch_time = millis(); |
|||
} |
|||
} |
|||
|
|||
/***
|
|||
if(e & (E_RELEASE)) { |
|||
Serial.printf("E_MOVE X:%d, Y:%d\r\n", e & E_MOVE ? e.from.x : e.to.x, e & E_MOVE ? e.from.y : e.to.y); |
|||
} |
|||
if(coordinate.x > 0 || coordinate.y > 0) |
|||
Serial.printf("x:%d, y:%d \r\n", coordinate.x, coordinate.y); |
|||
***/ |
|||
} |
|||
|
|||
BLEGamepad blegamepad; |
@ -0,0 +1,67 @@ |
|||
#ifndef _BLEGAMEPAD_H |
|||
#define _BLEGAMEPAD_H |
|||
|
|||
#include <Arduino.h> |
|||
#include <M5Core2.h> |
|||
#include "BLE.h" |
|||
#include "Inputs.h" |
|||
#include "Screen.h" |
|||
//// #include "Speak.h" |
|||
|
|||
enum gesture_axis { |
|||
NONE_AXIS, |
|||
HORIZONTAL, |
|||
VERTICAL |
|||
}; |
|||
|
|||
enum gimbal { |
|||
NONE_GIMBAL, |
|||
LEFT, |
|||
RIGHT |
|||
}; |
|||
|
|||
enum gesture_meanings { |
|||
NONE_GESTURE, |
|||
INCREASE, |
|||
DECREASE |
|||
}; |
|||
|
|||
enum trim_names { |
|||
NONE_TRIM, |
|||
TRIM_YAW_DECREASE, |
|||
TRIM_YAW_INCREASE, |
|||
TRIM_THROTTLE_DECREASE, |
|||
TRIM_THROTTLE_INCREASE, |
|||
TRIM_ROLL_INCREASE, |
|||
TRIM_ROLL_DECREASE, |
|||
TRIM_PITCH_INCREASE, |
|||
TRIM_PITCH_DECREASE |
|||
}; |
|||
|
|||
class BLEGamepad { |
|||
public: |
|||
TouchPoint_t coordinate; |
|||
uint32_t touch_time = 0; |
|||
bool ble_begin = false; |
|||
bool updated = false; |
|||
float voltage = 0.00; |
|||
float current = 0.00; |
|||
|
|||
uint32_t last_button1 = 0; |
|||
uint32_t last_button2 = 0; |
|||
uint32_t last_button3 = 0; |
|||
uint32_t last_button4 = 0; |
|||
uint32_t last_button5 = 0; |
|||
|
|||
BLEGamepad(void); |
|||
void begin(void); |
|||
void show(TFT_eSprite *m); |
|||
void draw_gimbal(TFT_eSprite *m, uint16_t x, uint16_t y); |
|||
|
|||
private: |
|||
uint32_t last_updated = 0; |
|||
uint32_t last_voltage_read = 0; |
|||
}; |
|||
|
|||
extern BLEGamepad blegamepad; |
|||
#endif |
@ -0,0 +1,33 @@ |
|||
#include "CustomWiFi.h"
|
|||
|
|||
CustomWiFi::CustomWiFi(void) { |
|||
// pinMode(CustomWiFi_INDICATOR, OUTPUT);
|
|||
last_wifi_check = 0; |
|||
wifi_check_duration = 10; |
|||
} |
|||
|
|||
void CustomWiFi::set_credential(char *ssid, char *passphrase) { |
|||
strcpy(CustomWiFi::_ssid, ssid); |
|||
strcpy(CustomWiFi::_passphrase, passphrase); |
|||
|
|||
// Initial value
|
|||
this->wifi_check_duration = 5000; |
|||
} |
|||
|
|||
|
|||
void CustomWiFi::begin(void) { |
|||
IPAddress local_ip(192,168,1,1); |
|||
IPAddress gateway(192,168,1,1); |
|||
IPAddress subnet(255,255,255,0); |
|||
|
|||
WiFi.softAP(WIFI_SSID, WIFI_PASSPHRASE); |
|||
delay(100); |
|||
|
|||
WiFi.softAPConfig(local_ip, gateway, subnet); |
|||
delay(100); |
|||
|
|||
IPAddress IP = WiFi.softAPIP(); |
|||
// Serial.print(F("AP IP address: "); Serial.prinln(IP);
|
|||
} |
|||
|
|||
CustomWiFi customwifi; |
@ -0,0 +1,24 @@ |
|||
#ifndef CUSTOMWIFI_H_ |
|||
#define CUSTOMWIFI_H_ |
|||
|
|||
#include <Arduino.h> |
|||
#include <WiFi.h> |
|||
#include <WiFiClient.h> |
|||
|
|||
#define WIFI_SSID "M5CoreTX" |
|||
#define WIFI_PASSPHRASE "LetMeIn123" |
|||
// #define CUSTOMWIFI_INDICATOR 2 |
|||
|
|||
class CustomWiFi { |
|||
public: |
|||
CustomWiFi(void); |
|||
void set_credential(char *ssid, char *passphrase); |
|||
void begin(void); |
|||
char _ssid[32], _passphrase[32]; |
|||
|
|||
private: |
|||
uint32_t last_wifi_check, wifi_check_duration; |
|||
}; |
|||
|
|||
extern CustomWiFi customwifi; |
|||
#endif |
@ -0,0 +1,5 @@ |
|||
#include "GimbalGraph.h"
|
|||
|
|||
GimbalGraph::GimbalGraph(void) { |
|||
} |
|||
|
@ -0,0 +1,14 @@ |
|||
#ifndef _GIMBAL_GRAPH_H |
|||
#define _GIMBAL_GRAPH_H |
|||
|
|||
#include <Arduino.h> |
|||
|
|||
class GimbalGraph { |
|||
public: |
|||
GimbalGraph(void); |
|||
|
|||
private: |
|||
}; |
|||
|
|||
extern GimbalGraph gimbalgraph; |
|||
#endif |
@ -0,0 +1,320 @@ |
|||
#include "Inputs.h"
|
|||
|
|||
Inputs::Inputs(void) { |
|||
} |
|||
|
|||
|
|||
void Inputs::begin(void) { |
|||
throttle_raw = 0; |
|||
yaw_raw = 0; |
|||
pitch_raw = 0; |
|||
roll_raw = 0; |
|||
|
|||
// Prepare the storage engine for TYPR
|
|||
for(int i = 0; i < MEDIAN_TOTAL; i++) { |
|||
throttle_pool[i] = 0; |
|||
yaw_pool[i] = 0; |
|||
pitch_pool[i] = 0; |
|||
roll_pool[i] = 0; |
|||
} |
|||
|
|||
// Reading trims
|
|||
String trim_str = ""; |
|||
String filename = ""; |
|||
|
|||
filename = "/TRIM_THROTTLE"; |
|||
if(!storage.exists(filename)) { |
|||
storage.writeFile(LITTLEFS, filename, String(trim_throttle)); |
|||
} else { |
|||
trim_str = storage.readFile(LITTLEFS, filename); |
|||
trim_throttle = trim_str.toInt(); |
|||
} |
|||
|
|||
filename = "/TRIM_YAW"; |
|||
if(!storage.exists(filename)) { |
|||
storage.writeFile(LITTLEFS, filename, String(trim_yaw)); |
|||
} else { |
|||
trim_str = storage.readFile(LITTLEFS, filename); |
|||
trim_yaw = trim_str.toInt(); |
|||
} |
|||
|
|||
filename = "/TRIM_PITCH"; |
|||
if(!storage.exists(filename)) { |
|||
storage.writeFile(LITTLEFS, filename, String(trim_pitch)); |
|||
} else { |
|||
trim_str = storage.readFile(LITTLEFS, filename); |
|||
trim_pitch = trim_str.toInt(); |
|||
} |
|||
|
|||
filename = "/TRIM_ROLL"; |
|||
if(!storage.exists(filename)) { |
|||
storage.writeFile(LITTLEFS, filename, String(trim_roll)); |
|||
} else { |
|||
trim_str = storage.readFile(LITTLEFS, filename); |
|||
trim_roll = trim_str.toInt(); |
|||
} |
|||
|
|||
// Pre-read
|
|||
read(); |
|||
|
|||
// Activate silent mode...
|
|||
//// if(pitch_raw < 1000) speak.silent = true;
|
|||
|
|||
// Activate low volume mode....
|
|||
//// if(pitch_raw > 3000) speak.low_volume = true;
|
|||
} |
|||
|
|||
|
|||
void Inputs::read(void) { |
|||
throttle_raw = analogRead(THROTTLE_PIN); |
|||
yaw_raw = analogRead(YAW_PIN); |
|||
pitch_raw = analogRead(PITCH_PIN); |
|||
roll_raw = analogRead(ROLL_PIN); |
|||
|
|||
if(invert_throttle) { |
|||
throttle_raw = 4095 - throttle_raw; |
|||
} |
|||
|
|||
if(invert_yaw) { |
|||
yaw_raw = 4095 - yaw_raw; |
|||
} |
|||
|
|||
if(invert_pitch) { |
|||
pitch_raw = 4095 - pitch_raw; |
|||
} |
|||
|
|||
if(invert_roll) { |
|||
roll_raw = 4095 - roll_raw; |
|||
} |
|||
|
|||
throttle = pool_insert(THROTTLE, throttle_raw) + trim_throttle; |
|||
yaw = pool_insert(YAW, yaw_raw) + trim_yaw; |
|||
pitch = pool_insert(PITCH, pitch_raw) + trim_pitch; |
|||
roll = pool_insert(ROLL, roll_raw) + trim_roll; |
|||
} |
|||
|
|||
|
|||
/*
|
|||
* median_get: |
|||
* To get the median from the data depending on the median_type |
|||
* |
|||
* E.g.: |
|||
* retval = median_get(THROTTLE); |
|||
*/ |
|||
uint16_t Inputs::median_get(uint8_t median_type) { |
|||
bool inserted = false; |
|||
|
|||
uint16_t median_tmp[MEDIAN_TOTAL]; |
|||
|
|||
// Loop through the variable to determine position to insert
|
|||
// [ 12, 32, 4 ,0, 50, 2, 10, 10, 5, 20, 0 ]
|
|||
/*
|
|||
[ 12 ] |
|||
[ 12, 32 ] |
|||
[ 4, 12, 32 ] |
|||
[ 0, 4, 12, 32 ] |
|||
[ 0, 4, 12, 32, 50 ] |
|||
[ 0, 2, 4, 12, 32, 50 ] |
|||
|
|||
*/ |
|||
|
|||
// Initial
|
|||
uint16_t temp_val = 0; |
|||
uint16_t total_insert = 1; |
|||
|
|||
// Search on type
|
|||
if(median_type == THROTTLE) { |
|||
median_tmp[0] = throttle_pool[0]; |
|||
} else if(median_type == YAW) { |
|||
median_tmp[0] = yaw_pool[0]; |
|||
} else if(median_type == PITCH) { |
|||
median_tmp[0] = pitch_pool[0]; |
|||
} else if(median_type == ROLL) { |
|||
median_tmp[0] = roll_pool[0]; |
|||
} |
|||
|
|||
// Loop insert and sort
|
|||
for(int raw_count = 1; raw_count < MEDIAN_TOTAL; raw_count++) { |
|||
inserted = false; |
|||
|
|||
if(median_type == THROTTLE) { |
|||
temp_val = throttle_pool[raw_count]; |
|||
} else if(median_type == YAW) { |
|||
temp_val = yaw_pool[raw_count]; |
|||
} else if(median_type == PITCH) { |
|||
temp_val = pitch_pool[raw_count]; |
|||
} else if(median_type == ROLL) { |
|||
temp_val = roll_pool[raw_count]; |
|||
} |
|||
|
|||
for(int median_count = 0; median_count < total_insert; median_count++) { |
|||
if(!inserted) { |
|||
|
|||
if(temp_val < median_tmp[median_count]) { |
|||
inserted = true; |
|||
|
|||
// Reverse copy and insert
|
|||
for(int median_reverse = total_insert + 1; median_reverse > median_count; median_reverse--) { |
|||
median_tmp[median_reverse] = median_tmp[median_reverse - 1]; |
|||
} |
|||
|
|||
// Insert the detected
|
|||
median_tmp[median_count] = temp_val; |
|||
|
|||
// Increase total insertion
|
|||
total_insert++; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Nothing was inserted... so put at the last.
|
|||
if(!inserted) { |
|||
median_tmp[total_insert++] = temp_val; |
|||
} |
|||
} |
|||
|
|||
// Return the result
|
|||
// return median_tmp[MEDIAN_POS];
|
|||
// Using RC dead band way....**** NEED TO IMPROvE!!
|
|||
if(median_type == THROTTLE) { |
|||
if(throttle > median_tmp[MEDIAN_POS]) { |
|||
if(throttle - median_tmp[MEDIAN_POS] > RC_DEADBAND) { |
|||
return median_tmp[MEDIAN_POS]; |
|||
} else { |
|||
if(median_tmp[MEDIAN_POS] - throttle > RC_DEADBAND) { |
|||
return median_tmp[MEDIAN_POS]; |
|||
} else { |
|||
return throttle; |
|||
} |
|||
} |
|||
} |
|||
} else if(median_type == YAW) { |
|||
if(yaw > median_tmp[MEDIAN_POS]) { |
|||
if(yaw - median_tmp[MEDIAN_POS] > RC_DEADBAND) { |
|||
return median_tmp[MEDIAN_POS]; |
|||
} else { |
|||
if(median_tmp[MEDIAN_POS] - yaw > RC_DEADBAND) { |
|||
return median_tmp[MEDIAN_POS]; |
|||
} else { |
|||
return yaw; |
|||
} |
|||
} |
|||
} |
|||
} else if(median_type == PITCH) { |
|||
if(pitch > median_tmp[MEDIAN_POS]) { |
|||
if(pitch - median_tmp[MEDIAN_POS] > RC_DEADBAND) { |
|||
return median_tmp[MEDIAN_POS]; |
|||
} else { |
|||
if(median_tmp[MEDIAN_POS] - pitch > RC_DEADBAND) { |
|||
return median_tmp[MEDIAN_POS]; |
|||
} else { |
|||
return pitch; |
|||
} |
|||
} |
|||
} |
|||
} else if(median_type == ROLL) { |
|||
if(roll > median_tmp[MEDIAN_POS]) { |
|||
if(roll - median_tmp[MEDIAN_POS] > RC_DEADBAND) { |
|||
return median_tmp[MEDIAN_POS]; |
|||
} else { |
|||
if(median_tmp[MEDIAN_POS] - roll > RC_DEADBAND) { |
|||
return median_tmp[MEDIAN_POS]; |
|||
} else { |
|||
return roll; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
uint16_t Inputs::throttle_insert(uint16_t val) { |
|||
// Insert to the array...at the last
|
|||
for(int i = 1; i < MEDIAN_TOTAL; i++) |
|||
// Shift to left
|
|||
throttle_pool[i - 1] = throttle_pool[i]; |
|||
|
|||
throttle_pool[MEDIAN_TOTAL - 1] = val; |
|||
|
|||
// Find median....
|
|||
return median_get(THROTTLE); |
|||
} |
|||
|
|||
|
|||
uint16_t Inputs::yaw_insert(uint16_t val) { |
|||
// Insert to the array...at the last
|
|||
for(int i = 1; i < MEDIAN_TOTAL; i++) |
|||
// Shift to left
|
|||
yaw_pool[i - 1] = yaw_pool[i]; |
|||
|
|||
yaw_pool[MEDIAN_TOTAL - 1] = val; |
|||
|
|||
// Find median....
|
|||
return median_get(YAW); |
|||
} |
|||
|
|||
|
|||
uint16_t Inputs::pitch_insert(uint16_t val) { |
|||
// Insert to the array...at the last
|
|||
for(int i = 1; i < MEDIAN_TOTAL; i++) |
|||
// Shift to left
|
|||
pitch_pool[i - 1] = pitch_pool[i]; |
|||
|
|||
pitch_pool[MEDIAN_TOTAL - 1] = val; |
|||
|
|||
// Find median....
|
|||
return median_get(PITCH); |
|||
} |
|||
|
|||
|
|||
uint16_t Inputs::roll_insert(uint16_t val) { |
|||
// Insert to the array...at the last
|
|||
for(int i = 1; i < MEDIAN_TOTAL; i++) |
|||
// Shift to left
|
|||
roll_pool[i - 1] = roll_pool[i]; |
|||
|
|||
roll_pool[MEDIAN_TOTAL - 1] = val; |
|||
|
|||
// Find median....
|
|||
return median_get(ROLL); |
|||
} |
|||
|
|||
|
|||
/*
|
|||
* pool_insert: |
|||
* To insert the data into the median pool depending on type of the variable |
|||
* |
|||
* E.g.: |
|||
* throttle_value = pool_insert(THROTTLE); |
|||
*/ |
|||
uint16_t Inputs::pool_insert(uint8_t pool_type, uint16_t val) { |
|||
// Insert to the array...at the last of the array
|
|||
for(int i = 1; i < MEDIAN_TOTAL; i++) { |
|||
// Shift to left
|
|||
if(pool_type == THROTTLE) { |
|||
throttle_pool[i - 1] = throttle_pool[i]; |
|||
} else if(pool_type == YAW) { |
|||
yaw_pool[i - 1] = yaw_pool[i]; |
|||
} else if(pool_type == PITCH) { |
|||
pitch_pool[i - 1] = pitch_pool[i]; |
|||
} else if(pool_type == ROLL) { |
|||
roll_pool[i - 1] = roll_pool[i]; |
|||
} |
|||
} |
|||
|
|||
// Find median....
|
|||
if(pool_type == THROTTLE) { |
|||
throttle_pool[MEDIAN_TOTAL - 1] = val; |
|||
} else if(pool_type == YAW) { |
|||
yaw_pool[MEDIAN_TOTAL - 1] = val; |
|||
} else if(pool_type == ROLL) { |
|||
roll_pool[MEDIAN_TOTAL - 1] = val; |
|||
} else if(pool_type == PITCH) { |
|||
pitch_pool[MEDIAN_TOTAL - 1] = val; |
|||
} |
|||
return median_get(pool_type); |
|||
} |
|||
|
|||
|
|||
Inputs inputs; |
@ -0,0 +1,88 @@ |
|||
#ifndef _INPUTS_H |
|||
#define _INPUTS_H |
|||
|
|||
#include <Arduino.h> |
|||
#include <M5Core2.h> |
|||
#include "Storage.h" |
|||
|
|||
#define THROTTLE_PIN 35 |
|||
#define YAW_PIN 36 |
|||
#define PITCH_PIN 32 |
|||
#define ROLL_PIN 33 |
|||
|
|||
#define MEDIAN_TOTAL 11 |
|||
#define MEDIAN_POS MEDIAN_TOTAL/2 |
|||
|
|||
// For RC Deadband |
|||
#define RC_DEADBAND 5 |
|||
|
|||
/*** |
|||
enum input_names { |
|||
THROTTLE = 0, |
|||
YAW, |
|||
PITCH, |
|||
ROLL |
|||
}; |
|||
***/ |
|||
// Enumerators - User Defined |
|||
enum rc { |
|||
ROLL, |
|||
PITCH, |
|||
THROTTLE, |
|||
YAW, |
|||
AUX1, |
|||
AUX2, |
|||
AUX3, |
|||
AUX4, |
|||
AUX5, |
|||
AUX6, |
|||
AUX7, |
|||
AUX8, |
|||
AUX9 |
|||
}; |
|||
|
|||
class Inputs { |
|||
public: |
|||
bool invert_throttle = false; |
|||
bool invert_yaw = false; |
|||
bool invert_pitch = false; |
|||
bool invert_roll = false; |
|||
|
|||
uint16_t throttle = 0; |
|||
uint16_t yaw = 0; |
|||
uint16_t pitch = 0; |
|||
uint16_t roll = 0; |
|||
|
|||
int16_t trim_throttle = 0; |
|||
int16_t trim_yaw = 0; |
|||
int16_t trim_pitch = 0; |
|||
int16_t trim_roll = 0; |
|||
|
|||
Inputs(void); |
|||
void begin(void); |
|||
void read(void); |
|||
|
|||
private: |
|||
uint32_t last_voltage_read = 0; |
|||
|
|||
uint16_t throttle_pool[MEDIAN_TOTAL]; |
|||
uint16_t yaw_pool[MEDIAN_TOTAL]; |
|||
uint16_t pitch_pool[MEDIAN_TOTAL]; |
|||
uint16_t roll_pool[MEDIAN_TOTAL]; |
|||
uint16_t tmp_median_store[MEDIAN_TOTAL]; |
|||
|
|||
uint16_t throttle_raw; |
|||
uint16_t yaw_raw; |
|||
uint16_t pitch_raw; |
|||
uint16_t roll_raw; |
|||
|
|||
uint16_t median_get(uint8_t median_type); |
|||
uint16_t throttle_insert(uint16_t val); |
|||
uint16_t yaw_insert(uint16_t val); |
|||
uint16_t pitch_insert(uint16_t val); |
|||
uint16_t roll_insert(uint16_t val); |
|||
uint16_t pool_insert(uint8_t pool_type, uint16_t val); |
|||
}; |
|||
|
|||
extern Inputs inputs; |
|||
#endif |
@ -0,0 +1,302 @@ |
|||
// REF:
|
|||
// https://github.com/m5stack/m5-docs/blob/master/docs/en/api/lcd.md
|
|||
// https://github.com/Bodmer/TFT_eSPI/blob/master/TFT_eSPI.h
|
|||
|
|||
#include "Screen.h"
|
|||
|
|||
TFT_eSprite mainscreen_buffer = TFT_eSprite(&M5.Lcd); |
|||
|
|||
|
|||
Screen::Screen(void) { |
|||
} |
|||
|
|||
|
|||
void Screen::begin(void) { |
|||
M5.begin(true, true, true, true); //Init M5Core2. Initialize M5Core2
|
|||
/*
|
|||
* Power chip connected to gpio21, gpio22, I2C device |
|||
* Set battery charging voltage and current |
|||
* If used battery, please call this function in your project |
|||
*/ |
|||
// Initial value
|
|||
updated = true; |
|||
|
|||
stickcalibration.begin(); |
|||
|
|||
M5.Lcd.fillScreen(BLACK); |
|||
mainscreen_buffer.createSprite(320, 240); |
|||
} |
|||
|
|||
|
|||
void Screen::update(void) { |
|||
M5.update(); |
|||
|
|||
// Update screen based on selected menu
|
|||
if((uint32_t)(millis() - last_screen_update) > REFRESH_TIME) { |
|||
if(current_screen == SCREEN_MENU) { |
|||
menu(); |
|||
} else if(current_screen == SCREEN_MENU_REBOOT) { |
|||
menu_reboot(); |
|||
} else if(current_screen == SCREEN_MENU_POWEROFF) { |
|||
menu_poweroff(); |
|||
} else if(current_screen == SCREEN_MENU_BLUETOOTH) { |
|||
blegamepad.show(&mainscreen_buffer); |
|||
} else if(current_screen == SCREEN_MENU_STICK_CALIBRATION) { |
|||
stickcalibration.show(&mainscreen_buffer); |
|||
} else if(current_screen == SCREEN_MENU_STICK_POSITIONS) { |
|||
stickpositions.main(&mainscreen_buffer); |
|||
} |
|||
|
|||
last_screen_update = millis(); |
|||
} |
|||
|
|||
/***
|
|||
if(M5.BtnA.wasPressed()) { |
|||
M5.Lcd.println("Button A Pressed"); |
|||
} |
|||
if(M5.BtnB.wasPressed()) { |
|||
M5.Lcd.println("Button B Pressed"); |
|||
} |
|||
if(M5.BtnC.wasPressed()) { |
|||
M5.Lcd.println("Button C Pressed"); |
|||
} |
|||
***/ |
|||
} |
|||
|
|||
|
|||
void Screen::menu_bluetooth(void) { |
|||
} |
|||
|
|||
|
|||
void Screen::intro(void) { |
|||
// M5.Lcd.drawRect(100, 100, 50, 50, BLUE);
|
|||
//
|
|||
//
|
|||
// vTaskDelay(INTRO_TIME);
|
|||
current_screen = SCREEN_MENU; |
|||
// M5.Lcd.fillScreen(BLACK);
|
|||
} |
|||
|
|||
|
|||
// Main Menu
|
|||
void Screen::menu(void) { |
|||
static uint8_t current_menu = MENU_BLUETOOTH; |
|||
static uint8_t menu_start_position = 0; |
|||
static uint8_t menu_max_position = MENU_MAX_ITEMS; |
|||
|
|||
static String menu_items_name[] = { |
|||
"Bluetooth ", |
|||
"Stick Calibration ", |
|||
"Stick Positions ", |
|||
"Trims ", |
|||
"WiFi ", |
|||
"RX Binding ", |
|||
"RF Settings ", |
|||
"RF Scanners ", |
|||
"Save Settings ", |
|||
"Reset ", |
|||
"Reboot ", |
|||
"Power Off " |
|||
}; |
|||
// Serial.println((int)sizeof(menu_items_name)/sizeof(menu_items_name[0]));
|
|||
|
|||
uint8_t menu_total = (int)sizeof(menu_items_name)/sizeof(menu_items_name[0]); |
|||
uint8_t menu_count = 0; |
|||
uint8_t menu_pointer = 0; |
|||
|
|||
if(updated) { |
|||
// List all menus
|
|||
mainscreen_buffer.fillRect(0, 0, 320, 240, BLACK); |
|||
mainscreen_buffer.setTextColor(WHITE, BLACK); |
|||
mainscreen_buffer.setTextSize(2); |
|||
|
|||
// for(uint8_t y = 0; y < menu_total * MENU_HEIGHT; y+=MENU_HEIGHT) {
|
|||
menu_pointer = menu_start_position; |
|||
for(uint8_t y = menu_start_position; y < menu_max_position * MENU_HEIGHT; y+=MENU_HEIGHT) { |
|||
if((current_menu - menu_start_position) == menu_count) { |
|||
mainscreen_buffer.setTextColor(BLACK, YELLOW); |
|||
|
|||
// Trigger voice
|
|||
// peak.speak_words = current_menu;
|
|||
// speak.speak_now = true;
|
|||
//// M5.Speaker.tone(7000, 200);
|
|||
|
|||
} else { |
|||
mainscreen_buffer.setTextColor(WHITE, BLACK); |
|||
} |
|||
|
|||
mainscreen_buffer.setCursor(10, y); |
|||
mainscreen_buffer.print(menu_items_name[menu_pointer]); |
|||
|
|||
menu_count++; |
|||
menu_pointer++; |
|||
} |
|||
|
|||
// Draw the touch button lines
|
|||
mainscreen_buffer.drawFastHLine(50, 239, 20, TFT_WHITE); |
|||
mainscreen_buffer.drawFastHLine(150, 239, 20, TFT_WHITE); |
|||
mainscreen_buffer.drawFastHLine(250, 239, 20, TFT_WHITE); |
|||
|
|||
|
|||
mainscreen_buffer.pushSprite(0,0); |
|||
|
|||
updated = false; |
|||
} |
|||
|
|||
// Key Input Handler
|
|||
if(M5.BtnC.wasPressed()) { |
|||
current_menu++; |
|||
|
|||
if(current_menu == menu_total) current_menu--; |
|||
|
|||
if(current_menu >= menu_max_position) { |
|||
if(menu_start_position + MENU_MAX_ITEMS < menu_total) { |
|||
menu_start_position++; |
|||
} |
|||
} |
|||
|
|||
updated = true; |
|||
} |
|||
|
|||
if(M5.BtnA.wasPressed()) { |
|||
current_menu--; |
|||
|
|||
if(current_menu == 255) current_menu = 0; |
|||
|
|||
if(menu_start_position > 0) { |
|||
if(current_menu == (menu_start_position - 1)) { |
|||
menu_start_position--; |
|||
} |
|||
} |
|||
|
|||
updated = true; |
|||
} |
|||
|
|||
// Handling Button B
|
|||
if(M5.BtnB.wasPressed()) { |
|||
if(current_menu == MENU_REBOOT) { |
|||
current_screen = SCREEN_MENU_REBOOT; |
|||
} else if(current_menu == MENU_POWER_OFF) { |
|||
current_screen = SCREEN_MENU_POWEROFF; |
|||
} else if(current_menu == MENU_BLUETOOTH) { |
|||
current_screen = SCREEN_MENU_BLUETOOTH; |
|||
} else if(current_menu == MENU_STICK_CALIBRATION) { |
|||
current_screen = SCREEN_MENU_STICK_CALIBRATION; |
|||
stickcalibration.updated = true; |
|||
} else if(current_menu == MENU_STICK_POSITIONS) { |
|||
current_screen = SCREEN_MENU_STICK_POSITIONS; |
|||
} |
|||
updated = true; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Reboot Confirmation
|
|||
void Screen::menu_reboot(void) { |
|||
static uint8_t current_menu_reboot_selection = SELECTION_YES; |
|||
static uint8_t menu_reboot_selection_total = 2; |
|||
|
|||
if(updated) { |
|||
// Draw box
|
|||
mainscreen_buffer.drawRoundRect(50, 50, 220, 140, 10, DIALOGBOX_FOREGROUND); |
|||
mainscreen_buffer.fillRoundRect(51, 51, 218, 138, 10, DIALOGBOX_BACKGROUND); |
|||
|
|||
// Show Text
|
|||
mainscreen_buffer.setTextColor(DIALOGBOX_FOREGROUND, DIALOGBOX_BACKGROUND); |
|||
mainscreen_buffer.setCursor(68, 110); |
|||
mainscreen_buffer.print("Reboot Now? "); |
|||
|
|||
mainscreen_buffer.setTextColor(GREEN); |
|||
if(current_menu_reboot_selection == SELECTION_YES) { |
|||
mainscreen_buffer.print("YES"); |
|||
} else { |
|||
mainscreen_buffer.print("NO"); |
|||
} |
|||
|
|||
mainscreen_buffer.pushSprite(0,0); |
|||
updated = false; |
|||
} |
|||
|
|||
// Key Input Handler
|
|||
if(M5.BtnC.wasPressed() || M5.BtnA.wasPressed()) { // Either up or down is the same because only two options
|
|||
current_menu_reboot_selection++; |
|||
|
|||
if(current_menu_reboot_selection == menu_reboot_selection_total) current_menu_reboot_selection = 0; |
|||
updated = true; |
|||
} |
|||
|
|||
if(M5.BtnB.wasPressed()) { |
|||
if(current_menu_reboot_selection == SELECTION_YES) { |
|||
// Trigger voice
|
|||
// speak.speak_miscs = PHRASE001;
|
|||
// speak.speak_now_other = true;
|
|||
|
|||
///// M5.Speaker.tone(6000, 200);
|
|||
vTaskDelay(1500); |
|||
|
|||
ESP.restart(); |
|||
} else if(current_menu_reboot_selection == SELECTION_NO) { |
|||
// Return back to main menu
|
|||
current_screen = SCREEN_MENU; |
|||
} |
|||
updated = true; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Power Off Confirmation
|
|||
void Screen::menu_poweroff(void) { |
|||
static uint8_t current_menu_poweroff_selection = SELECTION_YES; |
|||
static uint8_t menu_poweroff_selection_total = 2; |
|||
|
|||
if(updated) { |
|||
// Draw box
|
|||
mainscreen_buffer.drawRoundRect(50, 50, 220, 140, 10, DIALOGBOX_FOREGROUND); |
|||
mainscreen_buffer.fillRoundRect(51, 51, 218, 138, 10, DIALOGBOX_BACKGROUND); |
|||
|
|||
// Show Text
|
|||
mainscreen_buffer.setTextColor(DIALOGBOX_FOREGROUND, DIALOGBOX_BACKGROUND); |
|||
mainscreen_buffer.setCursor(68, 110); |
|||
mainscreen_buffer.print("Power Off? "); |
|||
|
|||
mainscreen_buffer.setTextColor(GREEN); |
|||
if(current_menu_poweroff_selection == SELECTION_YES) { |
|||
mainscreen_buffer.print("YES"); |
|||
} else { |
|||
mainscreen_buffer.print("NO"); |
|||
} |
|||
|
|||
mainscreen_buffer.pushSprite(0,0); |
|||
updated = false; |
|||
} |
|||
|
|||
// Key Input Handler
|
|||
if(M5.BtnC.wasPressed() || M5.BtnA.wasPressed()) { // Either up or down is the same because only two options
|
|||
current_menu_poweroff_selection++; |
|||
|
|||
if(current_menu_poweroff_selection == menu_poweroff_selection_total) current_menu_poweroff_selection = 0; |
|||
updated = true; |
|||
} |
|||
|
|||
if(M5.BtnB.wasPressed()) { |
|||
if(current_menu_poweroff_selection == SELECTION_YES) { |
|||
// Trigger voice
|
|||
// speak.speak_miscs = PHRASE000;
|
|||
// speak.speak_now_other = true;
|
|||
/***
|
|||
M5.Speaker.tone(6000, 200); |
|||
M5.Speaker.tone(11000, 200); |
|||
***/ |
|||
vTaskDelay(1200); |
|||
|
|||
M5.shutdown(); |
|||
} else if(current_menu_poweroff_selection == SELECTION_NO) { |
|||
// Return back to main menu
|
|||
current_screen = SCREEN_MENU; |
|||
} |
|||
updated = true; |
|||
} |
|||
} |
|||
|
|||
|
|||
Screen screen; |
@ -0,0 +1,70 @@ |
|||
#ifndef _SCREEN_H |
|||
#define _SCREEN_H |
|||
|
|||
#include <Arduino.h> |
|||
#include <M5Core2.h> |
|||
#include "BLEGamepad.h" |
|||
#include "StickCalibration.h" |
|||
#include "StickPositions.h" |
|||
|
|||
#define REFRESH_TIME 1 |
|||
#define INTRO_TIME 100 |
|||
|
|||
#define MENU_HEIGHT 20 |
|||
#define MENU_MAX_ITEMS 12 |
|||
|
|||
#define DIALOGBOX_FOREGROUND TFT_WHITE |
|||
#define DIALOGBOX_BACKGROUND TFT_NAVY |
|||
|
|||
enum screen_names { |
|||
SCREEN_INTRO, |
|||
SCREEN_MENU, |
|||
SCREEN_MENU_REBOOT, |
|||
SCREEN_MENU_POWEROFF, |
|||
SCREEN_MENU_BLUETOOTH, |
|||
SCREEN_MENU_STICK_CALIBRATION, |
|||
SCREEN_MENU_STICK_POSITIONS, |
|||
SCREEN_MENU_RC_CONTROLLER |
|||
}; |
|||
|
|||
enum menu_items { |
|||
MENU_BLUETOOTH, |
|||
MENU_STICK_CALIBRATION, |
|||
MENU_STICK_POSITIONS, |
|||
MENU_TRIMS, |
|||
MENU_WIFI, |
|||
MENU_RX_BINDING, |
|||
MENU_RF_SETTINGS, |
|||
MENU_RF_SCANNERS, |
|||
MENU_SAVE_SETTINGS, |
|||
MENU_RESET, |
|||
MENU_REBOOT, |
|||
MENU_POWER_OFF |
|||
}; |
|||
|
|||
enum selection_choice_yesno { |
|||
SELECTION_YES, |
|||
SELECTION_NO |
|||
}; |
|||
|
|||
|
|||
class Screen { |
|||
public: |
|||
uint8_t current_screen = SCREEN_INTRO; |
|||
bool updated = false; |
|||
|
|||
Screen(void); |
|||
void begin(void); |
|||
void intro(void); |
|||
void menu(void); |
|||
void menu_reboot(void); |
|||
void menu_poweroff(void); |
|||
void menu_bluetooth(void); |
|||
void update(void); |
|||
|
|||
private: |
|||
uint32_t last_screen_update = 0; |
|||
}; |
|||
|
|||
extern Screen screen; |
|||
#endif |
@ -0,0 +1,228 @@ |
|||
#include "StickCalibration.h"
|
|||
|
|||
StickCalibration::StickCalibration(void) { |
|||
updated = true; |
|||
} |
|||
|
|||
void StickCalibration::begin(void) { |
|||
updated = true; |
|||
|
|||
// Loading the default values...
|
|||
String filename; |
|||
String tmp_str; |
|||
|
|||
filename = "/THROTTLE_MAX"; |
|||
tmp_str = storage.readFile(LITTLEFS, filename); |
|||
throttle_max = tmp_str.toInt(); |
|||
|
|||
filename = "/THROTTLE_MIN"; |
|||
tmp_str = storage.readFile(LITTLEFS, filename); |
|||
throttle_min = tmp_str.toInt(); |
|||
|
|||
filename = "/YAW_MAX"; |
|||
tmp_str = storage.readFile(LITTLEFS, filename); |
|||
yaw_max = tmp_str.toInt(); |
|||
|
|||
filename = "/YAW_MIN"; |
|||
tmp_str = storage.readFile(LITTLEFS, filename); |
|||
yaw_min = tmp_str.toInt(); |
|||
|
|||
filename = "/PITCH_MAX"; |
|||
tmp_str = storage.readFile(LITTLEFS, filename); |
|||
pitch_max = tmp_str.toInt(); |
|||
|
|||
filename = "/PITCH_MIN"; |
|||
tmp_str = storage.readFile(LITTLEFS, filename); |
|||
pitch_min = tmp_str.toInt(); |
|||
|
|||
filename = "/ROLL_MAX"; |
|||
tmp_str = storage.readFile(LITTLEFS, filename); |
|||
roll_max = tmp_str.toInt(); |
|||
|
|||
filename = "/ROLL_MIN"; |
|||
tmp_str = storage.readFile(LITTLEFS, filename); |
|||
roll_min = tmp_str.toInt(); |
|||
} |
|||
|
|||
|
|||
void StickCalibration::show(TFT_eSprite *m) { |
|||
if((uint32_t) (millis() - last_updated) > 50) { |
|||
updated = true; |
|||
} |
|||
|
|||
if(inputs.throttle > throttle_max) throttle_max = inputs.throttle; |
|||
if(inputs.throttle < throttle_min) throttle_min = inputs.throttle; |
|||
if(inputs.yaw > yaw_max) yaw_max = inputs.yaw; |
|||
if(inputs.yaw < yaw_min) yaw_min = inputs.yaw; |
|||
if(inputs.pitch > pitch_max) pitch_max = inputs.pitch; |
|||
if(inputs.pitch < pitch_min) pitch_min = inputs.pitch; |
|||
if(inputs.roll > roll_max) roll_max = inputs.roll; |
|||
if(inputs.roll < roll_min) roll_min = inputs.roll; |
|||
|
|||
if(updated) { |
|||
m->fillRect(0, 0, 320, 240, BLACK); |
|||
m->setTextColor(WHITE, BLACK); |
|||
m->setTextSize(2); |
|||
m->setCursor(0,0); |
|||
m->print(String(inputs.throttle)); |
|||
m->setCursor(0,16); |
|||
m->print(String(inputs.yaw)); |
|||
m->setCursor(0,32); |
|||
m->print(String(inputs.pitch)); |
|||
m->setCursor(0,48); |
|||
m->print(String(inputs.roll)); |
|||
m->setCursor(0,160); |
|||
|
|||
m->setCursor(180,0); |
|||
m->print(String(throttle_min) + " " + String(throttle_max)); |
|||
m->setCursor(180,16); |
|||
m->print(String(yaw_min) + " " + String(yaw_max)); |
|||
m->setCursor(180,32); |
|||
m->print(String(pitch_min) + " " + String(pitch_max)); |
|||
m->setCursor(180,48); |
|||
m->print(String(roll_min) + " " + String(roll_max)); |
|||
m->setCursor(180,160); |
|||
m->print("Keep rolling both sticks to get the minimum and maximum values."); |
|||
|
|||
uint32_t last_millis = millis(); |
|||
|
|||
if((uint32_t)(last_millis - last_button_reset) > 500) { |
|||
m->drawRect(60, 60, 60, 60, TFT_WHITE); // Button RESET
|
|||
m->fillRect(61, 61, 58, 58, TFT_WHITE); // Button RESET
|
|||
} else { |
|||
m->drawRect(60, 60, 60, 60, TFT_WHITE); // Button RESET
|
|||
} |
|||
m->setTextColor(WHITE, BLACK); |
|||
m->setCursor(60, 125); |
|||
m->print("RESET"); |
|||
|
|||
if((uint32_t)(last_millis - last_button_save) > 500) { |
|||
m->drawRect(180, 60, 60, 60, TFT_WHITE); // Button SAVE
|
|||
m->fillRect(181, 61, 58, 58, TFT_WHITE); // Button SAVE
|
|||
} else { |
|||
m->drawRect(180, 60, 60, 60, TFT_WHITE); // Button SAVE
|
|||
} |
|||
m->setTextColor(WHITE, BLACK); |
|||
m->setCursor(180, 125); |
|||
m->print("SAVE"); |
|||
|
|||
m->pushSprite(0,0); |
|||
|
|||
updated = false; |
|||
last_updated = millis(); |
|||
} |
|||
|
|||
|
|||
// Key Input Handler
|
|||
if(M5.BtnC.wasPressed()) { |
|||
screen.current_screen = SCREEN_MENU; |
|||
screen.updated = true; |
|||
} |
|||
|
|||
|
|||
// Handling Inputs
|
|||
Event &e = M5.Buttons.event; |
|||
coordinate = M5.Touch.getPressPoint(); |
|||
|
|||
if((uint32_t)(millis() - touch_time) > 100) { |
|||
if(e & (E_TOUCH)) { |
|||
Serial.printf("E_TOUCH X:%d, Y:%d\r\n", e.to.x, e.to.y); |
|||
if(e.to.x > 60 && e.to.x < 120 && e.to.y > 60 && e.to.y < 120) { |
|||
last_button_reset = millis(); |
|||
Serial.printf("RESET BUTTON\n"); |
|||
reset_values(); |
|||
} else if(e.to.x > 180 && e.to.x < 240 && e.to.y > 60 && e.to.y < 120) { |
|||
last_button_save = millis(); |
|||
Serial.printf("SAVE BUTTON\n"); |
|||
save_values(); |
|||
} |
|||
touch_time = millis(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void StickCalibration::reset_values(void) { |
|||
throttle_max = 0; |
|||
throttle_min = 4096; |
|||
yaw_max = 0; |
|||
yaw_min = 4096; |
|||
pitch_max = 0; |
|||
pitch_min = 4096; |
|||
roll_max = 0; |
|||
roll_min = 4096; |
|||
} |
|||
|
|||
|
|||
void StickCalibration::save_values(void) { |
|||
/*
|
|||
throttle_max = 0; |
|||
throttle_min = 4096; |
|||
yaw_max = 0; |
|||
yaw_min = 4096; |
|||
pitch_max = 0; |
|||
pitch_min = 4096; |
|||
roll_max = 0; |
|||
roll_min = 4096; |
|||
*/ |
|||
String filename; |
|||
|
|||
filename = "/THROTTLE_MAX"; |
|||
storage.writeFile(LITTLEFS, filename, String(throttle_max)); |
|||
|
|||
filename = "/THROTTLE_MIN"; |
|||
storage.writeFile(LITTLEFS, filename, String(throttle_min)); |
|||
|
|||
filename = "/YAW_MAX"; |
|||
storage.writeFile(LITTLEFS, filename, String(yaw_max)); |
|||
|
|||
filename = "/YAW_MIN"; |
|||
storage.writeFile(LITTLEFS, filename, String(yaw_min)); |
|||
|
|||
filename = "/PITCH_MAX"; |
|||
storage.writeFile(LITTLEFS, filename, String(pitch_max)); |
|||
|
|||
filename = "/PITCH_MIN"; |
|||
storage.writeFile(LITTLEFS, filename, String(pitch_min)); |
|||
|
|||
filename = "/ROLL_MAX"; |
|||
storage.writeFile(LITTLEFS, filename, String(roll_max)); |
|||
|
|||
filename = "/ROLL_MIN"; |
|||
storage.writeFile(LITTLEFS, filename, String(roll_min)); |
|||
} |
|||
|
|||
|
|||
void StickCalibration::mydebug(TFT_eSprite *m) { |
|||
if((uint32_t) (millis() - last_updated) > 50) { |
|||
updated = true; |
|||
} |
|||
|
|||
if(updated) { |
|||
m->fillRect(0, 0, 320, 240, BLACK); |
|||
m->setTextColor(WHITE, BLACK); |
|||
m->setTextSize(2); |
|||
m->setCursor(0,0); |
|||
m->print(String(inputs.throttle)); |
|||
m->setCursor(0,16); |
|||
m->print(String(inputs.yaw)); |
|||
m->setCursor(0,32); |
|||
m->print(String(inputs.pitch)); |
|||
m->setCursor(0,48); |
|||
m->print(String(inputs.roll)); |
|||
|
|||
m->pushSprite(0,0); |
|||
|
|||
updated = false; |
|||
last_updated = millis(); |
|||
} |
|||
|
|||
|
|||
// Key Input Handler
|
|||
if(M5.BtnC.wasPressed()) { |
|||
screen.current_screen = SCREEN_MENU; |
|||
screen.updated = true; |
|||
} |
|||
} |
|||
|
|||
StickCalibration stickcalibration; |
@ -0,0 +1,41 @@ |
|||
#ifndef _STICK_CALIBRATION_H |
|||
#define _STICK_CALIBRATION_H |
|||
|
|||
#include <Arduino.h> |
|||
#include <M5Core2.h> |
|||
#include "Inputs.h" |
|||
#include "Screen.h" |
|||
#include "Storage.h" |
|||
|
|||
class StickCalibration { |
|||
public: |
|||
StickCalibration(void); |
|||
void begin(void); |
|||
void mydebug(TFT_eSprite *m); |
|||
void show(TFT_eSprite *m); |
|||
|
|||
void reset_values(void); |
|||
void save_values(void); |
|||
|
|||
bool updated = false; |
|||
bool last_button_reset = 0; |
|||
bool last_button_save = 0; |
|||
|
|||
uint16_t throttle_max = 0; |
|||
uint16_t throttle_min = 4096; |
|||
uint16_t yaw_max = 0; |
|||
uint16_t yaw_min = 4096; |
|||
uint16_t pitch_max = 0; |
|||
uint16_t pitch_min = 4096; |
|||
uint16_t roll_max = 0; |
|||
uint16_t roll_min = 4096; |
|||
|
|||
|
|||
private: |
|||
uint32_t last_updated = 0; |
|||
uint32_t touch_time = 0; |
|||
TouchPoint_t coordinate; |
|||
}; |
|||
|
|||
extern StickCalibration stickcalibration; |
|||
#endif |
@ -0,0 +1,40 @@ |
|||
#include "StickPositions.h"
|
|||
|
|||
StickPositions::StickPositions(void) { |
|||
updated = true; |
|||
} |
|||
|
|||
void StickPositions::begin(void) { |
|||
updated = true; |
|||
} |
|||
|
|||
|
|||
void StickPositions::main(TFT_eSprite *m) { |
|||
if((uint32_t) (millis() - last_updated) > 50) { |
|||
updated = true; |
|||
} |
|||
|
|||
if(updated) { |
|||
m->fillRect(0, 0, 320, 240, BLACK); |
|||
m->setTextColor(WHITE, BLACK); |
|||
m->setTextSize(2); |
|||
m->setCursor(0,0); |
|||
m->print("Stick Positions"); |
|||
|
|||
|
|||
// Update screen
|
|||
m->pushSprite(0,0); |
|||
|
|||
updated = false; |
|||
last_updated = millis(); |
|||
} |
|||
|
|||
|
|||
// Key Input Handler
|
|||
if(M5.BtnC.wasPressed()) { |
|||
screen.current_screen = SCREEN_MENU; |
|||
screen.updated = true; |
|||
} |
|||
} |
|||
|
|||
StickPositions stickpositions; |
@ -0,0 +1,21 @@ |
|||
#ifndef _STICK_POSITIONS_H |
|||
#define _STICK_POSITIONS_H |
|||
|
|||
#include <Arduino.h> |
|||
#include <M5Core2.h> |
|||
#include "Inputs.h" |
|||
#include "Screen.h" |
|||
|
|||
class StickPositions { |
|||
public: |
|||
StickPositions(void); |
|||
void begin(void); |
|||
void main(TFT_eSprite *m); |
|||
bool updated = false; |
|||
|
|||
private: |
|||
uint32_t last_updated = 0; |
|||
}; |
|||
|
|||
extern StickPositions stickpositions; |
|||
#endif |
@ -0,0 +1,220 @@ |
|||
#include "Storage.h"
|
|||
|
|||
// REF:
|
|||
// https://github.com/pbecchi/ESP32-Sprinkler/blob/master/Eeprom_ESP.cpp
|
|||
|
|||
Storage::Storage(void) { |
|||
// Just some initialization
|
|||
} |
|||
|
|||
|
|||
bool Storage::format(void) { |
|||
if(LITTLEFS.format()) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
|
|||
bool Storage::exists(String path) { |
|||
if(LITTLEFS.exists(path)) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
|
|||
bool Storage::begin(void) { |
|||
if(!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED)) { |
|||
Serial.println("LITTLEFS mount failed"); |
|||
return false; |
|||
} |
|||
|
|||
// Demo and checking only
|
|||
Storage::listDir(LITTLEFS, "/", 0); |
|||
|
|||
if(Storage::exists("/MSCoreTX")) { |
|||
Serial.println(F("MSCoreTX FOUND")); |
|||
} else { |
|||
Serial.println(F("MSCoreTX NOT FOUND - Formatting now")); |
|||
Storage::format(); |
|||
Storage::writeFile(LITTLEFS, "/MSCoreTX", ""); |
|||
load_defaults = true; |
|||
} |
|||
|
|||
// Check Total storage
|
|||
Serial.print(F("Storage size: ")); |
|||
Serial.print(LITTLEFS.totalBytes()); |
|||
Serial.println(F(" Bytes")); |
|||
|
|||
Serial.print(F("Storage used: " )); |
|||
Serial.print(LITTLEFS.usedBytes()); |
|||
Serial.println(F(" Bytes")); |
|||
return true; |
|||
} |
|||
|
|||
|
|||
void Storage::listDir(fs::FS &fs, const char * dirname, uint8_t levels){ |
|||
Serial.printf("Listing directory: %s\r\n", dirname); |
|||
|
|||
File root = fs.open(dirname); |
|||
if(!root){ |
|||
Serial.println(F("- failed to open directory")); |
|||
return; |
|||
} |
|||
if(!root.isDirectory()){ |
|||
Serial.println(F(" - not a directory")); |
|||
return; |
|||
} |
|||
|
|||
File file = root.openNextFile(); |
|||
while(file){ |
|||
if(file.isDirectory()){ |
|||
Serial.print(F(" DIR : ")); |
|||
|
|||
#ifdef CONFIG_LITTLEFS_FOR_IDF_3_2
|
|||
Serial.println(file.name()); |
|||
#else
|
|||
Serial.print(file.name()); |
|||
time_t t= file.getLastWrite(); |
|||
struct tm * tmstruct = localtime(&t); |
|||
Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); |
|||
#endif
|
|||
|
|||
if(levels){ |
|||
listDir(fs, file.name(), levels -1); |
|||
} |
|||
} else { |
|||
Serial.print(F(" FILE: ")); |
|||
Serial.print(file.name()); |
|||
Serial.print(F(" SIZE: ")); |
|||
|
|||
#ifdef CONFIG_LITTLEFS_FOR_IDF_3_2
|
|||
Serial.println(file.size()); |
|||
#else
|
|||
Serial.print(file.size()); |
|||
time_t t= file.getLastWrite(); |
|||
struct tm * tmstruct = localtime(&t); |
|||
Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); |
|||
#endif
|
|||
} |
|||
file = root.openNextFile(); |
|||
} |
|||
} |
|||
|
|||
|
|||
String Storage::readFile(fs::FS &fs, String path){ |
|||
// Serial.printf("Reading file: %s\r\n", path);
|
|||
String return_str = ""; |
|||
|
|||
File file = fs.open(path, "r"); |
|||
if(!file || file.isDirectory()){ |
|||
//// Serial.println(F("- failed to open file for reading"));
|
|||
return ""; |
|||
} |
|||
|
|||
while(file.available()){ |
|||
return_str = return_str + String((char)file.read()); |
|||
} |
|||
file.close(); |
|||
|
|||
return return_str; |
|||
} |
|||
|
|||
|
|||
void Storage::appendFile(fs::FS &fs, const char * path, const char * message){ |
|||
Serial.printf("Appending to file: %s\r\n", path); |
|||
|
|||
File file = fs.open(path, FILE_APPEND); |
|||
if(!file){ |
|||
Serial.println("- failed to open file for appending"); |
|||
return; |
|||
} |
|||
if(file.print(message)){ |
|||
Serial.println("- message appended"); |
|||
} else { |
|||
Serial.println("- append failed"); |
|||
} |
|||
file.close(); |
|||
} |
|||
|
|||
|
|||
void Storage::deleteFile(fs::FS &fs, const char * path){ |
|||
Serial.printf("Deleting file: %s\r\n", path); |
|||
if(fs.remove(path)){ |
|||
Serial.println("- file deleted"); |
|||
} else { |
|||
Serial.println("- delete failed"); |
|||
} |
|||
} |
|||
|
|||
|
|||
void Storage::writeFile(fs::FS &fs, String path, String message){ |
|||
File file = fs.open(path, "w+"); |
|||
if(!file){ |
|||
return; |
|||
} |
|||
|
|||
file.println(message); |
|||
file.close(); |
|||
} |
|||
|
|||
|
|||
//void Storage::write_block(uint8_t * data, const char * path, uint32_t len){
|
|||
void Storage::write_block(const void * data, const char * path, uint32_t len){ |
|||
uint32_t i; |
|||
fs::FS &fs = LITTLEFS; |
|||
// Serial.println("write block 1");
|
|||
// Serial.print("FILE: ");
|
|||
// Serial.println(path);
|
|||
// Serial.println(ESP.getFreeHeap());
|
|||
|
|||
File file = fs.open(path, FILE_WRITE); |
|||
// Serial.println("write block 2");
|
|||
if(!file) { |
|||
Serial.println("write_block append failed"); |
|||
return; |
|||
} |
|||
// Serial.println("write block 3");
|
|||
/**
|
|||
for(i = 0; i < len; i++){ |
|||
// EEPROM.write(address+i, data[i]);
|
|||
byte b = *((unsigned char*) data + i); |
|||
Serial.print("Wrote: "); Serial.println(b); |
|||
} |
|||
**/ |
|||
file.write((byte *)&data, sizeof(len)); |
|||
// Serial.println("write block 4");
|
|||
file.close(); |
|||
} |
|||
|
|||
|
|||
void Storage::read_block(void * data, const char * path, uint32_t len){ |
|||
uint32_t i; |
|||
fs::FS &fs = LITTLEFS; |
|||
|
|||
File file = fs.open(path); |
|||
if(!file) { |
|||
Serial.println("- File not exists."); |
|||
return; |
|||
} |
|||
|
|||
// for(i = 0; i < len; i++){
|
|||
/**
|
|||
i = 0; |
|||
while(file.available()) { |
|||
// data[i] = EEPROM.read(address+i);
|
|||
// data[i++] = file.read();
|
|||
// uint8_t b = file.read()
|
|||
*((char *)data + i) = file.read(); |
|||
Serial.print("read_block: "); Serial.println(*((char *)data + i)); |
|||
i++; |
|||
} |
|||
**/ |
|||
file.read((byte *)&data, sizeof(len)); |
|||
file.close(); |
|||
} |
|||
|
|||
Storage storage; |
@ -0,0 +1,42 @@ |
|||
#ifndef _Storage_H |
|||
#define _Storage_H |
|||
|
|||
#include <Arduino.h> |
|||
#include <LITTLEFS.h> |
|||
|
|||
#ifndef CONFIG_LITTLEFS_FOR_IDF_3_2 |
|||
#include <time.h> |
|||
#endif |
|||
|
|||
/* You only need to format LITTLEFS the first time you run a |
|||
test or else use the LITTLEFS plugin to create a partition |
|||
https://github.com/lorol/arduino-esp32littlefs-plugin */ |
|||
|
|||
#define FORMAT_LITTLEFS_IF_FAILED true |
|||
|
|||
|
|||
class Storage { |
|||
|
|||
public: |
|||
Storage(void); |
|||
bool begin(void); |
|||
void listDir(fs::FS &fs, const char * dirname, uint8_t levels); |
|||
String readFile(fs::FS &fs, String path); |
|||
void appendFile(fs::FS &fs, const char * path, const char * message); |
|||
void deleteFile(fs::FS &fs, const char * path); |
|||
void writeFile(fs::FS &fs, String path, String message); |
|||
|
|||
// Emulate EEPROM write to LITTLEFS |
|||
// void write_block(uint8_t * data, const char * path, uint32_t len); |
|||
void write_block(const void * data, const char * path, uint32_t len); |
|||
void read_block(void * data, const char * path, uint32_t len); |
|||
|
|||
bool format(void); |
|||
bool exists(String path); |
|||
bool load_defaults = false; // Default false. Only during reset this will auto set to true |
|||
|
|||
private: |
|||
}; |
|||
|
|||
extern Storage storage; |
|||
#endif |
@ -0,0 +1,225 @@ |
|||
#include "Web.h"
|
|||
|
|||
WebServer server(PORT_HTTP); |
|||
bool update_status = false; |
|||
bool opened = false; |
|||
File root; |
|||
String header_html_str = "<html><meta charset=\"UTF-8\"><title>M5Core2TX</title><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><body><pre>"; |
|||
|
|||
WEB::WEB(void){ |
|||
} |
|||
|
|||
void WEB::begin(void) { |
|||
// Allow CORS
|
|||
server.enableCORS(); |
|||
|
|||
// Index
|
|||
server.on("/", HTTP_GET, []() { |
|||
// File html_handler = LITTLEFS.open("/index.html.gz", FILE_READ);
|
|||
// server.streamFile(html_handler, "text/html");
|
|||
// html_handler.close();
|
|||
server.send(200, "text/html", "MSCoreTX Version 0.1"); |
|||
server.sendHeader("Connection", "close"); |
|||
}); |
|||
|
|||
// Multiple configuration
|
|||
server.on("/set", HTTP_GET, []() { |
|||
bool update_channel = false; |
|||
uint8_t channel_raw = 0; |
|||
|
|||
bool update_sbus_type = false; |
|||
bool sbus_slow = false; |
|||
|
|||
String msg = ""; |
|||
|
|||
// Server parameters
|
|||
for(uint8_t i = 0; i < server.args(); i++) { |
|||
String value = server.arg(i); |
|||
if(server.argName(i) == "channel") { |
|||
channel_raw = value.toInt(); |
|||
update_channel = true; |
|||
} else if(server.argName(i) == "sbus") { |
|||
sbus_slow = true; |
|||
update_sbus_type = true; |
|||
} else if(server.argName(i) == "sbusfast") { |
|||
sbus_slow = false; |
|||
update_sbus_type = true; |
|||
} |
|||
} |
|||
|
|||
/****
|
|||
if(update_channel) { |
|||
// Making sure is channel 1 ~ 125.
|
|||
if(channel_raw > 0 && channel_raw < 126) { |
|||
String filename = "/NRF_CHANNEL"; |
|||
msg = "CHANNEL SET: " + String(channel_raw); |
|||
storage.writeFile(LITTLEFS, filename, String(channel_raw)); |
|||
//// File file = LittleFS.open(filename, "w+");
|
|||
//// file.println(channel_raw);
|
|||
//// file.close();
|
|||
rccontroller.channel = channel_raw; |
|||
tx.setChannel(channel_raw); // Instant Change
|
|||
} else { |
|||
msg = "ERROR UPDATE CHANNEL"; |
|||
} |
|||
} |
|||
****/ |
|||
|
|||
String index_html_str = header_html_str + msg; |
|||
server.sendHeader("Connection", "close"); |
|||
server.send(200, "text/html", index_html_str); |
|||
}); |
|||
|
|||
|
|||
// Getting configuration
|
|||
server.on("/get", HTTP_GET, []() { |
|||
bool update_channel = false; |
|||
uint8_t channel_raw = 0; |
|||
String msg = ""; |
|||
|
|||
// Server parameters
|
|||
for(uint8_t i = 0; i < server.args(); i++) { |
|||
String value = server.arg(i); |
|||
if(server.argName(i) == "channel") { |
|||
String filename = "/NRF_CHANNEL"; |
|||
msg = storage.readFile(LITTLEFS, filename); |
|||
msg = "Channel (From FILE): " + msg; |
|||
} |
|||
} |
|||
|
|||
String index_html_str = header_html_str + msg; |
|||
server.sendHeader("Connection", "close"); |
|||
server.send(200, "text/html", index_html_str); |
|||
}); |
|||
|
|||
|
|||
// Update
|
|||
server.on("/update", HTTP_POST, []() { |
|||
server.sendHeader("Connection", "close"); |
|||
server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK. Device is rebooting"); |
|||
|
|||
if(Update.hasError()) { |
|||
update_status = false; |
|||
// web.set_update_status(false);
|
|||
} else { |
|||
delay(1000); |
|||
ESP.restart(); |
|||
} |
|||
}, []() { |
|||
HTTPUpload& upload = server.upload(); |
|||
if(!update_status) |
|||
update_status = true; |
|||
|
|||
if(upload.status == UPLOAD_FILE_START) { |
|||
Serial.printf("Update: %s\n", upload.filename.c_str()); |
|||
|
|||
// Start with maximum available size
|
|||
if(!Update.begin(UPDATE_SIZE_UNKNOWN)) |
|||
Update.printError(Serial); |
|||
} else if(upload.status == UPLOAD_FILE_WRITE) { |
|||
// Flashing firmware to ESP
|
|||
if( |
|||
Update.write(upload.buf, upload.currentSize) != upload.currentSize |
|||
) |
|||
Update.printError(Serial); |
|||
|
|||
} else if(upload.status == UPLOAD_FILE_END) { |
|||
if(Update.end(true)) |
|||
// True to set the size to the current progress
|
|||
Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); |
|||
else |
|||
Update.printError(Serial); |
|||
} |
|||
}); |
|||
|
|||
// Reset
|
|||
server.on("/reset", HTTP_GET, []() { |
|||
server.sendHeader("Connection", "close"); |
|||
server.send(200, "text/html", "1"); |
|||
storage.deleteFile(LITTLEFS, "/MSCoreTX"); |
|||
delay(1000); |
|||
ESP.restart(); |
|||
}); |
|||
|
|||
// Reboot
|
|||
server.on("/reboot", HTTP_GET, []() { |
|||
server.sendHeader("Connection", "close"); |
|||
server.send(200, "text/html", "1"); |
|||
delay(1000); |
|||
ESP.restart(); |
|||
}); |
|||
|
|||
// Remove files
|
|||
server.on("/erase", HTTP_GET, []() { |
|||
String filename; |
|||
uint8_t detected = 0; |
|||
|
|||
for(uint8_t i = 0; i < server.args(); i++) { |
|||
String value = server.arg(i); |
|||
|
|||
if(server.argName(i) == "filename") { |
|||
filename = value; |
|||
detected++; |
|||
} |
|||
} |
|||
|
|||
server.sendHeader("Connection", "close"); |
|||
if(detected) { |
|||
LITTLEFS.remove("/" + filename); |
|||
server.send(200, "text/plain", "REMOVED - [" + filename + "]"); |
|||
} else { |
|||
server.send(200, "text/plain", "Nothing remove."); |
|||
} |
|||
}); |
|||
|
|||
// TODO: create a way to upload files to the ESP32 and also handling it
|
|||
// REF: https://techtutorialsx.com/2019/03/04/esp32-arduino-serving-bootstrap/
|
|||
// SPIFFS File upload
|
|||
server.on("/webupload", HTTP_POST, []() { |
|||
server.sendHeader("Connection", "close"); |
|||
server.send(200, "text/plain", "webupload Done"); |
|||
}, []() { |
|||
HTTPUpload& upload = server.upload(); |
|||
|
|||
if(opened == false) { |
|||
opened = true; |
|||
root = LITTLEFS.open((String("/") + upload.filename).c_str(), FILE_WRITE); |
|||
if(!root) { |
|||
Serial.printf("Failed to open file for writing. [%s]\n", upload.filename.c_str()); |
|||
return; |
|||
} else { |
|||
Serial.println("Webupload file start."); |
|||
} |
|||
} |
|||
|
|||
if(upload.status == UPLOAD_FILE_START) { |
|||
Serial.printf("webupload: %s\n", upload.filename.c_str()); |
|||
} else if(upload.status == UPLOAD_FILE_WRITE) { |
|||
// Writting to SPIFFS
|
|||
if(root.write(upload.buf, upload.currentSize) != upload.currentSize) { |
|||
Serial.printf("Failed to write. [%s]\n", upload.filename.c_str()); |
|||
return; |
|||
} |
|||
} else if(upload.status == UPLOAD_FILE_END) { |
|||
root.close(); |
|||
opened = false; |
|||
Serial.printf("webupload done. [%s]\n", upload.filename.c_str()); |
|||
|
|||
#ifdef DEBUG_MODE
|
|||
Serial.printf("SPIFFS Files:\n"); |
|||
// list_files();
|
|||
storage.listDir(LITTLEFS, "/", 0); |
|||
#endif
|
|||
|
|||
} |
|||
}); |
|||
|
|||
server.begin(); |
|||
} |
|||
|
|||
void WEB::handler(void) { |
|||
server.handleClient(); |
|||
} |
|||
|
|||
|
|||
WEB web; |
@ -0,0 +1,23 @@ |
|||
#ifndef WEB_H_ |
|||
#define WEB_H_ |
|||
|
|||
#include <Arduino.h> |
|||
#include <LITTLEFS.h> |
|||
#include <WebServer.h> |
|||
#include <Update.h> |
|||
#include "Storage.h" |
|||
|
|||
#define PORT_HTTP 80 |
|||
|
|||
class WEB { |
|||
public: |
|||
WEB(void); |
|||
void begin(void); |
|||
void handler(void); |
|||
|
|||
private: |
|||
}; |
|||
|
|||
extern WEB web; |
|||
#endif |
|||
|
@ -0,0 +1,207 @@ |
|||
#include <Arduino.h>
|
|||
#include "main.h"
|
|||
|
|||
// Screen screen;
|
|||
// Speak speak;
|
|||
// Storage storage;
|
|||
// CustomWiFi customwifi;
|
|||
|
|||
void setup() { |
|||
Serial.begin(115200); |
|||
|
|||
// Just a debug and also trigger the PSRAM prevent reboot
|
|||
Serial.printf("Total heap: %d\n", ESP.getHeapSize()); |
|||
Serial.printf("Free heap: %d\n", ESP.getFreeHeap()); |
|||
Serial.printf("Total PSRAM: %d\n", ESP.getPsramSize()); |
|||
Serial.printf("Free PSRAM: %d\n", ESP.getFreePsram()); |
|||
|
|||
delay(100); |
|||
|
|||
screen.begin(); |
|||
M5.Spk.begin(); |
|||
// M5.Spk.setVolume(100);
|
|||
|
|||
// M5.Spk.DingDong();
|
|||
|
|||
// speak.begin();
|
|||
// speak.dingdong();
|
|||
|
|||
storage.begin(); |
|||
customwifi.begin(); |
|||
inputs.begin(); |
|||
stickcalibration.begin(); |
|||
// tx.begin();
|
|||
web.begin(); |
|||
|
|||
// speak.welcome();
|
|||
|
|||
|
|||
// Task Creation
|
|||
xTaskCreatePinnedToCore( |
|||
taskCharger, |
|||
"TaskCharger", // Name of the process
|
|||
4096, // This stack size can be checked & adjusted by reading the Stack Highwater
|
|||
NULL, |
|||
4, // Priority
|
|||
NULL, |
|||
CPU_1 |
|||
); |
|||
|
|||
xTaskCreatePinnedToCore( |
|||
taskScreen, |
|||
"TaskScreen", // Name of the process
|
|||
4096, // This stack size can be checked & adjusted by reading the Stack Highwater
|
|||
NULL, |
|||
4, // Priority
|
|||
NULL, |
|||
CPU_1 |
|||
); |
|||
|
|||
xTaskCreatePinnedToCore( |
|||
taskWeb, |
|||
"TaskWeb", // Name of the process
|
|||
4096, // This stack size can be checked & adjusted by reading the Stack Highwater
|
|||
NULL, |
|||
4, // Priority
|
|||
NULL, |
|||
CPU_0 |
|||
); |
|||
|
|||
/****
|
|||
xTaskCreatePinnedToCore( |
|||
taskSpeak, |
|||
"TaskSpeak", // Name of the process
|
|||
8192, // This stack size can be checked & adjusted by reading the Stack Highwater
|
|||
NULL, |
|||
5, // Priority
|
|||
NULL, |
|||
CPU_1 |
|||
); |
|||
****/ |
|||
|
|||
xTaskCreatePinnedToCore( |
|||
taskInput, |
|||
"TaskInput", // Name of the process
|
|||
8192, // This stack size can be checked & adjusted by reading the Stack Highwater
|
|||
NULL, |
|||
4, // Priority
|
|||
NULL, |
|||
CPU_0 |
|||
); |
|||
|
|||
xTaskCreatePinnedToCore( |
|||
taskBLE, |
|||
"TaskBLE", // Name of the process
|
|||
8192, // This stack size can be checked & adjusted by reading the Stack Highwater
|
|||
NULL, |
|||
4, // Priority
|
|||
NULL, |
|||
CPU_0 |
|||
); |
|||
} |
|||
|
|||
|
|||
void loop() { |
|||
} |
|||
|
|||
|
|||
void taskScreen(void *pvParameters) { |
|||
(void) pvParameters; |
|||
screen.intro(); |
|||
|
|||
for(;;) { |
|||
screen.update(); |
|||
vTaskDelay(2); |
|||
} |
|||
} |
|||
|
|||
|
|||
void taskCharger(void *pvParameters) { |
|||
(void) pvParameters; |
|||
|
|||
bool charging = false; |
|||
bool high_current_charging = false; |
|||
uint32_t last_charging_time = 0; |
|||
|
|||
for(;;) { |
|||
if(M5.Axp.isCharging()) { |
|||
// Just flipped...
|
|||
if(!charging) { |
|||
charging = true; |
|||
last_charging_time = millis(); |
|||
} else { |
|||
// Only after 5 seconds then put to 1A...
|
|||
if(!high_current_charging) { |
|||
if((uint32_t)(millis() - last_charging_time) > 5000) { |
|||
high_current_charging = true; |
|||
M5.Axp.SetCHGCurrent(M5.Axp.kCHG_1000mA); |
|||
} |
|||
} |
|||
} |
|||
} else { |
|||
if(charging) { |
|||
// Setting back to 100mA
|
|||
charging = false; |
|||
high_current_charging = false; |
|||
last_charging_time = millis(); |
|||
M5.Axp.SetCHGCurrent(M5.Axp.kCHG_100mA); |
|||
} |
|||
} |
|||
|
|||
vTaskDelay(1000); |
|||
} |
|||
} |
|||
|
|||
/***
|
|||
void taskSpeak(void *pvParameters) { |
|||
(void) pvParameters; |
|||
|
|||
for(;;) { |
|||
if(speak.speak_now == true) { |
|||
speak.speak_menu(speak.speak_words); |
|||
speak.speak_now = false; |
|||
} |
|||
|
|||
if(speak.speak_now_other == true) { |
|||
speak.speak_misc(speak.speak_miscs); |
|||
speak.speak_now_other = false; |
|||
} |
|||
|
|||
if(speak.speak_now_trim == true) { |
|||
speak.speak_trims(speak.speak_trim_word); |
|||
speak.speak_now_trim = false; |
|||
} |
|||
|
|||
vTaskDelay(100); |
|||
} |
|||
} |
|||
***/ |
|||
|
|||
void taskWeb(void *pvParameters) { |
|||
(void) pvParameters; |
|||
|
|||
for(;;) { |
|||
web.handler(); |
|||
vTaskDelay(100); |
|||
} |
|||
} |
|||
|
|||
|
|||
void taskInput(void *pvParameters) { |
|||
(void) pvParameters; |
|||
|
|||
for(;;) { |
|||
inputs.read(); |
|||
vTaskDelay(10); |
|||
} |
|||
} |
|||
|
|||
|
|||
void taskBLE(void *pvParameters) { |
|||
(void) pvParameters; |
|||
|
|||
for(;;) { |
|||
ble.update(); |
|||
vTaskDelay(5); |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
#ifndef MAIN_H |
|||
#define MAIN_H |
|||
|
|||
// CPU NAMES |
|||
#define CPU_1 1 |
|||
#define CPU_0 0 |
|||
|
|||
// VERSION |
|||
#define VERSION_MAJOR 1 |
|||
#define VERSION_MINOR 0 |
|||
#define VERSION_BUILD 9 |
|||
|
|||
#include <M5Core2.h> |
|||
#include "Inputs.h" |
|||
#include "BLE.h" |
|||
// #include "Speak.h" |
|||
#include "Screen.h" |
|||
#include "Storage.h" |
|||
#include "CustomWiFi.h" |
|||
#include "Web.h" |
|||
|
|||
// Task Handler |
|||
void taskScreen(void *pvParameters); |
|||
// void taskSpeak(void *pvParameters); |
|||
void taskWeb(void *pvParameters); |
|||
void taskInput(void *pvParameters); |
|||
void taskBLE(void *pvParameters); |
|||
void taskCharger(void *pvParameters); |
|||
|
|||
#endif |
@ -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 |
Write
Preview
Loading…
Cancel
Save
Reference in new issue