Englebert
1 year ago
9 changed files with 1449 additions and 0 deletions
-
31README.md
-
7bert2M_fat12M_16MB.csv
-
39include/README
-
46lib/README
-
27platformio.ini
-
225src/i2c_protocols.h
-
915src/main.cpp
-
148src/main.h
-
11test/README
@ -0,0 +1,31 @@ |
|||
### Structure |
|||
Buttons - |
|||
ADS1115 - Working |
|||
I2C - Working |
|||
Display - |
|||
RubyFPV - DONE |
|||
|
|||
==== RC Controller ==== |
|||
+-----+ +-----+ +-----+ |
|||
| | | | | | |
|||
| | | | | | |
|||
/ +-----+ +-----+ +-----+ \ |
|||
\ +-----+ +-----+ +-----+ / |
|||
| | | | | | |
|||
| | | | | | |
|||
+-----+ +-----+ +-----+ |
|||
14.8V I2C:180 R:200 RUBY |
|||
|
|||
|
|||
==== RC Controller ==== |
|||
* * |
|||
+---+---+ +---+---+ |
|||
* | | | * * | | | * |
|||
+---+---+ +---+---+ |
|||
/ | | | | | | \ |
|||
\ +---+---+ +---+---+ / |
|||
* * |
|||
14.8V I2C:180 R:200 ESP |
|||
|
|||
ToDO: |
|||
Send Gimbal Data by Reading ADS1115 |
@ -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,27 @@ |
|||
; 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 |
|||
robtillaart/ADS1X15@^0.3.10 |
|||
Wire |
|||
|
|||
; For OTA via WiFi |
|||
; upload_protocol = espota |
|||
; upload_port = 172.16.1.1 |
@ -0,0 +1,225 @@ |
|||
#pragma once |
|||
|
|||
#include <stdint.h> |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif |
|||
|
|||
typedef unsigned int u32; |
|||
typedef unsigned short u16; |
|||
typedef unsigned char u8; |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
|||
|
|||
// Protocol is: |
|||
// 1. * Master sends a command to slave (2...N bytes, depending on command type). First byte of all commands is the command start flag; |
|||
// 2. * Slave responds with the right answer based on the command (1...N bytes, depending on command response type); |
|||
// 3. * Repeat the steps above; |
|||
// That means: slave device (Ruby extension device) just waits on I2C for commands from master (Ruby main controller) and responds to those commands as needed. |
|||
// Note!!! All commands and responses have 1 extra byte at the end as the CRC |
|||
// Use the example function at the end of file to compute the CRC |
|||
// Note!!! Always check the CRC value when you get something. Ignore the commands with bad CRC. There can always be noise or bad I2C devices on the I2C bus. |
|||
|
|||
|
|||
#define I2C_DEVICE_DEFAULT_ADDRESS 0x60 |
|||
#define I2C_DEVICE_MIN_ADDRESS_RANGE 0x60 |
|||
#define I2C_DEVICE_MAX_ADDRESS_RANGE 0x6F |
|||
|
|||
// Note: the address [I2C_DEVICE_MAX_ADDRESS_RANGE-2] is used by the Ruby Pico Extender. If you use one, your plugin should use a different address. |
|||
|
|||
#define I2C_PROTOCOL_STRING_LENGTH 24 |
|||
|
|||
// Capabilities flags supported by the slave Ruby device; these capabilities will be queried by the Ruby controller and will be reported back to Ruby controller using the I2C interface; |
|||
|
|||
#define I2C_CAPABILITY_FLAG_SPI ((u16)(((u16)0x01)<<1)) // Set if the slave device does support SPI communication with the Ruby controller (if not, only I2C is used) |
|||
#define I2C_CAPABILITY_FLAG_BUTTONS ((u16)(((u16)0x01)<<2)) // Set if the slave device has buttons (for UI navigation in Ruby); |
|||
#define I2C_CAPABILITY_FLAG_ROTARY ((u16)(((u16)0x01)<<3)) // Set if the slave device has rotary encoder (for UI navigation/Camera control); |
|||
#define I2C_CAPABILITY_FLAG_ROTARY2 ((u16)(((u16)0x01)<<4)) // Set if the slave device has the secondary rotary encoder (for UI navigation/Camera control); |
|||
#define I2C_CAPABILITY_FLAG_LEDS ((u16)(((u16)0x01)<<5)) // Set if the slave device has LEDs to be controlled by the Ruby controller; |
|||
#define I2C_CAPABILITY_FLAG_RC_INPUT ((u16)(((u16)0x01)<<6)) // Set if the slave device has RC input hardware; |
|||
#define I2C_CAPABILITY_FLAG_RC_OUTPUT ((u16)(((u16)0x01)<<7)) // Set if the slave device should output RC frames to the FC; |
|||
#define I2C_CAPABILITY_FLAG_FLIGHT_CONTROL ((u16)(((u16)0x01)<<8)) // Set if the slave device wants to send flight commands to the vehicle; |
|||
#define I2C_CAPABILITY_FLAG_CAMERA_CONTROL ((u16)(((u16)0x01)<<9)) // Set if the slave device wants to send camera commands (brightness, contrast, etc); |
|||
#define I2C_CAPABILITY_FLAG_SOUNDS ((u16)(((u16)0x01)<<10)) // Set if the slave device can play sounds (alarms); |
|||
|
|||
#define I2C_COMMAND_START_FLAG 0xFF |
|||
|
|||
#define I2C_COMMAND_ID_GET_FLAGS 0x01 |
|||
// Get flags: master asks the slave for the capabilities it supports; |
|||
// Master sends: 1 byte: command id (I2C_COMMAND_ID_GET_FLAGS) |
|||
// Slave responds: 2 bytes: capabilities (flags as OR-ed bits) supported by the I2C device; |
|||
|
|||
|
|||
#define I2C_COMMAND_ID_GET_VERSION 0x02 |
|||
// Get flags: master asks the slave for the version; |
|||
// Master sends: 1 byte: command id (I2C_COMMAND_ID_GET_VERSION); |
|||
// Slave responds: 1 byte: version of the software on the I2C slave device; |
|||
|
|||
|
|||
#define I2C_COMMAND_ID_GET_NAME 0x03 |
|||
// Get name: master asks slave for the device name, to be used in the user interface; |
|||
// Master sends: 1 byte: command id; |
|||
// Slave responds: I2C_PROTOCOL_STRING_LENGTH bytes: null terminated string, padded with 0 up to I2C_PROTOCOL_STRING_LENGTH bytes; |
|||
|
|||
|
|||
#define I2C_COMMAND_ID_SET_ADDRESS 0x04 |
|||
// Set address: master asks slave to set it's address to a custom one, to avoid conflicts |
|||
// Master sends: 2 bytes: command id and the new I2C address to be used by slave device; |
|||
// Slave responds: 1 byte: 0 - ok, 1 - error |
|||
|
|||
|
|||
#define I2C_COMMAND_ID_GET_BUTTONS_EVENTS 0x10 |
|||
// Get buttons: master asks slave if any buttons where pressed; |
|||
// Master sends: 1 byte: command id; |
|||
// Slave responds: 4 bytes: |
|||
// first 2 bytes: each bit represents 1 if a button was pressed, for a possible of 16 buttons on the device; |
|||
// last 2 bytes: each bit represents 1 if a button was long pressed, for a possible of 16 buttons on the device; |
|||
// bit 0 - Menu/Ok button |
|||
// bit 1 - Cancel button |
|||
// bit 2 - Plus button |
|||
// bit 3 - Minus button |
|||
// bit 4 - QA1 button |
|||
// bit 5 - QA2 button |
|||
// bit 6 - QA3 button |
|||
// bit 7 - Action Plus button |
|||
// bit 8 - Action Minus button |
|||
// bit 9... - future use |
|||
|
|||
|
|||
|
|||
#define I2C_COMMAND_ID_GET_ROTARY_EVENTS 0x11 |
|||
#define I2C_COMMAND_ID_GET_ROTARY_EVENTS2 0x12 |
|||
// Get rotary encoder events (for main and secondary rotary encoders, if present); |
|||
// Master asks slave if any rotary encoder events (first or secondary rotary encoder) took place. |
|||
// Master sends: 1 byte: command id; |
|||
// Slave responds: 1 byte: each bit represents: |
|||
// bit 0: rotary encoder was pressed; |
|||
// bit 1: rotary encoder was long pressed; |
|||
// bit 2: rotary encoder was rotated CCW; |
|||
// bit 3: rotary encoder was rotated CW; |
|||
// bit 4: rotary encoder was rotated fast CCW; |
|||
// bit 5: rotary encoder was rotated fast CW; |
|||
|
|||
|
|||
#define I2C_COMMAND_ID_SET_RC_INPUT_FLAGS 0x20 |
|||
#define I2C_COMMAND_RC_FLAG_SBUS 1 |
|||
#define I2C_COMMAND_RC_FLAG_IBUS (1<<1) |
|||
#define I2C_COMMAND_RC_FLAG_PPM (1<<2) |
|||
#define I2C_COMMAND_RC_FLAG_INVERT_UART (1<<4) |
|||
// Set RC Input flags: master tells the slave what RC protocol to read and if the RC input UART should be inverted or not (SBUS should be inverted). |
|||
// Master sends: 2 bytes: command id and the RC input flags: |
|||
// bit 0-4: RC protocol: 1 - parse SBUS RC input; 2 - parse IBUS RC input; 4 - parse PPM RC input; |
|||
// bit 4: 0 - non inverted UART, 1 - invert UART; |
|||
// Slave responds: 1 byte: 0 - ok, 1 - error |
|||
|
|||
#define I2C_COMMAND_ID_RC_GET_CHANNELS 0x21 |
|||
// Gets the current RC channels values from the slave device; |
|||
// Master sends: 1 byte: command id; |
|||
// Slave responds: 26 bytes: 1 byte flags, 1 byte frame number, 24 bytes for RC channels values (16 channels, 12 bits per channel, for 0...4095 posible values). |
|||
// byte 0: flags: bit 0 is set if RC input failsafe event occured on the slave device. |
|||
// byte 1: frame number: monotonically increasing on each received RC frame |
|||
// byte 1-16 - LSBits (8 bits) of each channel, from ch 1 to ch 16; |
|||
// byte 17-24 - MSBits (4 bits) of each channel, from ch 1 to ch 16; |
|||
// Unused channels should be set to zero. |
|||
|
|||
|
|||
#define I2C_COMMAND_ID_SET_RC_OUTPUT_FLAGS 0x30 |
|||
// Set RC Output flags: master tells the slave what RC protocol to generate and if the RC output UART should be inverted or not (SBUS should be inverted). |
|||
// Master sends: 2 bytes: command id and the RC output flags (same bits as RC input flags): |
|||
// bit 0-4: RC protocol: 1 - SBUS RC output; 2 - IBUS RC output; 4 - PPM RC output; |
|||
// bit 4: 0 - non inverted UART, 1 - invert UART; |
|||
// Slave responds: 1 byte: 0 - ok, 1 - error |
|||
|
|||
|
|||
#define I2C_COMMAND_ID_RC_SET_CHANNELS 0x31 |
|||
// Sets the current RC channels values to the slave device; |
|||
// Master sends: 26 bytes: 1 byte command id, 1 byte flags, 24 bytes: RC channels values (16 channels, 12 bits per channel, for 0...4095 posible values). |
|||
// byte 0: command id (this one); |
|||
// byte 1: flags: bit 0: set if failsafe should be signaled by the slave device; not set otherways; |
|||
// byte 2-17 - LSBits (8 bits) of each channel, from ch 1 to ch 16; |
|||
// byte 18-25 - MSBits (4 bits) of each channel, from ch 1 to ch 16; |
|||
// Unused channels should be set to zero. |
|||
|
|||
|
|||
#define I2C_COMMAND_ID_FLIGHT_CTRL_QUERY_ARM 0x40 |
|||
// Ask the slave device if the vehicle should receive the arm or disarm command |
|||
// Master sends: 1 byte: command id; |
|||
// Slave responds: 1 byte: |
|||
// bit 0: 0 - no change; 1 - has new arm state |
|||
// bit 1: 0 - disarm; 1 - arm |
|||
|
|||
#define I2C_COMMAND_ID_FLIGHT_CTRL_QUERY_MODE 0x41 |
|||
// Ask the slave device if the vehicle should receive a new flight mode |
|||
// Master sends: 1 byte: command id; |
|||
// Slave responds: 1 byte: |
|||
// bit 0: 0 - no change; 1 - has new flight mode; |
|||
// bit 1..7: new flight mode as defined by ArduPilot; |
|||
|
|||
|
|||
#define I2C_COMMAND_ID_CAMERA_CTRL_QUERY 0x50 |
|||
// Ask the slave device if they have any pending camera params changes (or wants to change something); |
|||
// Master sends: 1 byte: command id; |
|||
// Slave responds: 1 byte: |
|||
// bit 0: 0 - no change; 1 - wants to do some changes |
|||
|
|||
#define I2C_COMMAND_ID_CAMERA_CTRL_GET_PARAMS 0x51 |
|||
// Ask the slave device for the new camera params; |
|||
// Master sends: 1 byte: command id; |
|||
// Slave responds: 4 bytes: |
|||
// byte 0: brightness (0..100) |
|||
// byte 1: contrast (0..100) |
|||
// byte 2: saturation (0..100) |
|||
// byte 3: sharpness (0..100) |
|||
|
|||
#define I2C_COMMAND_ID_VIDEO_GET_QUALITY 0x55 |
|||
// Ask the slave device if they want to change the video quality |
|||
// Master sends: 1 byte: command id; |
|||
// Slave responds: 1 byte: |
|||
// bit 0: 0 - no (video quality is auto, decided by Ruby), 0-100 sets a custom video quality (0=lowest quality) |
|||
|
|||
|
|||
#define I2C_COMMAND_ID_PLAY_SOUND_ALARM 0x60 |
|||
// Ask the slave to play a particular sound |
|||
// Master sends: 2 bytes: command id; sound id: |
|||
// 1 - battery alarm; |
|||
// 2 - arm alarm; |
|||
// 3 - disarm alarm; |
|||
// Slave responds: 1 byte: |
|||
// bit 0: 0 - failed; 1 - succeeded; |
|||
|
|||
|
|||
// CRC table used for CRC calculations |
|||
const u8 s_crc_i2c_table[256] = { |
|||
0x00,0x31,0x62,0x53,0xC4,0xF5,0xA6,0x97,0xB9,0x88,0xDB,0xEA,0x7D,0x4C,0x1F,0x2E, |
|||
0x43,0x72,0x21,0x10,0x87,0xB6,0xE5,0xD4,0xFA,0xCB,0x98,0xA9,0x3E,0x0F,0x5C,0x6D, |
|||
0x86,0xB7,0xE4,0xD5,0x42,0x73,0x20,0x11,0x3F,0x0E,0x5D,0x6C,0xFB,0xCA,0x99,0xA8, |
|||
0xC5,0xF4,0xA7,0x96,0x01,0x30,0x63,0x52,0x7C,0x4D,0x1E,0x2F,0xB8,0x89,0xDA,0xEB, |
|||
0x3D,0x0C,0x5F,0x6E,0xF9,0xC8,0x9B,0xAA,0x84,0xB5,0xE6,0xD7,0x40,0x71,0x22,0x13, |
|||
0x7E,0x4F,0x1C,0x2D,0xBA,0x8B,0xD8,0xE9,0xC7,0xF6,0xA5,0x94,0x03,0x32,0x61,0x50, |
|||
0xBB,0x8A,0xD9,0xE8,0x7F,0x4E,0x1D,0x2C,0x02,0x33,0x60,0x51,0xC6,0xF7,0xA4,0x95, |
|||
0xF8,0xC9,0x9A,0xAB,0x3C,0x0D,0x5E,0x6F,0x41,0x70,0x23,0x12,0x85,0xB4,0xE7,0xD6, |
|||
0x7A,0x4B,0x18,0x29,0xBE,0x8F,0xDC,0xED,0xC3,0xF2,0xA1,0x90,0x07,0x36,0x65,0x54, |
|||
0x39,0x08,0x5B,0x6A,0xFD,0xCC,0x9F,0xAE,0x80,0xB1,0xE2,0xD3,0x44,0x75,0x26,0x17, |
|||
0xFC,0xCD,0x9E,0xAF,0x38,0x09,0x5A,0x6B,0x45,0x74,0x27,0x16,0x81,0xB0,0xE3,0xD2, |
|||
0xBF,0x8E,0xDD,0xEC,0x7B,0x4A,0x19,0x28,0x06,0x37,0x64,0x55,0xC2,0xF3,0xA0,0x91, |
|||
0x47,0x76,0x25,0x14,0x83,0xB2,0xE1,0xD0,0xFE,0xCF,0x9C,0xAD,0x3A,0x0B,0x58,0x69, |
|||
0x04,0x35,0x66,0x57,0xC0,0xF1,0xA2,0x93,0xBD,0x8C,0xDF,0xEE,0x79,0x48,0x1B,0x2A, |
|||
0xC1,0xF0,0xA3,0x92,0x05,0x34,0x67,0x56,0x78,0x49,0x1A,0x2B,0xBC,0x8D,0xDE,0xEF, |
|||
0x82,0xB3,0xE0,0xD1,0x46,0x77,0x24,0x15,0x3B,0x0A,0x59,0x68,0xFF,0xCE,0x9D,0xAC }; |
|||
|
|||
// CRC-8 function: |
|||
|
|||
/* |
|||
u8 calculate_i2c_crc(u8* pData, int iLength) |
|||
{ |
|||
u8 uCrc = 0xFF; |
|||
if ( NULL == pData || iLength <= 0 ) |
|||
return uCrc; |
|||
for ( int i = 0; i < iLength; i++) |
|||
uCrc = s_crc_i2c_table[pData[i] ^ uCrc]; |
|||
return uCrc; |
|||
} |
|||
*/ |
@ -0,0 +1,915 @@ |
|||
#include <Arduino.h>
|
|||
#include "main.h"
|
|||
|
|||
TFT_eSprite mainscreen_buffer = TFT_eSprite(&M5.Lcd); // Handling Display
|
|||
ADS1115 ADS(0x48, &Wire1); // Hardware ADC for Gimbals
|
|||
|
|||
void setup() { |
|||
Serial.begin(115200); |
|||
|
|||
// Initialize variables
|
|||
init_var(); |
|||
|
|||
// Starting up display
|
|||
M5.begin(true, true, true, false); // Initialize M5Core2
|
|||
M5.Lcd.fillScreen(BLACK); |
|||
mainscreen_buffer.createSprite(320, 240); |
|||
|
|||
// REF: https://github.com/espressif/arduino-esp32/blob/master/docs/source/api/i2c.rst#i2c-begin
|
|||
Wire.onReceive(onI2C0Receive); |
|||
Wire.onRequest(onI2C0Request); |
|||
bool ready_i2c = Wire.begin(0x6D, I2C_SDA, I2C_SCL, 100000); |
|||
|
|||
if(!ready_i2c) { |
|||
Serial.println("I2C Slave init ERROR."); |
|||
delay(5000); |
|||
ESP.restart(); |
|||
} else { |
|||
i2c_status = true; |
|||
} |
|||
|
|||
// Init Hardware ADC
|
|||
if(!ADS.begin(ADS1115_SCL, ADS1115_SDA)) { |
|||
Serial.println("ADS1115 ERROR!"); |
|||
// delay(5000);
|
|||
} |
|||
|
|||
// Setting WiFi
|
|||
wifi_setup(); |
|||
|
|||
// Setup OTA
|
|||
ota_setup(); |
|||
|
|||
// Setting up Tasks
|
|||
xTaskCreatePinnedToCore( |
|||
taskSystem, |
|||
"TaskSystem", // Name of the process
|
|||
16384, // This stack size can be checked & adjusted by reading the Stack Highwater
|
|||
NULL, |
|||
5, // Priority
|
|||
NULL, |
|||
CPU_0 |
|||
); |
|||
|
|||
xTaskCreatePinnedToCore( |
|||
taskDisplay, |
|||
"TaskDisplay", // 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, |
|||
5, // Priority
|
|||
NULL, |
|||
CPU_0 |
|||
); |
|||
} |
|||
|
|||
void ota_setup(void) { |
|||
ArduinoOTA.begin(); |
|||
|
|||
// Set Port to 8266
|
|||
ArduinoOTA.setPort(8266); |
|||
|
|||
ArduinoOTA.onStart([]() { |
|||
String type; |
|||
if(ArduinoOTA.getCommand() == U_FLASH) { |
|||
type = "sketch"; |
|||
} else { // U_FS
|
|||
type = "filesystem"; |
|||
} |
|||
|
|||
// NOTE: if updating FS this would be the place to unmount FS using FS.end()
|
|||
if(DEBUG) Serial.println("Start updating " + type); |
|||
}); |
|||
ArduinoOTA.onEnd([]() { |
|||
OTA_NOW = true; |
|||
if(DEBUG) Serial.println("\nEnd"); |
|||
ESP.restart(); |
|||
}); |
|||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { |
|||
OTA_NOW = true; |
|||
if(DEBUG) Serial.printf("Progress: %u%%\r", (progress / (total / 100))); |
|||
}); |
|||
ArduinoOTA.onError([](ota_error_t error) { |
|||
if(DEBUG) Serial.printf("Error[%u]: ", error); |
|||
if(error == OTA_AUTH_ERROR) { |
|||
if(DEBUG) Serial.println("Auth Failed"); |
|||
} else if(error == OTA_BEGIN_ERROR) { |
|||
if(DEBUG) Serial.println("Begin Failed"); |
|||
} else if(error == OTA_CONNECT_ERROR) { |
|||
if(DEBUG) Serial.println("Connect Failed"); |
|||
} else if(error == OTA_RECEIVE_ERROR) { |
|||
if(DEBUG) Serial.println("Receive Failed"); |
|||
} else if(error == OTA_END_ERROR) { |
|||
if(DEBUG) Serial.println("End Failed"); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
void wifi_setup(void) { |
|||
IPAddress local_ip(172,16,1,1); |
|||
IPAddress gateway(172,16,1,1); |
|||
IPAddress subnet(255,255,255,0); |
|||
|
|||
WiFi.softAP(WIFI_SSID, WIFI_PASSPHRASE); |
|||
delay(100); |
|||
|
|||
WiFi.softAPConfig(local_ip, gateway, subnet); |
|||
delay(100); |
|||
IP = WiFi.softAPIP(); |
|||
} |
|||
|
|||
void onI2C0Request(void) { |
|||
} |
|||
|
|||
void onI2C0Receive(int len) { |
|||
// Prevent interrupted during OTA
|
|||
if(OTA_NOW) |
|||
return; |
|||
|
|||
int ch = Wire.read(); |
|||
if(!header_detected) { |
|||
if(ch == 0xFF) { |
|||
header_detected = true; |
|||
} else { |
|||
return; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Data...
|
|||
i2c_received[i2c_received_length++] = ch; |
|||
|
|||
/***
|
|||
All commands sent by Ruby are in this format: |
|||
1 byte: start command header, always 0xFF; |
|||
2 byte: command id, see below; |
|||
N bytes: command data, size depending on command; |
|||
1 byte: CRC; |
|||
***/ |
|||
/***
|
|||
for(int i = 0; i <=i2c_received_length; i++) { |
|||
Serial.print(i2c_received[i], HEX); Serial.print(" "); |
|||
} |
|||
Serial.println(); |
|||
***/ |
|||
if( |
|||
i2c_received[1] == I2C_COMMAND_ID_GET_FLAGS || |
|||
i2c_received[1] == I2C_COMMAND_ID_GET_NAME || |
|||
i2c_received[1] == I2C_COMMAND_ID_GET_BUTTONS_EVENTS || |
|||
i2c_received[1] == I2C_COMMAND_ID_GET_ROTARY_EVENTS || |
|||
i2c_received[1] == I2C_COMMAND_ID_RC_GET_CHANNELS |
|||
) { |
|||
|
|||
if(i2c_received_length == 2) { |
|||
parse_i2c_command(); |
|||
|
|||
// Reset back to starting point.
|
|||
i2c_received_length = 0; |
|||
header_detected = false; |
|||
} |
|||
} else if( |
|||
i2c_received[1] == I2C_COMMAND_ID_SET_RC_INPUT_FLAGS |
|||
) { |
|||
if(i2c_received_length == 3) { |
|||
parse_i2c_command(); |
|||
|
|||
// Reset back to starting point.
|
|||
i2c_received_length = 0; |
|||
header_detected = false; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void parse_i2c_command(void) { |
|||
uint8_t crc = 0xFF; |
|||
|
|||
|
|||
// Firewall (CRC)
|
|||
/**
|
|||
for(uint16_t i = 0; i < i2c_received_length - 1; i++) |
|||
crc = s_crc_i2c_table[i2c_received[i] ^ crc]; |
|||
|
|||
if(crc != i2c_received[i2c_received_length]) { |
|||
Serial.printf("Invalid Command CRC: RX = 0x%2X # CRC = 0x%2X # RET = 0x%2X\n", i2c_received[1], i2c_received[i2c_received_length], crc); |
|||
return; |
|||
} |
|||
**/ |
|||
i2c_send_length = 0; |
|||
|
|||
|
|||
// 0x01 - I2C_COMMAND_ID_GET_FLAGS
|
|||
if(i2c_received[1] == I2C_COMMAND_ID_GET_FLAGS) { |
|||
if(DEBUG) Serial.println("Get flags command"); |
|||
|
|||
i2c_send[0] = |
|||
I2C_CAPABILITY_FLAG_RC_INPUT | |
|||
I2C_CAPABILITY_FLAG_ROTARY | |
|||
I2C_CAPABILITY_FLAG_BUTTONS; |
|||
i2c_send[1] = 0; |
|||
|
|||
i2c_send_length = 2; |
|||
|
|||
// 0x20 - I2C_COMMAND_ID_SET_RC_INPUT_FLAGS
|
|||
} else if(i2c_received[1] == I2C_COMMAND_ID_SET_RC_INPUT_FLAGS) { |
|||
|
|||
if(DEBUG) Serial.println("Set RC Input Flags"); |
|||
uint8_t flags = i2c_received[2]; |
|||
if(flags & I2C_COMMAND_RC_FLAG_SBUS) |
|||
if(DEBUG) Serial.print("SBUS"); |
|||
if(flags & I2C_COMMAND_RC_FLAG_IBUS) |
|||
if(DEBUG) Serial.print("IBUS"); |
|||
if(flags & I2C_COMMAND_RC_FLAG_PPM) |
|||
if(DEBUG) Serial.print("PPM"); |
|||
|
|||
if(flags & I2C_COMMAND_RC_FLAG_INVERT_UART) |
|||
if(DEBUG) Serial.println(" INVERTED"); |
|||
else |
|||
if(DEBUG) Serial.println(); |
|||
|
|||
i2c_send[0] = 0; |
|||
i2c_send_length = 1; |
|||
|
|||
// 0x21 - I2C_COMMAND_ID_RC_GET_CHANNELS
|
|||
} else if(i2c_received[1] == I2C_COMMAND_ID_RC_GET_CHANNELS) { |
|||
|
|||
/**
|
|||
Gets the current RC channels values from the slave device; |
|||
Master sends: 1 byte: command id; |
|||
Slave responds: 26 bytes: 1 byte flags, 1 byte frame number, 24 bytes for RC channels values (16 channels, 12 bits per channel, for 0...4095 posible values). |
|||
byte 0: flags: bit 0 is set if RC input failsafe event occured on the slave device. |
|||
byte 1: frame number: monotonically increasing on each received RC frame |
|||
byte 2 -17 - LSBits (8 bits) of each channel, from ch 1 to ch 16; |
|||
byte 18-25 - MSBits (4 bits) of each channel, from ch 1 to ch 16; |
|||
Unused channels should be set to zero. |
|||
**/ |
|||
// Serial.println("RC get channels");
|
|||
i2c_send[0] = 0; // SBUS is Fail Safe.. temporary
|
|||
i2c_send[1] = frame_count; |
|||
|
|||
// Channel [ 1 ~ 16 ] LSB @ byte 2 ~ 17
|
|||
for(uint8_t i = 0; i < 16; i++) |
|||
i2c_send[2 + i] = sbusData.ch[i] & 0xFF; |
|||
|
|||
// Channel [ 1 ~ 16 ] MSB @ byte 18 ~ 25
|
|||
for(uint8_t i = 0; i < 16; i++) { |
|||
if((i % 2) == 0) { |
|||
i2c_send[18 + i/2] = (sbusData.ch[i] >> 8) & 0x0F; |
|||
} else { |
|||
i2c_send[18 + i/2] = (i2c_send[18 + i/2]) | ((sbusData.ch[i] >> 8) & 0x0F) << 4; |
|||
} |
|||
} |
|||
|
|||
i2c_send_length = 26; |
|||
|
|||
// 0x03 - I2C_COMMAND_ID_GET_NAME
|
|||
} else if(i2c_received[1] == I2C_COMMAND_ID_GET_NAME) { |
|||
/**
|
|||
Get name: master asks slave for the device name, to be used in the user interface; |
|||
Master sends: 1 byte: command id; |
|||
Slave responds: I2C_PROTOCOL_STRING_LENGTH bytes: null terminated string, padded with 0 up to I2C_PROTOCOL_STRING_LENGTH bytes; |
|||
**/ |
|||
uint8_t device_name[] = "ESP32 Extender"; |
|||
for(uint8_t i = 0; i < 14; i++) { |
|||
i2c_send[i] = device_name[i]; |
|||
} |
|||
|
|||
for(uint8_t i = 14; i < I2C_PROTOCOL_STRING_LENGTH; i++) { |
|||
i2c_send[i] = 0; |
|||
} |
|||
i2c_send_length = I2C_PROTOCOL_STRING_LENGTH; |
|||
|
|||
// 0x11 - I2C_COMMAND_ID_GET_ROTARY_EVENTS
|
|||
} else if(i2c_received[1] == I2C_COMMAND_ID_GET_ROTARY_EVENTS) { |
|||
/**
|
|||
Get rotary encoder events (for main and secondary rotary encoders, if present); |
|||
Master asks slave if any rotary encoder events (first or secondary rotary encoder) took place. |
|||
Master sends: 1 byte: command id; |
|||
Slave responds: 1 byte: each bit represents: |
|||
bit 0: rotary encoder was pressed; |
|||
bit 1: rotary encoder was long pressed; |
|||
bit 2: rotary encoder was rotated CCW; |
|||
bit 3: rotary encoder was rotated CW; |
|||
bit 4: rotary encoder was rotated fast CCW; |
|||
bit 5: rotary encoder was rotated fast CW; |
|||
**/ |
|||
uint8_t rotary_encoder = 0; |
|||
i2c_send[0] = rotary_encoder; |
|||
i2c_send_length = 1; |
|||
|
|||
// 0x10 - I2C_COMMAND_ID_GET_BUTTONS_EVENTS
|
|||
} else if(i2c_received[1] == I2C_COMMAND_ID_GET_BUTTONS_EVENTS) { |
|||
/**
|
|||
Get buttons: master asks slave if any buttons where pressed; |
|||
Master sends: 1 byte: command id; |
|||
Slave responds: 4 bytes: |
|||
first 2 bytes: each bit represents 1 if a button was pressed, for a possible of 16 buttons on the device; |
|||
last 2 bytes: each bit represents 1 if a button was long pressed, for a possible of 16 buttons on the device; |
|||
bit 0 - Menu/Ok button |
|||
bit 1 - Cancel button |
|||
bit 2 - Plus button |
|||
bit 3 - Minus button |
|||
bit 4 - QA1 button |
|||
bit 5 - QA2 button |
|||
bit 6 - QA3 button |
|||
bit 7 - Action Plus button |
|||
bit 8 - Action Minus button |
|||
bit 9... - future use |
|||
**/ |
|||
|
|||
for(uint8_t i = 0; i < 8; i++) |
|||
rubybutton[i] = 0; |
|||
|
|||
// MENU
|
|||
if((uint32_t)(millis() - rubybutton_last[0]) > rubybutton_delay) { |
|||
rubybutton[0] = button1; |
|||
|
|||
// Reset after read
|
|||
button1 = 0; |
|||
|
|||
// digitalWrite(BUTTON_MENU, rubybutton[0]);
|
|||
rubybutton_last[0] = millis(); |
|||
} |
|||
|
|||
// CANCEL
|
|||
if((uint32_t)(millis() - rubybutton_last[1]) > rubybutton_delay) { |
|||
rubybutton[1] = button2; |
|||
|
|||
// Reset after read
|
|||
button2 = 0; |
|||
|
|||
// digitalWrite(BUTTON_BACK, rubybutton[1]);
|
|||
rubybutton_last[1] = millis(); |
|||
} |
|||
|
|||
// PLUS
|
|||
if((uint32_t)(millis() - rubybutton_last[2]) > rubybutton_delay) { |
|||
rubybutton[2] = button6; |
|||
|
|||
// Reset after read
|
|||
button6 = 0; |
|||
|
|||
// digitalWrite(BUTTON_UP, rubybutton[2]);
|
|||
rubybutton_last[2] = millis(); |
|||
} |
|||
|
|||
// MINUS
|
|||
if((uint32_t)(millis() - rubybutton_last[3]) > rubybutton_delay) { |
|||
rubybutton[3] = button7; |
|||
|
|||
// Reset after read
|
|||
button7 = 0; |
|||
|
|||
// digitalWrite(BUTTON_DOWN, rubybutton[3]);
|
|||
rubybutton_last[3] = millis(); |
|||
} |
|||
|
|||
i2c_send[0] = |
|||
rubybutton[0] | // Menu/Ok button
|
|||
rubybutton[1] << 1 | // Cancel button
|
|||
rubybutton[2] << 2 | // Plus button
|
|||
rubybutton[3] << 3 ; // Minus button
|
|||
i2c_send[1] = 0; |
|||
i2c_send[2] = 0; |
|||
i2c_send[3] = 0; |
|||
i2c_send_length = 4; |
|||
|
|||
// Others undefined
|
|||
} else { |
|||
|
|||
if(DEBUG) Serial.print("Others: "); |
|||
for(uint16_t i = 0; i < i2c_received_length; i++) |
|||
if(DEBUG) Serial.printf("0x%2X ", i2c_received[i]); |
|||
if(DEBUG) Serial.println(); |
|||
|
|||
} |
|||
|
|||
// Calculating CRC
|
|||
if(i2c_send_length > 0) { |
|||
crc = 0xFF; |
|||
for(uint16_t i = 0; i < i2c_send_length; i++) { |
|||
crc = s_crc_i2c_table[i2c_send[i] ^ crc]; |
|||
} |
|||
|
|||
i2c_send[i2c_send_length++] = crc; |
|||
|
|||
Wire.slaveWrite(i2c_send, i2c_send_length); |
|||
i2c_counter_raw++; |
|||
} |
|||
} |
|||
|
|||
|
|||
// Tasks
|
|||
void taskSystem(void *pvParameters) { |
|||
(void) pvParameters; |
|||
|
|||
uint32_t last_update = 0; |
|||
uint32_t last_update_ota_handler = 0; |
|||
|
|||
for(;;) { |
|||
vTaskDelay(1); |
|||
if((uint32_t)(millis() - last_update) > 999) { |
|||
i2c_counter = i2c_counter_raw; |
|||
i2c_counter_raw = 0; |
|||
|
|||
uptime++; |
|||
|
|||
// frame_count = frame_count_raw;
|
|||
// frame_count_raw = 0;
|
|||
|
|||
// profile_gimbal_rate = profile_gimbal_rate_raw;
|
|||
// profile_gimbal_rate_raw = 0;
|
|||
|
|||
// if(counter == 0) {
|
|||
/// signalNotOK = true;
|
|||
// }
|
|||
|
|||
last_update = millis(); |
|||
} |
|||
|
|||
if((uint32_t)(millis() - last_update_ota_handler) > 20) { |
|||
ArduinoOTA.handle(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void taskDisplay(void *pvParameters) { |
|||
(void) pvParameters; |
|||
|
|||
uint32_t last_update = 0; |
|||
uint8_t delay = 50; // Screen refreshing time. 50mS delay. Roughly 20Hz for display rate.
|
|||
|
|||
for(;;) { |
|||
vTaskDelay(delay); |
|||
|
|||
if((uint32_t)(millis() - last_update) > delay) { |
|||
// Prevent processing when OTA
|
|||
if(OTA_NOW) |
|||
continue; |
|||
|
|||
M5.update(); |
|||
|
|||
// Preventing it back to intro screen
|
|||
if(current_screen <= SCREEN_INTRO) current_screen = SCREEN_BUTTONS; |
|||
|
|||
// Preventing over...
|
|||
if(current_screen > SCREEN_RUBYTOGGLES) current_screen = SCREEN_RUBYTOGGLES; |
|||
|
|||
|
|||
switch(current_screen) { |
|||
case SCREEN_INTRO: |
|||
screen_intro(); |
|||
break; |
|||
|
|||
case SCREEN_BUTTONS: |
|||
screen_buttons(); |
|||
break; |
|||
|
|||
case SCREEN_CONTROLLER: |
|||
screen_controller(); |
|||
break; |
|||
|
|||
case SCREEN_RUBYTOGGLES: |
|||
screen_rubytoggles(); |
|||
break; |
|||
} |
|||
last_update = millis(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void taskInput(void *pvParameters) { |
|||
(void) pvParameters; |
|||
|
|||
uint32_t last_update = 0; |
|||
uint32_t last_voltage_read = 0; |
|||
|
|||
for(;;) { |
|||
vTaskDelay(1); |
|||
// Prevent processing when OTA
|
|||
if(OTA_NOW) |
|||
continue; |
|||
|
|||
if((uint32_t)(millis() - last_update) > 5) { |
|||
last_update = millis(); |
|||
} |
|||
|
|||
if((uint32_t)(millis() - last_voltage_read) > 500) { |
|||
battery_voltage = (float)analogRead(BAT_PIN) * BAT_SCALE; |
|||
last_voltage_read = millis(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void draw_gimbal_huge(uint16_t x, uint16_t y) { |
|||
// Top
|
|||
mainscreen_buffer.drawFastHLine(x, y, 120, TFT_DARKGREEN); |
|||
// Bottom
|
|||
mainscreen_buffer.drawFastHLine(x, y + 119, 120, TFT_DARKGREEN); |
|||
// Left
|
|||
mainscreen_buffer.drawFastVLine(x, y, 120, TFT_DARKGREEN); |
|||
// Right
|
|||
mainscreen_buffer.drawFastVLine(x + 119, y, 120, TFT_DARKGREEN); |
|||
|
|||
// Middle dotted lines
|
|||
for(uint16_t x1 = x; x1 < x + 120; x1 += 10) { |
|||
mainscreen_buffer.drawFastHLine(x1, y + 60, 5, TFT_DARKGREEN); |
|||
} |
|||
for(uint16_t y1 = y; y1 < y + 120; y1 += 10) { |
|||
mainscreen_buffer.drawFastVLine(x + 60, y1, 5, TFT_DARKGREEN); |
|||
} |
|||
} |
|||
|
|||
|
|||
void screen_rubytoggles(void) { |
|||
mainscreen_buffer.fillRect(0, 0, 320, 240, BLACK); // Clear the screen
|
|||
mainscreen_buffer.setTextSize(2); |
|||
mainscreen_buffer.setTextColor(TFT_BLUE); |
|||
mainscreen_buffer.setCursor(0, 0); |
|||
mainscreen_buffer.print("> RC CONTROLLER "); // Title
|
|||
mainscreen_buffer.setTextColor(TFT_WHITE); |
|||
mainscreen_buffer.print("[RUBYFPV]"); // Title
|
|||
|
|||
|
|||
mainscreen_buffer.drawRect(16, 32, 60, 60, TFT_WHITE); |
|||
mainscreen_buffer.drawRect(16, 122, 60, 60, TFT_WHITE); |
|||
|
|||
mainscreen_buffer.drawRect(260, 32, 60, 60, TFT_WHITE); |
|||
mainscreen_buffer.drawRect(260, 122, 60, 60, TFT_WHITE); |
|||
|
|||
footer(); |
|||
mainscreen_buffer.pushSprite(0, 0); // Display it
|
|||
|
|||
|
|||
// Handle inputs
|
|||
e = M5.Buttons.event; |
|||
coordinate = M5.Touch.getPressPoint(); |
|||
|
|||
/***
|
|||
#define E_TOUCH 0x0001
|
|||
#define E_RELEASE 0x0002
|
|||
#define E_MOVE 0x0004
|
|||
#define E_GESTURE 0x0008
|
|||
#define E_TAP 0x0010
|
|||
#define E_DBLTAP 0x0020
|
|||
#define E_DRAGGED 0x0040
|
|||
#define E_PRESSED 0x0080
|
|||
#define E_PRESSING 0x0100
|
|||
#define E_LONGPRESSED 0x0200
|
|||
#define E_LONGPRESSING 0x0400
|
|||
***/ |
|||
if(e) { |
|||
if(DEBUG) { |
|||
bool touch = (e & E_TOUCH); |
|||
bool release = (e & E_RELEASE); |
|||
bool move = (e & E_MOVE); |
|||
bool gesture = (e & E_GESTURE); |
|||
bool tap = (e & E_TAP); |
|||
bool dtap = (e & E_DBLTAP); |
|||
bool dragged = (e & E_DRAGGED); |
|||
bool pressed = (e & E_PRESSED); |
|||
bool pressing = (e & E_PRESSING); |
|||
bool lpressed = (e & E_LONGPRESSED); |
|||
bool lpressing = (e & E_LONGPRESSING); |
|||
|
|||
Serial.printf("[%d%d%d%d%d%d%d%d%d%d%d]Pos X:%d, Y:%d\r\n", touch, release, move, gesture, tap, dtap, dragged, pressed, pressing, lpressed, lpressing, e.to.x, e.to.y); |
|||
} |
|||
|
|||
|
|||
if(e & E_TOUCH) { |
|||
if( |
|||
e.to.x >= 16 && |
|||
e.to.x <= 76 && |
|||
e.to.y >= 32 && |
|||
e.to.y <= 92 |
|||
) { |
|||
if(DEBUG) Serial.printf("RUBY BUTTON 1\r\n"); |
|||
button1 = true; |
|||
} |
|||
|
|||
if( |
|||
e.to.x >= 16 && |
|||
e.to.x <= 76 && |
|||
e.to.y >= 122 && |
|||
e.to.y <= 182 |
|||
) { |
|||
if(DEBUG) Serial.printf("RUBY BUTTON 2\r\n"); |
|||
button2 = true; |
|||
} |
|||
|
|||
if( |
|||
e.to.x >= 260 && |
|||
e.to.x <= 320 && |
|||
e.to.y >= 32 && |
|||
e.to.y <= 92 |
|||
) { |
|||
if(DEBUG) Serial.printf("RUBY BUTTON 6\r\n"); |
|||
button6 = true; |
|||
} |
|||
|
|||
if( |
|||
e.to.x >= 200 && |
|||
e.to.x <= 320 && |
|||
e.to.y >= 122 && |
|||
e.to.y <= 182 |
|||
) { |
|||
if(DEBUG) Serial.printf("RUBY BUTTON 7\r\n"); |
|||
button7 = true; |
|||
} |
|||
|
|||
|
|||
} // End of E_TOUCH
|
|||
|
|||
// Determine move position
|
|||
touch_reader(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void screen_controller(void) { |
|||
mainscreen_buffer.fillRect(0, 0, 320, 240, BLACK); // Clear the screen
|
|||
mainscreen_buffer.setTextSize(2); |
|||
mainscreen_buffer.setTextColor(TFT_BLUE); |
|||
mainscreen_buffer.setCursor(0, 0); |
|||
mainscreen_buffer.print("> RC CONTROLLER "); // Title
|
|||
mainscreen_buffer.setTextColor(TFT_WHITE); |
|||
mainscreen_buffer.print("[GIMBALS]"); // Title
|
|||
draw_gimbal_huge(39, 60); |
|||
draw_gimbal_huge(161, 60); |
|||
|
|||
footer(); |
|||
mainscreen_buffer.pushSprite(0, 0); // Display it
|
|||
|
|||
|
|||
// Handle inputs
|
|||
e = M5.Buttons.event; |
|||
coordinate = M5.Touch.getPressPoint(); |
|||
|
|||
/***
|
|||
#define E_TOUCH 0x0001
|
|||
#define E_RELEASE 0x0002
|
|||
#define E_MOVE 0x0004
|
|||
#define E_GESTURE 0x0008
|
|||
#define E_TAP 0x0010
|
|||
#define E_DBLTAP 0x0020
|
|||
#define E_DRAGGED 0x0040
|
|||
#define E_PRESSED 0x0080
|
|||
#define E_PRESSING 0x0100
|
|||
#define E_LONGPRESSED 0x0200
|
|||
#define E_LONGPRESSING 0x0400
|
|||
***/ |
|||
if(e) { |
|||
if(DEBUG) { |
|||
bool touch = (e & E_TOUCH); |
|||
bool release = (e & E_RELEASE); |
|||
bool move = (e & E_MOVE); |
|||
bool gesture = (e & E_GESTURE); |
|||
bool tap = (e & E_TAP); |
|||
bool dtap = (e & E_DBLTAP); |
|||
bool dragged = (e & E_DRAGGED); |
|||
bool pressed = (e & E_PRESSED); |
|||
bool pressing = (e & E_PRESSING); |
|||
bool lpressed = (e & E_LONGPRESSED); |
|||
bool lpressing = (e & E_LONGPRESSING); |
|||
|
|||
Serial.printf("[%d%d%d%d%d%d%d%d%d%d%d]Pos X:%d, Y:%d\r\n", touch, release, move, gesture, tap, dtap, dragged, pressed, pressing, lpressed, lpressing, e.to.x, e.to.y); |
|||
} |
|||
|
|||
|
|||
/**
|
|||
if(e & E_PRESSING) { |
|||
} // End of E_PRESSING
|
|||
**/ |
|||
|
|||
// Determine move position
|
|||
touch_reader(); |
|||
} |
|||
} |
|||
|
|||
void screen_buttons(void) { |
|||
mainscreen_buffer.fillRect(0, 0, 320, 240, BLACK); // Clear the screen
|
|||
mainscreen_buffer.setTextSize(2); |
|||
mainscreen_buffer.setTextColor(TFT_BLUE); |
|||
mainscreen_buffer.setCursor(0, 0); |
|||
mainscreen_buffer.print("> RC CONTROLLER"); // Title
|
|||
mainscreen_buffer.setTextColor(TFT_WHITE); |
|||
mainscreen_buffer.print(" [TOGGLES]"); // Title
|
|||
|
|||
// Buttons 1 - 3
|
|||
for(int i = 0; i < 3; i++) { |
|||
int x = i * 80 + 48; |
|||
mainscreen_buffer.drawRect(x, 32, 60, 60, TFT_WHITE); |
|||
if(buttons[i]) |
|||
mainscreen_buffer.fillRect(x+2, 34, 56, 56, TFT_RED); |
|||
} |
|||
|
|||
// Buttons 4 - 6
|
|||
for(int i = 0; i < 3; i++) { |
|||
int x = i * 80 + 48; |
|||
mainscreen_buffer.drawRect(x, 112, 60, 60, TFT_WHITE); |
|||
if(buttons[i+3]) |
|||
mainscreen_buffer.fillRect(x+2, 114, 56, 56, TFT_RED); |
|||
} |
|||
|
|||
footer(); |
|||
mainscreen_buffer.pushSprite(0, 0); // Display it
|
|||
|
|||
// Handle inputs
|
|||
e = M5.Buttons.event; |
|||
coordinate = M5.Touch.getPressPoint(); |
|||
|
|||
/***
|
|||
#define E_TOUCH 0x0001
|
|||
#define E_RELEASE 0x0002
|
|||
#define E_MOVE 0x0004
|
|||
#define E_GESTURE 0x0008
|
|||
#define E_TAP 0x0010
|
|||
#define E_DBLTAP 0x0020
|
|||
#define E_DRAGGED 0x0040
|
|||
#define E_PRESSED 0x0080
|
|||
#define E_PRESSING 0x0100
|
|||
#define E_LONGPRESSED 0x0200
|
|||
#define E_LONGPRESSING 0x0400
|
|||
***/ |
|||
if(e) { |
|||
if(DEBUG) { |
|||
bool touch = (e & E_TOUCH); |
|||
bool release = (e & E_RELEASE); |
|||
bool move = (e & E_MOVE); |
|||
bool gesture = (e & E_GESTURE); |
|||
bool tap = (e & E_TAP); |
|||
bool dtap = (e & E_DBLTAP); |
|||
bool dragged = (e & E_DRAGGED); |
|||
bool pressed = (e & E_PRESSED); |
|||
bool pressing = (e & E_PRESSING); |
|||
bool lpressed = (e & E_LONGPRESSED); |
|||
bool lpressing = (e & E_LONGPRESSING); |
|||
|
|||
Serial.printf("[%d%d%d%d%d%d%d%d%d%d%d]Pos X:%d, Y:%d\r\n", touch, release, move, gesture, tap, dtap, dragged, pressed, pressing, lpressed, lpressing, e.to.x, e.to.y); |
|||
} |
|||
|
|||
if(e & E_PRESSING) { |
|||
// Button 1
|
|||
if( |
|||
e.to.x >= 48 && |
|||
e.to.x <= 108 && |
|||
e.to.y >= 32 && |
|||
e.to.y <= 92 |
|||
) { |
|||
if(DEBUG) Serial.printf("BUTTON 1\r\n"); |
|||
buttons[0] = !buttons[0]; |
|||
} |
|||
|
|||
// Button 2
|
|||
if( |
|||
e.to.x >= 128 && |
|||
e.to.x <= 188 && |
|||
e.to.y >= 32 && |
|||
e.to.y <= 92 |
|||
) { |
|||
if(DEBUG) Serial.printf("BUTTON 2\r\n"); |
|||
buttons[1] = !buttons[1]; |
|||
} |
|||
|
|||
// Button 3
|
|||
if( |
|||
e.to.x >= 208 && |
|||
e.to.x <= 268 && |
|||
e.to.y >= 32 && |
|||
e.to.y <= 92 |
|||
) { |
|||
if(DEBUG) Serial.printf("BUTTON 3\r\n"); |
|||
buttons[2] = !buttons[2]; |
|||
} |
|||
|
|||
// Button 4
|
|||
if( |
|||
e.to.x >= 48 && |
|||
e.to.x <= 108 && |
|||
e.to.y >= 112 && |
|||
e.to.y <= 172 |
|||
) { |
|||
if(DEBUG) Serial.printf("BUTTON 4\r\n"); |
|||
buttons[3] = !buttons[3]; |
|||
} |
|||
|
|||
// Button 5
|
|||
if( |
|||
e.to.x >= 128 && |
|||
e.to.x <= 188 && |
|||
e.to.y >= 112 && |
|||
e.to.y <= 172 |
|||
) { |
|||
if(DEBUG) Serial.printf("BUTTON 5\r\n"); |
|||
buttons[4] = !buttons[4]; |
|||
} |
|||
|
|||
// Button 6
|
|||
if( |
|||
e.to.x >= 208 && |
|||
e.to.x <= 268 && |
|||
e.to.y >= 112 && |
|||
e.to.y <= 172 |
|||
) { |
|||
if(DEBUG) Serial.printf("BUTTON 6\r\n"); |
|||
buttons[5] = !buttons[5]; |
|||
} |
|||
|
|||
} // End of E_PRESSING
|
|||
|
|||
// Determine move position
|
|||
touch_reader(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void touch_reader(void) { |
|||
// Skipped if too fast
|
|||
if((uint32_t)(millis() - last_scroll) < 200) |
|||
return; |
|||
|
|||
if(e & E_MOVE) { |
|||
// Prev Page
|
|||
if((e.to.x - drag_start_x) > 100) { |
|||
current_screen--; |
|||
} |
|||
|
|||
// Next Page
|
|||
if((e.to.x - drag_start_x) < -100) { |
|||
current_screen++; |
|||
} |
|||
} |
|||
|
|||
// Detecting Touch Start of drag no matter what
|
|||
if(e & E_TOUCH) { |
|||
drag_start_x = e.to.x; |
|||
drag_start_y = e.to.y; |
|||
} |
|||
|
|||
// Detecting End of Touch of everything
|
|||
if(e & E_RELEASE) { |
|||
drag_start_x = -1; |
|||
drag_start_y = -1; |
|||
drag_end_x = -1; |
|||
drag_end_y = -1; |
|||
} |
|||
last_scroll = millis(); |
|||
} |
|||
|
|||
|
|||
void footer(void) { |
|||
mainscreen_buffer.setTextColor(TFT_WHITE); |
|||
mainscreen_buffer.setTextSize(2); |
|||
mainscreen_buffer.setCursor(0, 226); |
|||
// mainscreen_buffer.print("UPTIME: ");
|
|||
mainscreen_buffer.print(uptime); |
|||
|
|||
mainscreen_buffer.setTextColor(TFT_YELLOW); |
|||
mainscreen_buffer.setCursor(60, 226); |
|||
mainscreen_buffer.print(IP); |
|||
|
|||
mainscreen_buffer.setTextColor(TFT_WHITE); |
|||
mainscreen_buffer.setCursor(200, 226); |
|||
mainscreen_buffer.print(i2c_counter); |
|||
|
|||
mainscreen_buffer.setCursor(240, 226); |
|||
mainscreen_buffer.print(battery_voltage); |
|||
} |
|||
|
|||
|
|||
void screen_intro(void) { |
|||
mainscreen_buffer.fillRect(0, 0, 320, 240, BLACK); // Clear the screen
|
|||
mainscreen_buffer.drawRoundRect(50, 50, 220, 140, 10, DIALOGBOX_FOREGROUND); |
|||
mainscreen_buffer.fillRoundRect(51, 51, 218, 138, 10, DIALOGBOX_BACKGROUND); |
|||
mainscreen_buffer.setTextColor(DIALOGBOX_FOREGROUND, DIALOGBOX_BACKGROUND); |
|||
mainscreen_buffer.setCursor(72, 110); |
|||
mainscreen_buffer.setTextSize(2); |
|||
mainscreen_buffer.print("MiniX1RC V1.0"); |
|||
mainscreen_buffer.setTextSize(1); |
|||
mainscreen_buffer.setCursor(72, 130); |
|||
mainscreen_buffer.print("By BertFPV (20231021)"); |
|||
|
|||
mainscreen_buffer.pushSprite(0, 0); |
|||
delay(500); |
|||
current_screen = SCREEN_BUTTONS; |
|||
} |
|||
|
|||
|
|||
void init_var(void) { |
|||
for(uint8_t i = 0; i < 10; i++) |
|||
buttons[i] = false; |
|||
} |
|||
|
|||
|
|||
void loop() { |
|||
} |
@ -0,0 +1,148 @@ |
|||
#ifndef MAIN_H |
|||
#define MAIN_H |
|||
|
|||
// If DEBUG true will display log at serial |
|||
#define DEBUG true |
|||
bool OTA_NOW = false; |
|||
|
|||
#define CPU_0 0 |
|||
#define CPU_1 1 |
|||
|
|||
#define BAT_PIN 35 |
|||
#define BAT_SCALE .01 |
|||
float battery_voltage = 0.00; |
|||
|
|||
// Structures |
|||
typedef struct struct_data { |
|||
uint8_t message_type; |
|||
uint16_t throttle; |
|||
uint16_t yaw; |
|||
uint16_t roll; |
|||
uint16_t pitch; |
|||
uint16_t aux; |
|||
// bool aux[9]; |
|||
// bool rubybutton[9]; |
|||
uint16_t rubybutton; |
|||
} struct_data; |
|||
|
|||
typedef struct sbus_data { |
|||
bool failsafe; |
|||
bool ch17; |
|||
bool ch18; |
|||
uint16_t ch[16]; |
|||
} struct_sbus; |
|||
|
|||
enum screen_names { |
|||
SCREEN_INTRO, |
|||
SCREEN_BUTTONS, |
|||
SCREEN_CONTROLLER, |
|||
SCREEN_RUBYTOGGLES, |
|||
SCREEN_TRIM, |
|||
SCREEN_MENU_BLUETOOTH, |
|||
SCREEN_MENU_STICK_CALIBRATION, |
|||
SCREEN_MENU_STICK_POSITIONS, |
|||
SCREEN_MENU_RC_CONTROLLER |
|||
}; |
|||
|
|||
int16_t drag_start_x = -1; |
|||
int16_t drag_start_y = -1; |
|||
int16_t drag_end_x = -1; |
|||
int16_t drag_end_y = -1; |
|||
|
|||
|
|||
// For Ground Station Pi via I2C |
|||
#define MAX_BUFFER_SIZE 1024 |
|||
#define I2C_SCL 33 |
|||
#define I2C_SDA 32 |
|||
#define I2C_SLAVE_ADDR 0x60 |
|||
uint8_t i2c_received[MAX_BUFFER_SIZE]; |
|||
uint8_t i2c_send[MAX_BUFFER_SIZE]; |
|||
uint16_t i2c_received_length = 0; |
|||
uint16_t i2c_send_length = 0; |
|||
uint8_t i2c_received_max = 0; |
|||
uint8_t frame_count = 0; |
|||
bool header_detected = false; |
|||
|
|||
// ADS1115 |
|||
#define ADS1115_SDA 21 |
|||
#define ADS1115_SCL 22 |
|||
|
|||
#define DIALOGBOX_FOREGROUND TFT_WHITE |
|||
#define DIALOGBOX_BACKGROUND TFT_NAVY |
|||
|
|||
#include <Arduino.h> |
|||
#include <ArduinoOTA.h> |
|||
#include <M5Core2.h> |
|||
#include <BleGamepad.h> |
|||
#include "i2c_protocols.h" |
|||
#include "Wire.h" |
|||
#include "ADS1X15.h" |
|||
#include <WiFi.h> |
|||
#include <WiFiClient.h> |
|||
|
|||
#define WIFI_SSID "MiniX1RC" |
|||
#define WIFI_PASSPHRASE "letmein123" |
|||
IPAddress IP; |
|||
|
|||
// Functions |
|||
void onI2C0Receive(int len); |
|||
void onI2C0Request(void); |
|||
void parse_i2c_command(void); |
|||
|
|||
// Variables |
|||
bool signalNotOK = false; |
|||
bool i2c_status = false; |
|||
uint16_t i2c_counter = 0; |
|||
uint16_t i2c_counter_raw = 0; |
|||
uint16_t uptime = 0; |
|||
|
|||
|
|||
// Button 1, 2, 6 and 7 are for RubyFPV |
|||
// Button 3, 4, 5, 8, 9 and 10 are for the controller |
|||
Event &e = M5.Buttons.event; |
|||
TouchPoint_t coordinate; |
|||
bool buttons[10]; |
|||
uint8_t button1 = 0; |
|||
uint8_t button2 = 0; |
|||
uint8_t button3 = 0; |
|||
uint8_t button4 = 0; |
|||
uint8_t button5 = 0; |
|||
uint8_t button6 = 0; |
|||
uint8_t button7 = 0; |
|||
uint8_t button8 = 0; |
|||
uint8_t button9 = 0; |
|||
uint8_t button10 = 0; |
|||
|
|||
bool rubybutton[16]; |
|||
uint32_t rubybutton_last[16]; |
|||
uint16_t rubybutton_delay = 200; |
|||
struct_sbus sbusData; |
|||
|
|||
uint8_t current_screen = SCREEN_INTRO; |
|||
uint32_t last_scroll = 0; |
|||
|
|||
|
|||
// Task Handler |
|||
void taskSystem(void *pvParameters); |
|||
void taskDisplay(void *pvParameters); |
|||
void taskInput(void *pvParameters); |
|||
|
|||
// Other Functions |
|||
void onI2C0Receive(int len); |
|||
void onI2C0Request(void); |
|||
void parse_i2c_command(void); |
|||
|
|||
void wifi_setup(void); |
|||
void ota_setup(void); |
|||
void init_var(void); |
|||
|
|||
void screen_intro(void); |
|||
void screen_buttons(void); |
|||
void screen_controller(void); |
|||
void screen_rubytoggles(void); |
|||
void touch_reader(void); |
|||
void footer(void); |
|||
|
|||
void draw_gimbal_huge(uint16_t x, uint16_t y); |
|||
|
|||
#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