Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Автопилот для парусных лодок (автоматическая система рулевого управления)

Компоненты и расходные материалы

Arduino UNO
× 1
Arduino Nano R3
× 1

Об этом проекте

Предисловие:

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

Счастье - это безграничные горизонты, безупречная спортивная техника, оптимальный выбор, а также человеческие качества в виде хорошего напитка и вкусного бутерброда! Именно в это время на помощь приходит автопилот:он работает вместо вас, пока вы пьете свой чай с печеньем в 17:00 на море. :-)

Что автопилот может для вас сделать:

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

Вся работа выполняется моряком, мы должны понять это на данном этапе:обрезка парусов, контроль погоды и источника / скорости ветра, укрепление или ослабление канатов, контроль движения с другими лодками, определение направления и рулевого управления ... Когда Моряк решает сделать перерыв, скажем, всего на 10 секунд или несколько минут (знаменитое «время чая»), он включает автопилот. За несколько секунд его GPS определяет положение, скорость и направление лодки и может определять направление (маршрут). Система рулевого управления - ручка, соединенная с рулем, обычно перемещаемая руками опытного моряка, теперь находится под контролем автопилота через шаговый двигатель, соединенный с ним шкивами и тросами.

Управляйте рулем это непрерывная работа по тонкой или грубой настройке. Меньше (легче) лодка и сильнее будут изменения факторов направления, влияющих на нее:морские волны, направление и напор ветра, смещение груза на борту из-за движения моряка, морские течения. Но моряк всегда бодрствует, даже при включенном автопилоте, и вносит изменения в фактический маршрут с помощью дистанционного управления . :на нем 4 кнопки, обозначенные +1 -1 + 10-10, для малых или больших изменений в градусах, увеличения или уменьшения значения. Эти кнопки присутствуют на автопилоте . тоже зеленый (справа) и красный (слева). Синяя кнопка (в центре) предназначена для включения или выключения автопилота, паузы. Это также черная кнопка для настройки параметров . в памяти.

Схема:

Основная обработка выполняется MCU Arduino Uno . . Другой MCU, Arduino Nano . , является сторожевым псом:я знаю, что внутри Uno действительно существует своего рода сторожевой пес, но мне нравилось делать это с помощью независимого внешнего микроконтроллера, это мечта всей моей жизни, о которой я мечтал, и теперь я счастлив! Uno должен питать Nano через контакт 3 -> A0, устанавливая его высокий / низкий уровень, 5/0 вольт, по крайней мере, один раз каждые 2,5 секунды (FeedInterval); если нет, это означает, что Uno «спит» или «заблокирован», а Nano сбрасывает Uno ... Этого еще не было, вы можете поверить?

Используется популярный дисплей в сочетании с преобразователем схемы i2c оба спаяны друг с другом, и, наконец, использовалось всего 4 провода, чтобы значительно сэкономить цифровые контакты для связи с Uno. Также способ подключения кнопок и пульта дистанционного управления это делается резистивными делителями напряжения для достижения цели использовать как можно меньше портов MCU; Я выбрал резисторы с точностью 1%, значения аналогового сравнения должны быть между значениями, которые я ввел в код; в случае, если некоторые кнопки не распознаются из-за того, что вы выбрали другой тип резисторов, просто внесите некоторые изменения в константы (измените код в «checkRfRC ()» и «checkHWButtons ()»). Схема дистанционного управления (RC) RF 433Mhz работает хорошо; чтобы улучшить покрытие на расстоянии и увеличить шансы на успех, я добавил спиральную антенну, которую вы можете сделать самостоятельно из куска медной проволоки; Я тестировал его на расстоянии 10 метров, но я думаю, что он может работать даже на 20 метрах и более, что более чем достаточно, учитывая, что парусная лодка, которую я использовал для тестирования автопилота, имела длину всего 4,20 метра.

Для устройства GPS Вначале я использовал хороший EM406A, но, к сожалению, обнаружил, что он страдает неделей-опрокидыванием-ошибкой, он был слишком стар, затем мне пришлось заменить его на отличный и популярный Beitian BN-220T. С помощью его конфигурационного программного обеспечения, пожалуйста, установите его "выплевывать" 2 раза в секунду (2 Гц) только необходимое последовательное предложение "$ GNRMC" NMEA. GPS отправляет (TX) последовательные данные на контакт 0 (RX) Uno. Эти данные содержат все навигационные данные, которые используются для расчета поправок, вносимых двигателем:дату, время, широту и долготу местоположения, истинный курс, скорость и срок действия спутников. В связи с тем, что программирование IDE Arduino также использует порт вывода 0 (RX), не забудьте временно отключить GPS во время этой операции ...

Другой моей мечтой было использовать EEPROM . . IC 2404 - это красивая интегральная схема i2c емкостью 512 байт, которую я использовал для чтения / записи в этот чип памяти некоторых параметров движений шагового двигателя, которые я объясню позже в разделе «Программное обеспечение».

Список компонентов:

  • Arduino Uno в качестве MCU
  • Arduino Nano как WatchDog
  • GPS Beitian BN-220T
  • Шаговый двигатель, модель 23LM, 54 шага =1/4 оборота.
  • Контроллер Keyes L298 для двигателя
  • RF433Mhz RC XD-YK04 + 4-х кнопочный пульт + катушечная антенна
  • 6 кнопок нормально открыты (2 красных, 2 зеленых, 1 черный и 1 синий)
  • Переключатель включения / выключения (белый)
  • 6-контактные круглые разъемы "мама" и "папа" для внешнего шагового двигателя.
  • Зуммер
  • Дисплей LCD1602 2x16 символов + схема преобразователя i2c
  • 3 светодиода (красный, синий и желтый))
  • IC 24c04 i2c eeprom
  • Мультиплексор IC 4051
  • Батарея LiPo, 2 с, 7,4 В, 2600 мА
  • Регулятор напряжения IC 7805 + теплоотвод
  • Термистор NTC MF52-103 10к
  • Восстанавливаемый предохранитель 2А
  • 6x 1N4148 диодов (D1 – D6)
  • Резисторы на силовом щите (R1-R4 =10 кОм, R5 =100 кОм)
  • Резисторы на экране автопилота (R1 =330, R2 =1 кОм, R3 =2 кОм, R4 =5,1 кОм, R5 =1 кОм, R6 / R7 / R14 =330, R8-R13 =10 кОм, R15 =10 МОм)
  • Конденсаторы (C1 =470 мкФ, 16 В, C2 =100 н)
  • Резистор 2 Вт 0,22 Ом (R6)
  • Штифты с наружной резьбой
  • Заголовки с длинными штифтами, женские
  • Чехол прозрачный и «водонепроницаемый»

Есть несколько датчиков на схеме все подключено к Arduino Uno с помощью мультиплексора IC 4051 . Это Термистор . взять под контроль регулятор напряжения нагрева рассеивателя температуры, резистор 2Вт и 4x10k в качестве делителей напряжения, чтобы рассчитать ампер как потребляемую мощность . всей цепи. Также напряжение батареи взят под контроль:известно, что LiPo имеет решающее значение, когда отдельные элементы разряжаются ниже 3,3 В; Эта схема имеет два элемента (2S) LiPo в одном корпусе, в случае низкого напряжения (ниже 7,0 В) зуммер сообщит вам короткими короткими гудками. Не ждите слишком долго, чтобы выключиться, и скоро зарядитесь! Светодиоды :желтый, мигающий с частотой 1 Гц, означает, что WatchDog работает; синий горит, когда автопилот включен, выключен, если он приостановлен; красный светодиод мигает при нажатии одной из кнопок пульта ДУ.

Вся схема работает от 5,0 В от аккумулятора LiPo 2S 7,4 В, 2600 мА / ч и регулятора напряжения IC 7805 . . Ток не должен превышать 800 мА, но обычно он составляет около 100-450 мА. Наденьте рассеиватель тепла . . Термистор помещается на него, и зуммер издает звуковой сигнал, если температура превышает 50 ° C.

Печатные платы и сборка печатных плат:

Используется односторонние печатные платы по этой причине мне пришлось включить несколько перемычек (пунктирные), чтобы решить маршруты для целых цепей. Здесь показаны лицевые стороны компонентов, но ниже у вас есть все файлы, компоненты и поверхности для пайки, зеркальные, для загрузки и печати с помощью лазерного принтера на «желтых» или «синих» листах. Я использовал желтые, но они говорят, что синие лучше (но цена значительно выше). При печати не забудьте отключить настройки экономии тонера, вместо этого используйте разрешение 1200 dpi, чтобы получить глубокий настоящий черный результат. Процесс переноса тонера с волшебных листов на печатные платы осуществляется с помощью горячего утюга ... Печать на обеих сторонах, а также на лицевой стороне компонентов, позволяет легко распознавать расположение элементов и даже делает проект «профессиональным».

Обе печатные платы имеют размер, позволяющий поместиться одна поверх другой Arduino Uno в виде стека . :сначала блок питания, затем блок автопилота.

Я решил собрать все вместе:печатные платы, микроконтроллеры, RC, схему драйвера двигателя, батарею, GPS, кнопки, переключатель, провода, разъемы и т. Д., Думая использовать их повторно в один прекрасный день:я не спаял их вместе, я <сильный> использованный заголовки и популярные провода / соединения Dupont вместо. Таким образом, имеется около 200 непаянных соединений, это означает, что время от времени могут происходить неожиданные и нежелательные неисправности или другое поведение схемы, это нормально. Предлагаю все спаять для более стабильной схемы!

Параметры Настройка и отображение значений датчиков:

Нажатие черной кнопки сбоку от поля он входит в режим настройки ; это можно сделать и во время активной навигации, нет необходимости сначала вводить Паузу. На 1-й странице дисплея отображается напряжение аккумуляторной батареи (V =7,83), потребляемая мощность (мА =177) и температура термисторного датчика рядом с диссипатором (38 ° C); нажимая снова и снова черную кнопку, вы переходите на следующие страницы; 2-я, 3-я, 4-я и 5-я страницы показывают параметры, перечисленные ниже, и вы можете изменить эти значения с помощью кнопок -1 и +1. На 6-й странице отображается «Обновление ...», если вы что-то изменили, значения сохраняются в памяти EEPROM.

  • Интервал: то есть 2000 мсек, это время между одной попыткой шагового двигателя и другой попыткой восстановить курс «H» на маршрут «R», перемещая джойстик направления вправо или влево;
  • Минимум: то есть 2 °, это минимальное количество градусов отклонения от маршрута для вмешательства автопилота; до этого значения руль остается в центральном положении устойчиво;
  • Макс: то есть 40 °, это максимальное изменение рулевого управления шаговым двигателем за один раз; если расчет сделан на изменение на 50 °, в действительности шаговый двигатель будет перемещаться только на 40 °;
  • Коэффициент: т.е. 1,50 x ° - коэффициент изменения рулевого управления за раз; если расчет сделан на изменение 40 °, в действительности шаговый двигатель будет двигаться на (40 x 1,50) =60 °;

Эти параметры необходимы для точной настройки автопилота . при установке на парусную лодку. Отклик, чувствительность и плавность хода зависят от диаметра шкивов, количества шкивов, диаметра главного шкива на шаговом двигателе, чувствительности руля направления, длины прикрепленной к нему ручки руля и т. Д. Позвольте установить все и позвольте попробовать и испытать на борту. Конечно, для всех этапов тестирования выбирайте солнечный красивый день с легким ветром!

Как это работает "вживую":

Вы путешествуете по морю, озеру или просто вокруг гавани. Настало время чая, и ваш кокс и ваш любимый бутерброд ждут в кармане. Вот и мы: переключить автопилот on и пусть он принимает координаты спутникового GPS, теперь вы должны прочитать на дисплее фактическую скорость в узлах, часах и направлении курса, например H270 ° (R =маршрут, по которому нужно следовать, H =фактический курс) в градусах (помните, что 180 ° =юг, 270 ° =запад, 360 ° или 0 ° =север и 90 ° =восток). Значения R и H одинаковы в режиме паузы (отображается STOP). Теперь подключите трос рулевого управления от шагового двигателя к ручке руля направления и нажмите синюю кнопку, чтобы запустить рулевое управление автопилотом .; в этот момент автопилот держит R =направление маршрута и берет под контроль то, что происходит с H =курсом. Номер заголовка точно изменится , медленно или быстро в зависимости от погодных условий, о которых мы уже говорили. Затем автопилот попытается восстановить R =направление маршрута вносить поправки, то есть -10 °, + 5 ° и т. Д., Пока значение H не станет равным значению R. . Вы можете внести некоторые изменения в Маршрут, и вы можете изменить номер с помощью красных и зеленых кнопок на устройстве (-1-10 +1 +10) или с помощью пульта дистанционного управления. Чтобы вернуть себе контроль из the рулевое управление достаточно нажать синюю кнопку «Пауза», отсоединить трос от руля направления, продолжая работу руками. Молодец.

Программная сторона:

Код довольно длинный, но я надеюсь, что он достаточно ясен, чтобы его было легко понять. В любом случае я бы объяснил, как это происходит. Скетч использует около 65% программы и около 45% памяти. Даже при использовании класса String, в основном для манипуляций с последовательными предложениями NMEA, весь процесс разработки остается стабильным и надежным; он использует "serialEvent ()" для получения данных от GPS два раза в секунду , затем вызывает "nmeaExtractData ()" и, наконец, он проверяет пакет данных с помощью "nmea0183_checksum (), чтобы убедиться в целостности данных. Если вы используете другой GPS-навигатор марки и модели, убедитесь, что предложения имеют ту же структуру, или вам придется внести здесь некоторые изменения. . Например, EM406A использует идентификатор пакета $ GPRMC . , BT220 использует вместо этого "$ GNRMC" ... небольшое изменение имени ... Полезная ссылка может помочь вам с проверкой контрольной суммы:https://nmeachecksum.eqth.net - Вот пример полного предложения NMEA, оно содержит :идентификатор, время, срок действия, широта, долгота, скорость, истинный курс, дата, вариация и контрольная сумма.

$ GPRMC, 095836.000, A, 4551.9676, N, 01328.7118, E, 2.09, 341.84, 280519 ,, * 08

Во время "Setup ()" проверяется EEPROM :если новый или неизвестный, он инициализируется (форматируется). Параметры в памяти читаются / записываются как байты:0 =0x29, 1 =0x00, 2-3 =интервал, 4-5 =min, 6-7 =max, 8-11 =коэффициент (byte, byte, int, int, плавать). Я осторожно обращался с операциями чтения / записи EEPROM, возможно, слишком много защитных ... Датчики проверяются каждые 10 секунд через "readMuxSensors ()" мультиплексором и может вызвать тревогу, если батарея разряжена или температура высокая. Разрешение энергопотребления невысокое, с шагом около 40 мА. Аппаратные кнопки и кнопки ПДУ проверяются постоянно; то, что они делают, зависит от логического значения IsSetup, а также от отображения RefreshDisplay () . Ядро кода - это раздел «УПРАВЛЕНИЕ РУЛЕВОЕ УПРАВЛЕНИЕМ», который вызывает функцию «gomotor ()» для перемещения шагового двигателя вперед и назад .; да, он может перемещать руль направления на 10 ° вправо и после значения интервала он возвращается в нулевое положение руля направления, и так далее после нового раунда расчета. Как уже было сказано, работа рулевого управления выполняется также во время настройки, поскольку она влияет только на несколько кнопок и поведение дисплея. Кормление собакой породы очень просто, но важно:просто ставит / выключает свой пин как можно скорее.

Как установить его на парусную лодку:

Как показано на рисунке ниже, я решил разместить автопилот и шаговый двигатель на корме, оба хорошо закрепленные болтами и т. Д .; канат диаметром 6 мм начинается от главного шкива двигателя и огибает два других шкива, расположенных с обеих сторон. Эти два шкива должны быть «прикреплены» к лодке с помощью двух эластичных колец, чтобы канат был слегка натянут. На этом этапе, наконец, вы должны решить, как прикрепить трос к рулю руля направления (временное соединение); он должен быть подключен, пока вы хотите, чтобы автопилот работал, его легко подключить и легко отключить. Держите систему автопилота подальше от воды! :-)

Новости и обновления:

  • 10.05.2020, добавлены для загрузки файлы проекта .STEP 3D CAD для шагового шкива (мной) и монтажной пластины (автор Эндрю Барни), а также их изображение для предварительного просмотра в 3D.

Заявление об ограничении ответственности и предупреждения:

Допустим, мы играем в эту игру, ничего в серьезно! Несколько лет назад я отправился в длительное путешествие, 16 месяцев, вокруг света на парусной лодке. Мы много работали с настоящим автопилотом (НЕ ЭТО ОДИН!) в любых погодных условиях, даже в плохих погодных условиях. Настоящий автопилот - это нечто очень сильный как аппаратное, так и программное обеспечение, которому вы должны доверять много. Это автопилот Arduino вместо фантастическая игра с и с весело провести время.

Удачи!

Марко Зонка

Код

  • Эскиз автопилота (для Uno)
  • Эскиз WatchDog (для Nano)
Скетч автопилота (для Uno) Arduino
 / * Этот эскиз действует как автопилот для небольших парусных лодок, Марко Зонка, Arduino UNO 2019 в качестве процессора, Arduino Nano в качестве сторожевого таймера, GPS BT-220 nmea, шаговый двигатель + контроллер, RC RF433Mhz, 6 кнопок, зуммер, i2c дисплей, 2 светодиода, i2c 24c04 eeprom, Mux 4051 для датчиков, lipo 2s 7,4v 2600mA, регулятор напряжения 7805, термистор; * / # include  #include  #include  # include  String inputString =""; String nm_time ="00:00:00"; String nm_validity ="V"; String nm_latitude ="ddmm.mmmm'N"; String nm_longitude ="dddmm.mmmm'E "; String nm_knots =" 0.0kn "; float nmf_knots =0.0; String nm_truecourse =" 360 "; float nmf_truecourse =360; String nm_date =" dd / mm / yyyy "; String nm_routetofollow =" 000 "; float nmf_routetofollow =0; unsigned long previousStearingMillis =0; unsigned long currentStearingMillis =0; unsigned long prevCheckSensorsMillis =0; unsigned long currCheckSensorsMillis =0; int CheckSensorsInterval =10000; bool stringComplete =false; bool isfirstfix =true; bool ispause =true; bool isStearing =false; bool isSetup =false; int s =0; int y =0; int z =0; int d =0; int rfRemoteControlValue =0; int HWButtonValue =0; int SetupParameter =0; float calcmove =0; float cm =0; float Stearing =0; float prevStearing =0; float t =0; int EEdisk =0x50; int EEid1 =0x29; int EEid2 =0x00; unsigned int EEaddress =0; unsigned int EEbytes =12; byte EEdata [12]; byte EEbytedata; int EEerr =0; float SensorVBatt =0; float SensorVRes =0; float SensorTemp =0; float SensormAmp =0; // следующие параметры являются значениями по умолчанию, но доступны для чтения / записи eeprom // eeprom инициализируется, если по адресам 0 и 1 содержимое отличается. addres len type notes // 0-255 байтов на 0x50 EEdisk, 256-512 байтов на 0x51 (не используется) ---------- -------------------------------------------------- --- // 0 байт 1 Байт 01001001 (0x29 как id1 проекта автопилота) // 1 байт 1 Байт 00000000 (0x00 "" id2) int StearingInterval =2000; // миллисекунд между попыткой и ответом 2 2B int StearingInterval 1000-5000 шагов 100int StearingMinToMove =2; // compass_degrees 4 2B int StearingMinToMove 0-20 шагов 1int StearingMaxMove =40; // compass_degrees 6 2B int StearingMaxMove 10-45 шагов 1float StearingCoeffMove =1.5; // используется как (compass_degrees * coeff) 8 4B float StearingCoeffMove 0,1–4 шаги 0,1 // 12 свободных // байтов bStearingInterval [sizeof (int)]; byte bStearingMinToMove [sizeof (int)]; byte bStearingMaxMove [sizeof (int)]; byte bStearingCoeffMove [sizeof (float)]; int prev_StearingInterval =0; int prev_StearingMinToMove =0; int prev_StearingMaxMove =0; float prev_StearingCoeffMove =0; const int ledpausePin =2; const int watchDogPin =3; const int watchDogPin =3; // 00 =Vin 01 =Vbatt 10 =Tempconst int MuxSelBit1Pin =6; // const int motorsABenablePin =13; const int MuxIOPin =14; const int ButtonsPin =15; const int rfRemoteControlPin =16; const int speakerPin =17; const int RCleftbutton =201; const int RCrightbutton =202; const int RCleft10button =203; const int RCright10button =204; const int HWleftbutton =101; const int HWrightbutton =102; const int HWpausebutton =103; const int HWsetupbutton =104; const int HWleft10button =105; const int HWright10button =106; const int motorStepsPerRevolution =200; // 200 для модели 23LM, 54 шага =1/4 оборота LiquidCrystal_I2C lcd (0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); Шаговый двигатель (motorStepsPerRevolution, 9, 10, 11, 12 ); void setup () {Serial.begin (4800); lcd.begin (16,2); Wire.begin (); motor.setSpeed ​​(60); inputString.reserve (200); pinMode (motorsABenablePin, OUTPUT); pinMode (MuxSelBit0Pin, ВЫХОД); pinMode (MuxSelBit1Pin, ВЫХОД); digitalWrite (motorsABenablePin, LOW); digitalWrite (MuxSelBit0Pin, LOW); digitalWrite (MuxSelBit1Pin, LOW); pinMode (ledpausePin, ВЫХОД); pinMode (watchDogPin, ВЫХОД); digitalWrite (ledpausePin, LOW); digitalWrite (watchDogPin, LOW); // чтение + проверка EEPROM (форматирование, если новое (или не идентифицированное)) lcd.clear (); lcd.setCursor (0,0); lcd.print («Проверка памяти ...»); lcd.setCursor (0,1); для (s =0; s  =StearingInterval) {if (isStearing ==false &&ispause ==false) {// иди попробуй (переместиться) calcmove =nmf_routetofollow - nmf_truecourse; если (calcmove <(-180)) {calcmove =calcmove + 360; } else {if (calcmove> (+180)) {calcmove =calcmove - 360; }} if (abs (calcmove)> =StearingMinToMove) {if (abs (calcmove)> =StearingMaxMove) {if (calcmove <0) {cm =(StearingMaxMove * -1); calcmove =см; } else {см =(StearingMaxMove * 1); calcmove =см; }} Stearing =(calcmove * StearingCoeffMove); гомотор (int ((Stearing * 216) / 360)); // 54 шага =1/4 оборота prevStearing =Stearing; isStearing =true; }} else {// возвращаемся (перемещаемся в "нулевую" позицию) if (isStearing ==true) {Stearing =(prevStearing * -1); гомотор (int ((Stearing * 216) / 360)); // 54 шага =1/4 оборота Stearing =0; prevStearing =0; isStearing =false; }} previousStearingMillis =currentStearingMillis; } // КНОПКИ RC RF ------------------ rfRemoteControlValue =checkRfRC (); if (rfRemoteControlValue) {switch (rfRemoteControlValue) {case RCleftbutton:// Левая кнопка ПДУ goleft (); ломать; case RCrightbutton:// Правая кнопка RC goright (); ломать; case RCleft10button:// Левая кнопка пульта ДУ 10 goleft10 (); ломать; case RCright10button:// Вправо + 10 RC button goright10 (); ломать; }} // КНОПКИ ------------------------ HWButtonValue =checkHWButtons (); if (HWButtonValue) {переключатель (HWButtonValue) {case HWleftbutton:// Левая (-1) кнопка HW if (isSetup ==false) {goleft (); } еще {setupMinus (); } ломать; case HWrightbutton:// Правая (+1) HW кнопка if (isSetup ==false) {goright (); } еще {setupPlus (); } ломать; case HWpausebutton:// HW кнопка паузы gopause (); ломать; case HWsetupbutton:// Настройка кнопки HW gosetup (); ломать; case HWleft10button:// Левая (-10) HW кнопка goleft10 (); ломать; case HWright10button:// Вправо (+10) HW button goright10 (); ломать; }} // GPS NMEA ------------------ if (stringComplete ==true) {// получено nmea предложение через последовательный порт RX bool ret; ret =nmeaExtractData (); inputString =""; stringComplete =false; если (ret ==true) {RefreshDisplay (); }} // ПОДАЧА СМОТРА ---------------- if (digitalRead (watchDogPin) ==LOW) {digitalWrite (watchDogPin, HIGH); } else {digitalWrite (watchDogPin, LOW); }} // считываем датчики мультиплексора readMuxSensors () {float Vo =0; float n =0; float n1 =0; float v1ad =0; float v2ad =0; float corr =0; поплавок R1 =10000; с плавающей точкой logR2 =0; поплавок R2 =0; поплавок T =0; float c1 =1.009249522e-03; float c2 =2.378405444e-04; float c3 =2.019202697e-07; digitalWrite (MuxSelBit0Pin, LOW); // 00 =Vbatt digitalWrite (MuxSelBit1Pin, LOW); n =аналоговое чтение (MuxIOPin); v1ad =n; n1 =(((10,00 * n) / 1023,00)); SensorVBatt =(n1 + ((n1 * 0,0) / 100)); // произвольная коррекция (не активна =0,0%) digitalWrite (MuxSelBit0Pin, LOW); // 01 =Vres digitalWrite (MuxSelBit1Pin, HIGH); n =аналоговое чтение (MuxIOPin); v2ad =n; n1 =(((10,00 * n) / 1023,00)); SensorVRes =(n1 + ((n1 * 0,0) / 100)); // произвольная коррекция (не активна =0,0%) digitalWrite (MuxSelBit0Pin, HIGH); // 10 =NTC Temp digitalWrite (MuxSelBit1Pin, LOW); Vo =аналоговое чтение (MuxIOPin); R2 =R1 * (1023,0 / Vo - 1,0); logR2 =журнал (R2); Т =(1.0 / (c1 + c2 * logR2 + c3 * logR2 * logR2 * logR2)); SensorTemp =T - 273,15; // Цельсия n =(v1ad - v2ad); n1 =(n / 0,22) * 1000,00; SensormAmp =(((10.00 * n1) / 1023.00));} // извлекаем данные из nmea inputStringbool nmeaExtractData () {bool ret =false; // истина, если nmea предложение =$ GNRMC и действительный CHKSUM if ((inputString.substring (0,6) =="$ GNRMC") &&(inputString.substring (inputString.length () - 4, inputString.length () - 2) ==nmea0183_checksum (inputString))) {y =0; для (s =1; s <11; s ++) {y =inputString.indexOf (",", y); переключатель (ы) {case 1:// время z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nm_time =inputString.substring (y + 1, y + 2 + 1) + ":" + inputString.substring (y + 1 + 2, y + 4 + 1) + " :"+ inputString.substring (y + 1 + 4, y + 6 + 1); } y =z; ломать; case 2:// срок действия z =inputString.indexOf (",", y + 1); если (z> (y + 1)) {nm_validity =inputString.substring (y + 1, y + 1 + 1); } y =z; ломать; case 3:// широта z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nm_latitude =inputString.substring (y + 1, y + 2 + 1) + "" + inputString.substring (y + 1 + 2, y + 10 + 1) + "' "; } y =z; ломать; case 4:// север / юг z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nm_latitude =nm_latitude + inputString.substring (y + 1, y + 1 + 1); } y =z; ломать; case 5:// долгота z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nm_longitude =inputString.substring (y + 1, y + 3 + 1) + "" + inputString.substring (y + 1 + 3, y + 11 + 1) + "' "; } y =z; ломать; case 6:// восток / запад z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nm_longitude =nm_longitude + inputString.substring (y + 1, y + 1 + 1); } y =z; ломать; case 7:// узлы скорости z =inputString.indexOf (",", y + 1); если (z> (y + 1)) {nmf_knots =inputString.substring (y + 1, z) .toFloat (); t =roundOneDec (nmf_knots); nm_knots =Строка (t, 1) + «kn»; } y =z; ломать; case 8:// истинный курс z =inputString.indexOf (",", y + 1); если (z> (y + 1)) {nmf_truecourse =inputString.substring (y + 1, z) .toFloat (); d =nmf_truecourse; nm_truecourse =d; } y =z; ломать; case 9:// дата z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nm_date =inputString.substring (y + 1, y + 2 + 1) + "/" + inputString.substring (y + 1 + 2, y + 4 + 1) + " /20"+inputString.substring(y+1+4,y+6+1); } y =z; ломать; case 10:// операторы break; по умолчанию:// операторы break; }} если ((isfirstfix ==true) || (ispause ==true)) {nm_routetofollow =nm_truecourse; nmf_routetofollow =nmf_truecourse; isfirstfix =false; } ret =true; } return ret;} // увеличиваем (+) значение параметра во время setupvoid setupPlus () {switch (SetupParameter) {case 2:// interval StearingInterval =(StearingInterval + 100); если (StearingInterval> 5000) {StearingInterval =5000; } ломать; case 3:// мин. для перемещения StearingMinToMove =(StearingMinToMove + 1); если (StearingMinToMove> 20) {StearingMinToMove =20; } ломать; case 4:// макс. переместить StearingMaxMove =(StearingMaxMove + 1); если (StearingMaxMove> 45) {StearingMaxMove =45; } ломать; case 5:// коэффициент StearingCoeffMove =(StearingCoeffMove + 0.1); если (StearingCoeffMove> 4) {StearingCoeffMove =4; } ломать; } задержка (200); RefreshDisplay ();} // уменьшить (-) значение параметра во время настройкиvoid setupMinus () {switch (SetupParameter) {case 2:// interval StearingInterval =(StearingInterval - 100); если (StearingInterval <1000) {StearingInterval =1000; } ломать; case 3:// мин. для перемещения StearingMinToMove =(StearingMinToMove - 1); если (StearingMinToMove <0) {StearingMinToMove =0; } ломать; case 4:// макс. move StearingMaxMove =(StearingMaxMove - 1); если (StearingMaxMove <10) {StearingMaxMove =10; } ломать; case 5:// коэффициент StearingCoeffMove =(StearingCoeffMove - 0,1); если (StearingCoeffMove <0,1) {StearingCoeffMove =0,1; } ломать; } задержка (200); RefreshDisplay ();} // управление двигателем (+) =вперед (-) =назад избежать гомотора (int stepsToMove) {digitalWrite (motorsABenablePin, HIGH); motor.step (stepsToMove); digitalWrite (motorsABenablePin, LOW);} // обновляем данные на дисплееvoid RefreshDisplay () {if (isSetup ==false) {// --------- normal lcd.clear (); lcd.setCursor (0,0); lcd.print("R"+nm_routetofollow); lcd.write(0xDF); lcd.print(" H"+nm_truecourse); lcd.write(0xDF); if (ispause ==true) { lcd.print(" STOP"); } else { if (Stearing> 0) { lcd.print(" +"); } if (Stearing ==0) { lcd.print(" "); } if (Stearing <0) { lcd.print(" "); } lcd.print(int(Stearing)); } lcd.setCursor(0,1); lcd.print(nm_time+" "+nm_knots); } if (isSetup ==true) { //-----------setup lcd.clear(); lcd.setCursor (0,0); lcd.print("setup:"); switch (SetupParameter) { case 1://display sensors readMuxSensors(); lcd.print("V="); lcd.print(SensorVBatt); lcd.setCursor(1,1); lcd.print("mA="); lcd.print(int(SensormAmp)); lcd.print(" "); lcd.print(int(SensorTemp)); lcd.write(0xDF); lcd.print("C"); ломать; case 2://interval lcd.print("interval"); lcd.setCursor(7,1); lcd.print(StearingInterval); lcd.print(" mSec"); ломать; case 3://min. to move lcd.print("minimum"); lcd.setCursor(7,1); lcd.print(StearingMinToMove); lcd.write(0xDF); ломать; case 4://max. move lcd.print("max"); lcd.setCursor(7,1); lcd.print(StearingMaxMove); lcd.write(0xDF); ломать; case 5://coefficient lcd.print("coeffic."); lcd.setCursor(7,1); lcd.print(StearingCoeffMove); lcd.print(" x "); lcd.write(0xDF); ломать; } }}/* SerialEvent occurs whenever a new data comes in the hardware serial RX. This routine is run between each time loop() runs, so using delay inside loop can delay response. Multiple bytes of data may be available.*/void serialEvent() { while (Serial.available()) { char inChar =(char)Serial.read(); inputString +=inChar; // if the incoming character is a newline, set a flag so the main loop can // do something about it if (inChar =='\n') { stringComplete =true; } } }//calculate checksum of nmea sentenceString nmea0183_checksum(String nmea_data) { int crc =0; String chSumString =""; int i; // ignore the first $ sign, checksum in sentence for (i =1; i <(nmea_data.length()-5); i ++) { // remove the - 5 if no "*" + cksum + cr + lf are present crc ^=nmea_data[i]; } chSumString =String(crc,HEX); if (chSumString.length()==1) { chSumString="0"+chSumString.substring(0,1); } chSumString.toUpperCase(); return chSumString;}//check RC which button is pressedint checkRfRC() { int n =0; int res =0; n =analogRead(rfRemoteControlPin); if ((n>350) and (n<460)) { // button A res =RCleftbutton; } if ((n> 90) and (n<190)) { // button B res =RCrightbutton; } if ((n>540) and (n<640)) { // button C res =RCleft10button; } if ((n>225) and (n<325)) { // button D res =RCright10button; } return res; }//check HW which button is pressedint checkHWButtons() { int n =0; int res =0; n =analogRead(ButtonsPin); //Serial.println(n); if ((n>465) and (n<565)) { // button left res =HWleftbutton; } if ((n>290) and (n<390)) { // button right res =HWrightbutton; } if ((n>130) and (n<220)) { // button pause res =HWpausebutton; } if ((n>625) and (n<725)) { // button setup res =HWsetupbutton; } if ((n>975) and (n<1075)) { // button left-10 res =HWleft10button; } if ((n>800) and (n<900)) { // button right+10 res =HWright10button; } return res; }void gosetup() { // setup button if (isSetup ==false) { SetupParameter =1; isSetup =true; } else { if (SetupParameter <5) { SetupParameter ++; } else { if (prev_StearingInterval !=StearingInterval || prev_StearingMinToMove !=StearingMinToMove || prev_StearingMaxMove !=StearingMaxMove || prev_StearingCoeffMove !=StearingCoeffMove) { lcd.clear(); lcd.setCursor (0,0); lcd.print("updating... "); задержка (1000); goupdateEEPROM(); if (EEerr) { lcd.print("E="); lcd.print(EEerr); задержка (1000); } prev_StearingInterval =StearingInterval; prev_StearingMinToMove =StearingMinToMove; prev_StearingMaxMove =StearingMaxMove; prev_StearingCoeffMove =StearingCoeffMove; } isSetup =false; } } NewTone (speakerPin,2000); delay(200); noNewTone(); RefreshDisplay();}void goupdateEEPROM() { EEaddress =0; //id1 EEdata[0] =EEid1; EEbytedata =EEid1; writeEEPROM (EEdisk, EEaddress, EEbytedata); EEaddress =1; //id2 EEdata[1] =EEid2; EEbytedata =EEid2; writeEEPROM (EEdisk, EEaddress, EEbytedata); memcpy(bStearingInterval, &StearingInterval, sizeof(int)); memcpy(bStearingMinToMove, &StearingMinToMove, sizeof(int)); memcpy(bStearingMaxMove, &StearingMaxMove, sizeof(int)); memcpy(bStearingCoeffMove, &StearingCoeffMove, sizeof(float)); memcpy(EEdata+2,bStearingInterval,sizeof(int)); memcpy(EEdata+4,bStearingMinToMove,sizeof(int)); memcpy(EEdata+6,bStearingMaxMove,sizeof(int)); memcpy(EEdata+8,bStearingCoeffMove,sizeof(float)); for (s =2; s  360) { nmf_routetofollow =1; } d=nmf_routetofollow; nmf_routetofollow=d; nm_routetofollow=d; NewTone (speakerPin,800); delay(200); noNewTone(); } else { NewTone (speakerPin,1000); delay(50); noNewTone(); } RefreshDisplay();}void goright10() { // right 10x button/RC if (ispause ==false) { for (s =1; s <11; s ++) { nmf_routetofollow ++; if (nmf_routetofollow> 360) { nmf_routetofollow =1; } } d=nmf_routetofollow; nmf_routetofollow=d; nm_routetofollow=d; NewTone (speakerPin,800); delay(200); noNewTone(); } else { NewTone (speakerPin,1000); delay(50); noNewTone(); } RefreshDisplay();}void gopause() { // pause button/RC if (ispause ==true) { ispause=false; digitalWrite(ledpausePin, HIGH); NewTone (speakerPin,50); delay(200); NewTone (speakerPin,200); delay(800); noNewTone(); } else { ispause=true; digitalWrite(ledpausePin, LOW); NewTone (speakerPin,200); delay(200); NewTone (speakerPin,50); delay(800); noNewTone(); } RefreshDisplay();}// reading eeprombyte readEEPROM (int diskaddress, unsigned int memaddress) { byte rdata =0x00; Wire.beginTransmission (diskaddress); Wire.write (memaddress); if (Wire.endTransmission () ==0) { Wire.requestFrom (diskaddress,1); if (Wire.available()) { rdata =Wire.read(); } else { EEerr =1; //"READ no data available" } } else { EEerr =2; //"READ eTX error" } Wire.endTransmission (true); return rdata;}// writing eepromvoid writeEEPROM (int diskaddress, unsigned int memaddress, byte bytedata) { Wire.beginTransmission (diskaddress); Wire.write (memaddress); Wire.write (bytedata); if (Wire.endTransmission () !=0) { EEerr =3; //"WRITING eTX error" } Wire.endTransmission (true); delay(5); }// round zero decimalfloat roundZeroDec(float f) { float y, d; y =f*1; d =y - (int)y; y =(float)(int)(f*1)/1; if (d>=0.5) { y +=1; } else { if (d <-0.5) { y -=1; } } return y;}// round one decimalfloat roundOneDec(float f) { float y, d; y =f*10; d =y - (int)y; y =(float)(int)(f*10)/10; if (d>=0.5) { y +=0.1; } else { if (d <-0.5) { y -=0.1; } } return y;}// round two decimalfloat roundTwoDec(float f) { float y, d; y =f*100; d =y - (int)y; y =(float)(int)(f*100)/100; if (d>=0.5) { y +=0.01; } else { if (d <-0.5) { y -=0.01; } } return y;}
WatchDog sketch (for Nano)Arduino
/* * This sketch is a Watchdog to keep CLIENT under control, on Arduino NANO 3.0 by Marco Zonca * CLIENT must feed Whatcdog sooner then feedingInterval otherwise will be forced to restart * */const int feedingPin =14;const int ledPin =15;const int restartPin =16;const int buzzerPin =17;const long ledInterval =1000;const long feedingInterval =2500;const long timeForClientStart =16000;int ledState =LOW;int previousFeedingState =LOW;int feedingState =LOW;unsigned long previousLedMillis =0;unsigned long previousFeedingMillis =0;void setup() { digitalWrite(restartPin, HIGH); // LOW will force CLIENT to restart pinMode(ledPin, OUTPUT); pinMode(buzzerPin, OUTPUT); pinMode(restartPin, OUTPUT); pinMode(feedingPin, INPUT); delay(timeForClientStart); // let time to CLIENT to start...}void loop() { unsigned long currentMillis =millis(); // BLINK LED ------------------- if (currentMillis - previousLedMillis>=ledInterval) { previousLedMillis =currentMillis; if (ledState ==LOW) { ledState =HIGH; } else { ledState =LOW; } digitalWrite(ledPin, ledState); } // CHECK THE FEEDING ------------------- feedingState =digitalRead(feedingPin); // CLIENT must set pin HIGH -> LOW frequently to prove it's alive if (feedingState ==HIGH) { if (previousFeedingState ==LOW) { previousFeedingMillis =currentMillis; } previousFeedingState =HIGH; } else { previousFeedingState =LOW; } if (currentMillis - previousFeedingMillis> feedingInterval) { // CLIENT is sleeping ledState =HIGH; digitalWrite(ledPin, ledState); tone(buzzerPin,1500); delay(500); digitalWrite(restartPin, LOW); //restart CLIENT tone(buzzerPin,1500); delay(500); digitalWrite(restartPin, HIGH); tone(buzzerPin,1500); delay(timeForClientStart); // let CLIENT time to restart... noTone(buzzerPin); currentMillis =millis(); previousFeedingState =LOW; previousFeedingMillis =currentMillis; previousLedMillis =currentMillis; }} 

Изготовленные на заказ детали и корпуса

23lm-stepper-plate-v2_PlvJaff9Hl.step 23lm-stepper-pulley-56_UhsbaWbiBt.step

Схема


Производственный процесс

  1. Самодельная инфракрасная система датчиков движения для Raspberry Pi
  2. Создайте систему восстановления баллистического парашюта для вашего дрона
  3. Подходит ли вам система непрерывного мониторинга?
  4. Автоматические устройства смены инструмента для роботов
  5. Готова ли ваша система к IoT?
  6. Многоугольные фрезы для автоматических токарных станков
  7. Автоматический токарный станок с ЧПУ для прецизионных деталей
  8. Двигатель для гиперзвукового полета
  9. Принцип работы механической и автоматической коробки передач
  10. Понимание системы автоматической коробки передач