This article is about my auto pilot project for my sailing boat. First I wanted to buy a Rayteon SPX or ST6000 series AP but those things are expensive. Next The ST4000+ was on my list but still those go for prices above what I could afford. So I saw someone on the web who build his own AP. He never published any code though, and I want my own litle features.
For you non techies just scroll down to the next article…
So what are those features? I want to be able to use it next to my st3000 that still works. To control it by Ipad or maybe by my kobo reader, better viewable in sunlight. To control it by bluetooth with the peble watch. To be able to steer by a windvane ove NMEA (need to get one this winter). To tack with a maximum yaw, so to steer higher than normally would be done and prevent too much yaw (I am a pleasure boat sailor and want to keep ik fun and not fight). And ofcource just set a cource and go there and maybe waypoints. And if it works with the peble an overboard alarm is the bluetooth connection is suddenly lost so to steer to a stop.
The hardware consists out of a motor driver, a compass module and a pc that can later on in the project be replaced by a mcu. The compass module consists of an CMP01 module and an arduino and a rs232 driver with a power supply. The compass module gives the compass readings in 3600 counts and yaw (+/- 85 degrees) and tilt (+/-) 85 degrees, and is yaw and tilt compensated. The driver module consists of a FET driver board (pololu 36V20CS) max 36Volt 20Amp with coasting capability. The coasting is a feature to be able to turn the motor by hand is the board is enabled but not driving, normally a board like that has the motot in that case in break. A 20 amp driver is overkill but the costs of the driver board are lower than a less heavy one and it leaves room for other driving hardware in the future. The driver board also has nice features as error checking and measuring the current through the motor.The driver board is controlled by an arduino and also has a rs232 driver and power supply.
Both the modules are connected to a boat pc. The boat pc takes less than 10W and consists of a DN2800MT NM10 mini-ITX board with two Kingston SODIMM DDR3-1066 2GB modules and an 128GB SSD. It runs windows 7 and the development enviroment on it is Labview and VB6. It is hooked up to an wifi router so I can control it wit my iPad over a VNC connection. At this moment I also have a gps module connected to it. The board has two build in rs232 ports so I have to get an extra one. If you want to use the rs232 ports for powered devices over pin 9 you also have to connect pin 5 to the real ground. The pin 5 is connected to the ground through a resistor.
The whole computer including router uses 0.91A from my battery (14.58V) with a 20% cpu load. That is not bad I would say. That is measured with my BMV602S battery monitor.
the compass code:
/****************************************************************
* Arduino CMPS10 example code *
* CMPS10 running I2C mode *
* by James Henderson, 2012 *
* Modified by Jelbert Holtrop, 2012 *
*****************************************************************/
#include <Wire.h>
#include <SoftwareSerial.h>
#define ADDRESS 0x60 // Defines address of CMPS10
void setup(){
Wire.begin(); // Conects I2C
Serial.begin(9600);
// Serial.println("Compass unit 1");
}
void loop()
{
sendData();
}
void sendData()
{
byte highByte, lowByte, fine,
accelXh,accelXl,accelYh,accelYl,accelZh,accelZl; // highByte and lowByte store high and low bytes of the bearing and fine stores decimal place of bearing
char pitch, roll, dummy; // Stores pitch and roll values of CMPS10, chars are used because they support signed value
int bearing, accelX, accelY, accelZ, crc; // Stores full bearing
Wire.beginTransmission(ADDRESS); //starts communication with CMPS10
Wire.write(2); //Sends the register we wish to start reading from
Wire.endTransmission();
Wire.requestFrom(ADDRESS, 4); // Request 4 bytes from CMPS10
while(Wire.available() < 4); // Wait for bytes to become available
highByte = Wire.read();
lowByte = Wire.read();
pitch = Wire.read();
roll = Wire.read();
bearing = ((highByte<<8)+lowByte)/10; // Calculate full bearing
fine = ((highByte<<8)+lowByte)%10; // Calculate decimal place of bearing
Serial.print(bearing,DEC);
Serial.print(".");
Serial.print(fine,DEC);
Serial.print(",");
Serial.print(pitch,DEC);
Serial.print(",");
Serial.print(roll,DEC);
crc=abs(bearing)+abs(pitch)+abs(roll)+10; //hele simple crc + lege string detectie
Serial.print(",");
Serial.print(crc,DEC);
Serial.println(" ");
}
Hmm some of the formatting is gone bu you get the idea.
Next the code of the driver. This code now includes for reading the motor current and voltage. Added options for continious pwm drive and set deadband.
//program board Duemilanove w/ Atmega 328 com 1
int DIR=3;
int PWML=5;
int PWMH=6;
int CS=A0;
int FF1=8;
int FF2=9;
int command=0;
int halfspeed=127;
int fullspeed=255;
int err1;
int err2;
int rotation=0;
int deadBand=0;
void setup()
{
// drive/coast PWML=PWMH DIR=DIRECTION
// drive/brake-low PWMH=0 DIR=direction
// drive/brake-high PWML=0 Dir=direction
// coast=lowpower PWMH=PWML=0 DIR=x
pinMode(DIR, OUTPUT); //analogwrite(ledpin,value) pwm zet pinmode zelf
// pinmode(PWML, OUTPUT);
// pinmode(PWMH, OUTPUT);
// pinmode(CS, INPUT); //voor analoge pinnen is dat niet nodig
pinMode(FF1, INPUT);
pinMode(FF2, INPUT);
Serial.begin(9600);
Serial.print("Version 1 beta");
// establishContact(); // send a byte to establish contact until receiver responds
}
void loop()
{
int MotorCurrent;
int MotorVoltage;
int MaxCurrent;
if(Serial.available()>0)
{
//Commands
// drive/coast 50%=A 100%=B : PWML=PWMH DIR=DIRECTION
// drive/break 50%=C 100%=D : drive/brake-high PWML=0 Dir=direction
// Direction L,R
// COAST Z : coast=lowpower PWMH=PWML=0 DIR=x
command=Serial.read();
Serial.print(command); //echo
switch (command)
{
case 'A':
//max motor current = 8
MaxCurrent=8;
analogWrite(PWML,halfspeed);
analogWrite(PWMH,halfspeed);
break;
case 'B':
//max motor current = 19
MaxCurrent=19;
analogWrite(PWML,fullspeed);
analogWrite(PWMH,fullspeed);
break;
case 'C':
//max motor current = 10
MaxCurrent=10;
analogWrite(PWML,halfspeed);
analogWrite(PWMH,0);
break;
case 'D':
//max motor current = 17
MaxCurrent=17;
analogWrite(PWML,fullspeed);
analogWrite(PWMH,0);
break;
case 'L':
digitalWrite(DIR,HIGH);
break;
case 'R':
digitalWrite(DIR,LOW);
break;
case 'P':
//continious pwm
rotation=constrain(rotation,-255,255);
rotation=Serial.parseInt();
if(abs(rotation)<deadBand)
{
analogWrite(PWML,0);
analogWrite(PWMH,0);
break;
}
if(rotation>0)
digitalWrite(DIR,HIGH);
else
digitalWrite(DIR,LOW);
rotation=abs(rotation);
analogWrite(PWML,rotation);
analogWrite(PWMH,rotation);
break;
case 'W':
//set deadband The ammount of pwm value that results in no rotation.
//to make sure the motor is not continually working with low values of pwm
deadBand=Serial.parseInt();
break;
case 'Z':
analogWrite(PWML,0);
analogWrite(PWMH,0);
digitalWrite(DIR,LOW);
break;
}
}
err1 = digitalRead(FF1);
err2 = digitalRead(FF2);
if (err1 == HIGH && err2 == HIGH) Serial.println("ERROR: UnderVoltager");
if (err1 == HIGH && err2 == LOW) Serial.println("ERROR: OverTemprature");
if (err1 == LOW && err2 == HIGH) Serial.println("ERROR: ShortCircuit, need to reset");
// hier nog de code voor het uitlezen en weergeven van stroom en spanning I=I-470
// Motor current sense zit op A0
// Waar zit motor voltage sense ook al weer op A1?
MotorCurrent=analogRead(CS)-471;
MotorVoltage=analogRead(A1);
//Serial.print("Motor current=");
Serial.print(MotorCurrent,DEC);
Serial.print(",");
//Serial.print("Motor voltage=");
Serial.println(MotorVoltage,DEC);
/*if (abs(MotorCurrent)>=MaxCurrent)
{// put motor in Z = off, coast
analogWrite(PWML,0);
analogWrite(PWMH,0);
digitalWrite(DIR,LOW);
}*/
}
void establishContact() {
while (Serial.available() <= 0) {
Serial.println("Hello"); // send an initial string
delay(300);
}
}
So far for now but as the project has progress I will add to this post. Publisching labview code is very unpractical so for that I should find some solution.
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.