roboforum.ru

Технический форум по робототехнике.

машина на управлении по ESP с полным приводом по танковой сх

машина на управлении по ESP с полным приводом по танковой сх

ShadowOleg » 23 апр 2022, 17:43

как любой радиоэлектронщик собирает часы, решил вместе с ребенком сделать свою машинку на радио управлении.
В наличии нашел ESP NODE MCU v.3, джойстик HW-504(крайне отвратное устройство-конечная половина хода по всем осям просто не изменяет сопротивления, может кто дать совет по нему-доработать переделать),движки со встроенным редуктором с колесами,два двоййных H-моста на L9110, АЦП ADS1015(так-как на ESP только один АЦП), питание от двух 14500 Li-ion.
с наскока колесную машину с танковой схемой не нашел, поэтому ее и буду делать.
Поворотных колес нет, поворот осуществляется за счет разнонаправленного движения боковых колес относительно друг друга.
Скорость вращения колес меняется ШИМом с ESP.
в качестве шасси металлический конструктор моего детства, единственно дырочки под крепления редукторов надо надфилем чуток по ,5мм проточить, строительные перфорированные пластины подходят под конструктор идеально.
IMG_20220422_152443.jpg

Прикрутил модули на М3 через Festo трубочки.
Как-то вышло
IMG_20220422_165121.jpg

IMG_20220422_165142.jpg

при проверке вращения последнего двигателя задымил ключик в одном плече H-моста, спасибо Джек Ма, ладно, они мне серно не нравились(у них входы управления подтяянуты к + 10К резисторами в моем случае, с ними аккуратней проверяйте напряжение на них при вашем источнике питания, у меня при 2 последовательных LI было 2.9В).
Ставлю MX1508(по названию в инете, хотя в реале у меня стоят MX1616 1035H, datasheet на нее не нашел, поделитесь кто видел), у нее нет подтяжки к +, не надо инвертировать ШИМ и крутить ум.
Накидал проводки на пульт, пока так, пока все так.
IMG_20220423_103759.jpg

Вышло очень даже макетно
IMG_20220423_104534.jpg

Связь плат с помощью протокола ESP-NOW по mac адресам.
код на arduino ide, библиотеки все почти стандартные, накидал из того что было.

код для пульта

Код: Выделить всёРазвернуть

#include <ESP8266WiFi.h>
#include <espnow.h>
// ЗАМЕНИТЕ МАС-АДРЕС платы, на которую отправляем данные.
uint8_t broadcastAddress[] = {0xF4, 0xCF, 0xA2, 0xD7, 0xC6, 0x91};
#include <Adafruit_ADS1X15.h>
Adafruit_ADS1015 ads;

// Вводим переменные для хранения отправляемых данных
int16_t mot1p, mot1n, mot2p, mot2n, mot3p, mot3n, mot4p, mot4n;
const long interval = 100;// Обновляем показания датчика каждые 1 секунд
unsigned long previousMillis = 0;    // время последнего обновления
String success;// Переменная для хранения состояния отправки
//Пример структуры для отправки
//Должна совпадать со структурой на плате-приемнике
struct struct_message {
  int16_t mot1p;
  int16_t mot1n;
  int16_t mot2p;
  int16_t mot2n;
  int16_t mot3p;
  int16_t mot3n;
  int16_t mot4p;
  int16_t mot4n;
};
// Создаем переменную для хранения отправляемого сообщения
struct_message driveReadings;

int16_t adc0, adc1;
int16_t a0, a1, a2, a3;
int16_t aa0, aa1, aa2, aa3;
uint8_t v_x_min = 60;
uint8_t v_y_min = 50;
uint8_t v_x_max = 255;
uint8_t v_y_max = 150;
// определяем callback-функцию, которая выводит в монитор порта информацию о
//состоянии отправки. Если функция возвращает 0 – сообщение успешно доставлено
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  //  Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0) {
    //   Serial.println("удачно");
  }
  else {
    //  Serial.println("fail");
  }
}
void getReadings() {
  adc0 = ads.readADC_SingleEnded(0); //ось Х вперед-назад
  if (580 < adc0 ) {
    a0 = map(adc0, 569, 1103, v_x_min, v_x_max);
    a1 = 0;
  }
  else if (adc0 < 560) {
    a1 = map(adc0, 1, 568, v_x_max, v_x_min);
    a0 = 0;
  }
  else if ( 560 < adc0 && adc0 < 580 ) {
    a0 = 0;
    a1 = 0;
  }
  adc1 = ads.readADC_SingleEnded(1); // ось Y право-лево
  if (560 < adc1 ) {
    a3 = map(adc1, 552, 1103, v_y_min, v_y_max);
    a2 = 0;
  }
  else if (adc1 < 545) {
    a2 = map(adc1, 1, 552, v_y_max, v_y_min);
    a3 = 0;
  }
  else if ( 545 < adc1 && adc1 < 560 ) {
    a2 = 0;
    a3 = 0;
  }

  mot1p = a0 + a3;
  mot1n = a1+a2;
  mot2p = a0 + a2;
  mot2n = a1+a3;
  mot3p = a0 + a3;
  mot3n = a1+a2;
  mot4p = a0 + a2;
  mot4n = a1+a3;


  Serial.print(adc0);
  Serial.print(" a0="); Serial.print(a0); Serial.print(" a1="); Serial.print(a1);
  Serial.print("   "); Serial.print(adc1);
  Serial.print(" a2="); Serial.print(a2); Serial.print(" a3="); Serial.print(a3);
  Serial.print(" aa0="); Serial.print(aa0); Serial.print(" aa2="); Serial.print(aa2);


  // delay(100);
  //  digitalWrite(mot, 1);
  //   delay(1000);
}
void setup() {
  Serial.begin(115200);


  ads.setGain(GAIN_TWOTHIRDS);  // 2/3x gain +/- 6.144V  1 bit = 3mV      0.1875mV (default)
  if (!ads.begin()) {
    Serial.println("Failed to initialize ADS.");
    while (1);
  }
  analogWriteRange(255);// разрядность шим

  // Выставляем режим работы Wi-Fi
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  // Инициализируем протокол ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  // Указываем роль платы в сети
  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);

  // Регистрируем callback-функцию для получения статуса отправки
  esp_now_register_send_cb(OnDataSent);

  // Регистрируем пиры
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
}

void loop() {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // Сохраняем время последнего обновления показаний
    previousMillis = currentMillis;
    Serial.print(" mot1p ");   Serial.print(driveReadings.mot1p);
    Serial.print(" mot1n ");   Serial.print(driveReadings.mot1n);
    Serial.print(" mot2p ");   Serial.print(driveReadings.mot2p);
    Serial.print(" mot2n ");   Serial.println(driveReadings.mot2n);
    //Запрашиваем показания датчика
    getReadings();

    //Записываем их в переменные
    driveReadings.mot1p = mot1p;
    driveReadings.mot1n = mot1n;
    driveReadings.mot2p = mot2p;
    driveReadings.mot2n = mot2n;
    driveReadings.mot3p = mot3p;
    driveReadings.mot3n = mot3n;
    driveReadings.mot4p = mot4p;
    driveReadings.mot4n = mot4n;


    // Отправляем сообщение
    esp_now_send(broadcastAddress, (uint8_t *) &driveReadings, sizeof(driveReadings));

  }
}


код для машины

Код: Выделить всёРазвернуть

#include <ESP8266WiFi.h>
#include <espnow.h>

// ЗАМЕНИТЕ МАС-АДРЕС платы, на которую отправляем данные.
uint8_t broadcastAddress[] = {0x58, 0xBF, 0x25, 0xD7, 0x53, 0x07};

int16_t m1p = D1;
int16_t m1n = D2;
int16_t m2p = D3;
int16_t m2n = D4;
int16_t m3p = D6;
int16_t m3n = D5;
int16_t m4p = D8;
int16_t m4n = D7;

// Вводим переменные для хранения принимаемых данных
int16_t mot1p, mot1n, mot2p, mot2n, mot3p, mot3n, mot4p, mot4n;
// Обновляем показания датчика каждые 1 секунд
const long interval = 100;
unsigned long previousMillis = 0;    // время последнего обновления

// Переменная для хранения состояния отправки
String success;

//Пример структуры для отправки
//Должна совпадать со структурой на плате-приемнике
typedef struct struct_message {
  int16_t mot1p;
  int16_t mot1n;
  int16_t mot2p;
  int16_t mot2n;
  int16_t mot3p;
  int16_t mot3n;
  int16_t mot4p;
  int16_t mot4n;
} ;

// Создаем переменную для хранения отправляемого сообщения
// То же, но для принимаемого сообщения
struct_message incomingReadings;

// определяем callback-функцию, которая выводит в монитор порта информацию о
//состоянии отправки. Если функция возвращает 0 – сообщение успешно доставлено
// То же, для индикации состояния приема данных
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));
Serial.print("Bytes received: ");
Serial.print(len);
mot1p = incomingReadings.mot1p;
mot1n = incomingReadings.mot1n;
mot2p = incomingReadings.mot2p;
mot2n = incomingReadings.mot2n;
mot3p = incomingReadings.mot3p;
mot3n = incomingReadings.mot3n;
mot4p = incomingReadings.mot4p;
mot4n = incomingReadings.mot4n;

}


void printIncomingReadings(){
// Отображаем показания в мониторе порта

//Serial.print(mot1p);Serial.print("  ");Serial.print(mot1n); Serial.print(incomingReadings.mot1p);

}



void setup(){
// Запускаем монитор порта
Serial.begin(115200);
analogWriteRange(255);// разрядность шим
analogWriteFreq(300); //analogWriteFreq(100.. 40000 Гц) на высокой частоте хуже запускаются двигатели с низкой скорости

// Выставляем режим работы Wi-Fi
WiFi.mode(WIFI_STA);
WiFi.disconnect();

// Инициализируем протокол ESP-NOW
if (esp_now_init() != 0) {
Serial.println("Error initializing ESP-NOW");
return;
}

// Указываем роль платы в сети
esp_now_set_self_role(ESP_NOW_ROLE_COMBO);


// Регистрируем callback-функцию для получения статуса приема
esp_now_register_recv_cb(OnDataRecv);
}


void loop(){

unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// Сохраняем время последнего обновления показаний
previousMillis = currentMillis;


// Выводим входящие данные
printIncomingReadings();

Serial.print("mot1p ");Serial.print(mot1p);
Serial.print(" mot1n ");Serial.print(mot1n);
Serial.print("mot2p ");Serial.print(mot2p);
Serial.print(" mot2n ");Serial.print(mot2n);
Serial.print("mot3p ");Serial.print(mot3p);
Serial.print(" mot3n ");Serial.print(mot3n);
Serial.print("mot4p ");Serial.print(mot4p);
Serial.print(" mot4n ");Serial.println(mot4n);



}
analogWrite(m1p,mot1p);
analogWrite(m1n,mot1n);
analogWrite(m2p,mot2p);
analogWrite(m2n,mot2n);
analogWrite(m3p,mot3p);
analogWrite(m3n,mot3n);
analogWrite(m4p,mot4p);
analogWrite(m4n,mot4n);

}



пришлось частоту шима сделать 300Гц, минимальное заполнение шим при котором двигатели начинают крутиться 50 из 255.
При частоте ШИМ 5кГц(еле пищит) стартуют 130 из техже 255.
При частоте ШИМ 15кГц(тихо) стартуют 150 из техже 255.
Хотел узнать что за запрет(взято с интернета) использования в ESP NODE MCU v.3 пина D0(GPIO16) как ШИМ. Я по незнанке им регулировал св.диод все вроде работало, потом после более детального ознакомления с ESP прочитал про запрет, но никто не объясняет почему.

Re: машина на управлении по ESP с полным приводом по танково

ShadowOleg » 10 май 2022, 12:59

прикрутил экран на пульт, глубокий сон при разряде аккумулятора и бездействии.
Перепаял 224 резистор на 105 в плече делителя напряжения на аналоговом входе, так можно мерить до 11В.
IMG_20220510_112111.jpg

Меряю последовательное соединение двух банок Li. Как можно простенько организовать измерение каждой банки при наличии только одного аналогового входа?
Проверил дальность связи(выводом на экран отображения удачный и неудачных передач данных). В пределах комнаты и в соседней комнате через стену все идеально, через 1 комнату и 2 стены появляются потери примерно 1-5% от удачных, хотя машина остается более чем управляемой. До металлического корпуса было без потерь.
IMG_20220510_105615.jpg

машина
Код: Выделить всёРазвернуть
#include <ESP8266WiFi.h>
#include <espnow.h>

// ЗАМЕНИТЕ МАС-АДРЕС платы, на которую отправляем данные.
uint8_t broadcastAddress[] = {0x58, 0xBF, 0x25, 0xD6, 0x87, 0x17};
//подключение контактов драйвера двигателей
int16_t m1p = D1;
int16_t m1n = D2;
int16_t m2p = D3;
int16_t m2n = D4;
int16_t m3p = D6;
int16_t m3n = D5;
int16_t m4p = D8;
int16_t m4n = D7;
//float u_mashina=0;
// Вводим переменные для хранения принимаемых данных
int16_t mot1p, mot1n, mot2p, mot2n, mot3p, mot3n, mot4p, mot4n;
// Обновляем показания датчика каждые 1 секунд
const long interval = 100;
unsigned long previousMillis = 0;    // время последнего обновления
int32_t predZarydTime = 0; //время последнего считывания заряда акумулятора
int32_t predZarydTimeOff = 0; //время последней проверки на откл заряда акумулятора

bool flagPeredMashinaOk = 0; // флаг успешности передачи

String success;// Переменная для хранения состояния отправки

//Пример структуры для отправки
//Должна совпадать структура на плате-приемнике и плате-передатчике
typedef struct struct_message_m {
  uint16_t u_mashina = 0;
  bool flagMashinaOff = 0; // флаг выключения машины
  uint32_t peredMashinaOk = 0;
  uint32_t peredMashinaF = 0;
};// Создаем переменную для хранения отправляемого сообщения
struct_message_m messageMashina;

// структура приема
typedef struct struct_message_p {
  int16_t mot1p;
  int16_t mot1n;
  int16_t mot2p;
  int16_t mot2n;
  int16_t mot3p;
  int16_t mot3n;
  int16_t mot4p;
  int16_t mot4n;
  bool flagPultOff = 0;
} ; // То же, но для принимаемого сообщения
struct_message_p messagePult;

// определяем callback-функцию, которая выводит в монитор порта информацию о
//состоянии отправки. Если функция возвращает 0 – сообщение успешно доставлено
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  //  Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0) {
    //   Serial.println("удачно");
    messageMashina.peredMashinaOk++;
    flagPeredMashinaOk = 1;
  }
  else {
    //  Serial.println("fail");
    messageMashina.peredMashinaF++;
    flagPeredMashinaOk = 0;
  }
}


// То же, для индикации состояния приема данных
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&messagePult, incomingData, sizeof(messagePult));
  //Serial.print("Bytes received: ");
  //Serial.print(len);
}
/*void printSerial() {// Отображаем показания в мониторе порта
  Serial.print("mot1p="); Serial.print(mot1p);
  Serial.print(" mot1n="); Serial.print(mot1n);
  Serial.print(" mot2p="); Serial.print(mot2p);
  Serial.print(" mot2n="); Serial.print(mot2n);
  Serial.print(" mot3p="); Serial.print(mot3p);
  Serial.print(" mot3n="); Serial.print(mot3n);
  Serial.print(" mot4p="); Serial.print(mot4p);
  Serial.print(" mot4n="); Serial.print(mot4n);
  Serial.print(" ok="); Serial.print(messageMashina.peredMashinaOk);
  Serial.print(" F="); Serial.print(messageMashina.peredMashinaF);
  Serial.print(" U="); Serial.print((float)messageMashina.u_mashina / 100); Serial.println("V");
  // Serial.println((float)messageMashina.u_mashina / 100);
}*/
void zaryd() {// проверка на отключение по заряду акумулятора
  if (messageMashina.u_mashina > 670) { // если заряд больше 6,7В обновляем таймер
    predZarydTimeOff = millis();
  }
  // если заряд менише 6,7В и таймер превысил -> спать
  if ((messageMashina.u_mashina < 670) && millis() - predZarydTimeOff >= 4000) {
    messageMashina.flagMashinaOff = 1;
  }
}
void Sleep() { //проверка на сон сон
  if (messageMashina.flagMashinaOff or messagePult.flagPultOff ) {
    ESP.deepSleep(0);
  }
}

void drive() { // если неудачно передает на пульт выключить двигатели
  if (!flagPeredMashinaOk) {
    messagePult.mot1p = 0;
    messagePult.mot1n = 0;
    messagePult.mot2p = 0;
    messagePult.mot2n = 0;
    messagePult.mot3p = 0;
    messagePult.mot3n = 0;
    messagePult.mot4p = 0;
    messagePult.mot4n = 0;
  }
  analogWrite(m1p, messagePult.mot1p);
  analogWrite(m1n, messagePult.mot1n);
  analogWrite(m2p, messagePult.mot2p);
  analogWrite(m2n, messagePult.mot2n);
  analogWrite(m3p, messagePult.mot3p);
  analogWrite(m3n, messagePult.mot3n);
  analogWrite(m4p, messagePult.mot4p);
  analogWrite(m4n, messagePult.mot4n);
}
void setup() {
  // Запускаем монитор порта
  Serial.begin(115200);
  analogWriteRange(255);// разрядность шим
  analogWriteFreq(300); //analogWriteFreq(100.. 40000 Гц) на высокой частоте хуже запускаются двигатели с низкой скорости

  // Выставляем режим работы Wi-Fi
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  // Инициализируем протокол ESP-NOW
  if (esp_now_init() != 0) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }

  // Указываем роль платы в сети
  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);

  // Регистрируем callback-функцию отправки для получения статуса отправки
  esp_now_register_send_cb(OnDataSent);

  // Регистрируем callback-функцию приема для получения статуса приема
  esp_now_register_recv_cb(OnDataRecv);

  // Регистрируем пиры
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
}

void loop() {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // Сохраняем время последнего обновления показаний
    previousMillis = currentMillis;

   // printSerial(); // Выводим  данные в serial

    drive();// передаем полученные данные на H мосты

    if (millis() - predZarydTime > 1000) { //считываем заряда акум раз в сек
      predZarydTime = millis();
      messageMashina.u_mashina = map(analogRead(A0), 3, 1024, 0, 1095);// считываем напряжение акумулятора
    }
    zaryd();// проверка на отключение по заряду акумулятора

    // Отправляем сообщение
    esp_now_send(broadcastAddress, (uint8_t *) &messageMashina, sizeof(messageMashina));

    Sleep();//проверка на сон !после отправки

  }


}

пульт+ekr
Код: Выделить всёРазвернуть
#include <Wire.h>
//#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h>
//Adafruit_SSD1306 display(-1);
// указываем размер экрана в пикселях
Adafruit_SSD1306 display(128, 64, &Wire, -1);//4);  Reset pin # (or -1 if sharing Arduino reset pin)

#include <ESP8266WiFi.h>
#include <espnow.h>
// ЗАМЕНИТЕ МАС-АДРЕС платы, на которую отправляем данные.
uint8_t broadcastAddress[] = {0xF4, 0xCF, 0xA2, 0xD7, 0xC6, 0x91};
#include <Adafruit_ADS1X15.h>
Adafruit_ADS1015 ads;
//ADC_MODE(ADC_VCC);//для измерения напряжения питания МК


const long interval = 100;// интервал считывания джойстиков и отправки
uint32_t intervalOff = 300e3; //время в мс до отключения при бездействии
unsigned long previousMillis = 0;    // время последнего обновления
uint32_t predZarydTime = 0; //опрос заряда акумулятора
uint32_t predTime = 0; //проверка на сон
uint32_t predZarydTime2 = 0; //опрос заряда акумулятора
bool flagDisplay = 1; // флаг включения дисплея
bool flagButton = 0; // флаг нажатия кнопок
bool flagOff = 0;  // флаг отключения пульта
String success;// Переменная для хранения состояния отправки
uint32_t peredPultOk = 0;
uint32_t peredPultF = 0;
uint32_t prinPult = 0;
uint32_t peredMashina = 0;
uint32_t prinMashina = 0;

//Пример структуры для отправки
//Должна совпадать структура на плате-приемнике и плате-передатчике
struct struct_message_p {
  int16_t mot1p;
  int16_t mot1n;
  int16_t mot2p;
  int16_t mot2n;
  int16_t mot3p;
  int16_t mot3n;
  int16_t mot4p;
  int16_t mot4n;
  bool flagPultOff = 0; // флаг выключения пульта
};// Создаем переменную для хранения отправляемого сообщения
struct_message_p messagePult;

//Пример структуры для приема
//Должна совпадать структура на плате-приемнике и плате-передатчике
typedef struct struct_message_m {
  uint16_t u_mashina = 0;
  bool flagMashinaOff = 0; // флаг выключения машины
  uint32_t peredMashinaOk = 0;
  uint32_t peredMashinaF = 0;
};// Создаем переменную для хранения отправляемого сообщения
struct_message_m messageMashina;
uint16_t u_pult = 0; //напряжение акумулятора пульта

int16_t adc0, adc1; // сырые данные джойстика
int16_t adcsr0 = 559; // показания джойстика в 0 положении по х
int16_t adcsr1 = 535; // показания джойстика в 0 положении по у
int8_t adc0_dr = 20; // дрейф джойстика по х
int8_t adc1_dr = 20; // дрейф джойстика по y

int16_t a0, a1, a2, a3 = 0;

uint8_t v_x_min = 60;
uint8_t v_y_min = 50;
uint8_t v_x_max = 255;
uint8_t v_y_max = 150;

// определяем callback-функцию, которая выводит в монитор порта информацию о
//состоянии отправки. Если функция возвращает 0 – сообщение успешно доставлено
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  //  Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0) {
    //   Serial.println("удачно");
    peredPultOk++;
  }
  else {
    //  Serial.println("fail");
    peredPultF++;
  }
}

// То же, для индикации состояния приема данных
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&messageMashina, incomingData, sizeof(messageMashina));
  //Serial.print("Bytes received: ");
  //Serial.println(len);

}


void ReadADS() { //считывание джойстиков и приведение к мин -макс скоростям
  adc0 = ads.readADC_SingleEnded(0); //ось Х вперед-назад
  if (adcsr0 + adc0_dr < adc0 ) {
    a0 = map(adc0, adcsr0, 1103, v_x_min, v_x_max);
    a1 = 0;

  }
  else if (adc0 < adcsr0 - adc0_dr) {
    a1 = map(adc0, 1, adcsr0, v_x_max, v_x_min);
    a0 = 0;

  }
  else if (adcsr0 - adc0_dr < adc0 && adc0 < adcsr0 + adc0_dr ) {
    a0 = 0;
    a1 = 0;

  }
  adc1 = ads.readADC_SingleEnded(1); // ось Y право-лево
  if (adcsr1 + adc1_dr < adc1 ) {
    a3 = map(adc1, adcsr1, 1103, v_y_min, v_y_max);
    a2 = 0;

  }
  else if (adc1 < adcsr1 - adc1_dr) {
    a2 = map(adc1, 1, adcsr1, v_y_max, v_y_min);
    a3 = 0;

  }
  else if ( adcsr1 - adc1_dr < adc1 && adc1 < adcsr1 + adc1_dr ) {
    a2 = 0;
    a3 = 0;


  }
  if (a0 == 0 && a1 == 0 && a2 == 0 && a3 == 0)
    flagButton = 0;
  else
    flagButton = 1;
}

void DrivePWM() { //расчет шим двигателей для движений
  messagePult.mot1p = a0 + a3;
  messagePult.mot1n = a1 + a2;
  messagePult.mot2p = a0 + a2;
  messagePult.mot2n = a1 + a3;
  messagePult.mot3p = a0 + a3;
  messagePult.mot3n = a1 + a2;
  messagePult.mot4p = a0 + a2;
  messagePult.mot4n = a1 + a3;
}

void Display() { // Вывод данных на дисплей
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);

  display.print("drive"); display.print((float)messageMashina.u_mashina / 100);  display.print("V");
  display.print(" pult"); display.print((float)u_pult / 100);;  display.println("V");
  //display.println(ESP.getVcc()); //вывод напряжения МК
  // display.println((float)map(analogRead(A0), 5, 1024, 0, 1095) / 10);
  display.print("adc0 "); display.print(adc0);
  display.print("   adc1 "); display.println(adc1);
  display.print("a0 ");   display.print(a0);
  display.print("a1 ");   display.print(a1);
  display.print(" a2 ");   display.print(a2);
  display.print("a3 ");   display.println(a3);

  display.print("pult:"); display.print("OK ");   display.print(peredPultOk);
  display.print(" Fail "); display.println(peredPultF);

  display.print("mash:"); display.print("OK ");   display.print(messageMashina.peredMashinaOk);
  display.print(" Fail "); display.print(messageMashina.peredMashinaF);
  display.display();
}
void Sleep() {
  if (flagButton) {   // если кнопки нажаты обновляем таймер
    predTime = millis();
  }
  // если кнопки НЕ нажаты и таймер превысил-> спать
  if (!flagButton && millis() - predTime >= intervalOff) {
    display.clearDisplay();
    display.setTextSize(4);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.print("SLEEP");
    display.display();
    delay(5000);
    display.ssd1306_command(SSD1306_DISPLAYOFF);
    ESP.deepSleep(0);
    //  ESP.deepSleep(4200e6);
  }
  if ( messageMashina.flagMashinaOff) {
    display.clearDisplay();
    display.setTextSize(4);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("SLEEP");
    display.setTextSize(2);
    display.startscrollleft(0x04, 0x07);
    display.println(" MASHINA  ");
    display.println("   SPIT   ");
    display.display();
    delay(7000);
    display.ssd1306_command(SSD1306_DISPLAYOFF);
    ESP.deepSleep(0);
  }






}

void setup() {
  Serial.begin(115200);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  // Очистить буфер дисплея.
  display.clearDisplay();
  display.setTextSize(3);
  display.setTextColor(WHITE);
  display.setCursor(0, 6);
  display.print("poehali");
  display.display();
  delay(2000);
  ads.setGain(GAIN_TWOTHIRDS);  // 2/3x gain +/- 6.144V  1 bit = 3mV      0.1875mV (default)
  ads.begin();
  /*if (!ads.begin()) {
     Serial.println("Failed to initialize ADS.");
     while (1);
    }*/
  analogWriteRange(255);// разрядность шим

  // Выставляем режим работы Wi-Fi
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  // Инициализируем протокол ESP-NOW
  esp_now_init();
  /* if (esp_now_init() != 0) {
     Serial.println("Error initializing ESP-NOW");
     return;
    }*/
  // Указываем роль платы в сети
  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);

  // Регистрируем callback-функцию для получения статуса отправки
  esp_now_register_send_cb(OnDataSent);

  // Регистрируем callback-функцию для получения статуса приема
  esp_now_register_recv_cb(OnDataRecv);


  // Регистрируем пиры
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
}

void loop() {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;// Сохраняем время последнего обновления показаний
    //Запрашиваем показания датчика
    ReadADS();
    DrivePWM();
    if (millis() - predZarydTime > 1000) {
      predZarydTime = millis();
      u_pult = map(analogRead(A0), 3, 1024, 0, 1095);// считываем напряжение акумулятора
      if (u_pult < 660) { // если напряжение акумулятора меньше6.6-> спать
        Sleep();
      }
    }
    Display();
    // Отправляем сообщение
    esp_now_send(broadcastAddress, (uint8_t *) &messagePult, sizeof(messagePult));

    Sleep(); //проверка на сон !после отправки
  }


  /* if ( flagDisplay==1 &&   millis() - predZarydTime >= 5000) {
     predZarydTime1 = millis();
       predZarydTime1 = millis();
     display.ssd1306_command(SSD1306_DISPLAYOFF);
    flagDisplay=0;
     ESP.deepSleep(20e6);
    }

     if (flagDisplay==0 &&   millis() - predZarydTime1 >= 20000) {
       predZarydTime1 = millis();
       predZarydTime = millis();
       display.ssd1306_command(SSD1306_DISPLAYON);
       flagDisplay=1;
     }*/

  //ESP.deepSleep(5e6);




}

Re: машина на управлении по ESP с полным приводом по танково

ShadowOleg » 23 июн 2022, 01:45

прикрутил гироскоп 6050 испольуя DMп,правда ребенку не понравилось управлять наклонами, поэтому бросил равивать гироскоп. аодно пришли джойстики с плойки, в раы лучше ардуиновских,работают во всем диапаоне наклона, правда есть небольшой дрейф при самововрате в 3-6 ед их 1024, и вместо ардуиновских не апаять, не совпадают контакты. сделал свои
IMG_20220622_204146.jpg

pult_ekr_giroskop
Код: Выделить всёРазвернуть
#include <Wire.h>
//#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h>
// указываем размер экрана в пикселях
Adafruit_SSD1306 display(128, 64, &Wire, -1);//4);  Reset pin # (or -1 if sharing Arduino reset pin)

#include <ESP8266WiFi.h>
#include <espnow.h>
// ЗАМЕНИТЕ МАС-АДРЕС платы, на которую отправляем данные.
uint8_t broadcastAddress[] = {0xF4, 0xCF, 0xA2, 0xD7, 0xC6, 0x91};

#include <Adafruit_ADS1X15.h>
Adafruit_ADS1015 ads;
//ADC_MODE(ADC_VCC);//для измерения напряжения питания МК

#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
//#include "MPU6050.h" // not necessary if using MotionApps include file // не требуется, если используется включаемый файл MotionApps
#include "Wire.h"
MPU6050 mpu;



const long interval = 100;// интервал считывания джойстиков и отправки
uint32_t intervalOff = 300e3; //время в мс до отключения при бездействии
unsigned long previousMillis = 0;    // время последнего обновления
uint32_t predZarydTime = 0; //опрос заряда акумулятора
uint32_t predTime = 0; //проверка на сон
uint32_t predZarydTime2 = 0; //опрос заряда акумулятора
bool flagDisplay = 1; // флаг включения дисплея
bool flagButton = 0; // флаг нажатия кнопок
bool flagOff = 0;  // флаг отключения пульта
String success;// Переменная для хранения состояния отправки
uint32_t peredPultOk = 0;
uint32_t peredPultF = 0;
uint32_t prinPult = 0;
uint32_t peredMashina = 0;
uint32_t prinMashina = 0;

//Пример структуры для отправки
//Должна совпадать структура на плате-приемнике и плате-передатчике
struct struct_message_p {
  int16_t mot1p;
  int16_t mot1n;
  int16_t mot2p;
  int16_t mot2n;
  int16_t mot3p;
  int16_t mot3n;
  int16_t mot4p;
  int16_t mot4n;
  bool flagPultOff = 0; // флаг выключения пульта
};// Создаем переменную для хранения отправляемого сообщения
struct_message_p messagePult;

//Пример структуры для приема
//Должна совпадать структура на плате-приемнике и плате-передатчике
typedef struct struct_message_m {
  uint16_t u_mashina = 0;
  bool flagMashinaOff = 0; // флаг выключения машины
  uint32_t peredMashinaOk = 0;
  uint32_t peredMashinaF = 0;
};// Создаем переменную для хранения отправляемого сообщения
struct_message_m messageMashina;
uint16_t u_pult = 0; //напряжение акумулятора пульта

int16_t adc0, adc1; // сырые данные джойстика
int16_t adcsr0 = 559; // показания джойстика в 0 положении по х
int16_t adcsr1 = 535; // показания джойстика в 0 положении по у
int8_t adc0_dr = 20; // дрейф джойстика по х
int8_t adc1_dr = 20; // дрейф джойстика по y

int16_t x, y = 0; //данные гироскопа
int8_t x_dr = 15; // дрейф гироскопа по х
int8_t y_dr = 15; // дрейф гироскопа по y
#define ugol 120
int16_t a0, a1, a2, a3 = 0;
int16_t g0, g1, g2, g3 = 0;

uint8_t v_x_min = 60;
uint8_t v_y_min = 50;
uint8_t v_x_max = 255;
uint8_t v_y_max = 120;

// переменные управления/состояния MPU
// FIFO storage buffer // Буфер хранения FIFO
uint8_t fifoBuffer[45];
// orientation/motion vars
// переменные ориентации/движения
// [w, x, y, z]         quaternion container
// [w, x, y, z] контейнер кватернионов
Quaternion q;
// [x, y, z]            gravity vector
// [x, y, z]            gravity vector
VectorFloat gravity;
// [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
// [рыскание, тангаж, крен] контейнер рыскания/тангажа/крена и вектор гравитации
float ypr[3];

// определяем callback-функцию, которая выводит в монитор порта информацию о
//состоянии отправки. Если функция возвращает 0 – сообщение успешно доставлено
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  //  Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0) {
    //   Serial.println("удачно");
    peredPultOk++;
  }
  else {
    //  Serial.println("fail");
    peredPultF++;
  }
}

// То же, для индикации состояния приема данных
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&messageMashina, incomingData, sizeof(messageMashina));
  //Serial.print("Bytes received: ");
  //Serial.println(len);

}


void ReadADS() { //считывание джойстиков и приведение к мин -макс скоростям
  adc0 = ads.readADC_SingleEnded(0); //ось Х вперед-назад
  if (adcsr0 + adc0_dr < adc0 ) {
    a0 = map(adc0, adcsr0, 1103, v_x_min, v_x_max);
    a1 = 0;
  }
  else if (adc0 < adcsr0 - adc0_dr) {
    a1 = map(adc0, 1, adcsr0, v_x_max, v_x_min);
    a0 = 0;
  }
  else if (adcsr0 - adc0_dr < adc0 && adc0 < adcsr0 + adc0_dr ) {
    a0 = 0;
    a1 = 0;
  }
  adc1 = ads.readADC_SingleEnded(1); // ось Y право-лево
  if (adcsr1 + adc1_dr < adc1 ) {
    a3 = map(adc1, adcsr1, 1103, v_y_min, v_y_max);
    a2 = 0;
  }
  else if (adc1 < adcsr1 - adc1_dr) {
    a2 = map(adc1, 1, adcsr1, v_y_max, v_y_min);
    a3 = 0;
  }
  else if ( adcsr1 - adc1_dr < adc1 && adc1 < adcsr1 + adc1_dr ) {
    a2 = 0;
    a3 = 0;
  }
  if (a0 == 0 && a1 == 0 && a2 == 0 && a3 == 0)
    flagButton = 0;
  else
    flagButton = 1;
}
void readMPU() { // считывание гироскопа и приведение к мин -макс скоростям

  x = ypr[1] * 256;
  y = ypr[2] * 256;
x=constrain(x, -ugol, ugol);
y=constrain(y, -ugol, ugol);

  //ось Х вперед-назад
  if ( x_dr <= x ) { // вперед
    g0 = map(x, x_dr, ugol, v_x_min, v_x_max);
    g1 = 0;
  }
  else if (x <= -x_dr) { //назад
    g1 = map(x, -x_dr, -ugol, v_x_min, v_x_max);
    g0 = 0;
  }
  else if (-x_dr < x && x < x_dr ) { //стоп
    g0 = 0;
    g1 = 0;
  }
// ось Y право-лево
  if (y_dr <= y ) { // право
    g2 = map(y, y_dr, ugol, v_y_min, v_y_max);
    g3 = 0;
  }
  else if (y <= -y_dr) { // лево
    g3 = map(y, -y_dr, -ugol, v_y_min, v_y_max );
    g2 = 0;
  }
  else if ( -y_dr < y && y < y_dr ) { // стоп
    g2 = 0;
    g3 = 0;
  }


 
}



void DrivePWM() { //расчет шим двигателей для движений
if(a0<g0){
  a0=g0;
}

if(a1<g1) a1=g1;
if(a2<g2) a2=g2;
if(a3<g3) a3=g3;
 
  messagePult.mot1p = a0 + a3;
  messagePult.mot1n = a1 + a2;
  messagePult.mot2p = a0 + a2;
  messagePult.mot2n = a1 + a3;
  messagePult.mot3p = a0 + a3;
  messagePult.mot3n = a1 + a2;
  messagePult.mot4p = a0 + a2;
  messagePult.mot4n = a1 + a3;
}

void Display() { // Вывод данных на дисплей
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);

  display.print("drive"); display.print((float)messageMashina.u_mashina / 100);  display.print("V");
  display.print(" pult"); display.print((float)u_pult / 100);  display.println("V");
  //display.println(ESP.getVcc()); //вывод напряжения МК
  // display.println((float)map(analogRead(A0), 5, 1024, 0, 1095) / 10);
  display.print("adc0 "); display.print(adc0); display.print("   adc1 "); display.println(adc1);
  display.print("a0=");   display.print(a0);
  display.print("a1=");   display.print(a1);
  display.print(" a2=");   display.print(a2);
  display.print("a3=");   display.println(a3);

    display.print("x="); display.print(x); display.print("       y="); display.println(y);
  display.print("g0=");   display.print(g0);
  display.print("g1=");   display.print(g1);
  display.print(" g2=");   display.print(g2);
  display.print("g3=");   display.println(g3);

  display.print("pult:"); display.print("OK ");   display.print(peredPultOk);
  display.print(" Fail "); display.println(peredPultF);

  display.print("mash:"); display.print("OK ");   display.print(messageMashina.peredMashinaOk);
  display.print(" Fail "); display.print(messageMashina.peredMashinaF);
  display.display();
}
void Sleep() {
  if (flagButton) {   // если кнопки нажаты обновляем таймер
    predTime = millis();
  }
  // если кнопки НЕ нажаты и таймер превысил-> спать
  if (!flagButton && millis() - predTime >= intervalOff) {
    display.clearDisplay();
    display.setTextSize(4);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.print("SLEEP");
    display.display();
    delay(5000);
    display.ssd1306_command(SSD1306_DISPLAYOFF);
    ESP.deepSleep(0);
    //  ESP.deepSleep(4200e6);
  }
  if ( messageMashina.flagMashinaOff) {
    display.clearDisplay();
    display.setTextSize(4);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("SLEEP");
    display.setTextSize(2);
    display.startscrollleft(0x04, 0x07);
    display.println(" MASHINA  ");
    display.println("   SPIT   ");
    display.display();
    delay(7000);
    display.ssd1306_command(SSD1306_DISPLAYOFF);
    ESP.deepSleep(0);
  }






}

void setup() {
  Serial.begin(115200);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  // Очистить буфер дисплея.
  display.clearDisplay();
  display.setTextSize(3);
  display.setTextColor(WHITE);
  display.setCursor(0, 6);
  display.print("poehali");
  display.display();
  delay(1000);
  ads.setGain(GAIN_TWOTHIRDS);  // 2/3x gain +/- 6.144V  1 bit = 3mV      0.1875mV (default)
  ads.begin();

  //  analogWriteRange(255);// разрядность шим

  // подключаемся к шине I2C (библиотека I2Cdev не делает этого автоматически)
  Wire.begin();
  // Wire.setClock(400000);
  // initialize device  // инициализируем устройство
  mpu.initialize();
  // загрузить и настроить DMP
  mpu.dmpInitialize();
  // укажите здесь собственные смещения гироскопа, масштабированные для минимальной чувствительности
  //  mpu.setXGyroOffset(220);
  //  mpu.setYGyroOffset(76);
  // mpu.setZGyroOffset(-85);
  // mpu.setZAccelOffset(1788); // 1688 factory default for my test chip // 1688 по умолчанию для моего тестового чипа

  // Время калибровки: генерируем смещения и калибруем наш MPU6050
  mpu.CalibrateAccel(6);
  mpu.CalibrateGyro(6);
  //   mpu.PrintActiveOffsets();
  // включаем DMP, теперь, когда он готов
  mpu.setDMPEnabled(true);


  // Выставляем режим работы Wi-Fi
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  // Инициализируем протокол ESP-NOW
  esp_now_init();
  /* if (esp_now_init() != 0) {
     Serial.println("Error initializing ESP-NOW");
     return;
    }*/
  // Указываем роль платы в сети
  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);

  // Регистрируем callback-функцию для получения статуса отправки
  esp_now_register_send_cb(OnDataSent);

  // Регистрируем callback-функцию для получения статуса приема
  esp_now_register_recv_cb(OnDataRecv);


  // Регистрируем пиры
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
}

void loop() {

  static uint32_t tmr;
  if (millis() - tmr >= 11) {  // таймер на 11 мс
    tmr = millis();  // сброс таймера
    // read a packet from FIFO  // читаем пакет из FIFO
    if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) { // Get the Latest packet // Получить последний пакет
      // гироскоп угол поворота
      mpu.dmpGetQuaternion(&q, fifoBuffer);
      mpu.dmpGetGravity(&gravity, &q);
      mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
    }
  }

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;// Сохраняем время последнего обновления показаний
    //Запрашиваем показания датчика
    ReadADS();
    readMPU();
    DrivePWM();
    if (millis() - predZarydTime > 1000) {
      predZarydTime = millis();
      u_pult = map(analogRead(A0), 3, 1024, 0, 1095);// считываем напряжение акумулятора
      if (u_pult < 660) { // если напряжение акумулятора меньше6.6-> спать
        Sleep();
      }
    }
    Display();
    // Отправляем сообщение
    esp_now_send(broadcastAddress, (uint8_t *) &messagePult, sizeof(messagePult));

    Sleep(); //проверка на сон !после отправки
  }






}


Добавлено спустя 35 минут 3 секунды:
Re: машина на управлении по ESP с полным приводом по танковой сх
клава купалась в пиве. Собрал платкувкл выкл одной кнопкой с воможностью выкл с МК т.к. ребенок часто абывает выкл.
Schematic_knopka1_2022-06-22.png

IMG_20220619_122257.jpg

схема не любит емкость на выходепокрутил С1 R2.
арядка на тп4056, повышайку мт3608 и 220 мкФ на ее входе поставил на4.5В, 30 витков 2провода на маленький еленый феррит и 50мкФ фильтр пульсаций пред ES8266(не лучший вариант для 1.2 МГц,но чтобыло, бе него дальность падает ра в 5).
Также вписал в код OTA для прошивки cWiFi
pult_ekr_OTA
Код: Выделить всёРазвернуть
#include <ESP8266WiFi.h>         //provides ESP8266 specific Wi-Fi routines we are calling to connect to network.
#include <espnow.h>
#include <Adafruit_SSD1306.h>
#include <ArduinoOTA.h>  // OTA library (библиотека для работы с технологией OTA)
Adafruit_SSD1306 display(128, 64, &Wire, -1);//4);  Reset pin # (or -1 if sharing Arduino reset pin)
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_ADS1X15.h>

Adafruit_ADS1015 ads;
// ЗАМЕНИТЕ МАС-АДРЕС платы, на которую отправляем данные.
uint8_t broadcastAddress[] = {0xF4, 0xCF, 0xA2, 0xD7, 0xC6, 0x91};

#ifndef STASSID
#define STASSID "Shadow"  // имя wifi
#define STAPSK  "1111" //пароль
#endif
const char* ssid = STASSID;
const char* password = STAPSK;


const long interval = 100;// интервал считывания джойстиков и отправки
uint32_t intervalOff = 120e3; //время в мс до отключения при бездействии
unsigned long previousMillis = 0;    // время последнего обновления
uint32_t predZarydTime = 0; //опрос заряда акумулятора
uint32_t predTime = 0; //проверка на сон
uint32_t predZarydTime2 = 0; //опрос заряда акумулятора
bool flagDisplay = 1; // флаг включения дисплея
bool flagButton = 0; // флаг нажатия кнопок
bool flagOff = 0;  // флаг отключения пульта
String success;// Переменная для хранения состояния отправки
uint32_t peredPultOk = 0;
uint32_t peredPultF = 0;
uint32_t prinPult = 0;
uint32_t peredMashina = 0;
uint32_t prinMashina = 0;

//Пример структуры для отправки
//Должна совпадать структура на плате-приемнике и плате-передатчике
struct struct_message_p {
  int16_t mot1p;
  int16_t mot1n;
  int16_t mot2p;
  int16_t mot2n;
  int16_t mot3p;
  int16_t mot3n;
  int16_t mot4p;
  int16_t mot4n;
  bool flagPultOff = 0; // флаг выключения пульта
};// Создаем переменную для хранения отправляемого сообщения
struct_message_p messagePult;

//Пример структуры для приема
//Должна совпадать структура на плате-приемнике и плате-передатчике
typedef struct struct_message_m {
  uint16_t u_mashina = 0;
  bool flagMashinaOff = 0; // флаг выключения машины
  uint32_t peredMashinaOk = 0;
  uint32_t peredMashinaF = 0;
};// Создаем переменную для хранения отправляемого сообщения
struct_message_m messageMashina;
uint16_t u_pult = 0; //напряжение акумулятора пульта

int16_t adc0, adc1; // сырые данные джойстика
int16_t adcsr0 = 530; // показания джойстика в 0 положении по х
int16_t adcsr1 = 545; // показания джойстика в 0 положении по у
int8_t adc0_dr = 40; // дрейф джойстика по х
int8_t adc1_dr = 50; // дрейф джойстика по y

int16_t a0, a1, a2, a3 = 0;

uint8_t v_x_min = 60;
uint8_t v_y_min = 50;
uint8_t v_x_max = 255;
uint8_t v_y_max = 150;

// определяем callback-функцию, которая выводит в монитор порта информацию о
//состоянии отправки. Если функция возвращает 0 – сообщение успешно доставлено
void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) {
  //  Serial.print("Last Packet Send Status: ");
  if (sendStatus == 0) {
    //   Serial.println("удачно");
    peredPultOk++;
  }
  else {
    //  Serial.println("fail");
    peredPultF++;
  }
}

// То же, для индикации состояния приема данных
void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) {
  memcpy(&messageMashina, incomingData, sizeof(messageMashina));
  //Serial.print("Bytes received: ");
  //Serial.println(len);

}


void ReadADS() { //считывание джойстиков и приведение к мин -макс скоростям
  adc0 = ads.readADC_SingleEnded(0); //ось Х вперед-назад
  if (adcsr0 + adc0_dr < adc0 ) {
    a1 = map(adc0, adcsr0, 975, v_x_min, v_x_max);
    a0 = 0;

  }
  else if (adc0 < adcsr0 - adc0_dr) {
    a0 = map(adc0, 45, adcsr0, v_x_max, v_x_min);
    a1 = 0;

  }
  else if (adcsr0 - adc0_dr < adc0 && adc0 < adcsr0 + adc0_dr ) {
    a0 = 0;
    a1 = 0;

  }
  adc1 = ads.readADC_SingleEnded(1); // ось Y право-лево
  if (adcsr1 + adc1_dr < adc1 ) {
    a2 = map(adc1, adcsr1, 1010, v_y_min, v_y_max);
    a3 = 0;

  }
  else if (adc1 < adcsr1 - adc1_dr) {
    a3 = map(adc1, 98, adcsr1, v_y_max, v_y_min);
    a2 = 0;

  }
  else if ( adcsr1 - adc1_dr < adc1 && adc1 < adcsr1 + adc1_dr ) {
    a2 = 0;
    a3 = 0;


  }
  if (a0 == 0 && a1 == 0 && a2 == 0 && a3 == 0)
    flagButton = 0;
  else
    flagButton = 1;
}

void DrivePWM() { //расчет шим двигателей для движений
  messagePult.mot1p = a0 + a3;
  messagePult.mot1n = a1 + a2;
  messagePult.mot2p = a0 + a2;
  messagePult.mot2n = a1 + a3;
  messagePult.mot3p = a0 + a3;
  messagePult.mot3n = a1 + a2;
  messagePult.mot4p = a0 + a2;
  messagePult.mot4n = a1 + a3;
}

void Display() { // Вывод данных на дисплей
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0, 0);

  display.print("drive"); display.print((float)messageMashina.u_mashina / 100);  display.print("V");
  //display.print(" pult"); display.print((float)u_pult / 100);  display.println("V");
  display.print("  pult:"); display.print(map(u_pult, 300, 420, 0, 100));  display.println("%");
  //display.println(ESP.getVcc()); //вывод напряжения МК
  // display.println((float)map(analogRead(A0), 5, 1024, 0, 1095) / 10);
  display.print("adc0 "); display.print(adc0);
  display.print("   adc1 "); display.println(adc1);
  display.print("a0 ");   display.print(a0);
  display.print("a1 ");   display.print(a1);
  display.print(" a2 ");   display.print(a2);
  display.print("a3 ");   display.println(a3);

  display.print("pult:"); display.print("OK ");   display.print(peredPultOk);
  display.print(" Fail "); display.println(peredPultF);

  display.print("mash:"); display.print("OK ");   display.print(messageMashina.peredMashinaOk);
  display.print(" Fail "); display.print(messageMashina.peredMashinaF);
  display.display();
}
void OFF() {
  if (flagButton) {   // если кнопки нажаты обновляем таймер
    predTime = millis();
  }
  // если кнопки НЕ нажаты и таймер превысил-> спать
  if (!flagButton && millis() - predTime >= intervalOff) {
    display.clearDisplay();
    display.setTextSize(4);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.print("SLEEP");
    display.display();
    delay(5000);
    pinMode(D5, OUTPUT);
    digitalWrite(D5, 0);

  }
  if ( messageMashina.flagMashinaOff) {
    display.clearDisplay();
    display.setTextSize(4);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("SLEEP");
    display.setTextSize(2);
    display.startscrollleft(0x04, 0x07);
    display.println(" MASHINA  ");
    display.println("   SPIT   ");
    display.display();
    delay(7000);
    pinMode(D5, OUTPUT);
    digitalWrite(D5, 0);
  }

}

void setup() {
  pinMode(D6, INPUT_PULLUP);
  pinMode(D7, INPUT_PULLUP);

  Serial.begin(115200);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextSize(3);
  display.setTextColor(WHITE);
  display.setCursor(0, 6);
  display.print("poehali");
  display.display();
  delay(2000);


  /////////OTA///////
  if (!digitalRead(D7) && !digitalRead(D6) ) {
    //если при агруке нажаты две кнопки входм в режим прошивки по WiFi, нет груим как обычно
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(WHITE);
    display.setCursor(0, 0);
    display.println("Booting");
    display.display();
    // Step to connect ESP with the Wi-Fi
    WiFi.mode(WIFI_STA);               //Set ESP as station mode (устанавливаем режим станции)
    WiFi.begin(ssid, password);      //Wi-Fi Credentials (параметры для подключения к сети Wi-Fi)
    while (WiFi.waitForConnectResult() != WL_CONNECTED)      //Connecting  ESP to wi-fi takes some time, so wait till it gets connected
    {
      display.println("Connection Failed! Rebooting...");
      display.display();
      delay(3000);
      ESP.restart();
    }

    ArduinoOTA.onStart([]() {
      String type;
      if (ArduinoOTA.getCommand() == U_FLASH) {
        type = "sketch";
      } else { // U_SPIFFS
        type = "filesystem";
      }
      // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
      Serial.println("Start updating " + type);
      display.println("Start updating " + type);
      display.display();
    });
    ArduinoOTA.onEnd([]() {
      display.println("\nEnd");
      display.println("RESTART");
      display.display();
      delay(3000);
      ESP.restart();
    });
    ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
      // display.drawRect(0, 40, 7, 8, WHITE);
      display.fillRect(0, 40, 100, 8, BLACK);
      display.setCursor(0, 40);
      display.print("Progress:");
      display.print((progress / (total / 100)));
      display.print("%");
      display.display();
    });
    ArduinoOTA.onError([](ota_error_t error) {
      display.printf("Error[%u]: ", error);
      display.display();
      if (error == OTA_AUTH_ERROR) {
        display.println("Auth Failed");
        display.display();
      } else if (error == OTA_BEGIN_ERROR) {
        display.println("Begin Failed");
        display.display();
      } else if (error == OTA_CONNECT_ERROR) {
        display.println("Connect Failed");
        display.display();
      } else if (error == OTA_RECEIVE_ERROR) {
        display.println("Receive Failed");
        display.display();
      } else if (error == OTA_END_ERROR) {
        display.println("End Failed");
        display.display();
      }
    });
    ArduinoOTA.begin();          //инициализация OTA
    display.println("Ready");
    display.print("IPadr:");
    display.println(WiFi.localIP());
    display.display();    // отображаем IP адрес модуля ESP в окне монитора последовательной связи

label:
    ArduinoOTA.handle();
    delay(100);
    goto label; // переходим к метке label
    /////////OTA///////
  }

  ads.setGain(GAIN_TWOTHIRDS);  // 2/3x gain +/- 6.144V  1 bit = 3mV      0.1875mV (default)
  ads.begin();
  /*if (!ads.begin()) {
     Serial.println("Failed to initialize ADS.");
     while (1);
    }*/
  analogWriteRange(255);// разрядность шим

  // Выставляем режим работы Wi-Fi
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  // Инициализируем протокол ESP-NOW
  esp_now_init();
  /* if (esp_now_init() != 0) {
     Serial.println("Error initializing ESP-NOW");
     return;
    }*/
  // Указываем роль платы в сети
  esp_now_set_self_role(ESP_NOW_ROLE_COMBO);

  // Регистрируем callback-функцию для получения статуса отправки
  esp_now_register_send_cb(OnDataSent);

  // Регистрируем callback-функцию для получения статуса приема
  esp_now_register_recv_cb(OnDataRecv);


  // Регистрируем пиры
  esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 1, NULL, 0);
}

void loop() {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;// Сохраняем время последнего обновления показаний
    //Запрашиваем показания датчика
    ReadADS();
    DrivePWM();
    if (millis() - predZarydTime > 1000) {
      predZarydTime = millis();
      u_pult = map(analogRead(A0), 3, 1024, 0, 1095);// считываем напряжение акумулятора
      if (u_pult < 300) { // если напряжение акумулятора меньше3.0В-> выключить
        pinMode(D5, OUTPUT);
        digitalWrite(D5, 0);
      }
    }
    Display();
    // Отправляем сообщение
    esp_now_send(broadcastAddress, (uint8_t *) &messagePult, sizeof(messagePult));

    OFF(); //проверка на сон !после отправки
  }


}


Добавлено спустя 8 минут 5 секунд:
Re: машина на управлении по ESP с полным приводом по танковой сх
Получился прототип устройства
IMG_20220623_003914.jpg

Re: машина на управлении по ESP с полным приводом по танково

Scorpio » 25 июн 2022, 02:05

Вполне себе не плохо получилось. :good:


Rambler\'s Top100 Mail.ru counter