Прикормочный кораблик, автоматизация на ардуино

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Добрый день форумчане!

Попросил приятель помочь автоматизировать процесс сброса прикормки на его прикормочной кораблике.
Для привода двигателя использовать модуль L298N от него же запитать и arduino nano. Датчики начального и конечного бункера - безконтактные V191028 -TCST2103.
Ссылки на комплектующие с Алиэкспрева приведу позднее.
Код, по мере реализации проекта буду выкладывать в следующем посте, прошу не цитировать!

Что должна уметь делать программа?
1. Принимать сигнал PWM от приемника и если значение выше порогового произвести однократный сброс
прикормки, выдержать некоторую паузу на опустошение бункера и вернуть его в исходное положение.

2. После сброса прикормки не зависимо от входного сигнала ничего не делать, с повторной зарядкой бункера ТС не определился, я предложить поставить кнопку для приведения системы в исходное состояние готовности.
 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А можно чертёж (рисунок) кинематики мехнизма выгрузки?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

А можно чертёж (рисунок) кинематики мехнизма выгрузки?

Наверное можно, кораблик работает, в настоящий момент двигатель переменного тока запитан от контроллера, принимающего шигнал по PWM, реверс организован переключателем на самом бункере, переключающегося двумя ограничителями.
ТС не нравится, что периодически забывает вернуть тумблер на аппаратуре в исходное состояние и шёркат бункер туда-сюда до возвращения кораблика к берегу.
Попрошу отфотографировать, устроит?
Да, кстати, всё работет на ура, за 300 метров вытягивает зеркальных карпов от 7 до 15 килограмм )))

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Будьте так добры. А то мой отец попросил нас с внуком сделать ему, а мы что-то никак не можем толковый механизм выгрузки придумать.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Начало кода:
1. Реализация обработчика сигнала PWM с приёмника радиоуправления или контроллера APM-2.6 (на 6-й канал, так как 7-й канал завязывается на стик отмечающий координаты сброса прикормки, для возврата кораблика и последующего заброса снасти)

Вчерне заработало )))

В пианиста не стрелять, играет как может...
 

//******* Автоматика управления бункером прикормочного кораблика *******//

// Актуализация на 23.10.2018
// переменные обработчика входного сигнала PWM
// сигнал PWM с приемника или контроллера подаётся на
// 3 пин контроллера arduino nano, обрабатывается через 1-е прерывание

int pin_PWM = 3;                    // Первое прерывание жёстко привязано к этому пину
volatile int Recv;                  // Длина импульса
int Norm;                           // mapped value to be between 0-100
volatile long  CH1PulseStartTicks;  // Фиксируем время начала импульса
int CH1Ready;
int count_1 = 0;                    // Для защиты от ложных срабатываний
int Simp = 7;                       // накапливаем импульсы до 7
                                    // после накопления принимаем решение    
// Новая функция обработчика прерывания, подключаем
#include  "TimeMeasure.h"

// Переменные для обработки прикормочного контейнера
                              // Оптические датчики
const int  sq1 = 7;   // Начальное положение бункера 
const int  sq2 = 8;   // Конечное положение бункера
const int ledPin = 13;        // Пин подключения светодиода

int IN1 = 5;                  // Input1 L298N подключен к выводу 5 
int IN2 = 4;                  // Input2 L298N подключен к выводу 4
int ENA = 6;                  // Разрешение канала A L298N подключено к выводу 6

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int sq_status = 0;           // current state of the button
int sq_status2 = 0;
int lastsq_status = 0;     // previous state of the button
int x = 0;
int f_sbros = 0;             // флаг осуществленного сброса прикормки 


/******************************************************/
// Обработчик модуля L298N
void l298(){
   // На пару выводов "IN" поданы разноуровневые сигналы, мотор готов к вращению 
   // Процедура однократного сброса прикормки выставляет флаг f_sbros
   if (f_sbros == 0){
   sq_status2 = digitalRead(sq2);   //Проверяем датчик конечного положения
   // Выгружаем 
    do  
{
  digitalWrite (IN1, LOW);
  digitalWrite (IN2, HIGH);
  // подаем на вывод ENA управляюший ШИМ сигнал 
  analogWrite(ENA,250);
  delay(10);
   x = digitalRead(sq2); // проверить датчики начального положения
} while (x < 1);
    analogWrite(ENA,0);

    // Выдерживаем паузу на сброс 1 - 3 секунды
    delay(3000);

    // Возвращаем бункер на место
    sq_status = digitalRead(sq1);
    do  
{
  digitalWrite (IN1, HIGH);
  digitalWrite (IN2, LOW);
  // подаем на вывод ENA управляюший ШИМ сигнал 
  analogWrite(ENA,250);
  delay(10);
   x = digitalRead(sq1); // проверить датчики начального положения
} while (x < 1);
    analogWrite(ENA,0);
   } // конец сброса
   f_sbros = 1;
}


/******************************************************/
// Процедура обработки контейнера прикормки
void box()
{
  // Читаем :
  sq_status =  digitalRead(sq1);
  sq_status2 = digitalRead(sq2);
  
  l298();        // Сбрасываем прикормку 
} 
  

/******************************************************/

void setup()  { 
  // Сконфигурируем пины на ввод:
  pinMode(sq1, INPUT);
  pinMode(sq2, INPUT);  
   
  pinMode(10, OUTPUT);    // Запитаем оптические датчики (с запасом на третий,
    pinMode(11, OUTPUT);  // для переключения в режим довода лотка сброса
      pinMode(12, OUTPUT);   
  digitalWrite (10, HIGH);
    digitalWrite (11, HIGH);
      digitalWrite (12, HIGH);
      
  // Сконфигурируем светодиод как выход:
  pinMode(ledPin, OUTPUT);

  // Сконфигурируем L298N
  pinMode (ENA, OUTPUT); 
  pinMode (IN1, OUTPUT);
  pinMode (IN2, OUTPUT);

  
  // Сконфигурируем последовательный порт:
   Serial.begin(9600);

  // Если бункер прикормки не в нулевой позиции поставить в исходную
   sq_status = digitalRead(sq1);
   sq_status2 = digitalRead(sq2);
   if (sq_status == 0 && sq_status2 == 0){
  /*
   do  
{
  digitalWrite (IN1, HIGH);
  digitalWrite (IN2, LOW);
  // подаем на вывод ENA управляюший ШИМ сигнал 
  analogWrite(ENA,250);
  delay(10);
   x = digitalRead(sq1); // проверить датчики начального положения
} while (x < 1);
    analogWrite(ENA,0);
    } */}
    f_sbros = 1;
  // Сконфигурируем приемник и обработчик прерывания
  // для подсчета ширины импульса
  pinMode(pin_PWM, INPUT);            //Пин подключения приемника радиоуправления(3)на ввод 
                                      // Назначаем обработчик 1-го прерывания (цифрофой pin 3) 
                                      // положительный фрон импульса
  // Новая функция обработчика прерывания, инициируем
  digitalWrite (ledPin, HIGH);
  delay(300);
  digitalWrite (ledPin, LOW);
  initTimeMeasuring();
  
  } 


/*******************************************************/
void loop()
{ 
   sq_status = digitalRead(sq1);
   sq_status2 = digitalRead(sq2);
   const uint16_t res = measureResult();
  if (res){
    Recv = ticks2Microseconds(res);
    Norm = map(constrain(Recv, 1000, 2000), 1000, 2000, 0, 100);
    if ( count_1 == 0 &&  sq_status == 1 && Norm == 0 && f_sbros ==1 ){ f_sbros =0;} 
   
   if (Norm >= 1 && f_sbros == 0){count_1++;}
       if (count_1 >= Simp)
    {
      // Далее сбрасываем прикормку
             box();
                                        // прикормка сброшена, бункер в исходной
             count_1 = 0;              // Сбросить счетчик после сброса прикормки
    } 
  }
}

}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Будьте так добры. А то мой отец попросил нас с внуком сделать ему, а мы что-то никак не можем толковый механизм выгрузки придумать.

Завтра отзвоню товарищу!
По коду посмотрите пожалуйста, вот не нравится мне принцип замера длительности импульса
(код не мой, нашел на просторах инета, переработал немного под себя)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А что за сдвиг в строке 53? Опечатка? Или что он там означает?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:
вот не нравится мне принцип замера длительности импульса
Мне тоже.

О каких вообще длительностях идёт речь? Есть оценка? Тогда и посмотрим как лучше измерять.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:
вот не нравится мне принцип замера длительности импульса
Мне тоже.

О каких вообще длительностях идёт речь? Есть оценка? Тогда и посмотрим как лучше измерять.


Есть, аппаратура управления выдает ШИМ сигнал с длительностью П импульса от 800 до 2300 микросекунд, общую тау импульса не подскажу, да и не суть, отслеживаем именно по длительности П импульса

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А про сдвиг?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

А про сдвиг?

там сравнение на меньше

Novice User
Offline
Зарегистрирован: 25.09.2017

ua6em пишет:

ЕвгенийП пишет:

А можно чертёж (рисунок) кинематики мехнизма выгрузки?


Да, кстати, всё работет на ура, за 300 метров вытягивает зеркальных карпов от 7 до 15 килограмм )))

а что он фидер кидает на такое расстояние 300 метров?Ну как приманку доставить на такое расстояние???

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Novice User пишет:

ua6em пишет:

ЕвгенийП пишет:

А можно чертёж (рисунок) кинематики мехнизма выгрузки?


Да, кстати, всё работет на ура, за 300 метров вытягивает зеркальных карпов от 7 до 15 килограмм )))

а что он фидер кидает на такое расстояние 300 метров?Ну как приманку доставить на такое расстояние???

да, кораблик и завозит

Novice User
Offline
Зарегистрирован: 25.09.2017

обалдеть,я своим только прикармливаю,надо бы попробовать )) но я уже все убрал до весны-закрыл летний сезон и жду первый лед...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так, про  800 - 2300 микросекунд я понял.Точность какая устравивает. например, у микрос точность 4 мкс.Нормально? Может и погрубее можно? нет?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Так, про  800 - 2300 микросекунд я понял.Точность какая устравивает. например, у микрос точность 4 мкс.Нормально? Может и погрубее можно? нет?

В рамках той функции, что реализова, ПОКАЗЫВАЕТ при нулях 1000 и иногда 1004 )))
В принципе, надо только два состояния 0 и 1 на момент принятия решения...
Но если есть возможность сделать измерение в функции с точностью с 0,1 мксек было бы неплохо
есть идеи куда применить ещё )))
 

Код поправил немного, накидал и пробных строк на обработку двигателя

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Так, про  800 - 2300 микросекунд я понял.

Евгений Петрович! ТС в отъезде, смогу скинуть фотографии только на следующей неделе.
А если есть желание сделать такой девайс на серьёзном уровне, то могу перечислить, что для полного счастья надо.
Выйдет однозначно дешевле, чем покупать готовый.
(я об автоматике, во что обойдётся кораблик водоизмещением в 30-40 килограмм - не знаю)

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Да, собственно, всё есть - восьмой GPS, компас, движки, драйверы к ним, винты правые и левые, лора для связи, шаговики для механизмов выгрузки подкорма и сброса снасти. Всё по отдельности опробовано. Проблема с механикой выгрузки. Вернее с собственной криворукостью и криворукостью доступных токарей (заказывал работу - погрешность в миллиметр!). Вот всё думаю над простым механизмом выгрузки, таким. чтобы позволял такие огромные допуски и при этом нормально работал (в общем, типа револьвера Нагана).

sadman41
Offline
Зарегистрирован: 19.10.2016

И как вам LoRa в сравнении с остальными доступными простому человеку каналами управления?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:
с точностью с 0,1 мксек

У ардуины кварц 16МГц, т.е. такт - 1/16 мкс. 0,1 - не кратно. Есть возможность поставить кварц на 20МГц? Тогда можно и на 0,1 мкс замахнуться, т.к. это будет ровно два такта.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:
с точностью с 0,1 мксек

У ардуины кварц 16МГц, т.е. такт - 1/16 мкс. 0,1 - не кратно. Есть возможность поставить кварц на 20МГц? Тогда можно и на 0,1 мкс замахнуться, т.к. это будет ровно два такта.

в Греции всё есть, там кроме кварца еще что-то перешить придётся видимо...
Можно делать на максимально возможную на стандартном кварце...для это проекта это излишне,
но мне нравятся "красивые женщины"...

По кораблику, ТС всё делал сам, выгрузка через корму, лоток стоит сверху широкой стороной к корме, при доезде лотка при выгрузке до максимальной точки опрокидывается через корму на 90 градусов, потом задержка на 1-2 секунды, и возврат на исходное место...
 

А телеметрия у Вас как планируется, и какой программой будете запоминать точки сброса, для повторного завоза прикормки и снастей?
 

GPS годный, из личной практики имеют достаточно большой разброс по чувствительности, мой модуль берёт 15 спутников, но этот, оставшийся грубоват по чуйке, утерянный был раза в два чувствительнее, брал 5-7 спутников в помещении квартиры в метре от окна, модуль четырёхсистемный GPS, Глонасс, Байду и Европейская
ТС использует модули телеметрии на 900 Mhz, я на 430, антенны у него были не айс, купил другие сейчас метров на 400 работают.
Контроллер APM-2.6, он хорошо для этих дел подходит

Да, как Вы планируете реализовать возврат в точку старта при обрыве управления?
 

Посмотрел LoRa, а скорость не маловата? У стандартных передатчиков телеметрии она 57600

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так я и не понял как делать? На 20 или на 16 МГц?

----

Мой гпс отловил 12 спутников в доме (крыша железная, до ближайшего окна метров 5), я очень доволен.

Задача возврата в точку сброса не ставилась заказчиком. Ставилась задача приехать, сбросить и прикорм, и снасть.

Проблема возврата домой имеется. Так то - для неё и компас, но там есть беда - если ехать по прямой, то на пути могут оказаться заросли камыша или ещё чего. Пока не решил что делать, но эо по люому проблема программирования, а не чего-то другого.

----

Теперь к сбросу. Вот он поднимает на 90 градусов. Как и чем? Степпером с винтовой направляющей? Сервой? Вот на эту тему рисунок бы.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

Так я и не понял как делать? На 20 или на 16 МГц?

----

Мой гпс отловил 12 спутников в доме (крыша железная, до ближайшего окна метров 5), я очень доволен.

Задача возврата в точку сброса не ставилась заказчиком. Ставилась задача приехать, сбросить и прикорм, и снасть.

Проблема возврата домой имеется. Так то - для неё и компас, но там есть беда - если ехать по прямой, то на пути могут оказаться заросли камыша или ещё чего. Пока не решил что делать, но эо по люому проблема программирования, а не чего-то другого.

----

Теперь к сбросу. Вот он поднимает на 90 градусов. Как и чем? Степпером с винтовой направляющей? Сервой? Вот на эту тему рисунок бы.

Отличнейший GPS...
Смотрите в сторону контроллера APM, он позволяет вернуться по запомненному пути (возврат по контрольным точкам) Можно и другой, но я их не пробовал (по INAV), на APM заливается прошивка Mission Planer
APM - специализированная ардуино Mega в маленьком корпусе

Я отфотографирую, никаких серв, там чистая механика, сам привод шорканья и опускает и поднимает корыто )))
Я видел, но в подробности не вдавался, всё проще чем может показаться
Давайте сделаем на 20 mhz, ардуин штук десят есть для экспериментов )))

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Novice User пишет:

обалдеть,я своим только прикармливаю,надо бы попробовать )) но я уже все убрал до весны-закрыл летний сезон и жду первый лед...


Для подводного лёда надо подлодку строить (чтобы по вашим лункам не юзали) )))

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

Смотрите в сторону контроллера APM

Да, нет, я так не играю. Круизный контроллер я буду программировать сам. Иначе вообще не понимаю, нафига мне за это браться.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:

Смотрите в сторону контроллера APM

Да, нет, я так не играю. Круизный контроллер я буду программировать сам. Иначе вообще не понимаю, нафига мне за это браться.

Круто! Я думал Вам рыбку половить )))
Кстати, мой набросок кода заработал и вроде правильно, осталось выкинуть ненужное, и сделать правильное определение ширины импульса.
Но кнопку надо вводить, иначе откуда система узнает, что корыто по новой зарядили )))

Ладно с контроллером, но ПО на смартфон можно и TOWER оставить )))

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

Я думал Вам рыбку половить )))

Не, рыбку половить, это моему отцу - он заказчик. А мы с внуком делать будем и удовольствие получать.

ua6em пишет:

сделать правильное определение ширины импульса.

Ну, вот смотрите. Я не стал морочиться с 16 или 20 МГц - сделал универсально - просто замер времени в тактах. А уж что там за такт ... столько микросекунд и получится. Определена константа для пересчёта тактов в микросекунды (через F_CPU определена, так что нормально посчитает - руками менять ничего не надо). Ну, делить целые числа с коруглением до ближайшего, Вы, надеюсь умеете).

Результат иногда подвирает, но не более, чем на пару-тройку тактов (т.е. при 20МГц - на 0,1 микросекунды) и связано это с фронтами сигнала - фронт же не строго-вертикальный и ХЗ когда от там свой HIGH словит. На идеальном сигнале всё считает точно, я проверял на модели. Ну, а так, чем положе и неустойчивее фронты, тем больше может подвирать.

В общем, что интересует - спрашивайте.

Собственно файл-пример

#include	"TimeMeasure.h"

void setup() {
	Serial.begin(57600);
	initTimeMeasuring();
}

//
//	Если готов результат измерения, печатаем
//
void loop() {
	const uint16_t res = measureResult();
	if (res) Serial.println(res);
}

Файл TimeMeasure.h

#ifndef	TimeMeasure_h
#define	TimeMeasure_h
//
//	Измеряем время высокого уровня на пине 2 (aka PD2) или 3 (PD3)
//	(см. комментарий в начале файла TimeMeasure.cpp)
//
//	Измеряемый интервал - от 80 до 65534 тактов процессора
//	(при 20МГц это 4 - 3275 микросекунд, при 16МГц это 5 - 4095 микросекунд
//	Если высокий уровень держится 65535 тактов, то
//	считаем это ошибкой и выдаём результат ENDLESS_INTERVAL
//
//	Результат измерений - количество тактов процессора.
//	Чтобы получить время в микросекундах, нужно полученный результат
//	разделить на константу TICKS_PER_MICROSECOND
//	

//
// Константа для 
//		1. пересчёта тактов в микросекунды (нужно поделить количество тактов на неё)
//		2. пересчёта микросекунд в такты (нужно умножить количество микросекунд на неё)
static const uint8_t TICKS_PER_MICROSECOND = static_cast <uint8_t> (F_CPU / 1000000UL);
//
// Интервал (в тактах), который считаем ошибкой (слишком большой)
static const uint16_t ENDLESS_INTERVAL = UINT16_MAX;

//
//	Инициализация всего (нужно вызвать из setup)
//
extern void initTimeMeasuring(void);

//
//	Функция возвращает результат измерения, заодно обнуляя его
//
extern uint16_t measureResult(void);

//
//	Пересчёт тактов в целые микросекунды с округлением до ближайшего целого
//
inline uint16_t ticks2Microseconds(const uint16_t ticks) {
	return static_cast <uint16_t> ((static_cast <uint32_t> (ticks) + TICKS_PER_MICROSECOND / 2) / TICKS_PER_MICROSECOND);
}

//
#endif	//	TimeMeasure_h

Файл TimeMeasure.cpp

/////////////////////////////////////////////////////////////////////////////////
//
// ЛИЦЕНЗИЯ
//
// Данный код поставляется по лицензии ПНХ.
//
// 1. Вы можете свободно использовать или не использовать его в коммерческих, 
//    некоммерческих, и любых других, не запрещённых законом, целях.
//
// 2. Автор не несёт решительно никакой ответственности за любые положительные
//    или отрицательные результаты использования или неиспользования данного кода.
//
// 3. Если Вам таки хочется сделать автору предъяву, то … Вы знаете куда
//    Вам следует пройти, А если не знаете, то см. название лицензии.
//
// 4. Если данный код вдруг пригодился (как учебник или ещё как что) и Вам
//    почему-либо (ну, приболели, может) захотелось отблагодарить автора рублём,
//    то это всегда пожалуйста – WebMoney, кошелёк № R626206676373
//    (тут название лицензии не канает). 
//
#include	<arduino.h>
#include	"TimeMeasure.h"

//
//	ATmega328/P и ничего другого!
//
//	Измеряем время высокого уровня на пине 2 (aka PD2)
//	(чтобы измерять на пине 3 (PD3), нужно раскомментировать следующую строку)
//#define	USE_INT_1
//
//	Испольует таймер №1, но если надо работать с тамером №2, нужно раскомментировать сл. строку
#define	USE_TIMER_2
//
//	Измеряемый интервал - от 80 до 65534 тактов процессора
//	(при 20МГц это 4 - 3275 микросекунд, при 16МГц это 5 - 4095 микросекунд
//	Если высокий уровень держится 65535 тактов, то
//	считаем это ошибкой и выдаём результат ENDLESS_INTERVAL
//
//	Результат измерений - количество тактов процессора.
//	Чтобы получить время в микросекундах, нужно полученный результат
//	разделить на константу TICKS_PER_MICROSECOND
//	

#ifdef	USE_INT_1 // используем пин 3

	#define	PIN_NUMBER	3
	#define	PIN_MASK	(bit(PD3))
	#define	INT_FLAG	INT1
	#define	MODE_BIT	ISC10
	#define	INT_VECT	INT1_vect

#else	// используем пин 2

	#define	PIN_NUMBER	2
	#define	PIN_MASK	(bit(PD2))
	#define	INT_FLAG	INT0
	#define	MODE_BIT	ISC00
	#define	INT_VECT	INT0_vect
	
#endif

//
//	Инициализация таймера-счётчика 
//	Состоит из начальной инициализации, которая выполняется один раз
//	и дополнительной инициализации, которая выполняется перед каждым 
//	измерением (stopTimerCounter)
//
#ifdef	USE_TIMER_2
	static volatile uint16_t ovfCounter = 0;

	static void stopTimerCounter(void) {
		TCCR2B = 0;	//	остановим таймер
		TCNT2 = 0;	// считаем с 0
		ovfCounter = 0;
	}
	static void initTimerCounter(void) {
		stopTimerCounter();
		TCCR2A = 0;
		TCCR2B = 0;
		TIMSK2 = bit(TOIE2);	//	Разрешить прерывание по переполнению
	}
#else
	static void stopTimerCounter(void) {
		TCCR1B = 0;	//	остановим таймер
		TCNT1 = 0;	// считаем с 0
	}
	static void initTimerCounter(void) {
		stopTimerCounter();
		TIMSK1 = bit(TOIE1);	//	Разрешить прерывание по переполнению
		TCCR1A = 0;
		TCCR1C = 0;
	}
#endif	//	USE_TIMER_2

//
//	Инициализация внешнего прерывания 0/1 на смену состояния
//
static void initInterrupt0(void) {
	EICRA = bit(MODE_BIT);	// на смену состояния
	EIMSK = bit(INT_FLAG);	// прерывание 0/1
}

//
//	Инициализация всего (нужно вызвать из setup)
//
void initTimeMeasuring(void) {
	pinMode(PIN_NUMBER, INPUT);
	initTimerCounter();
	initInterrupt0();
}

static volatile uint16_t result = 0;

//
//	Функция возвращает результат измерения, заодно обнуляя его
//
uint16_t measureResult(void) {
	cli();
	const uint16_t res = result;
	result = 0;
	sei();
	return res;
}

#ifdef	USE_TIMER_2
	//
	//	Если случилось 16-битное переполнение, значит истёк таймаут
	//	Ставим таймаут результатом и реинициализируем таймер 
	//	для следующегг измерения
	//
	ISR(TIMER2_OVF_vect) {
		ovfCounter += 256;
		if (!ovfCounter) {
			result = ENDLESS_INTERVAL;
			stopTimerCounter();
		}
	}
	
	//
	//	Прилетело прерывание
	//	Если на пинe PD2/PD3 высокий уровень - начинаем отсчёт
	//	Если на пине PD2/PD3 низкий уровень - заканчиваем отсчёт
	//
	//	Примечание: в этой функции количество тактов до включения таймера
	//	и до запоминания результата одинаковое, т.е. поправки не нужны.
	//	Теоретически одинаковость может пропасть при новой версии компилятора
	//	или при других опциях. Если есть сомнения, можно её убрать, а вместо ней
	//	использовать закомментированную (ниже) функцию с ассемблерным кодом.
	//	Там то уж никто не нагадит.
	//
	ISR(INT_VECT) {
		if (PIND & PIN_MASK) {
			TCCR2B = 1; // запускаем таймер
		} else {
			TCCR2B = 0;	//	остановим таймер
			result = TCNT2;
			result += ovfCounter;
			stopTimerCounter();
		}
	}

#else	//	USE_TIMER_2
	//
	//	Если случилось переполнение, значит истёк таймаут
	//	Ставим таймаут результатом и реинициализируем таймер 
	//	для следующегг измерения
	//
	ISR(TIMER1_OVF_vect) {
		result = ENDLESS_INTERVAL;
		stopTimerCounter();
	}
	
	//
	//	Прилетело прерывание
	//	Если на пинe PD2/PD3 высокий уровень - начинаем отсчёт
	//	Если на пине PD2/PD3 низкий уровень - заканчиваем отсчёт
	//
	//	Примечание: в этой функции количество тактов до включения таймера
	//	и до запоминания результата одинаковое, т.е. поправки не нужны.
	//	Теоретически одинаковость может пропасть при новой версии компилятора
	//	или при других опциях. Если есть сомнения, можно её убрать, а вместо ней
	//	использовать закомментированную (ниже) функцию с ассемблерным кодом.
	//	Там то уж никто не нагадит.
	//
	ISR(INT_VECT) {
		if (PIND & PIN_MASK) {
			TCCR1B = 1; // запускаем таймер
		} else {
			TCCR1B = 0;	//	остановим таймер
			result = TCNT1;
			stopTimerCounter();
		}
	}
#endif	//	USE_TIMER_2
/*
ISR(INT_VECT) {
asm volatile(
	 // if (PIND & bit(PDx))
			"sbis	%[Prt],%[Mask]	\r\n"	
			"rjmp	SignalEnds		\r\n"
	// {
			"ldi	r24, 1			\r\n"	//	Единицу пихаем в TCCRB1
			"sts	%[TCCRB], r24	\r\n"	//	т.е. запускаем таймер
			"rjmp	AllDone			\r\n"  
	// } else {
		"SignalEnds: \r\n"
			"lds	r24, %[TCNTL]	\r\n"	// младший байт TCNT1 читаем раньше старшего
			"lds	r25, %[TCNTH]	\r\n"
			"sts	%[res]+1, r25	\r\n"	//	Значение TCNT1 пихаем 
			"sts	%[res], r24		\r\n"	//	в переменнуюresult
			"sts	%[TCCRB], r1	\r\n" //	Пихаем 0 в TCCRB1 - останавливаем таймер
			"sts	%[TCNTH], r1	\r\n"	// Пихаем 0 в TCNT1 на будущее 
			"sts	%[TCNTL], r1	\r\n"	//	старший байт раньше младшего
	// }	  
	  "AllDone: \r\n"
  	:
		[res] "=m" (result)
	: 
		[Prt] "I" (_SFR_IO_ADDR(PIND)), 
		[Mask] "M" (PIN_NUMBER),
		[TCCRB] "M" (_SFR_MEM_ADDR(TCCR1B)),
		[TCNTH] "M" (_SFR_MEM_ADDR(TCNT1H)),
		[TCNTL] "M" (_SFR_MEM_ADDR(TCNT1L))
	:
		"r24", "r25"
	);
}
*/

//

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

ЕвгенийП пишет:

Будьте так добры. А то мой отец попросил нас с внуком сделать ему, а мы что-то никак не можем толковый механизм выгрузки придумать.

А принцип самосвала где вместо гидроцилининдра серва стоит не работает? Сюда прикрутете у будет вам бюджнтный кораблик https://usamodelkina.ru/10876-prosteyshaya-lodka-na-pulte-upravleniya-iz-butylok.html

https://radikal.ru/lfp/s020.radikal.ru/i710/1401/4a/940ac2ebd0eb.jpg/htm

Рама ось с корытом. Гидроцилиндр задирает вверх корыто с грузом.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Я плохо знаком с устройством самосвала.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Может быть поможет.......
Ключевое слово RC Switch
На Attyny13 (25,45,85)....
Куча ссылок на форуме:
http://forum.rcdesign.ru/f87/thread297998.html
http://www.parkflyer.ru/ru/blogs/view_entry/2358/

bwn
Offline
Зарегистрирован: 25.08.2014

ЕвгенийП пишет:

Я плохо знаком с устройством самосвала.

Как по мне, то нижняя или задняя часть бункера откидная, а серва стопор выдергивает. Заодно и проблема полный-пустой решается (концевик). ИМХО.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

принцип мышеловки. на право серву правый бункер откинулся , на лево левый бункер.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Благодарю!
Вот хотел жеж 2-й пин распаять, а начал гребенки с 3-его, завтра попробую )))
На следующей неделе выложу фотографии отлаженного девайса, оставалось только забывчивость ТС ликвидировать автоматизацией механизма сброса прикормки, надеюсь, что эта проблема уже решена.
Вопрос, а на 1-и прерывании счетчик какой размерности можно организовать?
 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

trembo пишет:
Может быть поможет....... Ключевое слово RC Switch На Attyny13 (25,45,85).... Куча ссылок на форуме: http://forum.rcdesign.ru/f87/thread297998.html http://www.parkflyer.ru/ru/blogs/view_entry/2358/

Там бинарники! Это не наш метод. (хоть по рублю но на свои В.Высоцкий)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

Вот хотел жеж 2-й пин распаять, а начал гребенки с 3-его, ... Вопрос, а на 1-и прерывании счетчик какой размерности можно организовать?

Точно такой же, это не от прерывания. а от таймера зависит. Перекинуть на первое - как два пальца, надо, скажите, сделаем.

kasper007
Offline
Зарегистрирован: 23.05.2016
kasper007
Offline
Зарегистрирован: 23.05.2016
ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:

Вот хотел жеж 2-й пин распаять, а начал гребенки с 3-его, ... Вопрос, а на 1-и прерывании счетчик какой размерности можно организовать?

Точно такой же, это не от прерывания. а от таймера зависит. Перекинуть на первое - как два пальца, надо, скажите, сделаем.

1. Надо!

2. Я так понимаю, что Вы в состоянии замахнуться на действительно необходимый НАРОДНЫЙ ПРОЕКТ - разработку ПО сети наземных и мобильных  GS станций??? Я буду первый, кто у себя таковую развернет и даже очень точно привяжет. Ну тогда никакие камыши, бугорки и прочие препятствия для ползающих, плавающих, летающих и т.д. самодельных конструкций станут не страшны, ведь точность определения координат станет фантастической - 5 см ))) Как говорит мой приятель - антненна, не та конструкция, которую можно передать внукам, а вот этот проект уж точно можно будет им передать )))
3. Мои соображения - ПО для GS делать под OpenWRT, экономически самое то

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

kasper007 пишет:
https://youtu.be/LOQBAePShiw

Мы лёгких путей не ищем )))
Уже давно сделано, настроено, работает, рыба на столы его превосходительства (то биш мне) иногда поставляется )))

sadman41
Offline
Зарегистрирован: 19.10.2016

Предлагаю для спасения корабля использовать воздушные силы под управлением рыбака - квадрокоптер, который за веревочку потащит потерпевшее бедствие судно к берегу.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:

Предлагаю для спасения корабля использовать воздушные силы под управлением рыбака - квадрокоптер, который за веревочку потащит потерпевшее бедствие судно к берегу.

Если клеить из пенопласта его невозможно утопить )))
А коптер над водой плохое решение, потом потребуются водолазы

sadman41
Offline
Зарегистрирован: 19.10.2016

Ну дак полный набор будет: Air forces, Marine forces & Submarine Forces. Удовольствия втрое больше!

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:

Ну дак полный набор будет: Air forces, Marine forces & Submarine Forces. Удовольствия втрое больше!

С Air forces не всё так просто, у моего коршуны GPS вырвали, видимо подумали, что я вторгся на их территорию и отнимаю кормовую базу

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

1. Надо!

Я там, в #26 заменил коды.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:

1. Надо!

Я там, в #26 заменил коды.

Отлично!
Проверил, чуток поправил пробный и получаю в микросекундах, всё пучком.

#include  "TimeMeasure.h"
//
void setup() {
  Serial.begin(57600);
  initTimeMeasuring();
}

//
//  Если готов результат измерения, печатаем
//
void loop() {
  const uint16_t res = measureResult();
  if (res) Serial.println(res/16);
}

И со знаками после запятой работает:
 

#include  "TimeMeasure.h"
float pwm = 0;
//
void setup() {
  Serial.begin(57600);
  initTimeMeasuring();
}

//
//  Если готов результат измерения, печатаем
//
void loop() {
  const uint16_t res = measureResult();
  if (res){ Serial.println(res/16);
  pwm = res;
  Serial.println(pwm/16);}
}

Видео и фото для Вас  тут

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

ua6em пишет:

получаю в микросекундах

Serial.println(res/16);

Нет, так не надо. Целочисленное деление - оно такое целочисленное. Например, 16/16 = 1, 20/16 тоже 1, но и 31/16 - всё ещё один, только 32/16 становится 2!

Чтобы получить "ближайшее целое", нужно перед делением прибавить к делимому половину делителя (или воспользоваться готовой функцией ticks2Microseconds, которую я для Вас сделал - он там есть посмотрите, она именно это и делает. Вот тогда Вы получите "ближайшее целое".А так Вы делите с отбрасыванием дробной части какой бы она ни была.

ua6em пишет:

Видео и фото для Вас  тут

Спасибо.

sadman41
Offline
Зарегистрирован: 19.10.2016

Кстати, на алишке есть drv8833 - можно попытаться присобачить вместо L298N. Во всяком случае он компактней.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

sadman41 пишет:

Кстати, на алишке есть drv8833 - можно попытаться присобачить вместо L298N. Во всяком случае он компактней.


Это лишнее, их 40кг водоизмещения занято только 8, места - вагон.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ЕвгенийП пишет:

ua6em пишет:

получаю в микросекундах

Serial.println(res/16);

Нет, так не надо. Целочисленное деление - оно такое целочисленное. Например, 16/16 = 1, 20/16 тоже 1, но и 31/16 - всё ещё один, только 32/16 становится 2!

Чтобы получить "ближайшее целое", нужно перед делением прибавить к делимому половину делителя (или воспользоваться готовой функцией ticks2Microseconds, которую я для Вас сделал - он там есть посмотрите, она именно это и делает. Вот тогда Вы получите "ближайшее целое".А так Вы делите с отбрасыванием дробной части какой бы она ни была.

ua6em пишет:

Видео и фото для Вас  тут

Спасибо.

Идею правильного округления уяснил, функцию увидел )))
Для себя я обычно описывал функции в самом верху листинга, так как 99% обычных людей читают книжки с начала, технари - с конца, из них кому там нужно было знать что там партия и правительство )))

Если нужны пояснения по кораблику, спрашивайте...
Запчасти все с негодного струйного принтера, кроме двигателя.
Вместе с прикормкой заправляется и леска, это тот вертикальный разрез.
Открываем шпингалет, заводим леску, протягиваем ее в следующий - маленький вырез,
шпингалет закрываем. Леска с крючками и грузилами в лотке, засыпаем кукурузу, закрываем
крышку и всё. Пропил под леску точно посередине корабля.
При сбросе прикормки в это же место сбрасывается и снасть.
После сброса стиком аппаратуры управления отмечаем WP на карте, чтобы при последующих забросах прикормки приплывать точно в это же самое место.

В этом скетче от функции мне надо было всего два состояния 0 и 1, поэтому результат деления не критичен.
Разбор значения осуществляет эта функция
Norm = map(constrain(Recv, 1000, 2000), 1000, 2000, 0, 100);,  
Использую в скетче для будущих применений
Основа точного сервотестера имеется!
добавил float, поисваиваю ему значение res, делю на TICKS_PER_MICROSECOND

Перешил с новой функцией, всё работает, от ненужного скетч не чистил, черновой вариант ниже, немно изменю на предмет действительности окна значений ШИМ, апааратура FLYSKY достаточно стабильно себя ведет, ограничусь окном в 30 единиц микросекунд.

//******* Автоматика управления бункером прикормочного кораблика *******//

// переменные обработчика входного сигнала PWM
// сигнал PWM с приемника или контроллера подаётся на
// 3 пин контроллера arduino nano, обрабатывается через 1-е прерывание

int pin_PWM = 3;                   // Первое прерывание жёстко привязано к этому пину
volatile int Recv;                  // Длина импульса
int Norm;                           // mapped value to be between 0-100
volatile long  CH1PulseStartTicks;  // Фиксируем время начала импульса
int CH1Ready;
int count_1 = 0;                   // Для защиты от ложных срабатываний
int Simp = 7;                       // накапливаем импульсы до 7
                                    // после накопления принимаем решение    
// Новая функция обработчика прерывания, подключаем
#include  "TimeMeasure.h"

// Переменные для обработки прикормочного контейнера
                              // Оптические датчики
const int  sq1 = 7;   // Начальное положение бункера 
const int  sq2 = 8;   // Конечное положение бункера
const int ledPin = 13;        // Пин подключения светодиода

int IN1 = 5;                  // Input1 L298N подключен к выводу 5 
int IN2 = 4;                  // Input2 L298N подключен к выводу 4
int ENA = 6;                  // Разрешение канала A L298N подключено к выводу 6

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int sq_status = 0;           // current state of the button
int sq_status2 = 0;
int lastsq_status = 0;     // previous state of the button
int x = 0;
int f_sbros = 0;             // флаг осуществленного сброса прикормки 


/******************************************************/
// Обработчик модуля L298N
void l298(){
   // На пару выводов "IN" поданы разноуровневые сигналы, мотор готов к вращению 
   // Процедура однократного сброса прикормки выставляет флаг f_sbros
   if (f_sbros == 0){
   sq_status2 = digitalRead(sq2);   //Проверяем датчик конечного положения
   // Выгружаем 
    do  
{
  digitalWrite (IN1, LOW);
  digitalWrite (IN2, HIGH);
  // подаем на вывод ENA управляюший ШИМ сигнал 
  analogWrite(ENA,55);
  delay(10);
   x = digitalRead(sq2); // проверить датчики начального положения
} while (x < 1);
    analogWrite(ENA,0);

    // Выдерживаем паузу на сброс 1 - 3 секунды
    delay(3000);

    // Возвращаем бункер на место
    sq_status = digitalRead(sq1);
    do  
{
  digitalWrite (IN1, HIGH);
  digitalWrite (IN2, LOW);
  // подаем на вывод ENA управляюший ШИМ сигнал 
  analogWrite(ENA,55);
  delay(10);
   x = digitalRead(sq1); // проверить датчики начального положения
} while (x < 1);
    analogWrite(ENA,0);
   } // конец сброса
   f_sbros = 1;
}


/******************************************************/
// Процедура обработки контейнера прикормки
void box()
{
  // Читаем :
  sq_status =  digitalRead(sq1);
  sq_status2 = digitalRead(sq2);
  l298();        // Сбрасываем прикормку 
} 
  

/******************************************************/

void setup()  { 
  // Сконфигурируем пины на ввод:
  pinMode(sq1, INPUT);
  pinMode(sq2, INPUT);   
  
  // Сконфигурируем светодиод как выход:
  pinMode(ledPin, OUTPUT);

  // Сконфигурируем L298N
  pinMode (ENA, OUTPUT); 
  pinMode (IN1, OUTPUT);
  pinMode (IN2, OUTPUT);
  
  // Сконфигурируем последовательный порт:
   Serial.begin(9600);

  // Если бункер прикормки не в нулевой позиции поставить в исходную
   sq_status = digitalRead(sq1);
   sq_status2 = digitalRead(sq2);
   if (sq_status == 0 && sq_status2 == 0){
   do  
{
  digitalWrite (IN1, HIGH);
  digitalWrite (IN2, LOW);
  // подаем на вывод ENA управляюший ШИМ сигнал 
  analogWrite(ENA,55);
  delay(10);
   x = digitalRead(sq1); // проверить датчики начального положения
} while (x < 1);
    analogWrite(ENA,0);
    }
  // Сконфигурируем приемник и обработчик прерывания
  // для подсчета ширины импульса
  pinMode(pin_PWM, INPUT);            //Пин подключения приемника радиоуправления(3)на ввод 
                                      // Назначаем обработчик 1-го прерывания (цифрофой pin 3) 
                                      // положительный фрон импульса
  // Новая функция обработчика прерывания, инициируем
  initTimeMeasuring();
  
  } 


/*******************************************************/
void loop()
{ 
   sq_status = digitalRead(sq1);
   sq_status2 = digitalRead(sq2);
   const uint16_t res = measureResult();
  if (res){
    Recv = ticks2Microseconds(res);
    Norm = map(constrain(Recv, 1000, 2000), 1000, 2000, 0, 100);
    if ( count_1 == 0 &&  sq_status == 1 && Norm == 0 && f_sbros ==1 ){ f_sbros =0;} 
   
   if (Norm >= 1 && f_sbros == 0){count_1++;}
       if (count_1 >= Simp)
    {
      // Далее сбрасываем прикормку
             box();
                                        // прикормка сброшена, бункер в исходной
             count_1 = 0;              // Сбросить счетчик после сброса прикормки
    } 
  }
}
   

Отдал заказчику. Выяснился косяк с малым напряжением ШИМ, надо делать как учили, вводить еще два концевичка и делать переключение с высокой скорости на малую и обратно или по времени, плавный разгон.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Простой сервотестер перенес в раздел проекты!

Евгений Петрович, не посмотрите, почему с собственного генератора PWM не меряет?
 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Странное поведение устройства:
Использую такой датчик

Если датчик сработал, на выходе 1, подсоединен к пину 8 адуино нано, то при подаче питания штатно на вывод 5 вольт, не проходит аппаратный ресет, если вывести флажок с датчика и подать питание (на выходе датчика 0) то сброс проходит.
Вывод 8 нано сконфигурирован на ввод.
У кого есть какие мысли?

Если подавать питание на USB вход, то аппаратный сброс проходит не зависимо от положения датчика, естественно кнопка reset приводит устройство в чувство ))), а вот замыкание контакта rst на gnd - НЕТ!