Files
Bachelorarbeit-Rover/src/main.cpp
T

374 lines
12 KiB
C++

/**
* @file main.cpp
* @author Alexander Klein (alex@kleiax.de)
* @brief The main file.
*
* Sets up Network stuff, PS3-Controller, Menu and Lcd
* Handles Controller Input
*
* @version 0.1
* @date 2022-02-15
*
* @copyright Copyright (c) 2022
*
*/
#include <Arduino.h>
#include "config.h"
#include <iostream>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include "Version.h"
#include "driveModi/driveManager.h"
#include "OutputBuf/outputBuf.h"
#include "LcdWrapper.h"
#include "moveControl.h"
#include "network.h"
#include "networkConfig.h"
#include "debugMqtt.h"
#include "battery.h"
#include "sensorData.h"
#include "debugTimes.h"
#include "controlPad.h"
#include "menu.h"
#include "menuAction.h"
#include "SpecialMenus/driveModi/ManualDrive/menuManualDrive.h"
#include "SpecialMenus/driveModi/CaptureRoute/menuCaptureRoute.h"
#include "SpecialMenus/driveModi/Autopilot/menuAutopilot.h"
#include "SpecialMenus/driveModi/TestMode/menuTestMode.h"
#include "SpecialMenus/driveModi/CalibrateCompass/menuCalibrateCompass.h"
#include "SpecialMenus/Systeminformation/menuSysteminformation.h"
#include "SpecialMenus/PID/menuPidSettings.h"
#include "SpecialMenus/Route/menuRoute.h"
#include "SpecialMenus/SensorData/menuSensorData.h"
#include "SpecialMenus/CalibrateBattery/menuCalibrateBattery.h"
MoveControl moveController;
Network *network;
DriveManager *driveManager;
Menu *main_m;
LiquidCrystal_I2C *lcd;
LcdWrapper *lcdWrapper;
OutputBuf *outputBuf;
DebugMqtt *debugMqtt = nullptr;
Battery *mainBattery;
SPIClass *spiPort;
SensorData *sensorData;
ControlPad *controlPad;
constexpr uint16_t displayUpdateDelay = 500;
void i2cScanner();
void makeMenu();
void restart();
void receiveCallback(const uint8_t *mac, const uint8_t *incomingData, int len);
void sendCallback(const uint8_t *mac_addr, esp_now_send_status_t status);
void lcdWrapperCallback(const char data[][LcdWrapper::totalRows], uint8_t lines, uint8_t rows);
NetworkAddresses setIPs();
void setup()
{
Serial.begin(Settings::baudRate);
// DebugTimes::setConsolOutput(true);
DebugTimes setupTime;
spiPort = new SPIClass(HSPI);
spiPort->begin(PinNumbers::spiSck, PinNumbers::spiCipo, PinNumbers::spiCopi, PinNumbers::gnssSpiCs);
mainBattery = new Battery(PinNumbers::battery);
controlPad = new ControlPad();
Wire.begin(PinNumbers::sda, PinNumbers::scl);
Wire.setClock(Settings::i2cSpeed);
i2cScanner();
lcd = new LiquidCrystal_I2C(0x3F, 16, 2);
lcd->init();
lcd->clear();
lcd->noBacklight();
network = new Network(static_cast<const char *>(NetworkConfig::ssid),
static_cast<const char *>(NetworkConfig::password),
setIPs());
network->activateEspNow(receiveCallback, sendCallback);
if (NetworkConfig::mqtt)
{
if (network->activateMqtt(static_cast<const char *>(MqttConfig::user),
static_cast<const char *>(MqttConfig::password)))
{
DebugMqtt::init(network->getMqttClient(), Loglevel::debug);
debugMqtt = new DebugMqtt("Console");
outputBuf = new OutputBuf(debugMqtt);
outputBuf->activateMqtt(true);
}
else
{
outputBuf = new OutputBuf();
}
}
else
{
outputBuf = new OutputBuf();
}
std::cout.rdbuf(outputBuf);
std::cout << "Welcome to Kleiax-Rover" << std::endl;
std::cout << "Project verion: " << VERSION << std::endl;
std::cout << "Build timestamp: " << BUILD_TIMESTAMP << std::endl;
std::cout << "All actions from the main program run on Core -> " << xPortGetCoreID() << std::endl;
sensorData = new SensorData();
sensorData->enableGnss(spiPort, PinNumbers::gnssSpiCs);
sensorData->enableRealCompass();
sensorData->enableNtrip(static_cast<const char *>(NtripConfig::host),
NtripConfig::port,
static_cast<const char *>(NtripConfig::mountPoint),
static_cast<const char *>(NtripConfig::user),
static_cast<const char *>(NtripConfig::password),
NtripConfig::sendOwnPosition);
sensorData->enableGyroscope();
driveManager = new DriveManager(&moveController, sensorData, controlPad->getControlPadDataPtr());
char wifiIndicator = 'X';
if (network->isWifiConnected())
{
wifiIndicator = '-';
}
lcd->backlight();
lcd->printf("%c Kleiax-Rover %c", wifiIndicator, wifiIndicator);
lcd->setCursor(0, 1);
lcd->printf("WiFi channel %u", Network::getCurrentChannel());
lcdWrapper = new LcdWrapper(lcd);
lcdWrapper->setCallback(lcdWrapperCallback);
makeMenu();
setupTime.stopConsol("Setup");
}
void loop()
{
network->loop();
sensorData->loop();
driveManager->loop();
controlPad->loop();
main_m->update();
lcdWrapper->loop();
mainBattery->loop();
// new Value ervery 0.5s
if (mainBattery->isNewValue())
{
static uint8_t batteryLowCounter = 0;
static constexpr uint8_t minVoltage = 10;
if (mainBattery->isBatteryLow(minVoltage))
{
batteryLowCounter++;
}
else
{
batteryLowCounter = 0;
}
if (batteryLowCounter >= 10)
{
moveController.emergencyStop();
const auto voltage = static_cast<uint16_t>(mainBattery->getBatteryVoltage() * 100);
lcd->setCursor(0, 0);
lcd->printf("Low Battery: %u", voltage);
lcd->setCursor(0, 1);
lcd->print("Please turn off.");
while (true)
{
}
}
}
}
void i2cScanner()
{
constexpr uint8_t checkForLength = 16;
constexpr uint8_t maxAdresses = UINT8_MAX / 2;
std::cout << "\nI2C Scanner" << std::endl;
byte error = 0;
byte address = 0;
int nDevices = 0;
std::cout << "Scanning..." << std::endl;
for (address = 1; address < maxAdresses; address++)
{
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0)
{
std::cout << "I2C device found at address 0x";
if (address < checkForLength)
{
std::cout << "0";
}
std::cout << std::hex << static_cast<int>(address) << std::dec << std::endl;
// std::cout << (int) address << std::endl;
nDevices++;
}
else if (error == 4)
{
std::cout << "Unknow error at address 0x";
if (address < checkForLength)
{
std::cout << "0";
}
std::cout << std::hex << static_cast<int>(address) << std::dec << std::endl;
}
}
if (nDevices == 0)
{
std::cout << "No I2C devices found\n"
<< std::endl;
}
else
{
std::cout << "done\n"
<< std::endl;
}
}
void makeMenu()
{
auto dummy = []()
{
std::cout << "Dummy in Action" << std::endl;
};
// Create Menu
main_m = new Menu();
main_m->setLcd(lcdWrapper);
auto *mode_m = new Menu();
auto *set_m = new Menu();
auto *pid_m = new Menu();
auto *pidl_m = new MenuIntInput(3, new MenuPidSettings(moveController.getPID(0)));
auto *pidr_m = new MenuIntInput(3, new MenuPidSettings(moveController.getPID(1)));
auto *speed_m = new MenuIntInput(2, new MenuSpeed(driveManager->getDrivingSpeedsRef()));
auto *man_m = new MenuManualControl(driveManager);
auto *cap_m = new MenuCaptureRoute(driveManager);
auto *auto_m = new MenuAutopilot(driveManager);
auto *testM_m = new MenuTestMode(driveManager);
auto *comp_m = new MenuCalibrateCompass(driveManager);
auto *sys_m = new MenuSysteminformation(mainBattery);
auto *sen_m = new MenuSensorData(sensorData);
// TODO: Wie bekommt jeder die dumme Route?
auto *rout_m = new MenuRoute(new Route());
auto *bat_m = new MenuCalibrateBattery(mainBattery);
auto_m->setUpdateDelay(displayUpdateDelay);
sys_m->setUpdateDelay(displayUpdateDelay);
sen_m->setUpdateDelay(displayUpdateDelay);
// Entry for the main menu
main_m->addEntry(new MenuAction("Mode", mode_m));
main_m->addEntry(new MenuAction("Sensor", sen_m));
main_m->addEntry(new MenuAction("Route", rout_m));
main_m->addEntry(new MenuAction("Settings", set_m));
main_m->addEntry(new MenuAction("Systeminfo", sys_m));
main_m->addEntry(new MenuAction("Restart", restart));
// Entry for the mode Menu
mode_m->addEntry(new MenuAction("Manual Control", man_m));
mode_m->addEntry(new MenuAction("Capture Route", cap_m));
mode_m->addEntry(new MenuAction("Autopilot", auto_m));
mode_m->addEntry(new MenuAction("Gauge Compass", comp_m));
mode_m->addEntry(new MenuAction("Test Mode", testM_m));
mode_m->addEntry(new MenuAction("Consol Control", dummy));
// Entry for the setting menu
set_m->addEntry(new MenuAction("PID", pid_m));
set_m->addEntry(new MenuAction("Speed", speed_m));
set_m->addEntry(new MenuAction("Distance", dummy));
set_m->addEntry(new MenuAction("WiFi", dummy));
set_m->addEntry(new MenuAction("Battery", bat_m));
// Entry for the PID Menu
pid_m->addEntry(new MenuAction("Left", pidl_m));
pid_m->addEntry(new MenuAction("Right", pidr_m));
// Other config
pidl_m->setMinMax(0, UINT8_MAX);
pidl_m->setEntry(0, "P-Part", static_cast<uint8_t>(moveController.getPID(0)->GetKp()));
pidl_m->setEntry(1, "I-Part", static_cast<uint8_t>(moveController.getPID(0)->GetKi()));
pidl_m->setEntry(2, "D-Part", static_cast<uint8_t>(moveController.getPID(0)->GetKd()));
pidr_m->setMinMax(0, UINT8_MAX);
pidr_m->setEntry(0, "P-Part", static_cast<uint8_t>(moveController.getPID(1)->GetKp()));
pidr_m->setEntry(1, "I-Part", static_cast<uint8_t>(moveController.getPID(1)->GetKi()));
pidr_m->setEntry(2, "D-Part", static_cast<uint8_t>(moveController.getPID(1)->GetKd()));
speed_m->setMinMax(5, UINT8_MAX);
speed_m->setEntry(0, "x m/s e-1", driveManager->getDrivingSpeedsRef().x * 10);
speed_m->setEntry(1, "z rad/s e-1", driveManager->getDrivingSpeedsRef().rot * 10);
man_m->addSensorMenu(sen_m);
controlPad->setMenuControl(main_m);
}
void restart()
{
lcdWrapper->clear();
lcdWrapper->setCursor(0, 0);
lcdWrapper->print("Rebooting ...");
ESP.restart();
}
void receiveCallback(const uint8_t *mac, const uint8_t *incomingData, int len)
{
if (len != sizeof(ControlPadInput))
{
return;
}
controlPad->insertData(incomingData);
}
void sendCallback(const uint8_t *mac_addr, esp_now_send_status_t status)
{
if (status != ESP_NOW_SEND_SUCCESS)
{
std::cout << "sendCallback - Delivery Fail" << std::endl;
}
}
void lcdWrapperCallback(const char data[][LcdWrapper::totalRows], uint8_t lines, uint8_t rows)
{
if (!controlPad->isControlPadConnected())
{
return;
}
const esp_err_t result = esp_now_send(network->getBroadcastAddress(), reinterpret_cast<const uint8_t *>(data), lines * rows);
if (result != ESP_OK)
{
Serial.println("Error sending the data");
}
}
NetworkAddresses setIPs()
{
NetworkAddresses adresses;
adresses.localIP.fromString(static_cast<const char *>(NetworkConfig::ip));
adresses.subnet.fromString(static_cast<const char *>(NetworkConfig::subnet));
adresses.gateway.fromString(static_cast<const char *>(NetworkConfig::gateway));
adresses.dnsServer.fromString(static_cast<const char *>(NetworkConfig::dns));
if (NetworkConfig::mqtt)
{
adresses.mqttServer.fromString(static_cast<const char *>(MqttConfig::server));
adresses.mqttPort = MqttConfig::port;
}
return adresses;
}