Browse Source

Initial working

master
Englebert 1 year ago
parent
commit
f0b0c2d8ee
  1. 31
      README.md
  2. 7
      bert2M_fat12M_16MB.csv
  3. 39
      include/README
  4. 46
      lib/README
  5. 27
      platformio.ini
  6. 225
      src/i2c_protocols.h
  7. 915
      src/main.cpp
  8. 148
      src/main.h
  9. 11
      test/README

31
README.md

@ -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

7
bert2M_fat12M_16MB.csv

@ -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

39
include/README

@ -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

46
lib/README

@ -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

27
platformio.ini

@ -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

225
src/i2c_protocols.h

@ -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;
}
*/

915
src/main.cpp

@ -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() {
}

148
src/main.h

@ -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

11
test/README

@ -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
Loading…
Cancel
Save