Manufaturação industrial
Internet das coisas industrial | Materiais industriais | Manutenção e reparo de equipamentos | Programação industrial |
home  MfgRobots >> Manufaturação industrial >  >> Manufacturing Technology >> Processo de manufatura

Piloto automático para barcos à vela (sistema de direção automática)

Componentes e suprimentos

Arduino UNO
× 1
Arduino Nano R3
× 1

Sobre este projeto





Prefácio:


Adoro velejar sozinho porque quando um homem está no mar com o seu barco à vela tem tudo o que precisa para evoluir para um nível superior. Velejar em mar revolto com mau tempo pode ser muito difícil mas se escolher dias de bom tempo com sol e vento agradável o desfrute será no máximo.

Felicidade significa horizontes infinitos, técnicas atléticas perfeitas, escolhas ótimas, mas também coisas humanas como uma boa bebida e um sanduíche saboroso! Exatamente nessa hora chega a ajuda do piloto automático:ele funciona em vez de você enquanto você toma seu chá e biscoitos às 17h no mar. :-)





O que o Autopilot pode fazer por você:


Um barco à vela não tem motor e não pode percorrer um caminho programado do porto à praia, depois ao pesqueiro, dar a volta ao Farol e voltar, sozinho, não pode.

Todo o trabalho é feito pelo marinheiro, temos que entender isso neste ponto:aparar velas, controlar o tempo e a fonte / velocidade do vento, endurecer ou soltar as cordas, cuidar do tráfego com outros barcos, decidir direção e direção ... Quando o marinheiro decide fazer uma pausa, digamos apenas 10 segundos ou alguns minutos (a famosa "hora do chá"), ele liga o piloto automático. Em segundos, seu GPS adquire a posição, velocidade e direção do barco e é capaz de manter a direção (rota). O sistema de direção, um manche conectado ao leme, normalmente movido pelas mãos experientes do marinheiro, agora está sob o controle do piloto automático por meio do motor de passo conectado a ele por roldanas e cordas.

Controle o leme é um trabalho contínuo de sintonia fina ou grosseira. Menor (mais leve) é o barco e maiores serão as mudanças de direção que os fatores influenciam:ondas do mar, direção e pressão do vento, deslocamento do peso a bordo pelos movimentos do marinheiro, correntes marítimas. Mas o marinheiro está sempre acordado, mesmo com o piloto automático ligado, fazendo alterações na rota real por meio do controle remoto :existem 4 botões, marcados +1 -1 +10 -10, para pequenas ou grandes mudanças em graus, aumentando ou diminuindo o valor. Esses botões estão presentes no piloto automático também, as verdes (direita) e as vermelhas (esquerda). O botão azul (meio) é para ativar ou desativar o piloto automático, a pausa. É também um botão preto para configurar parâmetros em memória.





O circuito:


O processamento principal é feito pelo MCU Arduino Uno . O outro MCU, Arduino Nano , é o watchdog:sei que existe uma espécie de watchdog dentro do Uno mas gostei de fazê-lo com um microcontrolador externo independente, é um sonho de toda a vida que cobri, estou feliz agora! O Uno tem que alimentar o Nano pelo pino 3 -> A0 colocando alto / baixo, 5/0 volts, pelo menos uma vez a cada 2,5 segundos (feedInterval); se não, significa que o Uno está "dormindo" ou "bloqueado", e o Nano zera o Uno ... Ainda não aconteceu, dá para acreditar?

É usado um display popular em conjunto com um conversor de circuito i2c ambos soldados juntos, finalmente usando apenas 4 fios para poupar significativamente os pinos digitais para se comunicarem com o Uno. Também a forma de conectar botões e controle remoto é feito por divisores de tensão de resistência para atingir a meta de usar o menor número possível de portas de MCU; Eu escolhi resistores de precisão de 1%, os valores de comparação analógicos devem estar entre os valores que coloquei no código; no caso de não reconhecimento de alguns botões porque você escolheu outro tipo de resistores, apenas faça algumas mudanças nas constantes também (modifique o código em "checkRfRC ()" e "checkHWButtons ()"). O circuito de controle remoto (RC) RF 433Mhz funciona bem; para melhorar a cobertura à distância e as chances de sucesso, adicionei uma antena em espiral que você poderia fazer com um pedaço de fio de cobre; Testei-o a 10 metros de distância, mas acho que pode funcionar até a 20 metros ou mais, mais do que suficiente considerando que o veleiro alvo que usei para testar o piloto automático tem apenas 4,20 metros de comprimento.

Para a unidade GPS Usei no começo um bom EM406A mas infelizmente descobri que tinha Week-Rollover-Bug, era muito velho, então tive que trocar por um Beitian BN-220T excelente e popular. Com seu software de configuração, defina-o para "cuspir" 2 vezes por segundo (2 Hz) apenas a frase de série NMEA "$ GNRMC" necessária. O GPS envia (TX) dados seriais para o pino 0 (RX) do Uno. Os dados contêm todos os dados de navegação usados ​​para calcular a correção a ser feita pelo Motor:data, hora, posição, latitude e longitude, curso verdadeiro, velocidade e validade dos satélites fixados. Devido ao fato de a programação IDE do Arduino usar a porta do pino 0 (RX) também, lembre-se de desconectar temporariamente o GPS durante esta operação ...

Outro sonho meu era usar um EEPROM . O IC 2404 é um lindo circuito integrado i2c de 512 bytes que usei para ler / escrever neste chip de memória alguns parâmetros para movimentos do motor de passo que explicarei mais tarde no parágrafo "Software".





Lista de componentes:

  • Arduino Uno como MCU
  • Arduino Nano como cão de guarda
  • GPS Beitian BN-220T
  • Motor de passo, modelo 23LM, 54 etapas =1/4 de revolução
  • Chaves do controlador L298 para motor
  • RF433Mhz RC XD-YK04 + controle remoto de 4 botões + antena helicoidal
  • 6 botões normalmente abertos (2xRed, 2xGreen, 1xBlack e 1xBlue)
  • Botão para ligar / desligar (branco)
  • Conectores redondos de 6 pinos fêmea + macho para motor de passo externo
  • Buzzer
  • Display LCD1602 2x16 caracteres + circuito conversor i2c
  • 3 LEDS (vermelho, azul e amarelo))
  • IC 24c04 i2c eeprom
  • multiplexador IC 4051
  • Bateria LiPo 2s 7,4v 2600mA
  • IC 7805 regulador de tensão + dissipador de calor
  • Termistor NTC MF52-103 10k
  • Fusível reajustável 2A
  • 6x 1N4148 diodos (D1-D6)
  • Resistores na blindagem de energia (R1-R4 =10k, R5 =100k)
  • Resistores na blindagem do piloto automático (R1 =330, R2 =1k, R3 =2k, R4 =5,1k, R5 =1k, R6 / R7 / R14 =330, R8-R13 =10k, R15 =10M)
  • Capacitores (C1 =470uF 16v, C2 =100n)
  • 2W resistor de 0,22 Ohm (R6)
  • distintivos masculinos
  • Cabeçalhos de alfinetes femininos
  • Caixa transparente e "à prova d'água"

Existem alguns sensores no circuito, todos conectados ao Arduino Uno com a ajuda do multiplexador IC 4051 . É um termistor para controlar a temperatura do dissipador de aquecimento do regulador de tensão, um resistor de 2W e 4x10k como divisores de tensão para calcular Ampère como consumo de energia de todo o circuito. Além disso, a tensão da bateria é mantido sob controle:LiPo são conhecidos por serem críticos quando elementos individuais são descarregados abaixo de 3,3 V; este circuito tem dois elementos (2S) LiPo em um pacote, em caso de baixa tensão (abaixo de 7,0v) o buzzer irá informá-lo com bipes curtos e rápidos. Não espere muito para desligar e recarregue logo! Os Leds :amarelo piscando em 1 Hz permite que você saiba que o WatchDog está funcionando; o azul fica ligado quando o piloto automático está ligado, desligado se pausado; o led vermelho pisca quando um dos botões do controle remoto é pressionado.

Todo o circuito funciona a 5,0 V fornecido por bateria LiPo 2S 7,4 V 2600mA / he IC 7805 regulador de tensão . A corrente não deve ser maior que 800mA, mas geralmente é em torno de 100-450mA. Coloque nele um dissipador de aquecimento . O termistor é colocado nele e a campainha soará se a temperatura ultrapassar 50 ° C.





As placas de circuito impresso PCB e montagem:


São usados ​​ PCBs de face única por esse motivo, tive que incluir alguns jumpers de fio (os de linha pontilhada) para resolver rotas para circuitos inteiros. Aqui são mostradas as faces dos componentes, mas abaixo você tem todos os arquivos, componentes e faces de solda, espelhados, para download e impressão por meio de uma impressora a laser em folhas "amarelas" ou "azuis". Usei os amarelos, mas dizem que os azuis são melhores (mas o preço é significativamente mais alto). Ao imprimir, lembre-se de desativar as configurações de economia de toner, em vez disso, use a resolução de 1200 dpi para obter um resultado de preto real profundo. O processo de transferência do toner das folhas mágicas para os PCBs é feito com o uso de um ferro quente ... A impressão nas duas faces, também na face dos componentes, facilita o reconhecimento do posicionamento dos itens e até torna o projeto "profissional".

Ambos os PCBs são dimensionados para caber um sobre o outro Arduino Uno como uma pilha :primeiro a unidade de potência e depois a unidade de piloto automático em geral.

Minha escolha tem sido colocar todas as coisas juntas, PCBs, MCUs, RC, circuito do Motor Driver, bateria, GPS, botões, switch, fios, conectores, etc. pensando em reutilizá-los um dia:Eu não os soldei juntos, eu usado cabeçalhos e fios / conexões Dupont populares em vez de. Existem cerca de 200 conexões não soldadas, então, isso significa que malfuncionamentos inesperados e indesejados ou comportamentos diferentes do circuito podem acontecer de vez em quando, é normal. A sugestão é soldar tudo para um circuito mais estável!





Configuração de parâmetros e valores de sensores de exibição:


Pressionando o botão preto na lateral da caixa, ele entra no modo de configuração ; isso também pode ser feito durante a navegação ativa, não é necessário inserir a pausa primeiro. A 1ª página do display mostra a tensão da bateria (V =7,83), o consumo de energia (mA =177) e a temperatura do sensor termistor próximo ao dissipador (38 ° C); pressionando repetidamente o botão preto para acessar as próximas páginas; a 2ª, 3ª, 4ª e 5ª páginas mostram os parâmetros listados abaixo e você pode alterar esses valores por meio dos botões -1 e +1. A 6ª página mostra "Atualizando ..." se você alterou algo, os valores são salvos na memória EEPROM.
  • Intervalo: ou seja, 2000 mSec, é o tempo entre uma tentativa e outra do motor de passo para restaurar o rumo "H" para a rota "R", movendo o manche do leme para a direita ou esquerda;
  • Mínimo: isto é, 2 °, é a quantidade mínima de graus fora da rota para a intervenção do piloto automático; até este valor, o leme permanece estável na posição central;
  • Máx .: ou seja, 40 °, é a mudança máxima de direção por vez pelo motor de passo; se o cálculo for feito para uma mudança de 50 °, na realidade o Stepper se moverá apenas 40 °;
  • Coeficiente: isto é, 1,50 x °, é o coeficiente para mudança de direção de cada vez; se o cálculo feito for para mudança de 40 °, na realidade o motor de passo se moverá para (40 x 1,50) =60 °;

Esses parâmetros são necessários para ajustar o piloto automático quando instalado no barco à vela. A resposta, a sensibilidade e a suavidade dependem do diâmetro das polias, de quantas polias, do diâmetro da polia principal no motor de passo, da sensibilidade do leme, do comprimento do manche do leme conectado a ele e assim por diante. Vamos instalar tudo e experimentar fazer experiência a bordo. Claro que escolha um lindo dia ensolarado com vento fraco para todas as fases de testes!





Como funciona "ao vivo":


Você está navegando no mar, no lago ou apenas ao redor do porto. É a hora do chá e sua coca e seu sanduíche favoritos estão esperando no bolso. Aqui estamos: mudar de piloto automático ligado e deixe-o obter a posição do GPS do satélite, agora você deve ler no visor a velocidade real em nós, o relógio e a direção do rumo, ou seja, H270 ° (R =rota a seguir, H =rumo real) em graus (lembre-se de 180 ° =sul, 270 ° =oeste, 360 ° ou 0 ° =norte e 90 ° =leste). Os valores de R e H são iguais no modo de pausa (STOP é mostrado). Conecte agora a corda de direção, do motor de passo ao manete do leme e pressione o botão azul para iniciar a direção do piloto automático ; neste ponto, o piloto automático mantém a direção da rota R =e assume o controle do que acontece com o rumo H =. O número do cabeçalho muda com certeza , devagar ou rápido dependendo das condições meteorológicas de que já falamos. O piloto automático, em seguida, tente restaurar para R =direção da rota fazendo correções, ou seja, -10 °, + 5 °, etc. até que o valor H seja igual ao valor R . Você pode decidir algumas mudanças na rota e pode modificar o número usando os botões vermelho e verde na unidade (-1 -10 +1 +10) ou por meio do controle remoto. Para retomar o controle de o direção você só precisa pressionar o botão azul de pausa, desconectar a corda do manche do leme e continuar o trabalho com as mãos. Bem feito.





Lado do software:


O código é bastante longo, mas espero que seja claro o suficiente para ser facilmente compreensível. Em qualquer caso, eu explicaria como está indo. O esboço usa cerca de 65% do programa e cerca de 45% da memória. Mesmo usando a classe String, principalmente para manipulação de sentenças Serial NMEA, todo o fluxo de elaboração é estável e sólido; ele usa "serialEvent ()" para receber dados do GPS duas vezes por segundo , então chama "nmeaExtractData ()" e finalmente verifica o pacote de dados com "nmea0183_checksum () para ter certeza da integridade dos dados. Se você usar outra marca e modelo de GPS, certifique-se de que as frases tenham a mesma estrutura ou você terá que fazer algumas alterações aqui . Por exemplo, EM406A usa ID de pacote "$ GPRMC" , BT220 usa "$ GNRMC" em vez ... apenas uma pequena mudança de nome ... Um link útil pode ajudá-lo com o teste de checksum:https://nmeachecksum.eqth.net - Aqui um exemplo de uma frase NMEA completa, ele contém :id, tempo, validade, latitude, longitude, velocidade, curso verdadeiro, data, variação e soma de verificação.

$ GPRMC, 095836.000, A, 4551,9676, N, 01328,7118, E, 2,09, 341,84, 280519 ,, * 08

Durante "Configuração ()" a EEPROM é verificada :se novo ou desconhecido, é inicializado (formatado). Os parâmetros na memória são lidos / gravados como bytes:0 =0x29, 1 =0x00, 2-3 =intervalo, 4-5 =min, 6-7 =max, 8-11 =coeficiente (byte, byte, int, int, flutuador). Lidei com as operações EEPROM r / w com cuidado, talvez muito defensivas ... Os sensores são verificados a cada 10 segundos através de "readMuxSensors ()" pelo multiplexador e pode causar alarme se a bateria estiver baixa ou a temperatura alta. A resolução do consumo de energia é baixa, passos em torno de 40mA. Hardware e botões RC são verificados continuamente; o que eles fazem depende do valor booleano "IsSetup" e também da exibição de "RefreshDisplay ()" . O núcleo do código é a seção STEERING CONTROL que chama a função "gomotor ()" para mover o Stepper para fora e para trás ; sim, pode mover o leme 10 ° para a direita e após o valor do intervalo voltar à posição zero do leme, e assim por diante após uma nova rodada de cálculo. Como já foi dito, o trabalho de direção também é feito durante a configuração, pois afeta apenas alguns botões e o comportamento do display. Alimentação Whatchdog é muito simples, mas importante:basta ligar / desligar o pino assim que possível.





Como instalar em um barco à vela:


Como pode ser visto na foto abaixo optei por colocar o piloto automático e o motor de passo na popa, ambos bem fixados com parafusos, etc .; um cabo de 6 mm de diâmetro parte da polia do motor principal e passa ao redor de duas outras polias colocadas em ambos os lados. Essas duas polias devem ser "fixadas" ao barco por meio de dois anéis elásticos para manter a corda levemente tensionada. Neste ponto, finalmente, você deve decidir como conectar a corda ao manche do leme (conexão temporária); ele deve estar conectado enquanto você deseja que o Autopilot esteja em ação, fácil de conectar e fácil de desconectar. Mantenha o sistema de piloto automático longe da água! :-)





Notícias e atualizações:

  • 10/05/2020, adicionado para download de arquivos de projeto CAD 3D .STEP para polia de passo (por mim) e placa de montagem (por Andrew Barney), e uma imagem de visualização 3D deles.





Isenção de responsabilidade e avisos:


Digamos que este é um jogo que estamos jogando aqui, nada para leve a sério! Há alguns anos fiz uma longa viagem, de 16 meses, ao redor do mundo em um veleiro. Nós navegamos extensivamente com um piloto automático real (NÃO ISTO UM!) em todas as condições meteorológicas, mesmo nas condições meteorológicas adversas. Um verdadeiro piloto automático é algo muito forte hardware e software você deve confiar muito. Em vez disso, este Arduino Autopilot é um jogo fantástico para jogar com e para passe um tempo se divertindo.

Divirta-se!

Marco Zonca

Código

  • Esboço do piloto automático (para Uno)
  • Esboço do WatchDog (para Nano)
Esboço do piloto automático (para Uno) Arduino
 / * Este esboço atua como piloto automático para pequenos barcos à vela, por Marco Zonca, 2019 Arduino UNO como CPU, Arduino Nano como watchdog, GPS BT-220 nmea, motor de passo + controlador, rf433Mhz RC, 6 botões, campainha, i2c display, 2 leds, i2c 24c04 eeprom, Mux 4051 para sensores, lipo 2s 7,4v 2600mA, regulador de tensão 7805, termistor; * / # include  #include  #include  # incluir  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 / aaaa "; String nm_routetofollow =" 000 "; float nmf_routetofollow =0; sem sinal longo anteriorStearingMillis =0; sem sinal longo currentStearingMillis =0; sem sinal longo prevCheckSensorsMillis =0; longo sem sinal currCheckSensorsMillis =0; int CheckSensorsInterval =10000; bool stringComplete =falso; bool isfirstfix =verdadeiro; 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; sem sinal int EEaddress =0; sem sinal int EEbytes =12; byte EEdata [12]; byte EEbytedata; int EEerr =0; flutuante SensorVBatt =0; flutuante SensorVRes =0; flutuante SensorTemp =0; float SensormAmp =0; // os parâmetros a seguir são os padrões, mas são lidos / gravados eeprom // eeprom é inicializado se nos endereços 0 e 1 o conteúdo for diferente addres len type notes // 0-255 bytes em 0x50 EEdisk, 256-512 bytes em 0x51 (não usado) ---------- -------------------------------------------------- --- // 0 1B byte 01001001 (0x29 como projeto de piloto automático id1) // 1 1B byte 00000000 (0x00 "" id2) int StearingInterval =2000; // milis entre a tentativa e retorno 2 2B int StearingInterval 1000-5000 passos 100int StearingMinToMove =2; // compass_degrees 4 2B int StearingMinToMove 0-20 etapas 1int StearingMaxMove =40; // compass_degrees 6 2B int StearingMaxMove 10-45 etapas 1float StearingCoeffMove =1.5; // usado como (compass_degrees * coeff) 8 4B float StearingCoeffMove 0.1-4 passos 0.1 // 12 free // byte 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; int MuxS // 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 =200; const int HWpausButton // 200 para o modelo 23LM, 54 etapas =1/4 de revoluçãoLiquidCrystal_I2C lcd (0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVO); Motor de passo (motorStepsPerRevolution, 9, 10, 11, 12 ); configuração vazia () {Serial.begin (4800); lcd.begin (16,2); Wire.begin (); motor.setSpeed ​​(60); inputString.reserve (200); pinMode (motorsABenablePin, OUTPUT); pinMode (MuxSelBit0Pin, OUTPUT); pinMode (MuxSelBit1Pin, OUTPUT); digitalWrite (motorsABenablePin, LOW); digitalWrite (MuxSelBit0Pin, LOW); digitalWrite (MuxSelBit1Pin, LOW); pinMode (ledpausePin, OUTPUT); pinMode (watchDogPin, OUTPUT); digitalWrite (ledpausePin, LOW); digitalWrite (watchDogPin, LOW); // leia + verifique a EEPROM (formatação se for nova (ou não identificada)) lcd.clear (); lcd.setCursor (0,0); lcd.print ("Verificação de memória ..."); lcd.setCursor (0,1); para (s =0; s  =CheckSensorsInterval) {readMuxSensors (); if ((SensorVBatt <=7,0) || (SensorTemp>
 =50)) {lcd.clear (); lcd.setCursor (0,0); lcd.print ("Sensores de alarme!"); lcd.setCursor (1,1); lcd.print ("V ="); lcd.print (SensorVBatt); lcd.print (""); lcd.print (int (SensorTemp)); lcd.write (0xDF); lcd.print ("C"); NewTone (speakerPin, 10); atraso (1000); noNewTone (); } prevCheckSensorsMillis =currCheckSensorsMillis; } // STEARING CONTROL ---------------- currentStearingMillis =millis (); if (currentStearingMillis - previousStearingMillis> =StearingInterval) {if (isStearing ==false &&ispause ==false) {// tente (mover stearing) calcmove =nmf_routetofollow - nmf_truecourse; if (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 =cm; } else {cm =(StearingMaxMove * 1); calcmove =cm; }} Stearing =(calcmove * StearingCoeffMove); gomotor (int ((Stearing * 216) / 360)); // 54 passos =1/4 de revolução prevStearing =Stearing; isStearing =true; }} else {// voltar (mover stearing para a posição "zero") if (isStearing ==true) {Stearing =(prevStearing * -1); gomotor (int ((Stearing * 216) / 360)); // 54 passos =1/4 de revolução Stearing =0; prevStearing =0; isStearing =false; }} previousStearingMillis =currentStearingMillis; } // BOTÕES RC RF ------------------ rfRemoteControlValue =checkRfRC (); if (rfRemoteControlValue) {switch (rfRemoteControlValue) {case RCleftbutton:// Botão RC esquerdo goleft (); pausa; case RCrightbutton:// Botão direito do RC goright (); pausa; case RCleft10button:// Botão Left-10 RC goleft10 (); pausa; case RCright10button:// Botão direito + 10 RC goright10 (); pausa; }} // BOTÕES ------------------------ HWButtonValue =checkHWButtons (); if (HWButtonValue) {switch (HWButtonValue) {case HWleftbutton:// Left (-1) HW button if (isSetup ==false) {goleft (); } else {setupMinus (); } pausa; case HWrightbutton:// Botão HW Right (+1) if (isSetup ==false) {goright (); } else {setupPlus (); } pausa; case HWpausebutton:// Botão de pausa HW gopause (); pausa; case HWsetupbutton:// Configurar botão HW gosetup (); pausa; case HWleft10button:// Left (-10) HW button goleft10 (); pausa; case HWright10button:// Right (+10) botão HW goright10 (); pausa; }} // GPS NMEA ------------------ if (stringComplete ==true) {// recebeu nmea sentença pela porta serial RX bool ret; ret =nmeaExtractData (); inputString =""; stringComplete =false; if (ret ==true) {RefreshDisplay (); }} // ALIMENTAÇÃO DO WATCHDOG ---------------- if (digitalRead (watchDogPin) ==LOW) {digitalWrite (watchDogPin, HIGH); } else {digitalWrite (watchDogPin, LOW); }} // lê os sensores no multiplexervoid readMuxSensors () {float Vo =0; float n =0; float n1 =0; float v1ad =0; float v2ad =0; float corr =0; float R1 =10000; float logR2 =0; flutuante R2 =0; float 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 =analogRead (MuxIOPin); v1ad =n; n1 =(((10,00 * n) / 1023,00)); SensorVBatt =(n1 + ((n1 * 0,0) / 100)); // correção arbitrária (não ativa =0,0%) digitalWrite (MuxSelBit0Pin, LOW); // 01 =Vres digitalWrite (MuxSelBit1Pin, HIGH); n =analogRead (MuxIOPin); v2ad =n; n1 =(((10,00 * n) / 1023,00)); SensorVRes =(n1 + ((n1 * 0,0) / 100)); // correção arbitrária (não ativa =0,0%) digitalWrite (MuxSelBit0Pin, HIGH); // 10 =NTC Temp digitalWrite (MuxSelBit1Pin, LOW); Vo =analogRead (MuxIOPin); R2 =R1 * (1023,0 / Vo - 1,0); logR2 =log (R2); T =(1,0 / (c1 + c2 * logR2 + c3 * logR2 * logR2 * logR2)); SensorTemp =T - 273,15; // Celsius n =(v1ad - v2ad); n1 =(n / 0,22) * 1000,00; SensormAmp =(((10,00 * n1) / 1023,00));} // extrair dados de nmea inputStringbool nmeaExtractData () {bool ret =false; // verdadeiro se nmea frase =$ GNRMC e CHKSUM válido if ((inputString.substring (0,6) =="$ GNRMC") &&(inputString.substring (inputString.length () - 4, inputString.length () - 2) ==nmea0183_checksum (inputString))) {y =0; para (s =1; s <11; s ++) {y =inputString.indexOf (",", y); switch (s) {case 1:// time 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; pausa; caso 2:// validade z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nm_validity =inputString.substring (y + 1, y + 1 + 1); } y =z; pausa; caso 3:// latitude 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; pausa; caso 4:// norte / sul z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nm_latitude =nm_latitude + inputString.substring (y + 1, y + 1 + 1); } y =z; pausa; caso 5:// longitude 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; pausa; caso 6:// leste / oeste z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nm_longitude =nm_longitude + inputString.substring (y + 1, y + 1 + 1); } y =z; pausa; caso 7:// nós de velocidade z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nmf_knots =inputString.substring (y + 1, z) .toFloat (); t =roundOneDec (nmf_knots); nm_knots =String (t, 1) + "kn"; } y =z; pausa; caso 8:// curso verdadeiro z =inputString.indexOf (",", y + 1); if (z> (y + 1)) {nmf_truecourse =inputString.substring (y + 1, z) .toFloat (); d =nmf_truecourse; nm_truecourse =d; } y =z; pausa; caso 9:// data 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; pausa; caso 10:// quebra de instruções; default:// instruções break; }} if ((isfirstfix ==true) || (ispause ==true)) {nm_routetofollow =nm_truecourse; nmf_routetofollow =nmf_truecourse; isfirstfix =false; } ret =verdadeiro; } return ret;} // aumenta (+) o valor do parâmetro durante a configuraçãovoid setupPlus () {switch (SetupParameter) {case 2:// intervalo StearingInterval =(StearingInterval + 100); if (StearingInterval> 5000) {StearingInterval =5000; } pausa; caso 3:// min. para mover StearingMinToMove =(StearingMinToMove + 1); if (StearingMinToMove> 20) {StearingMinToMove =20; } pausa; caso 4:// máx. mover StearingMaxMove =(StearingMaxMove + 1); if (StearingMaxMove> 45) {StearingMaxMove =45; } pausa; caso 5:// coeficiente StearingCoeffMove =(StearingCoeffMove + 0,1); if (StearingCoeffMove> 4) {StearingCoeffMove =4; } pausa; } atraso (200); RefreshDisplay ();} // diminui (-) o valor do parâmetro durante a configuraçãovoid setupMinus () {switch (SetupParameter) {case 2:// intervalo StearingInterval =(StearingInterval - 100); if (StearingInterval <1000) {StearingInterval =1000; } pausa; caso 3:// min. para mover StearingMinToMove =(StearingMinToMove - 1); if (StearingMinToMove <0) {StearingMinToMove =0; } pausa; caso 4:// máx. mover StearingMaxMove =(StearingMaxMove - 1); if (StearingMaxMove <10) {StearingMaxMove =10; } pausa; caso 5:// coeficiente StearingCoeffMove =(StearingCoeffMove - 0,1); if (StearingCoeffMove <0,1) {StearingCoeffMove =0,1; } pausa; } atraso (200); RefreshDisplay ();} // controle do motor (+) =para a frente (-) =para trásvoid gomotor (int stepsToMove) {digitalWrite (motorsABenablePin, HIGH); motor.step (stepsToMove); digitalWrite (motorsABenablePin, LOW);} // atualiza os dados no displayvoid 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 ("configuração:"); switch (SetupParameter) {case 1:// exibe os sensores 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"); pausa; caso 2:// intervalo lcd.print ("intervalo"); lcd.setCursor (7,1); lcd.print (StearingInterval); lcd.print ("mSec"); pausa; caso 3:// min. para mover lcd.print ("mínimo"); lcd.setCursor (7,1); lcd.print (StearingMinToMove); lcd.write (0xDF); pausa; caso 4:// máx. mover lcd.print ("max"); lcd.setCursor (7,1); lcd.print (StearingMaxMove); lcd.write (0xDF); pausa; caso 5:// coeficiente lcd.print ("coeficiente."); lcd.setCursor (7,1); lcd.print (StearingCoeffMove); lcd.print ("x"); lcd.write (0xDF); pausa; }}} / * SerialEvent ocorre sempre que um novo dado chega no RX serial do hardware. Essa rotina é executada entre cada execução do loop de tempo (), portanto, usar o atraso dentro do loop pode atrasar a resposta. Vários bytes de dados podem estar disponíveis. * / Void serialEvent () {while (Serial.available ()) {char inChar =(char) Serial.read (); inputString + =inChar; // se o caractere de entrada for uma nova linha, defina um sinalizador para que o loop principal // faça algo a respeito if (inChar =='\ n') {stringComplete =true; }}} // calcula a soma de verificação de nmea fraseString nmea0183_checksum (String nmea_data) {int crc =0; String chSumString =""; int i; // ignorar o primeiro sinal $, checksum na frase para (i =1; i <(nmea_data.length () - 5); i ++) {// remover o - 5 se não "*" + cksum + cr + Se estiverem presentes crc ^ =nmea_data [i]; } chSumString =String (crc, HEX); if (chSumString.length () ==1) {chSumString ="0" + chSumString.substring (0,1); } chSumString.toUpperCase (); return chSumString;} // verifica RC qual botão está pressionadoint checkRfRC () {int n =0; int res =0; n =analogRead (rfRemoteControlPin); if ((n> 350) e (n <460)) {// botão A res =RC botão esquerdo; } if ((n> 90) e (n <190)) {// botão B res =RCbotão direito; } 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... "); atraso (1000); goupdateEEPROM(); if (EEerr) { lcd.print("E="); lcd.print(EEerr); atraso (1000); } prev_StearingInterval =StearingInterval; prev_StearingMinToMove =StearingMinToMove; prev_StearingMaxMove =StearingMaxMove; prev_StearingCoeffMove =StearingCoeffMove; } isSetup =false; } } NewTone (speakerPin,2000); atraso (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); atraso (200); noNewTone(); } else { NewTone (speakerPin,1000); atraso (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); atraso (200); noNewTone(); } else { NewTone (speakerPin,1000); atraso (50); noNewTone(); } RefreshDisplay();}void gopause() { // pause button/RC if (ispause ==true) { ispause=false; digitalWrite(ledpausePin, HIGH); NewTone (speakerPin,50); atraso (200); NewTone (speakerPin,200); atraso (800); noNewTone(); } else { ispause=true; digitalWrite(ledpausePin, LOW); NewTone (speakerPin,200); atraso (200); NewTone (speakerPin,50); atraso (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); atraso (5); }// round zero decimalfloat roundZeroDec(float f) { float y, d; y =f * 1; d =y - (int) y; y =(flutuante) (int) (f * 1) / 1; se (d> =0,5) {y + =1; } else {if (d <-0,5) {y - =1; }} return y;} // arredondar uma casa decimal roundOneDec (float f) {float y, d; y =f * 10; d =y - (int) y; y =(flutuante) (int) (f * 10) / 10; se (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 =(flutuante) (int) (f * 100) / 100; se (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); atraso (500); digitalWrite(restartPin, LOW); //restart CLIENT tone(buzzerPin,1500); atraso (500); digitalWrite(restartPin, HIGH); tone(buzzerPin,1500); delay(timeForClientStart); // let CLIENT time to restart... noTone(buzzerPin); currentMillis =millis(); previousFeedingState =LOW; previousFeedingMillis =currentMillis; previousLedMillis =currentMillis; }} 

Peças personalizadas e gabinetes

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

Esquemas


Processo de manufatura

  1. Sistema de sensor de movimento infravermelho faça você mesmo para Raspberry Pi
  2. Construa um sistema de recuperação de pára-quedas balístico para seu drone
  3. Um sistema de monitoramento contínuo é adequado para você?
  4. Trocadores automáticos de ferramentas para robôs
  5. Seu sistema está pronto para a IoT?
  6. Cortadores de polígono para tornos automáticos
  7. Torno automático CNC para peças de precisão
  8. Sistema de propulsão para voo hipersônico
  9. Princípio de funcionamento do sistema de transmissão manual e automática
  10. Entendendo o sistema de transmissão automática