Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing

Onderdelen en benodigdheden

Arduino UNO × 1
Arduino Nano R3 × 1

Over dit project

Voorwoord

Ik zeil graag alleen – op zee met je zeilboot heb je alles om te groeien. Slecht weer is zwaar, maar bij goed weer met zon en wind is de genot maximaal. Geluk betekent oneindige horizonnen, perfecte techniek, optimale keuzes, maar ook menselijke dingen: een goed drinkje en een lekkere boterham! Precies dan komt de autopiloot van pas: hij werkt voor je terwijl je om 17:00 je thee & biscuits op zee geniet. :-)

Wat kan de autopiloot voor je doen?

Een zeilboot heeft geen motor en kan geen geprogrammeerde route afvaren. Al het werk wordt gedaan door de zeiler: zeilen trimmen, weer/wind beheersen, touwen bedienen, verkeer waarnemen, koers bepalen en sturen. Bij een pauze (10 seconden tot minuten – de "thee-tijd") schakel je de autopiloot in. Binnen seconden verkrijgt de GPS positie, snelheid en koers en houdt de richting (route) aan. Het stuurstysteem (stok verbonden met roer), normaal bediend door de zeiler, wordt nu bestuurd door de autopiloot via een stappenmotor met katrollen en touwen.

Het roer besturen is continu fijn/GROF afstellen. Kleinere (lichtere) boten zijn gevoeliger voor invloeden: golven, windrichting/druk, gewichtsverschuiving, stromingen. De zeiler blijft waakzaam, zelfs met autopiloot aan, en wijzigt de koers via afstandsbediening: 4 knoppen (+1, -1, +10, -10 graden). Deze knoppen zitten ook op de autopiloot (groen/rechts, rood/links). Blauwe knop (midden) = activeren/deactiveren (pauze). Zwart knop = parameters instellen in geheugen.

Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing

De schakeling

Hoofdverwerking: Arduino Uno. De Arduino Nano is de watchdog: ik weet dat de Uno een interne watchdog heeft, maar ik wilde een onafhankelijke externe microcontroller – een levenslange droom die nu waar is! Uno voedt Nano via pin 3 → A0 (high/low, 5/0V), minstens 1x per 2,5 seconden (feedingInterval); zo niet, dan is Uno "slapend" of "geblokkeerd" en reset Nano de Uno. Is nog nooit vorgekommen!

Gebruikt: populair display met I2C-converter (beide gesoldeerd) → 4 draden, spaart digitale pinnen. Knoppen en afstandsbediening aangesloten via spanningsdelers (1% precisie weerstanden) om pinnen te besparen. Analogevergelijkingswaarden in code; bij andere weerstanden: constants aanpassen in checkRfRC() en checkHWButtons(). RF 433MHz RC werkt goed; spoelantenne van koperdraad verbetert bereik. Getest op 10m, waarschijnlijk 20m+ – voldoende voor 4,20m testboot.

GPS-unit: Begin met EM406A, maar Week-Rollover-Bug → vervangen door Beitian BN-220T. Configuratie: 2Hz, alleen $GNRMC NMEA-zin. GPS TX → pin 0 (RX) van Uno. Let op: tijdens programmeren GPS tijdelijk loskoppelen (pin 0 wordt gebruikt).

EEPROM: IC 24C04 (512 bytes I2C) voor parameters van stappenmotor-beweging (zie Software).

Componentenlijst

  • Arduino Uno (MCU)
  • Arduino Nano (Watchdog)
  • Beitian BN-220T GPS
  • Stappenmotor, model 23LM, 54 stappen = 1/4 omwenteling
  • Controller Keyes L298 voor motor
  • RF433MHz RC XD-YK04 + 4-knoppen afstandsbediening + spoelantenne
  • 6 Knoppen normally open (2×Rood, 2×Groen, 1×Zwart, 1×Blauw)
  • Schakelaar aan/uit (Wit)
  • 6-pins ronde connectors (M/F) voor externe stappenmotor
  • Buzzer
  • Display LCD1602 2×16 tekens + I2C-converter
  • 3 LEDs (rood, blauw, geel)
  • IC 24C04 I2C EEPROM
  • IC 4051 multiplexer
  • LiPo 2S 7.4V 2600mAh
  • IC 7805 spanningsregelaar + warmteafvoerkap
  • Thermistor NTC MF52-103 10k
  • Herstelbare zekering 2A
  • 6× 1N4148 diodes (D1-D6)
  • Weerstanden op Power shield (R1-R4=10k, R5=100k)
  • Weerstanden op Autopilot shield (R1=330, R2=1k, R3=2k, R4=5.1k, R5=1k, R6/R7/R14=330, R8-R13=10k, R15=10M)
  • Condensators (C1=470µF 16V, C2=100n)
  • 2W 0,22 Ohm weerstand (R6)
  • Male pins, Female long-pins headers
  • Behuizing transparant en "waterdicht"

Sensoren (via IC 4051 multiplexer aan Uno): Thermistor voor temperatuur warmteafvoerkap, 2W weerstand + 4×10k als spanningsdelers voor stroommeting (verbruik), accuspanning (LiPo kritisch <3,3V per cel; 2S pakket → alarm bij <7,0V via buzzer). LEDs: geel knippert 1Hz = watchdog actief; blauw aan = autopiloot aan, uit = pauze; rood knippert bij RC-knopindruk.

Alles werkt op 5,0V via LiPo 2S 7,4V 2600mAh en IC 7805. Stroom <800mA (meestal 100-450mA). Warmteafvoerkap op 7805 verplicht. Thermistor erop; buzzer piept bij >50°C.

Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing
Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing
Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing
Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing
Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing
Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing
Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing
Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing

PCB's en montage

Enkelzijdige PCB's → enkele draadbruggen (stippellijntjes). Getoond: componentenzijde; downloadbestanden bevatten beide zijden (gespiegeld) voor laserprinter op "geel" of "blauw" papier. Ik gebruikte geel; blauw is beter maar duurder. Toner-saving UIT, 1200 dpi voor diep zwart. Tonertransfer met strijkijzer. Dubbelzijdig printen (ook componentenzijde) maakt positionering makkelijk en project "professioneel".

Beide PCB's passen gestapeld op Arduino Uno: eerst Power-unit, dan Autopilot-unit erbovenop.

Alles samengevoegd (PCB's, MCU's, RC, motordriver, accu, GPS, knoppen, schakelaar, draden, connectors) met headers en Dupont-draden/connectors (niet gesoldeerd) voor herbruikbaarheid. ~200 ongesoldeerde verbindingen → onverwachte storingen mogelijk. Advies: alles solderen voor stabiele schakeling!

Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing
Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing

Parameters instellen en sensorwaarden weergeven

Druk op zwarte knop aan de zijde → Setup-modus (ook tijdens actieve navigatie, geen pauze nodig). Displaypagina 1: accuspanning (V=7,83), verbruik (mA=177), temperatuur thermistor (38°C). Herhaaldelijk zwarte knop → volgende pagina's (2-5): parameters wijzigen met -1/+1 knoppen. Pagina 6: "Updating..." bij wijzigingen → opslaan in EEPROM.

  • Interval: bv. 2000 ms, tijd tussen stuurcorrecties.
  • Minimum: bv. 2°, minimale afwijking voor interventie.
  • Max: bv. 40°, maximale stuurwijziging per keer.
  • Coëfficiënt: bv. 1,50 × °, vermenigvuldiger voor stuurwijziging.

Deze parameters zijn nodig voor fijnafstelling op de boot. Responsiviteit, gevoeligheid, vloeiendheid hangen af van katrol diameters, aantal katrollen, roergevoeligheid, stoklengte, etc. Alles installeren, testen, ervaring opdoen. Kies een mooie zonnige dag met licht wind!

Hoe het "live" werkt

Je vaart op zee/meer/hafen. Thee-tijd! Cola en boterham in zak. Autopiloot AAN → GPS fix wachten. Display toont: snelheid (knopen), klok, koers bv. H270° (R=route, H=werkelijke koers). 180°=zuid, 270°=west, 360/0°=noord, 90°=oost. R en H gelijk in pauze (STOP). Stuurlijn verbinden (stappenmotor → roerstok), blauwe knop → autopiloot sturen START. Autopiloot houdt R en corrigeert H (langzaam/snel afhankelijk van weer). Koers wijzigen via rood/groen knoppen op unit of RC. Controle terugvatten: blauwe knop (pauze), lijn loskoppelen, zelf sturen. Klaar.

Software-kant

Code is lang maar helder. ~65% programma-geheugen, ~45% RAM. String-klasse voor NMEA-zinnen, stabiliteit goed. serialEvent() ontvangt GPS-data 2x/sec, roept nmeaExtractData(), checkt checksum met nmea0183_checksum(). Andere GPS? Zorg voor zelfde structuur of pas aan (bijv. EM406A: $GPRMC, BN-220T: $GNRMC). Checksum-test: nmeachecksum.eqth.net.

Voorbeeld NMEA-zin: $GPRMC,095836.000,A,4551.9676,N,01328.7118,E,2.09,341.84,280519,,*08

In Setup(): EEPROM check – indien nieuw/onbekend initialiseren. Parameters als bytes: 0=0x29, 1=0x00, 2-3=interval, 4-5=min, 6-7=max, 8-11=coëfficiënt (byte, byte, int, int, float). Defensief EEPROM lezen/schrijven. Sensoren elke 10s via readMuxSensors() (multiplexer) → alarm bij lage accu/hoge temp. Stroommeting resolutie ~40mA. Knoppen (HW/RC) continu gecheckt; gedrag afhankelijk van IsSetup boolean en RefreshDisplay(). Kern: STURINGCONTROLE → gomotor() voor stappenmotor uit/en terug. Sturing ook tijdens Setup. Watchdog voeden: pin toggelen zo vaak mogelijk.

Installatie op een zeilboot

Zie foto: autopiloot en stappenmotor op achterschip, stevig bevestigd. Touw 6mm van hoofdkatrol stappenmotor → 2 andere katrollen aan beide kanten. Deze katrollen "vast" aan boot via 2 bungee-ringen voor lichte spanning. Eindelijk: touw verbinden met roerstok (tijdelijke verbinding) – makkelijk aan/af. Autopiloot systeem van water fernhouden! :-)

Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing

Nieuws & Updates

  • 10.05.2020: .STEP 3D CAD-bestanden toegevoegd voor stappenmotor-katrol (door mij) en montageplaat (door Andrew Barney), + 3D-preview.

Disclaimer & Waarschuwingen

Dit is een spel, niets om serieus te nemen! Ik reisde 16 maanden rond de wereld op zeilboot. We navigeerden intensief met een echte autopiloot (NIET DEZE!) in alle weersomstandigheden. Een echte autopiloot is heel robuust (hardware/software) en moet vertrouwd worden. Deze Arduino-autopiloot is een fantastisch spel om mee te experimenteren en plezier te beleven.

Veel plezier!

Marco Zonca

Code

Autopiloot sketch (voor Uno)
/* Autopiloot voor kleine zeilboten, Marco Zonca 2019
   Arduino UNO CPU, Arduino Nano watchdog, GPS BT-220 NMEA, stappenmotor + controller,
   RF433MHz RC, 6 knoppen, buzzer, I2C display, 2 LEDs, I2C 24C04 EEPROM, Mux 4051,
   LiPo 2S 7.4V 2600mA, 7805 regelaar, Thermistor */
#include <LiquidCrystal_I2C.h>
#include <NewTone.h>
#include <Stepper.h>
#include <Wire.h>

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, y=0, z=0, d=0;
int rfRemoteControlValue = 0;
int HWButtonValue = 0;
int SetupParameter = 0;
float calcmove = 0, cm = 0, Stearing = 0, prevStearing = 0, t = 0;

int EEdisk = 0x50;
int EEid1 = 0x29, EEid2 = 0x00;
unsigned int EEaddress = 0, EEbytes = 12;
byte EEdata[12], EEbytedata;
int EEerr = 0;

float SensorVBatt=0, SensorVRes=0, SensorTemp=0, SensormAmp=0;

int StearingInterval = 2000;
int StearingMinToMove = 2;
int StearingMaxMove = 40;
float StearingCoeffMove = 1.5;

byte bStearingInterval[sizeof(int)];
byte bStearingMinToMove[sizeof(int)];
byte bStearingMaxMove[sizeof(int)];
byte bStearingCoeffMove[sizeof(float)];

int prev_StearingInterval=0, prev_StearingMinToMove=0, prev_StearingMaxMove=0;
float prev_StearingCoeffMove=0;

const int ledpausePin = 2;
const int watchDogPin = 3;
const int MuxSelBit0Pin = 7;
const 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, RCrightbutton = 202, RCleft10button = 203, RCright10button = 204;
const int HWleftbutton = 101, HWrightbutton = 102, HWpausebutton = 103, HWsetupbutton = 104;
const int HWleft10button = 105, HWright10button = 106;

const int motorStepsPerRevolution = 200;

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
Stepper motor(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, 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);

  lcd.clear(); lcd.setCursor(0,0); lcd.print("Memory check...");
  lcd.setCursor(0,1);
  for (s = 0; s < EEbytes; s++) {
    EEaddress = s;
    EEbytedata = readEEPROM(EEdisk, EEaddress);
    EEdata[s] = EEbytedata;
  }
  if (EEerr) { lcd.print("E="); lcd.print(EEerr); delay(1000); }
  if ((EEdata[0] != EEid1) || (EEdata[1] != EEid2)) {
    lcd.print(" init!"); goupdateEEPROM();
    if (EEerr) { lcd.print("E="); lcd.print(EEerr); delay(1000); }
  }
  memcpy(bStearingInterval, EEdata+2, sizeof(int));
  memcpy(bStearingMinToMove, EEdata+4, sizeof(int));
  memcpy(bStearingMaxMove, EEdata+6, sizeof(int));
  memcpy(bStearingCoeffMove, EEdata+8, sizeof(float));
  StearingInterval = *((int*)bStearingInterval);
  StearingMinToMove = *((int*)bStearingMinToMove);
  StearingMaxMove = *((int*)bStearingMaxMove);
  StearingCoeffMove = *((float*)bStearingCoeffMove);
  prev_StearingInterval = StearingInterval;
  prev_StearingMinToMove = StearingMinToMove;
  prev_StearingMaxMove = StearingMaxMove;
  prev_StearingCoeffMove = StearingCoeffMove;
  lcd.print(" OK"); delay(1000);
  lcd.clear(); lcd.print("GPS reading..."); delay(1000);
}

void loop() {
  currCheckSensorsMillis = millis();
  if (currCheckSensorsMillis - prevCheckSensorsMillis >= CheckSensorsInterval) {
    readMuxSensors();
    if ((SensorVBatt <= 7.0) || (SensorTemp >= 50)) {
      lcd.clear(); lcd.setCursor(0,0); lcd.print("Alarm sensors! ");
      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); delay(1000); noNewTone();
    }
    prevCheckSensorsMillis = currCheckSensorsMillis;
  }

  currentStearingMillis = millis();
  if (currentStearingMillis - previousStearingMillis >= StearingInterval) {
    if (!isStearing && !ispause) {
      calcmove = nmf_routetofollow - nmf_truecourse;
      if (calcmove < -180) calcmove += 360;
      else if (calcmove > 180) calcmove -= 360;
      if (abs(calcmove) >= StearingMinToMove) {
        if (abs(calcmove) >= StearingMaxMove) {
          cm = (calcmove < 0) ? -StearingMaxMove : StearingMaxMove;
          calcmove = cm;
        }
        Stearing = calcmove * StearingCoeffMove;
        gomotor(int((Stearing * 216) / 360));
        prevStearing = Stearing;
        isStearing = true;
      }
    } else {
      if (isStearing) {
        Stearing = -prevStearing;
        gomotor(int((Stearing * 216) / 360));
        Stearing = 0; prevStearing = 0; isStearing = false;
      }
    }
    previousStearingMillis = currentStearingMillis;
  }

  rfRemoteControlValue = checkRfRC();
  if (rfRemoteControlValue) {
    switch (rfRemoteControlValue) {
      case RCleftbutton: goleft(); break;
      case RCrightbutton: goright(); break;
      case RCleft10button: goleft10(); break;
      case RCright10button: goright10(); break;
    }
  }

  HWButtonValue = checkHWButtons();
  if (HWButtonValue) {
    switch (HWButtonValue) {
      case HWleftbutton: if (!isSetup) goleft(); else setupMinus(); break;
      case HWrightbutton: if (!isSetup) goright(); else setupPlus(); break;
      case HWpausebutton: gopause(); break;
      case HWsetupbutton: gosetup(); break;
      case HWleft10button: goleft10(); break;
      case HWright10button: goright10(); break;
    }
  }

  if (stringComplete) {
    bool ret = nmeaExtractData();
    inputString = ""; stringComplete = false;
    if (ret) RefreshDisplay();
  }

  if (digitalRead(watchDogPin) == LOW) digitalWrite(watchDogPin, HIGH);
  else digitalWrite(watchDogPin, LOW);
}

void readMuxSensors() {
  float Vo, n, n1, v1ad, v2ad, corr, R1=10000, logR2, R2, T;
  float c1=1.009249522e-03, c2=2.378405444e-04, c3=2.019202697e-07;
  digitalWrite(MuxSelBit0Pin, LOW); digitalWrite(MuxSelBit1Pin, LOW);
  n = analogRead(MuxIOPin); v1ad=n;
  n1=(((10.00*n)/1023.00)); SensorVBatt=(n1+((n1*0.0)/100));
  digitalWrite(MuxSelBit0Pin, LOW); digitalWrite(MuxSelBit1Pin, HIGH);
  n = analogRead(MuxIOPin); v2ad=n;
  n1=(((10.00*n)/1023.00)); SensorVRes=(n1+((n1*0.0)/100));
  digitalWrite(MuxSelBit0Pin, HIGH); 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;
  n = (v1ad-v2ad); n1 = (n/0.22)*1000.00;
  SensormAmp = (((10.00*n1)/1023.00));
}

bool nmeaExtractData() {
  bool ret = false;
  if ((inputString.substring(0,6)=="$GNRMC") && (inputString.substring(inputString.length()-4,inputString.length()-2)==nmea0183_checksum(inputString))) {
    y=0;
    for (s=1; s<11; s++) {
      y=inputString.indexOf(",",y);
      int z=inputString.indexOf(",",y+1);
      switch(s) {
        case 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; break;
        case 2: if(z>y+1) nm_validity=inputString.substring(y+1,y+1+1); y=z; break;
        case 3: if(z>y+1) nm_latitude=inputString.substring(y+1,y+2+1)+""+inputString.substring(y+1+2,y+10+1)+"'"; y=z; break;
        case 4: if(z>y+1) nm_latitude+=inputString.substring(y+1,y+1+1); y=z; break;
        case 5: if(z>y+1) nm_longitude=inputString.substring(y+1,y+3+1)+""+inputString.substring(y+1+3,y+11+1)+"'"; y=z; break;
        case 6: if(z>y+1) nm_longitude+=inputString.substring(y+1,y+1+1); y=z; break;
        case 7: if(z>y+1) { nmf_knots=inputString.substring(y+1,z).toFloat(); t=roundOneDec(nmf_knots); nm_knots=String(t,1)+"kn"; } y=z; break;
        case 8: if(z>y+1) { nmf_truecourse=inputString.substring(y+1,z).toFloat(); d=nmf_truecourse; nm_truecourse=d; } y=z; break;
        case 9: 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; break;
      }
    }
    if (isfirstfix || ispause) { nm_routetofollow=nm_truecourse; nmf_routetofollow=nmf_truecourse; isfirstfix=false; }
    ret=true;
  }
  return ret;
}

void setupPlus() {
  switch(SetupParameter) {
    case 2: StearingInterval+=100; if(SteeringInterval>5000) StearingInterval=5000; break;
    case 3: StearingMinToMove++; if(StearingMinToMove>20) StearingMinToMove=20; break;
    case 4: StearingMaxMove++; if(StearingMaxMove>45) StearingMaxMove=45; break;
    case 5: StearingCoeffMove+=0.1; if(StearingCoeffMove>4) StearingCoeffMove=4; break;
  }
  delay(200); RefreshDisplay();
}

void setupMinus() {
  switch(SetupParameter) {
    case 2: StearingInterval-=100; if(StearingInterval<1000) StearingInterval=1000; break;
    case 3: StearingMinToMove--; if(StearingMinToMove<0) StearingMinToMove=0; break;
    case 4: StearingMaxMove--; if(StearingMaxMove<10) StearingMaxMove=10; break;
    case 5: StearingCoeffMove-=0.1; if(StearingCoeffMove<0.1) StearingCoeffMove=0.1; break;
  }
  delay(200); RefreshDisplay();
}

void gomotor(int stepsToMove) {
  digitalWrite(motorsABenablePin, HIGH);
  motor.step(stepsToMove);
  digitalWrite(motorsABenablePin, LOW);
}

void RefreshDisplay() {
  if (!isSetup) {
    lcd.clear(); lcd.setCursor(0,0);
    lcd.print("R"+nm_routetofollow); lcd.write(0xDF); lcd.print(" H"+nm_truecourse); lcd.write(0xDF);
    if (ispause) 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);
  } else {
    lcd.clear(); lcd.setCursor(0,0); lcd.print("setup: ");
    switch(SetupParameter) {
      case 1: 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"); break;
      case 2: lcd.print("interval"); lcd.setCursor(7,1); lcd.print(StearingInterval); lcd.print(" mSec"); break;
      case 3: lcd.print("minimum"); lcd.setCursor(7,1); lcd.print(StearingMinToMove); lcd.write(0xDF); break;
      case 4: lcd.print("max"); lcd.setCursor(7,1); lcd.print(StearingMaxMove); lcd.write(0xDF); break;
      case 5: lcd.print("coeffic."); lcd.setCursor(7,1); lcd.print(StearingCoeffMove); lcd.print(" x "); lcd.write(0xDF); break;
    }
  }
}

void serialEvent() {
  while(Serial.available()) {
    char inChar=(char)Serial.read(); inputString+=inChar;
    if(inChar=='\n') stringComplete=true;
  }
}

String nmea0183_checksum(String nmea_data) {
  int crc=0; String chSumString="";
  for(int i=1;i<(nmea_data.length()-5);i++) crc^=nmea_data[i];
  chSumString=String(crc,HEX);
  if(chSumString.length()==1) chSumString="0"+chSumString.substring(0,1);
  chSumString.toUpperCase(); return chSumString;
}

int checkRfRC() {
  int n=analogRead(rfRemoteControlPin), res=0;
  if(n>350&&n<460) res=RCleftbutton;
  if(n>90&&n<190) res=RCrightbutton;
  if(n>540&&n<640) res=RCleft10button;
  if(n>225&&n<325) res=RCright10button;
  return res;
}

int checkHWButtons() {
  int n=analogRead(ButtonsPin), res=0;
  if(n>465&&n<565) res=HWleftbutton;
  if(n>290&&n<390) res=HWrightbutton;
  if(n>130&&n<220) res=HWpausebutton;
  if(n>625&&n<725) res=HWsetupbutton;
  if(n>975&&n<1075) res=HWleft10button;
  if(n>800&&n<900) res=HWright10button;
  return res;
}

void gosetup() {
  if(!isSetup) { 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... "); delay(1000); goupdateEEPROM();
        if(EEerr) { lcd.print("E="); lcd.print(EEerr); delay(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; EEdata[0]=EEid1; EEbytedata=EEid1; writeEEPROM(EEdisk,EEaddress,EEbytedata);
  EEaddress=1; 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;s360) 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() {
  if(!ispause) { 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() {
  if(ispause) { 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();
}

byte 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;
  } else EEerr=2;
  Wire.endTransmission(true); return rdata;
}

void writeEEPROM(int diskaddress, unsigned int memaddress, byte bytedata) {
  Wire.beginTransmission(diskaddress); Wire.write(memaddress); Wire.write(bytedata);
  if(Wire.endTransmission()!=0) EEerr=3;
  Wire.endTransmission(true); delay(5);
}

float roundZeroDec(float f) { float 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; }
float roundOneDec(float f) { float 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; }
float roundTwoDec(float f) { float 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 (voor Nano)
/* Watchdog voor CLIENT op Arduino NANO 3.0, Marco Zonca
   CLIENT moet Watchdog voeden vóór feedingInterval, anders herstart */
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, previousFeedingState = LOW, feedingState = LOW;
unsigned long previousLedMillis = 0, previousFeedingMillis = 0;

void setup() {
  digitalWrite(restartPin, HIGH);
  pinMode(ledPin, OUTPUT); pinMode(buzzerPin, OUTPUT); pinMode(restartPin, OUTPUT); pinMode(feedingPin, INPUT);
  delay(timeForClientStart);
}

void loop() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousLedMillis >= ledInterval) {
    previousLedMillis = currentMillis;
    ledState = (ledState == LOW) ? HIGH : LOW;
    digitalWrite(ledPin, ledState);
  }
  feedingState = digitalRead(feedingPin);
  if (feedingState == HIGH) {
    if (previousFeedingState == LOW) previousFeedingMillis = currentMillis;
    previousFeedingState = HIGH;
  } else previousFeedingState = LOW;

  if (currentMillis - previousFeedingMillis > feedingInterval) {
    ledState = HIGH; digitalWrite(ledPin, ledState);
    tone(buzzerPin,1500); delay(500);
    digitalWrite(restartPin, LOW); tone(buzzerPin,1500); delay(500);
    digitalWrite(restartPin, HIGH); tone(buzzerPin,1500); delay(timeForClientStart);
    noTone(buzzerPin);
    currentMillis = millis();
    previousFeedingState = LOW; previousFeedingMillis = currentMillis; previousLedMillis = currentMillis;
  }
}

Custom parts en behuizingen

Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing 23lm-stepper-plate-v2_PlvJaff9Hl.step 23lm-stepper-pulley-56_UhsbaWbiBt.step

Schema's

Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing Geavanceerd autopiloot-systeem voor zeilboten – Precieze automatische sturing

Productieproces

  1. Is Continu Monitoring de Juiste Keuze voor Uw Operaties?
  2. Een DIY Infrarood Bewegingssensor Bouwen voor Raspberry Pi – Stapsgewijze Handleiding
  3. Een Ballistisch Parachute-herstelsysteem Ontwerpen voor UAV's
  4. Hoe Handmatige en Automatische Transmissies Vermogen Overdragen: Een Duidelijk Overzicht
  5. Uitgebreide Gids voor Automatische Transmissiesystemen
  6. Automatische smeringssystemen: Hoe ze werken en waarom ze belangrijk zijn
  7. ATI Industrial Automation: Toonaangevende Automatische Tool Changers Voor Industriële Robots
  8. Precisie polygon frezen voor automatische draaibanken – Instelbare, hoogpresterende snijoplossingen
  9. R07 Cincom CNC Draaibank – Precisie 7 mm Bewerking voor Kleine Onderdelen
  10. Revolutionair hypersonisch voortstuwingsysteem stelt sub-30-minuten transcontinentale reizen in
  11. Is uw productiefaciliteit klaar voor IoT? Een praktische gids