Browse Source

Added SBUS protocol. Yet to test due to board burned

master
Englebert 3 years ago
parent
commit
04fefc9b13
  1. 39
      include/README
  2. 46
      lib/README
  3. 19
      platformio.ini
  4. 35
      src/CustomWiFi.cpp
  5. 20
      src/CustomWiFi.h
  6. 49
      src/OTA.cpp
  7. 18
      src/OTA.h
  8. 236
      src/Storage.cpp
  9. 43
      src/Storage.h
  10. 160
      src/Web.cpp
  11. 25
      src/Web.h
  12. 229
      src/main.cpp
  13. 34
      src/main.h
  14. 1318
      src/nrf24l01.cpp
  15. 1360
      src/nrf24l01.h
  16. 197
      src/sbus.cpp
  17. 22
      src/sbus.h
  18. 11
      test/README

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

19
platformio.ini

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

35
src/CustomWiFi.cpp

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

20
src/CustomWiFi.h

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

49
src/OTA.cpp

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

18
src/OTA.h

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

236
src/Storage.cpp

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

43
src/Storage.h

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

160
src/Web.cpp

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

25
src/Web.h

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

229
src/main.cpp

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

34
src/main.h

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

1360
src/nrf24l01.h
File diff suppressed because it is too large
View File

197
src/sbus.cpp

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

22
src/sbus.h

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

11
test/README

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