Englebert
3 years ago
18 changed files with 3861 additions and 0 deletions
-
39include/README
-
46lib/README
-
19platformio.ini
-
35src/CustomWiFi.cpp
-
20src/CustomWiFi.h
-
49src/OTA.cpp
-
18src/OTA.h
-
236src/Storage.cpp
-
43src/Storage.h
-
160src/Web.cpp
-
25src/Web.h
-
229src/main.cpp
-
34src/main.h
-
1318src/nrf24l01.cpp
-
1360src/nrf24l01.h
-
197src/sbus.cpp
-
22src/sbus.h
-
11test/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 |
@ -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,19 @@ |
|||
; 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:esp12e] |
|||
platform = espressif8266 |
|||
board = esp12e |
|||
framework = arduino |
|||
; Comment out if upload via USB. Now is via OTA |
|||
upload_protocol = espota |
|||
upload_port = 192.168.4.1 |
|||
board_build.filesystem = littlefs |
|||
lib_deps = arkhipenko/TaskScheduler@^3.6.0 |
@ -0,0 +1,35 @@ |
|||
#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,4,1); |
|||
IPAddress gateway(192,168,4,1); |
|||
IPAddress subnet(255,255,255,0); |
|||
|
|||
|
|||
// WiFi.softAPConfig(local_IP, gateway, subnet);
|
|||
WiFi.softAPConfig(local_ip, gateway, subnet); |
|||
WiFi.softAP("SIMPLERX"); |
|||
// WiFi.softAP(WIFI_SSID, WIFI_PASSPHRASE);
|
|||
Serial.println(WiFi.softAPIP()); |
|||
delay(100); |
|||
|
|||
// IPAddress IP = WiFi.softAPIP();
|
|||
// Serial.print(F("AP IP address: "); Serial.prinln(IP);
|
|||
} |
|||
|
|||
CustomWiFi customwifi; |
@ -0,0 +1,20 @@ |
|||
#ifndef CUSTOMWIFI_H_ |
|||
#define CUSTOMWIFI_H_ |
|||
|
|||
#include <Arduino.h> |
|||
#include <ESP8266WiFi.h> |
|||
// #include <WiFiClient.h> |
|||
|
|||
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,49 @@ |
|||
#include "OTA.h"
|
|||
|
|||
OTA::OTA(void) { |
|||
} |
|||
|
|||
void OTA::begin(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()
|
|||
Serial.println("Start updating " + type); |
|||
}); |
|||
ArduinoOTA.onEnd([]() { |
|||
Serial.println("\nEnd"); |
|||
}); |
|||
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { |
|||
Serial.printf("Progress: %u%%\r", (progress / (total / 100))); |
|||
}); |
|||
ArduinoOTA.onError([](ota_error_t error) { |
|||
Serial.printf("Error[%u]: ", error); |
|||
if(error == OTA_AUTH_ERROR) { |
|||
Serial.println("Auth Failed"); |
|||
} else if(error == OTA_BEGIN_ERROR) { |
|||
Serial.println("Begin Failed"); |
|||
} else if(error == OTA_CONNECT_ERROR) { |
|||
Serial.println("Connect Failed"); |
|||
} else if(error == OTA_RECEIVE_ERROR) { |
|||
Serial.println("Receive Failed"); |
|||
} else if(error == OTA_END_ERROR) { |
|||
Serial.println("End Failed"); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
void OTA::run(void) { |
|||
ArduinoOTA.handle(); |
|||
} |
|||
|
|||
OTA ota; |
@ -0,0 +1,18 @@ |
|||
#ifndef OTA_H_ |
|||
#define OTA_H_ |
|||
|
|||
#include <Arduino.h> |
|||
#include <ArduinoOTA.h> |
|||
|
|||
class OTA { |
|||
public: |
|||
OTA(void); |
|||
void begin(void); |
|||
void run(void); |
|||
|
|||
private: |
|||
uint32_t last_wifi_check, wifi_check_duration; |
|||
}; |
|||
|
|||
extern OTA ota; |
|||
#endif |
@ -0,0 +1,236 @@ |
|||
#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()) {
|
|||
if(LittleFS.format()) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
|
|||
bool Storage::exists(const char * path) { |
|||
// if(LITTLEFS.exists(path)) {
|
|||
if(LittleFS.exists(path)) { |
|||
return true; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
|
|||
bool Storage::begin(void) { |
|||
// if(!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED)) {
|
|||
if(!LittleFS.begin()) { |
|||
Serial.println("LITTLEFS mount failed"); |
|||
return false; |
|||
} |
|||
|
|||
// Demo and checking only
|
|||
// Storage::listDir(LITTLEFS, "/", 0);
|
|||
Storage::listDir(LittleFS, "/", 0); |
|||
|
|||
if(Storage::exists("/SIMPLERX")) { |
|||
Serial.println(F("SIMPLERX FOUND")); |
|||
} else { |
|||
Serial.println(F("SIMPLERX NOT FOUND - Formatting now")); |
|||
Storage::format(); |
|||
// Storage::writeFile(LITTLEFS, "/MSCoreTX", "");
|
|||
Storage::writeFile(LittleFS, "/SIMPLERX", ""); |
|||
load_defaults = true; |
|||
} |
|||
|
|||
// Check Total storage
|
|||
// Serial.print(F("Storage size: "));
|
|||
// Serial.print(LITTLEFS.totalBytes());
|
|||
// Serial.print(LittleFS.totalBytes());
|
|||
// Serial.println(F(" Bytes"));
|
|||
|
|||
// Serial.print(F("Storage used: " ));
|
|||
// Serial.print(LITTLEFS.usedBytes());
|
|||
// 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, "r"); |
|||
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(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void Storage::readFile(fs::FS &fs, const char * path){ |
|||
Serial.printf("Reading file: %s\r\n", path); |
|||
|
|||
File file = fs.open(path, "r"); |
|||
if(!file || file.isDirectory()){ |
|||
Serial.println(F("- failed to open file for reading")); |
|||
return; |
|||
} |
|||
|
|||
Serial.println(F("- read from file:")); |
|||
while(file.available()){ |
|||
Serial.write(file.read()); |
|||
} |
|||
file.close(); |
|||
} |
|||
|
|||
|
|||
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, "w+"); |
|||
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, const char * path, const char * message){ |
|||
Serial.printf("Writing file: %s\r\n", path); |
|||
|
|||
File file = fs.open(path, "w+"); |
|||
if(!file){ |
|||
Serial.println("- failed to open file for writing"); |
|||
return; |
|||
} |
|||
|
|||
if(file.print(message)){ |
|||
Serial.println("- file written"); |
|||
} else { |
|||
Serial.println("- write failed"); |
|||
} |
|||
|
|||
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;
|
|||
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);
|
|||
File file = fs.open(path, "w+"); |
|||
// 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;
|
|||
fs::FS &fs = LittleFS; |
|||
|
|||
File file = fs.open(path, "r"); |
|||
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,43 @@ |
|||
#ifndef _Storage_H |
|||
#define _Storage_H |
|||
|
|||
#include <Arduino.h> |
|||
// #include <LITTLEFS.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); |
|||
void readFile(fs::FS &fs, const char * 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, const char * path, const char * 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(const char * path); |
|||
bool load_defaults = false; // Default false. Only during reset this will auto set to true |
|||
|
|||
private: |
|||
}; |
|||
|
|||
extern Storage storage; |
|||
#endif |
@ -0,0 +1,160 @@ |
|||
#include "Web.h"
|
|||
|
|||
// WebServer server(PORT_HTTP);
|
|||
ESP8266WebServer server(80); |
|||
bool update_status = false; |
|||
bool opened = false; |
|||
File root; |
|||
|
|||
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", "SimpleRX 0.1"); |
|||
server.sendHeader("Connection", "close"); |
|||
}); |
|||
|
|||
/*** Temporary disable update for ESP8266
|
|||
// 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");
|
|||
storage.deleteFile(LittleFS, "/ADAM"); |
|||
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);
|
|||
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);
|
|||
root = LittleFS.open((String("/") + upload.filename).c_str(), "w"); |
|||
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("LITTLEFS Files:\n"); |
|||
// list_files();
|
|||
// storage.listDir(LITTLEFS, "/", 0);
|
|||
storage.listDir(LittleFS, "/", 0); |
|||
#endif
|
|||
|
|||
} |
|||
}); |
|||
|
|||
server.begin(); |
|||
} |
|||
|
|||
void WEB::handler(void) { |
|||
server.handleClient(); |
|||
} |
|||
|
|||
|
|||
WEB web; |
@ -0,0 +1,25 @@ |
|||
#ifndef WEB_H_ |
|||
#define WEB_H_ |
|||
|
|||
#include <Arduino.h> |
|||
#include <ESP8266WebServer.h> |
|||
#include "Storage.h" |
|||
// #include <LittleFS.h> |
|||
// // #include <WebServer.h> |
|||
// #include <ESP8266WebServer.h> |
|||
// // #include <Update.h> |
|||
// #include <ArduinoOTA.h> |
|||
// |
|||
#define PORT_HTTP 80 |
|||
|
|||
class WEB { |
|||
public: |
|||
WEB(void); |
|||
void begin(void); |
|||
void handler(void); |
|||
|
|||
private: |
|||
}; |
|||
|
|||
extern WEB web; |
|||
#endif |
@ -0,0 +1,229 @@ |
|||
#include <Arduino.h>
|
|||
// #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns
|
|||
// #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
|
|||
// #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only
|
|||
// #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids
|
|||
// #define _TASK_LTS_POINTER // Compile with support for local task storage pointer
|
|||
// #define _TASK_PRIORITY // Support for layered scheduling priority
|
|||
// #define _TASK_MICRO_RES // Support for microsecond resolution
|
|||
// #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY)
|
|||
// #define _TASK_DEBUG // Make all methods and variables public for debug purposes
|
|||
// #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations
|
|||
// #define _TASK_TIMEOUT // Support for overall task timeout
|
|||
// #define _TASK_OO_CALLBACKS // Support for dynamic callback method binding
|
|||
#include <TaskScheduler.h>
|
|||
#include "main.h"
|
|||
|
|||
#define LED_STATUS 14
|
|||
#define LED_HEARTBEAT 2
|
|||
bool led_heartbeat_state = false; |
|||
|
|||
uint16_t rcCommand[18]; // Signal to ESC
|
|||
|
|||
Scheduler scheduler; |
|||
RXMessage rxMessage; |
|||
|
|||
// Every 5 miliseconds
|
|||
Task task_sbus_generator(10, TASK_FOREVER, &send_sbus); |
|||
Task task_rx(50, TASK_FOREVER, &nrf_rx); |
|||
|
|||
// Every 200 miliseconds
|
|||
Task task_web(200, TASK_FOREVER, &web_handler); |
|||
Task task_ota(200, TASK_FOREVER, &ota_handler); |
|||
|
|||
// Every 0.5 seconds
|
|||
Task task_heartbeat(500, TASK_FOREVER, &status_heartbeat); |
|||
|
|||
// Every second
|
|||
Task task_uptime(1000, TASK_FOREVER, &uptime_tracker); |
|||
|
|||
void setup() { |
|||
Serial.begin(115200); |
|||
|
|||
// Delay a bit
|
|||
delay(100); |
|||
|
|||
// Setting up status led
|
|||
pinMode(LED_STATUS, OUTPUT); |
|||
pinMode(LED_HEARTBEAT, OUTPUT); |
|||
|
|||
// Starting up storage
|
|||
storage.begin(); |
|||
|
|||
// Starting up SBUS
|
|||
sbus.begin(); |
|||
|
|||
// One wire as SBUS (TX)
|
|||
// Serial2.begin(100000, SERIAL_8E2);
|
|||
// swSer1.begin(100000, SWSERIAL_8N2, D5, D5, false, 256);
|
|||
|
|||
// Start up NRF24L01
|
|||
//////// rx.begin();
|
|||
|
|||
// Setup Receiver
|
|||
// setup_rx();
|
|||
|
|||
// For OTA
|
|||
customwifi.begin(); |
|||
ota.begin(); |
|||
web.begin(); |
|||
|
|||
scheduler.init(); |
|||
|
|||
Serial.println("Enabling Tasks..."); |
|||
// Add task to scheduler
|
|||
scheduler.addTask(task_sbus_generator); |
|||
////////// scheduler.addTask(task_rx);
|
|||
scheduler.addTask(task_heartbeat); |
|||
scheduler.addTask(task_web); |
|||
scheduler.addTask(task_ota); |
|||
scheduler.addTask(task_uptime); |
|||
|
|||
// Enabling Tasks
|
|||
task_sbus_generator.enable(); |
|||
//////////// task_rx.enable();
|
|||
task_heartbeat.enable(); |
|||
task_web.enable(); |
|||
task_ota.enable(); |
|||
task_uptime.enable(); |
|||
|
|||
|
|||
Serial.println("SimpleRX OK"); |
|||
} |
|||
|
|||
void loop() { |
|||
scheduler.execute(); |
|||
} |
|||
|
|||
void web_handler(void) { |
|||
web.handler(); |
|||
} |
|||
|
|||
void ota_handler(void) { |
|||
ota.run(); |
|||
} |
|||
|
|||
|
|||
uint32_t gpio_count = 0; |
|||
uint32_t profile_start = 0; |
|||
|
|||
void status_heartbeat(void) { |
|||
led_heartbeat_state = !led_heartbeat_state; |
|||
if(led_heartbeat_state) { |
|||
GPOS = (1 << LED_HEARTBEAT); |
|||
GPOS = (1 << LED_STATUS); |
|||
} else { |
|||
GPOC = (1 << LED_HEARTBEAT); |
|||
GPOC = (1 << LED_STATUS); |
|||
} |
|||
|
|||
/*****
|
|||
gpio_count = 0; |
|||
profile_start = millis(); |
|||
|
|||
while((uint32_t)(millis() - profile_start) < 1000) { |
|||
GPOS = (1 << LED_STATUS); |
|||
GPOC = (1 << LED_STATUS); |
|||
gpio_count++; |
|||
} |
|||
|
|||
Serial.println(gpio_count); |
|||
****/ |
|||
} |
|||
|
|||
// Keep sending SBUS
|
|||
void send_sbus(void) { |
|||
sbus.writePackets(); |
|||
} |
|||
|
|||
|
|||
uint32_t uptime_total = 0; |
|||
void uptime_tracker(void) { |
|||
uptime_total++; |
|||
} |
|||
|
|||
|
|||
// DEBUGGING...
|
|||
uint32_t printagain = 0; |
|||
bool first_load = true; |
|||
// Processing received data
|
|||
void nrf_rx(void) { |
|||
if(first_load == true) { |
|||
if(uptime_total < 5) { |
|||
// Do nothing till it stablized...
|
|||
return; |
|||
} |
|||
|
|||
// Next loop wont be here....
|
|||
first_load = false; |
|||
|
|||
setup_rx(); |
|||
Serial.println("NRF READY TO LISTEN..."); |
|||
} |
|||
|
|||
if(rx.available()) { |
|||
// Getting signals from Receiver
|
|||
rx.read(&rxMessage, sizeof(RXMessage)); |
|||
|
|||
// Debugging only. Remove this once working.
|
|||
Serial.print(rxMessage.Byte00); |
|||
Serial.print(" "); |
|||
Serial.print(rxMessage.Byte01); |
|||
Serial.print(" "); |
|||
Serial.print(rxMessage.Byte02); |
|||
Serial.print(" "); |
|||
Serial.print(rxMessage.Byte03); |
|||
Serial.print(" "); |
|||
Serial.print(rxMessage.Byte04); |
|||
Serial.print(" "); |
|||
Serial.print(rxMessage.Byte05); |
|||
Serial.println(" "); |
|||
|
|||
// static bool firstload = true;
|
|||
|
|||
// 2021 New Data format for saving 2 bytes:
|
|||
// 1234 5678 9TTT TTTT TTTT -YYY YYYY YYYY -PPP PPPP PPPP -RRR RRRR RRRR (7 Bytes)
|
|||
// |-------| |-------| |-------| |-------| |-------| |-------| |-------|
|
|||
// Byte 00 Byte 01 Byte 02 Byte 03 Byte 04 Byte 05 Byte 06
|
|||
|
|||
// Extracting data
|
|||
// Processing switches channel....
|
|||
/****
|
|||
rcCommand[AUX1] = ((rxMessage.Byte00 >> 7) & 0x01) ? 2000 : 1000; // 1
|
|||
rcCommand[AUX2] = ((rxMessage.Byte00 >> 6) & 0x01) ? 2000 : 1000; // 2
|
|||
rcCommand[AUX3] = ((rxMessage.Byte00 >> 5) & 0x01) ? 2000 : 1000; // 3
|
|||
rcCommand[AUX4] = ((rxMessage.Byte00 >> 4) & 0x01) ? 2000 : 1000; // 4
|
|||
rcCommand[AUX5] = ((rxMessage.Byte00 >> 3) & 0x01) ? 2000 : 1000; // 5
|
|||
rcCommand[AUX6] = ((rxMessage.Byte00 >> 2) & 0x01) ? 2000 : 1000; // 6
|
|||
rcCommand[AUX7] = ((rxMessage.Byte00 >> 1) & 0x01) ? 2000 : 1000; // 7
|
|||
rcCommand[AUX8] = ((rxMessage.Byte00 >> 0) & 0x01) ? 2000 : 1000; // 8
|
|||
rcCommand[AUX9] = ((rxMessage.Byte01 >> 7) & 0x01) ? 2000 : 1000; // 9
|
|||
***/ |
|||
rcCommand[AUX1] = ((rxMessage.Byte00 >> 7) & 0x01) ? 1 : 0; // 1
|
|||
rcCommand[AUX2] = ((rxMessage.Byte00 >> 6) & 0x01) ? 1 : 0; // 2
|
|||
rcCommand[AUX3] = ((rxMessage.Byte00 >> 5) & 0x01) ? 1 : 0; // 3
|
|||
rcCommand[AUX4] = ((rxMessage.Byte00 >> 4) & 0x01) ? 1 : 0; // 4
|
|||
rcCommand[AUX5] = ((rxMessage.Byte00 >> 3) & 0x01) ? 1 : 0; // 5
|
|||
rcCommand[AUX6] = ((rxMessage.Byte00 >> 2) & 0x01) ? 1 : 0; // 6
|
|||
rcCommand[AUX7] = ((rxMessage.Byte00 >> 1) & 0x01) ? 1 : 0; // 7
|
|||
rcCommand[AUX8] = ((rxMessage.Byte00 >> 0) & 0x01) ? 1 : 0; // 8
|
|||
rcCommand[AUX9] = ((rxMessage.Byte01 >> 7) & 0x01) ? 1 : 0; // 9
|
|||
|
|||
rcCommand[THROTTLE] = ((rxMessage.Byte01 & 0X7F) << 4) | (rxMessage.Byte02 & 0xF0) >> 4; // Throttle
|
|||
rcCommand[YAW] = ((rxMessage.Byte02 & 0x07) << 8) | rxMessage.Byte03; // Yaw
|
|||
rcCommand[PITCH] = ((rxMessage.Byte04 & 0x7F) << 4) | (rxMessage.Byte05 & 0xF0) >> 4; // Pitch
|
|||
rcCommand[ROLL] = ((rxMessage.Byte05 & 0x07) << 8) | rxMessage.Byte06; // Roll
|
|||
|
|||
} |
|||
} |
|||
|
|||
void setup_rx(void) { |
|||
rx.stopListening(); |
|||
rx.setAutoAck(false); |
|||
rx.setChannel(0x08); // Temporary at 8 Later read from LittleFS
|
|||
rx.setPayloadSize(sizeof(RXMessage)); |
|||
rx.setDataRate(RF24_250KBPS); |
|||
rx.setPALevel(RF24_PA_MAX); |
|||
rx.openReadingPipe(1, pipeIn); |
|||
rx.startListening(); |
|||
} |
@ -0,0 +1,34 @@ |
|||
#ifndef MAIN_H |
|||
#define MAIN_H |
|||
|
|||
// For NRF24L01P |
|||
#define MAX_CHANNELS 125 |
|||
#define RX_CE_PIN 5 |
|||
#define RX_CSN_PIN 4 |
|||
#define _SPI SPI |
|||
|
|||
const uint64_t pipeIn = 0xE8E8F0F0E1LL; // Must be same as the transmission |
|||
|
|||
// Enable type of microcontroller for the nrf24l01 library to compile. |
|||
// #define ESP32 |
|||
#define ESP12E |
|||
|
|||
#include "nrf24l01.h" |
|||
#include "CustomWiFi.h" |
|||
#include "OTA.h" |
|||
#include "sbus.h" |
|||
#include "Storage.h" |
|||
#include "Web.h" |
|||
|
|||
void send_sbus(void); |
|||
void setup_nrf_rx(void); |
|||
void setup_rx(void); |
|||
void nrf_rx(void); |
|||
void sbusPreparePacket(uint8_t packet[], int channels[], bool isSignalLoss, bool isFailsafe); |
|||
void status_heartbeat(void); |
|||
void web_handler(void); |
|||
void ota_handler(void); |
|||
void uptime_tracker(void); |
|||
|
|||
|
|||
#endif |
1318
src/nrf24l01.cpp
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1360
src/nrf24l01.h
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,197 @@ |
|||
// REFERENCES:
|
|||
// https://www.circuitbasics.com/basics-uart-communication/
|
|||
// https://www.analog.com/en/analog-dialogue/articles/uart-a-hardware-communication-protocol.html
|
|||
// https://github.com/uzh-rpg/rpg_quadrotor_control/wiki/SBUS-Protocol
|
|||
// https://github.com/plerup/espsoftwareserial/blob/main/src/SoftwareSerial.cpp
|
|||
// https://lucidar.me/en/serialib/most-used-baud-rates-table/
|
|||
|
|||
#include "sbus.h"
|
|||
|
|||
// SBUS data structure
|
|||
#define RC_CHANS 18
|
|||
#define RC_CHANNEL_MIN 990
|
|||
#define RC_CHANNEL_MAX 2010
|
|||
|
|||
#define SBUS_MIN_OFFSET 173
|
|||
#define SBUS_MID_OFFSET 992
|
|||
#define SBUS_MAX_OFFSET 1811
|
|||
#define SBUS_CHANNEL_NUMBER 16
|
|||
#define SBUS_PACKET_LENGTH 25
|
|||
#define SBUS_FRAME_HEADER 0x0f
|
|||
#define SBUS_FRAME_FOOTER 0x00
|
|||
#define SBUS_FRAME_FOOTER_V2 0x04
|
|||
#define SBUS_STATE_FAILSAFE 0x08
|
|||
#define SBUS_STATE_SIGNALLOSS 0x04
|
|||
#define SBUS_UPDATE_RATE 15 // ms
|
|||
#define SBUS_PORT 13 // Trigger pulse from the port
|
|||
|
|||
uint8_t sbusPacket[SBUS_PACKET_LENGTH]; |
|||
int rcChannels[SBUS_CHANNEL_NUMBER]; |
|||
uint32_t sbusTime = 0; |
|||
bool signalNotOK = false; |
|||
|
|||
uint32_t counter = 900; |
|||
uint32_t last_count = 0; |
|||
|
|||
|
|||
SBUS::SBUS(void) { |
|||
} |
|||
|
|||
void SBUS::begin(void) { |
|||
pinMode(SBUS_PORT, OUTPUT); |
|||
|
|||
// ON it first as IDLE
|
|||
GPOS = (1 << SBUS_PORT); |
|||
// swSer1.begin(220000, SWSERIAL_8E2, SBUS_PORT, SBUS_PORT, false, 256);
|
|||
// swSer1.enableIntTx(false);
|
|||
// swSer1.enableTx(true);
|
|||
} |
|||
|
|||
void SBUS::run(void) { |
|||
} |
|||
|
|||
// Sending SBUS packets
|
|||
void SBUS::writePackets(void) { |
|||
if((uint32_t)(millis() - sbusTime) > SBUS_UPDATE_RATE) { |
|||
if(signalNotOK) { |
|||
// DEBUG ONLY...later put back to 900
|
|||
rcChannels[2] = 1800; |
|||
rcChannels[3] = 900; |
|||
rcChannels[1] = 900; |
|||
rcChannels[0] = 900; |
|||
} |
|||
|
|||
sbusPreparePacket(sbusPacket, rcChannels, signalNotOK, false); |
|||
// Serial2.write(sbusPacket, SBUS_PACKET_LENGTH);
|
|||
/// swSer1.write(sbusPacket, SBUS_PACKET_LENGTH);
|
|||
|
|||
// Looping through the bytes - Generating Parity Bits
|
|||
// Is using SBUS_FAST (200000 Baud), 8E2 (Even Parity and 2 bits stop
|
|||
// For sending at 200000 baud, each bit used about 5uS
|
|||
// Profiling each bits timing before sending
|
|||
uint32_t clock_cycles_per_micro = 0; |
|||
uint32_t clock_cycles = 0; |
|||
uint32_t clock_cycles_total_start = millis(); |
|||
|
|||
while((uint32_t) millis() - clock_cycles_total_start < 1) { |
|||
clock_cycles_per_micro++; |
|||
} |
|||
|
|||
// bool tx_state = true;
|
|||
uint8_t parity_count = 0; |
|||
uint8_t data_bits = 0; |
|||
bool current_bit = 0; |
|||
|
|||
|
|||
// Now we roughly know how much per clock cycles per microseconds
|
|||
for(uint8_t pos = 0; pos < SBUS_PACKET_LENGTH; pos++) { |
|||
// Now we are going to send the data from LSB and with EVEN parity on each byte with 2 bits stop.
|
|||
// Looping through each bytes with:
|
|||
// START BIT | DATA FRAME | PARITY BITS | STOP BITS
|
|||
|
|||
// START BIT - For 1 clock cycle = 5uS = clock_per_micro
|
|||
// Set to LOW as start bit
|
|||
GPOC = (1 << SBUS_PORT); |
|||
for(clock_cycles = 0; clock_cycles < clock_cycles_per_micro; clock_cycles++); |
|||
|
|||
// DATA FRAME
|
|||
// Reading the data sending it byte by byte
|
|||
for(data_bits = 0; data_bits < 8; data_bits++) { |
|||
current_bit = (sbusPacket[pos] >> data_bits) & 0x01; |
|||
// Sending it out...
|
|||
if(current_bit) { |
|||
parity_count++; |
|||
|
|||
// HIGH
|
|||
GPOS = (1 << SBUS_PORT); |
|||
} else { |
|||
// LOW
|
|||
GPOC = (1 << SBUS_PORT); |
|||
} |
|||
for(clock_cycles = 0; clock_cycles < clock_cycles_per_micro; clock_cycles++); |
|||
} |
|||
|
|||
// PARITY BITS
|
|||
// Parity Calculation
|
|||
if(parity_count & 0x01) { |
|||
// ODD
|
|||
// Need to make it EVEN....
|
|||
GPOS = (1 << SBUS_PORT); |
|||
} else { |
|||
// EVEN
|
|||
// Nothing needs to be set
|
|||
GPOC = (1 << SBUS_PORT); |
|||
} |
|||
for(clock_cycles = 0; clock_cycles < clock_cycles_per_micro; clock_cycles++); |
|||
|
|||
// STOP BITS
|
|||
GPOS = (1 << SBUS_PORT); |
|||
for(clock_cycles = 0; clock_cycles < clock_cycles_per_micro; clock_cycles++); |
|||
for(clock_cycles = 0; clock_cycles < clock_cycles_per_micro; clock_cycles++); |
|||
} |
|||
|
|||
|
|||
// Wait for next round
|
|||
sbusTime = millis(); |
|||
} |
|||
} |
|||
|
|||
// Preparing SBUS packets
|
|||
void SBUS::sbusPreparePacket(uint8_t packet[], int channels[], bool isSignalLoss, bool isFailsafe){ |
|||
if(millis() - last_count > 2000) { |
|||
counter+= 100; |
|||
|
|||
if(counter > 2000) { |
|||
counter = 900; |
|||
} |
|||
|
|||
last_count = millis(); |
|||
} |
|||
static int output[SBUS_CHANNEL_NUMBER] = {0}; |
|||
|
|||
/*
|
|||
* Map 1000-2000 with middle at 1500 chanel values to |
|||
* 173-1811 with middle at 992 S.BUS protocol requires |
|||
*/ |
|||
for (uint8_t i = 0; i < SBUS_CHANNEL_NUMBER; i++) { |
|||
// output[i] = map(channels[i], RC_CHANNEL_MIN, RC_CHANNEL_MAX, SBUS_MIN_OFFSET, SBUS_MAX_OFFSET);
|
|||
// DEBUG oNly
|
|||
output[i] = counter; |
|||
} |
|||
|
|||
uint8_t stateByte = 0x00; |
|||
if (isSignalLoss) { |
|||
stateByte |= SBUS_STATE_SIGNALLOSS; |
|||
} |
|||
if (isFailsafe) { |
|||
stateByte |= SBUS_STATE_FAILSAFE; |
|||
} |
|||
packet[0] = SBUS_FRAME_HEADER; //Header
|
|||
|
|||
packet[1] = (uint8_t) (output[0] & 0x07FF); |
|||
packet[2] = (uint8_t) ((output[0] & 0x07FF)>>8 | (output[1] & 0x07FF)<<3); |
|||
packet[3] = (uint8_t) ((output[1] & 0x07FF)>>5 | (output[2] & 0x07FF)<<6); |
|||
packet[4] = (uint8_t) ((output[2] & 0x07FF)>>2); |
|||
packet[5] = (uint8_t) ((output[2] & 0x07FF)>>10 | (output[3] & 0x07FF)<<1); |
|||
packet[6] = (uint8_t) ((output[3] & 0x07FF)>>7 | (output[4] & 0x07FF)<<4); |
|||
packet[7] = (uint8_t) ((output[4] & 0x07FF)>>4 | (output[5] & 0x07FF)<<7); |
|||
packet[8] = (uint8_t) ((output[5] & 0x07FF)>>1); |
|||
packet[9] = (uint8_t) ((output[5] & 0x07FF)>>9 | (output[6] & 0x07FF)<<2); |
|||
packet[10] = (uint8_t) ((output[6] & 0x07FF)>>6 | (output[7] & 0x07FF)<<5); |
|||
packet[11] = (uint8_t) ((output[7] & 0x07FF)>>3); |
|||
packet[12] = (uint8_t) ((output[8] & 0x07FF)); |
|||
packet[13] = (uint8_t) ((output[8] & 0x07FF)>>8 | (output[9] & 0x07FF)<<3); |
|||
packet[14] = (uint8_t) ((output[9] & 0x07FF)>>5 | (output[10] & 0x07FF)<<6); |
|||
packet[15] = (uint8_t) ((output[10] & 0x07FF)>>2); |
|||
packet[16] = (uint8_t) ((output[10] & 0x07FF)>>10 | (output[11] & 0x07FF)<<1); |
|||
packet[17] = (uint8_t) ((output[11] & 0x07FF)>>7 | (output[12] & 0x07FF)<<4); |
|||
packet[18] = (uint8_t) ((output[12] & 0x07FF)>>4 | (output[13] & 0x07FF)<<7); |
|||
packet[19] = (uint8_t) ((output[13] & 0x07FF)>>1); |
|||
packet[20] = (uint8_t) ((output[13] & 0x07FF)>>9 | (output[14] & 0x07FF)<<2); |
|||
packet[21] = (uint8_t) ((output[14] & 0x07FF)>>6 | (output[15] & 0x07FF)<<5); |
|||
packet[22] = (uint8_t) ((output[15] & 0x07FF)>>3); |
|||
packet[23] = stateByte; //Flags byte
|
|||
packet[24] = SBUS_FRAME_FOOTER; //Footer
|
|||
} |
|||
|
|||
SBUS sbus; |
@ -0,0 +1,22 @@ |
|||
#ifndef SBUS_H_ |
|||
#define SBUS_H_ |
|||
|
|||
#include <Arduino.h> |
|||
// #include "SoftwareSerial.h" |
|||
|
|||
class SBUS { |
|||
public: |
|||
bool invert; |
|||
|
|||
SBUS(void); |
|||
void begin(void); |
|||
void run(void); |
|||
void sbusPreparePacket(uint8_t packet[], int channels[], bool isSignalLoss, bool isFailsafe); |
|||
void writePackets(void); |
|||
|
|||
private: |
|||
uint32_t last_wifi_check, wifi_check_duration; |
|||
}; |
|||
|
|||
extern SBUS sbus; |
|||
#endif |
@ -0,0 +1,11 @@ |
|||
|
|||
This directory is intended for PlatformIO Unit Testing 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/page/plus/unit-testing.html |
Write
Preview
Loading…
Cancel
Save
Reference in new issue