Teensy 3.6 + Westward 1
Video
Please click thumbnail image to start the video


Photo


Sketch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Teensy 3.6 and Westward Yacht | |
// Copyright (C) 2019 https://www.roboticboat.uk | |
// f716cacd-29ff-4a41-a0b7-fb465c6e608d | |
// | |
// This program is free software: you can redistribute it and/or modify | |
// it under the terms of the GNU General Public License as published by | |
// the Free Software Foundation, either version 3 of the License, or | |
// (at your option) any later version. | |
// | |
// This program is distributed in the hope that it will be useful, | |
// but WITHOUT ANY WARRANTY; without even the implied warranty of | |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
// GNU General Public License for more details. | |
// | |
// You should have received a copy of the GNU General Public License | |
// along with this program. If not, see <https://www.gnu.org/licenses/>. | |
// These Terms shall be governed and construed in accordance with the laws of | |
// England and Wales, without regard to its conflict of law provisions. | |
// Libraries are available here | |
// https://github.com/pwesson/roboticboat/tree/master/libraries | |
#include <SD.h> | |
#include <Wire.h> | |
#include <Servo.h> | |
#include <math.h> | |
// ____ __ __ ____ ____ | |
// / ___| | \/ | | _ \ / ___| | |
// | | | |\/| | | |_) | \___ \ . | |
// | |___ | | | | | __/ ___) | | |
// \____| |_| |_| |_| |____/ | |
#include <CompassCMPS11.h> | |
// ____ ____ ____ | |
// / ___| | _ \ / ___| | |
// | | _ | |_) | \___ \ . | |
// | |_| | | __/ ___) | | |
// \____| |_| |____/ | |
#include <AdaUltimateGPS3.h> | |
// __ __ _ _ | |
// \ \ / / (_) _ __ __| | | |
// \ \ /\ / / | | | '_ \ / _` | | |
// \ V V / | | | | | | | (_| | | |
// \_/\_/ |_| |_| |_| \__,_| | |
// | |
#include <Wind.h> | |
// ____ _ _ | |
// | _ \ _ _ __| | __| | ___ _ __ | |
// | |_) | | | | | / _` | / _` | / _ \ | '__| | |
// | _ < | |_| | | (_| | | (_| | | __/ | | | |
// |_| \_\ \__,_| \__,_| \__,_| \___| |_| | |
#include <Rudder.h> | |
//Is Robot in control of the human | |
bool IsHuman = true; | |
//Radio Control Receiver | |
int ch1 = 0; | |
int ch2 = 0; | |
int ch3 = 0; | |
//LED light | |
bool islighton = false; | |
// Timer | |
unsigned long mytime; | |
unsigned long lasttime; | |
//Radio Control Servo | |
Servo servoRudder; | |
Servo servoSail; | |
int rudder = 95; | |
// Declare the compass class | |
CompassCMPS11 compass1; | |
//A GPS library | |
AdaUltimateGPS3 gps(&Serial5); | |
// Global variables | |
String inputSerial5 = ""; // a string to hold incoming data | |
boolean IsReadySerial5 = false; // whether the string is complete | |
// Setup the weighted least squares | |
float BoatHeading = 0; | |
float oldBoatHeading = 0; | |
float desiredHeading = 0; | |
signed char pitch = 0; | |
signed char roll = 0; | |
Wind mywind; | |
Rudder myrudder; | |
void setup() | |
{ | |
// Keep the User informed | |
Serial.begin(9600); | |
// initialize the LED pin as an output. | |
pinMode(13, OUTPUT); | |
// Setup the input pins used received by the RC receiver | |
pinMode(6, INPUT); | |
pinMode(7, INPUT); | |
pinMode(8, INPUT); //IsRobot flag | |
// Setup the output pins used by the servos | |
pinMode(14, OUTPUT); | |
pinMode(16, OUTPUT); | |
//Setup servo connection for rudder and sail | |
servoRudder.attach(14); | |
servoSail.attach(16); | |
//Set the rudder range | |
myrudder.maxposition = 90 + 40; | |
myrudder.minposition = 90 - 40; | |
// Initialise the GPS module | |
Serial.println("Initializing GPS"); | |
Serial5.begin(9600); | |
delay(100); | |
// Just want RMC and GGA messages | |
// Send setup command to the GPS module | |
Serial5.println("$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"); | |
delay(100); | |
// 1MHz update | |
// Send setup command to the GPS module | |
Serial5.println("$PMTK220,1000*1F"); | |
delay(100); | |
// Initialize the compass and initial readings | |
compass1.begin(); | |
BoatHeading = compass1.getBearing(); | |
// Initialise the SD card | |
if (!SD.begin(BUILTIN_SDCARD)) | |
{ | |
Serial.println("SD failed"); | |
return; | |
} | |
//Set the last time | |
lasttime = 0; | |
} | |
void serialEvent5() | |
{ | |
//This event is called when Serial5 receives new bytes | |
while (Serial5.available()) | |
{ | |
// Read the new byte: | |
gps.read((char)Serial5.read()); | |
} | |
} | |
void loop() | |
{ | |
// Timer | |
mytime = millis(); | |
// Has 1 seconds passed yet? | |
// Approx once an hour this will roll over to zero again. | |
if (abs(mytime - lasttime) > 1000) | |
{ | |
// So we enter this section every 1 second | |
lasttime = mytime; | |
//The GPS signal must be active (not void) | |
if (gps.gpsstatus == 'A') | |
{ | |
//Flash the light when receiving a GPS signal | |
if (islighton) | |
{ | |
// Turn off the light | |
digitalWrite(13, LOW); | |
islighton = false; | |
} | |
else | |
{ | |
// Turn on the light | |
digitalWrite(13, HIGH); | |
islighton = true; | |
} | |
} | |
else | |
{ | |
// Turn off the light as no valid GPS location | |
digitalWrite(13, LOW); | |
islighton = false; | |
} | |
} | |
// Read the pulse width of each channel. | |
// The problem here is that the code blocks until it has read the signal. | |
// This can take up to 20ms (per channel) if the radio control transmitter is off (not transmitting) | |
ch1 = pulseIn(6, HIGH, 20000); | |
ch2 = pulseIn(7, HIGH, 20000); | |
ch3 = pulseIn(8, HIGH, 20000); | |
// Map the value to the range 0 to 180 which is what radio control servos like | |
ch1 = map(ch1, 1000, 2000, 0, 180); | |
ch2 = map(ch2, 1000, 2000, 0, 180); | |
ch3 = map(ch3, 1000, 2000, 0, 180); | |
//If the left RC lever is down - turn on robot | |
if (ch3 > 100) | |
{ | |
//The transmitter is indicating to turn on the Robot by the position of channel2 | |
if (IsHuman) | |
{ | |
//The Human was in control, so now initialise the Robot task | |
//The Robot wants to know the line AB to follow. | |
myrudder.NewLine(gps.latitude, gps.longitude, BoatHeading); | |
} | |
//The Human has handed control of the boat to the Robot | |
IsHuman = false; | |
} | |
else | |
{ | |
//The Human has regained control of the boat | |
IsHuman = true; | |
myrudder.ClearLine(); | |
} | |
// Read the compass. | |
compass1.ReadCompass(); | |
BoatHeading = compass1.bearing; | |
pitch = compass1.pitch; | |
roll = compass1.roll; | |
if (BoatHeading == 0) BoatHeading = oldBoatHeading; | |
// Read the compass gyro | |
compass1.ReadGyro(); | |
// Read the compass accelerator | |
compass1.ReadAccelerator(); | |
myrudder.GyroUpdateMean(BoatHeading, compass1.gyroZ); | |
myrudder.AddGyroReading(rudder, compass1.gyroZ); | |
//Want to update the Wind direction estimate | |
mywind.Update(BoatHeading, roll); | |
mywind.UpdateDistribution(roll); | |
mywind.UpdateRoll(BoatHeading, roll); | |
// The Human has to control the boat if there is no GPS signal | |
if (IsHuman) | |
{ | |
// Human sets the rudder position | |
servoRudder.write(ch1); | |
// Human sets the sails position | |
servoSail.write(ch2); | |
//Have sometime for the servo to move | |
delay(100); | |
} | |
else | |
{ | |
// Update the new rudder position | |
// Setup a tack - which is estimated wind direction +/- 50 degrees | |
desiredHeading = myrudder.ChooseTarget(mywind.Direction, BoatHeading); | |
// Update the rudder to point towards desiredHeading | |
rudder = myrudder.UpdateTarget(desiredHeading, BoatHeading); | |
// Robot updates the rudder position | |
servoRudder.write(rudder); | |
// Human still controls the sails | |
servoSail.write(ch2); | |
} | |
// Print data to Serial Monitor window | |
Serial.print("westwood14,$TMR,"); | |
Serial.print(mytime); | |
Serial.print(",$RC,"); | |
Serial.print(ch1); | |
Serial.print(","); | |
Serial.print(ch2); | |
Serial.print(","); | |
Serial.print(ch3); | |
Serial.print("\t$CMP,"); | |
Serial.print(BoatHeading); | |
Serial.print(","); | |
Serial.print(pitch); | |
Serial.print(","); | |
Serial.print(roll); | |
Serial.print("\t$GYR,"); | |
Serial.print(compass1.gyroX, 4); | |
Serial.print(","); | |
Serial.print(compass1.gyroY, 4); | |
Serial.print(","); | |
Serial.print(compass1.gyroZ - myrudder.gyrozmean, 4); | |
Serial.print(","); | |
Serial.print(myrudder.gyro.slope); | |
Serial.print(","); | |
Serial.print(myrudder.gyro.intercept); | |
Serial.print("\t$GPS,"); | |
Serial.print(gps.gpsdate); | |
Serial.print(","); | |
Serial.print(gps.gpstime); | |
Serial.print(","); | |
Serial.print(gps.latitude, 8); | |
Serial.print(","); | |
Serial.print(gps.latNS); | |
Serial.print(","); | |
Serial.print(gps.longitude, 8); | |
Serial.print(","); | |
Serial.print(gps.lonEW); | |
Serial.print(","); | |
Serial.print(gps.altitude); | |
Serial.print(","); | |
Serial.print(gps.fixquality); | |
Serial.print(","); | |
Serial.print(gps.numsatelites); | |
Serial.print(","); | |
Serial.print(gps.gpsknots); | |
Serial.print(","); | |
Serial.print(gps.gpstrack); | |
Serial.print(","); | |
Serial.print(gps.gpsstatus); | |
Serial.print(",$WND,"); | |
Serial.print(mywind.Direction); | |
Serial.print(","); | |
Serial.print(mywind.rollPort); | |
Serial.print(","); | |
Serial.print(mywind.rollCentre); | |
Serial.print(","); | |
Serial.print(mywind.rollStarboard); | |
Serial.print("\t$WLS,"); | |
Serial.print(myrudder.wls.slope); | |
Serial.print(","); | |
Serial.print(myrudder.wls.intercept); | |
Serial.print("\t$LINE,"); | |
Serial.print(myrudder.alongtrack); | |
Serial.print(","); | |
Serial.print(myrudder.crosstrack); | |
Serial.print(",$WAYA,"); | |
Serial.print(myrudder.waypointA.latitude, 8); | |
Serial.print(","); | |
Serial.print(myrudder.waypointA.longitude, 8); | |
Serial.print(",$WAYB,"); | |
Serial.print(myrudder.waypointB.latitude, 8); | |
Serial.print(","); | |
Serial.print(myrudder.waypointB.longitude, 8); | |
Serial.print(",$TAR,"); | |
Serial.print(myrudder.targetbearing); | |
Serial.print(",$RUD,"); | |
Serial.print(rudder); | |
Serial.print(","); | |
Serial.print(myrudder.localdistance); | |
Serial.print(","); | |
Serial.println(myrudder.sensitivity); | |
File dataFile = SD.open("datalog.txt", FILE_WRITE); | |
if (dataFile) | |
{ | |
dataFile.print("westwood14,$TMR,"); | |
dataFile.print(mytime); | |
dataFile.print(",$RC,"); | |
dataFile.print(ch1); | |
dataFile.print(","); | |
dataFile.print(ch2); | |
dataFile.print(","); | |
dataFile.print(ch3); | |
dataFile.print(",$CMP,"); | |
dataFile.print(BoatHeading); | |
dataFile.print(","); | |
dataFile.print(pitch); | |
dataFile.print(","); | |
dataFile.print(roll); | |
dataFile.print(",$ACC,"); | |
dataFile.print(compass1.accelX, 4); | |
dataFile.print(","); | |
dataFile.print(compass1.accelY, 4); | |
dataFile.print(","); | |
dataFile.print(compass1.accelZ, 4); | |
dataFile.print(",$GYR,"); | |
dataFile.print(compass1.gyroX, 4); | |
dataFile.print(","); | |
dataFile.print(compass1.gyroY, 4); | |
dataFile.print(","); | |
dataFile.print(compass1.gyroZ, 4); | |
dataFile.print(",$GPS,"); | |
dataFile.print(gps.gpsdate, 0); | |
dataFile.print(","); | |
dataFile.print(gps.gpstime, 0); | |
dataFile.print(","); | |
dataFile.print(gps.latitude, 8); | |
dataFile.print(","); | |
dataFile.print(gps.latNS); | |
dataFile.print(","); | |
dataFile.print(gps.longitude, 8); | |
dataFile.print(","); | |
dataFile.print(gps.lonEW); | |
dataFile.print(","); | |
dataFile.print(gps.altitude); | |
dataFile.print(","); | |
dataFile.print(gps.fixquality); | |
dataFile.print(","); | |
dataFile.print(gps.numsatelites); | |
dataFile.print(","); | |
dataFile.print(gps.gpsknots); | |
dataFile.print(","); | |
dataFile.print(gps.gpstrack); | |
dataFile.print(","); | |
dataFile.print(gps.gpsstatus); | |
dataFile.print(",$WND,"); | |
dataFile.print(mywind.Direction); | |
dataFile.print(","); | |
dataFile.print(mywind.rollPort); | |
dataFile.print(","); | |
dataFile.print(mywind.rollCentre); | |
dataFile.print(","); | |
dataFile.print(mywind.rollStarboard); | |
dataFile.print(",$WLS,"); | |
dataFile.print(myrudder.wls.slope); | |
dataFile.print(","); | |
dataFile.print(myrudder.wls.intercept); | |
dataFile.print(",$LINE,"); | |
dataFile.print(myrudder.alongtrack); | |
dataFile.print(","); | |
dataFile.print(myrudder.alongtrackobjective); | |
dataFile.print(","); | |
dataFile.print(myrudder.crosstrack); | |
dataFile.print(",$WAYA,"); | |
dataFile.print(myrudder.waypointA.latitude, 8); | |
dataFile.print(","); | |
dataFile.print(myrudder.waypointA.longitude, 8); | |
dataFile.print(",$WAYB,"); | |
dataFile.print(myrudder.waypointB.latitude, 8); | |
dataFile.print(","); | |
dataFile.print(myrudder.waypointB.longitude, 8); | |
dataFile.print(",$WAYD,"); | |
dataFile.print(myrudder.waypointD.latitude, 8); | |
dataFile.print(","); | |
dataFile.print(myrudder.waypointD.longitude, 8); | |
dataFile.print(",$WAYE,"); | |
dataFile.print(myrudder.waypointE.latitude, 8); | |
dataFile.print(","); | |
dataFile.print(myrudder.waypointE.longitude, 8); | |
dataFile.print(",$TAR,"); | |
dataFile.print(myrudder.targetbearing); | |
dataFile.print(",$RUD,"); | |
dataFile.print(rudder); | |
dataFile.print(","); | |
dataFile.print(myrudder.localdistance); | |
dataFile.print(","); | |
dataFile.println(myrudder.sensitivity); | |
dataFile.close(); | |
} | |
} | |