agrobot_base/Firmware/Direcional/Direcional_v9/Direcional_v9.ino

406 lines
11 KiB
C++

// Dispositivo: Direcional
// Versão Firmware: 9
// Ultima atualização: 14/05/2024
// Atualização: Inclusão do potenciometro para aferição do angulo
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\SerialService.h"
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\Utils.h"
#include "C:\ZendionInc\agrobot_base\Firmware\Modulos\Pinout.h"
#include <SimpleKalmanFilter.h>
#define D_Code Dir
#define VERSION 9
#define _pinoLED RGB_BUILTIN
long _baudRate = 115200;
bool Conectado = false;
int _TaxaAmostragem;
int FrequenciaMaxima = 10000;
double _Margem = 0.5;
double _AgMin = 380;
double _AgMax = 3430;
int RefMin = -90;
int RefMax = 90;
int dutyCycle = 512;
double _frequencia = 4000;
PinoModel _pinoGeral;
class Motor {
public:
// Construtor
Motor(String desc) {
_ID = desc;
}
// Definições
String _ID;
int _canal;
PinoModel _pinoENA;
PinoModel _pinoDIR;
PinoModel _pinoPUL;
PinoModel _pinoEncoder;
SimpleKalmanFilter* encoderKalman;
bool Iniciado = false;
bool Testando = false;
bool Referenciando = false;
// Consumo
double _Angulo;
Sentido _Sentido;
// Entrada de dados
double _Angulo_SP;
int _Angulo_Offset = 0;
Sentido _Sentido_SP = Parado;
bool _MotorLigado = false;
bool _Estabilizar = true;
bool _Referenciar = true;
bool Referenciado = false;
void Inicializar() {
if (Iniciado) {
EnviarDadosSerial(_ID + " ja inicializado");
EnviarDadosSerial(MontarProtocoloSensor(sCFG, _ID, Iniciado ? "1" : "0"));
return;
}
if (_pinoPUL.Definido()) {
AtualizarPWM();
}
_pinoENA.Conectar(true);
_pinoDIR.Conectar(false);
_pinoEncoder.Conectar();
encoderKalman = new SimpleKalmanFilter(1, 2, 0.01);
xTaskCreatePinnedToCore(&Motor::RampaTaskWrapper, "RampaTask", 5000, this, 20 - _canal, &RampaTaskHandle, tskNO_AFFINITY);
ResetRef(_Referenciar);
EnviarDadosSerial(_ID + " Iniciado");
Iniciado = true;
EnviarDadosSerial(MontarProtocoloSensor(sCFG, _ID, Iniciado ? "1" : "0"));
}
void ResetRef(bool _Ref) {
_Sentido_SP = Parado;
_Sentido = Parado;
Referenciado = !_Ref;
Referenciando = _Ref;
_MotorLigado = _Ref;
}
void AtualizarPWM() {
ledcSetup(_canal, _frequencia, 10);
ledcAttachPin(_pinoPUL.num, _canal);
ledcWrite(_canal, dutyCycle);
}
void Desligar() {
if (!Iniciado) {
EnviarDadosSerial(_ID + " nao esta inicializado");
EnviarDadosSerial(MontarProtocoloSensor(sCFG, _ID, Iniciado ? "1" : "0"));
return;
}
// Parar a execução das tarefas
vTaskDelete(RampaTaskHandle);
delete encoderKalman;
// Redefinir as configurações para os valores iniciais
_pinoENA.Desconectar();
_pinoDIR.Desconectar();
_pinoPUL.Desconectar();
_pinoEncoder.Desconectar();
EnviarDadosSerial(_ID + " Desligado");
Iniciado = false;
EnviarDadosSerial(MontarProtocoloSensor(sCFG, _ID, Iniciado ? "1" : "0"));
}
void Testar() {
Testando = true;
Testando = false;
}
private:
TaskHandle_t RampaTaskHandle = NULL;
static void RampaTaskWrapper(void *pvParameters) {
Motor* motor = static_cast<Motor*>(pvParameters);
motor->RampaTask();
}
void RampaTask() {
unsigned long previousMillisRampa = millis();
while (true) { // Loop infinito mais claro
// Verifica a conexão antes de prosseguir
if (!Conectado) {
// Espera por 1 segundo para reduzir o uso da CPU se não estiver conectado
vTaskDelay(1000 / portTICK_PERIOD_MS);
continue; // Pula para a próxima iteração do loop
}
// Executa as funções de controle e monitoramento do motor
AferirPosicao();
if (Referenciando) {
ReferenciarMotor();
} else {
VerificarAnguloSP();
}
Atualizar();
// Envia dados via serial em intervalos definidos por _TaxaAmostragem
if (millis() - previousMillisRampa >= _TaxaAmostragem) {
previousMillisRampa = millis(); // Atualiza o tempo para o próximo envio
EnviarDadosSerial(MontarProtocoloSensor(sENC, _ID, MontarDadosProtocoloEncoderAbsoluto(_Sentido, _Angulo, Referenciando, Referenciado)));
}
// Libera a CPU para outras tarefas por um curto período
vTaskDelay(1 / portTICK_PERIOD_MS); // Torna o delay mais explícito e ajustado ao tick do sistema
}
}
void Atualizar() {
_pinoENA.set(!_MotorLigado);
_pinoDIR.set(_Sentido_SP != Antihorario);
}
void AferirPosicao() {
if (_pinoEncoder.Definido()) {
int leitura = _pinoEncoder.get();
float leituraNormalizada = encoderKalman->updateEstimate(leitura);
double _angulo = fmap(leituraNormalizada, _AgMin, _AgMax, RefMin, RefMax);
double angulo_com_offset = _angulo - _Angulo_Offset;
// Normaliza o ângulo para o intervalo de -90 a 90 graus
while (angulo_com_offset > 90) angulo_com_offset -= 180;
while (angulo_com_offset < -90) angulo_com_offset += 180;
_Angulo = angulo_com_offset;
DefinirSentidoDeGiro();
}
}
double _anguloAnterior = 0;
int leituras = 0;
void DefinirSentidoDeGiro() {
double _anguloAtual = _Angulo;
if (leituras > 100) {
if (_MotorLigado) {
if (_anguloAtual > _anguloAnterior) {
_Sentido = Horario;
}
else if (_anguloAtual < _anguloAnterior) {
_Sentido = Antihorario;
}
}
else {
_Sentido = Parado;
}
_anguloAnterior = _anguloAtual;
leituras = 0;
}
leituras++;
}
unsigned long previousMillisCorrecao = millis();
void VerificarAnguloSP() {
bool ModoSP = (_Angulo_SP > -1);
if (_MotorLigado) {
double AnguloComparar = ModoSP ? _Angulo_SP : RefMax;
if (_Sentido_SP == Horario && _Angulo >= AnguloComparar) {
_MotorLigado = false;
}
else if (_Sentido_SP == Antihorario && _Angulo <= (AnguloComparar * -1)) {
_MotorLigado = false;
}
}
else if (ModoSP) {
if (millis() - previousMillisCorrecao >= 1500) {
previousMillisCorrecao = millis();
bool NoRangeZero = (_Angulo >= -_Margem && _Angulo <= _Margem);
bool NoRangeSP = (_Angulo >= (_Angulo_SP - _Margem) && _Angulo <= (_Angulo_SP + _Margem));
if (_Angulo_SP == 0 && !NoRangeZero && _Estabilizar) {
_Sentido_SP = _Angulo > 0 ? Antihorario : Horario;
_MotorLigado = true;
}
else if (!NoRangeSP) {
_MotorLigado = true;
}
}
}
}
void ReferenciarMotor() {
_Angulo_SP = 0;
Referenciado = false;
if (Referenciando && (_Angulo >= -_Margem && _Angulo <= _Margem)) {
Referenciando = false;
Referenciado = true;
_MotorLigado = false;
}
else {
_Sentido_SP = (_Angulo > 0) ? Antihorario : Horario;
}
}
};
Motor M1("ET");
Motor M2("EF");
Motor M3("DT");
Motor M4("DF");
Motor* MotorPorID(String ID) {
Motor* _motor =
ID == "ET" ? &M1 :
ID == "EF" ? &M2 :
ID == "DT" ? &M3 :
ID == "DF" ? &M4 :
nullptr;
return _motor;
}
void setup() {
Serial.begin(_baudRate);
//iniciarMCP(D_Code, 8, 18, 0x20, false);
}
void loop() {
std::vector<ProtocoloSerial> protocolos = LerBufferSerialFila();
for (int i = 0; i < protocolos.size(); i++) {
ProcessarProtocolo(protocolos[i].protocolo, protocolos[i].funcao, protocolos[i].idMensagem);
}
}
void ProcessarProtocolo(String Protocolo, F_Code _funcao, int idMensagem) {
if (_funcao == Chk) {
EnviarDadosSerial(MontarProtocoloVerificacao(D_Code, VERSION));
}
else if (_funcao == Cfg) {
std::vector<String> Partes = SplitString(Protocolo, (char*)",");
String ID = Partes[0];
bool Conectar = Partes[1] == "1";
if (ID == "MD") {
_TaxaAmostragem = Partes[2].toInt();
EnviarDadosSerial(MontarProtocoloSensor(sCFG, "MD", Conectar ? "1" : "0"));
vTaskDelay(pdMS_TO_TICKS(100));
Conectado = Conectar;
}
else {
Motor* _motor = MotorPorID(ID);
int canal = Partes[2].toInt();
bool motor_ativado = Partes[3] == "1";
bool estabilizar = Partes[4] == "1";
bool referenciar = Partes[5] == "1";
int _offset = Partes[6].toInt();
TiposBarramentos pulB = (TiposBarramentos)Partes[7].toInt();
int pul = Partes[8].toInt();
TiposBarramentos dirB = (TiposBarramentos)Partes[9].toInt();
int dir = Partes[10].toInt();
TiposBarramentos enaB = (TiposBarramentos)Partes[11].toInt();
int ena = Partes[12].toInt();
TiposBarramentos encB = (TiposBarramentos)Partes[13].toInt();
int encoder = Partes[14].toInt();
if (motor_ativado) {
if (Conectar) {
_motor->_canal = canal;
_motor->_Estabilizar = estabilizar;
_motor->_Referenciar = referenciar;
_motor->_Angulo_Offset = _offset;
_motor->_pinoPUL = PinoModel(pul, pulB, _PWM);
_motor->_pinoDIR = PinoModel(dir, dirB, _OUTPUT);
_motor->_pinoENA = PinoModel(ena, enaB, _OUTPUT);
_motor->_pinoEncoder = PinoModel(encoder, encB, _INPUT, ANALOGICO);
_motor->Inicializar();
}
else {
_motor->Desligar();
}
vTaskDelay(500);
}
}
}
else if (_funcao == Cmd) {
std::vector<String> Partes = SplitString(Protocolo, (char*)",");
String ID = Partes[0];
Motor* _motor = MotorPorID(ID);
Sentido sentido = (Sentido)Partes[1].toInt();
int angulo = Partes[2].toInt();
int porcentagem = Partes[3].toInt();
double frequencia = (porcentagem / 100.0) * FrequenciaMaxima;
bool referenciar = Partes[4] == "1";
int offset = Partes[5].toInt();
_frequencia = frequencia;
_motor->_Sentido_SP = sentido;
_motor->_Angulo_SP = angulo;
_motor->_Angulo_Offset = offset;
_motor->AtualizarPWM();
if (referenciar) {
_motor->ResetRef(referenciar);
}
else {
_motor->_MotorLigado = sentido != Parado;
}
}
else if (_funcao == Tst) {
std::vector<String> Partes = SplitString(Protocolo, (char*)",");
String ID = Partes[0];
Motor* _motor = MotorPorID(ID);
bool EmTeste = _motor->Testando;
if (!EmTeste) {
_motor->Testar();
}
}
EnviarDadosSerial(MontarProtocoloMensagemRecebida(idMensagem));
}