3D printed Remote Controlled model boat
This summer vacation we printed a RC boat model which we found on Thinginverse, it is designed bywersy, see http://www.thingiverse.com/thing:274054
Some pictures of the finishing boat, we printed in three parts on our Prusa I3 steel printer. After gluing the parts together, doing some sanding, applying primer and spraypainting, we added the motors and electronics.
Martijn is showing the final result.
To control the boat we used a cheap ESP8266, which is an embedded micro processor, it has a built-in Wifi capabilities. So we wrote some software to run on the ESP chip, which is basically a web server that hosts an embedded html page with some javascript.
The DC motor (for the impeller) is salvaged from an old toy car, we create a small MOSFET circuit to control the speed of the DC motor. The S9 servo can be controlled directly from the ESP8266.
As a battery pack we used a cheap USB power bank, however, we soldered some additional wires directly on the 18650 batteries to power the DC motor and the servo.
After final assembly we took to boat to sail on the pond in front of our house. It did suprisingly well. The second try we had to inflate the rubber boat to rescue the RC boat, the issue was that the propeller became loose, well Martijn did not mind.
We 3D printed another propeller and used a slightly different design, which turned out to work a lot better than the original propeller.
Software (ESP8266 arduino)
#include
#include
#include
#include
Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards
const int SERVO_PIN = 13;
const int MOTOR_PIN = 14;
static int inByte = 0;
static int servoAngle = 90;
static int curServoAngle = 90;
static int motorPwm = 0;
static bool debug = false;
static int loopCnt = 0;
//#ifdef USE_SERIAL
static void UpdateServoMotor()
{
myservo.write(curServoAngle);
analogWrite(MOTOR_PIN, motorPwm);
if ( curServoAngle < servoAngle) ++curServoAngle;
if ( curServoAngle > servoAngle) --curServoAngle;
}
static void SetServoAngle( int pos_in_degrees)
{
if ( pos_in_degrees < 60 ) pos_in_degrees = 60; if ( pos_in_degrees > 120 ) pos_in_degrees = 120;
if ( debug )
{
Serial.print("servoAngle to : ");
Serial.print(pos_in_degrees);
Serial.println();
Serial.flush();
}
servoAngle = pos_in_degrees;
}
static void ChangeMotorPwm( int deltaPwm)
{
motorPwm += deltaPwm;
if ( motorPwm < 0 ) motorPwm = 0; if ( motorPwm > PWMRANGE ) motorPwm = PWMRANGE;
if ( debug )
{
Serial.print("ChangeMotorPwm to : ");
Serial.print(motorPwm);
Serial.println();
Serial.flush();
}
}
const char *ssid = "EspBoat";
const char *password = "thereisnospoon";
ESP8266WebServer server(80);
static char page_txt[] = {
"\n"
"
"\n"
"\n"
"\n"
"\n"
"\t\n"
"\n"
"
\n"
"\n"
"
"\n"
"\n"
"
Boot Besturing version 1.0
\n"
"
Throttle=?
\n"
"
Rudder=?
\n"
"\n"
"\n"
"
\n"
"\t\n"
"
\n"
"
dbg
\n"
"\n"
"\n"
"\n"
"\n"
""
""};
// return main page with java script at http://192.168.4.1 in a web browser
void handleRoot() {
Serial.print("Sending page...");
Serial.println(page_txt);
Serial.println("done...");
server.send(200, "text/html", page_txt);
}
void handleBoat() {
String getAction=server.arg("action");
if ( debug )
{
if ( getAction.length() > 0)
{
Serial.print("Handle boat...");
Serial.print(" action is :");
Serial.print(getAction);
Serial.println(":");
}
}
if (getAction == "stop")
{
servoAngle = 90;
motorPwm = 0;
}
else if (getAction.startsWith("throttle_fwd") )
{
ChangeMotorPwm(+90);
}
else if (getAction.startsWith("throttle_rev") )
{
ChangeMotorPwm(-90);
}
else if (getAction.startsWith("rudder_port"))
{
SetServoAngle( servoAngle - 4 ) ;
}
else if (getAction.startsWith("rudder_starboard"))
{
SetServoAngle( servoAngle + 4 ) ;
}
String responseTxt = "{ \"rudder\" : ";
responseTxt += curServoAngle;
responseTxt += ", \"throttle\" : ";
responseTxt += (motorPwm * 100) / PWMRANGE;
responseTxt += "}";
if ( debug ) {
Serial.print(" sending response:");
Serial.println(responseTxt);
}
server.send(200, "api/json", responseTxt);
}
void setup() {
UpdateServoMotor();
delay(1000);
Serial.begin(115200);
Serial.println();
Serial.println("(c) Albert & Martijn Faber, RC boat version 1.0");
pinMode(SERVO_PIN, OUTPUT);
myservo.attach(SERVO_PIN);
Serial.print("Configuring access point...");
WiFi.softAP(ssid, password);
IPAddress myIP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(myIP);
server.on("/", handleRoot);
server.on("/api/boat", handleBoat);
server.begin();
Serial.println("HTTP server started");
// get max power
WiFi.setOutputPower(20.5);
}
void loop() {
server.handleClient();
#ifdef USE_SERIAL
if (Serial.available() > 0)
{
inByte = Serial.read();
switch ( inByte )
{
case '1': SetServoAngle(130); break;
case '2': SetServoAngle(120); break;
case '3': SetServoAngle(110); break;
case '4': SetServoAngle(100); break;
case '5': SetServoAngle( 90); break;
case '6': SetServoAngle( 80); break;
case '7': SetServoAngle( 70); break;
case '8': SetServoAngle( 60); break;
case '9': SetServoAngle( 50); break;
case 'a': ChangeMotorPwm( -10 ); break;
case 's': ChangeMotorPwm( -100 ); break;
case 'd': ChangeMotorPwm( 10 ); break;
case 'f': ChangeMotorPwm( 100 ); break;
case '0':
ChangeMotorPwm( 90 );
SetServoAngle( 40);
break;
case '?':
Serial.print("HELP .... ");
break;
case 'D':
{
debug = !debug;
}
break;
}
}
#endif
if (debug)
{
if ( (loopCnt % 100)==0 )
{
Serial.print("Debug => ");
Serial.print("servoAngle : ");
Serial.print(servoAngle);
Serial.print(" curservoAngle : ");
Serial.print(curServoAngle);
Serial.print(" motorPwm : ");
Serial.print(motorPwm);
Serial.println();
}
}
UpdateServoMotor();
delay(10);
loopCnt++;
}