Joris Bijnens
Published © GPL3+

Tiny Tank, Drive Around, Forever!

Easy to print, control & extend, treaded vehicle. And with the onboard wireless power component, you can charge the tank while parked.

AdvancedFull instructions provided10 hours3,198
Tiny Tank, Drive Around, Forever!

Things used in this project

Hardware components

Photon
Particle Photon
One of the best boards on the market today
×1
Microsoft XBox Controller
×1
Tiny Tank
This is a 3D printable tank designed by me. Download it for free or have it printed at for example 3dHubs.com
×1
Qi 5W Transmitter Prototype Kit
IDT Qi 5W Transmitter Prototype Kit
×1
Qi 5W Receiver Prototype Kit
IDT Qi 5W Receiver Prototype Kit
×1

Software apps and online services

Visual Studio 2015
Microsoft Visual Studio 2015

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
3D Printer (generic)
3D Printer (generic)

Story

Read more

Schematics

Tiny Tank + Wireless Receiver schema

This schema shows how the tank is wired: LEDs, servo's and wireless receiver

Tank schema, png version

Code

Particle Photon code

Arduino
This is the code to flash your Photon with. This is an Arduino dialect I believe
String iCommand = "";

Servo iServoLeft;
Servo iServoRight;

TCPClient iClient;
byte iServerIP[] = { 192, 168, 1, 6 };

int iLED0 = A0;
int iLED1 = A1;
int iLED2 = A2;


void setup() 
{
    pinMode(iLED0, OUTPUT);
    pinMode(iLED1, OUTPUT);
    pinMode(iLED2, OUTPUT);
   
    //digitalWrite(iLED0, HIGH);
    digitalWrite(iLED1, HIGH);
    //digitalWrite(iLED2, HIGH);
    
    iServoLeft.attach(D0);
    iServoRight.attach(D1);
    
    Particle.function("connect", ConnectToServer);
    
    Particle.publish("I am up at " + WiFi.localIP().toString());
}

int ConnectToServer(String p)
{
    //Particle.publish("Parameter: " + p + "--");
    
    int lP1 = p.indexOf('.');
    int lP2 = p.indexOf('.', lP1 + 1);
    int lP3 = p.indexOf('.', lP2 + 1);
    int lP4 = p.indexOf('.', lP3 + 1);
    int lP5 = p.indexOf('.', lP4 + 1);
    String l1 = p.substring(0, lP1);
    String l2 = p.substring(lP1 + 1, lP2);
    String l3 = p.substring(lP2 + 1, lP3);
    String l4 = p.substring(lP3 + 1, lP4);
    String l5 = p.substring(lP4 + 1, lP5);
    
    Particle.publish("Connecting to '" + p + "' --> '" + l1 + "+" + l2 + "+" + l3 + "+" + l4 + ":" + l5 + "'");
    iClient.connect({ l1.toInt(), l2.toInt(), l3.toInt(), l4.toInt() }, l5.toInt());
    Particle.publish("Connected!"); 
    
   return 1;
}

void loop() 
{
    if (!iClient.connected())
    {
        iClient.stop();
        Particle.publish("Lost connection!");
        digitalWrite(iLED0, LOW);
        digitalWrite(iLED1, HIGH);
        digitalWrite(iLED0, LOW);
        iServoLeft.write(90);
        iServoRight.write(90);
    }
    /*if (!iClient.connected())
    {
        Particle.publish("Waiting for connection...");
        delay(1000);
    }
    */
    while (iClient.available()) 
    {
        char lChar = iClient.read();
        if(lChar == '#')
        {
            ProcessCommand();
        }
        else
        {
            iCommand += lChar;
        }
    } 
    
}

void ProcessCommand()
{
    Particle.publish("Processing '" + iCommand + "'");
    String lType = iCommand.substring(0, 1);
    String lParameters = iCommand.substring(1);
    iCommand = "";
    
    if(lType == "S")
    {
        // Example: SL10#
        SetServo(lParameters);
       // iClient.printf("S");
    }
    else if(lType == "L")
    {
        // Example: L11# 1 = HIGH, 0 = LOW
        SetLED(lParameters);
        //iClient.printf("L");
    }
    else if(lType == "W")
    {
        delay(lParameters.toInt());
        //iClient.printf("W");
    }
}

int SetLED(String pParameters)
{
    String lLED = pParameters.substring(0, 1);
    String lValue = pParameters.substring(1);
    //Particle.publish("##" + pParameters + "-" + lLED + "-" + lValue + "-");
    
    if(lLED == "0")
    {
        if(lValue == "1")
        {
            digitalWrite(iLED0, HIGH);    
        }
        else if(lValue == "0")
        {
            digitalWrite(iLED0, LOW);
        }
    }
    else if(lLED == "1")
    {
        if(lValue == "1")
        {
            digitalWrite(iLED1, HIGH);    
        }
        else if(lValue == "0")
        {
            digitalWrite(iLED1, LOW);
        }
    }
    else if(lLED == "2")
    {
        if(lValue == "1")
        {
            digitalWrite(iLED2, HIGH);    
        }
        else if(lValue == "0")
        {
            digitalWrite(iLED2, LOW);
        }
    }
    return lValue.toInt();    
}

int SetServo(String pParameters)
{
    String lServo = pParameters.substring(0, 1);
    String lValueRaw = pParameters.substring(1);
    
    int lValue = lValueRaw.toInt();
    
    if(lValue < -89) lValue = -89;
    if(lValue > 89) lValue = 89;
    if(lServo == "L")
    {
        iServoLeft.write(90 + lValue);
    }
    if(lServo == "R")
    {
        iServoRight.write(90 - lValue);
    }
    if(lServo == "X")
    {
        iServoLeft.write(90 + lValue);
        iServoRight.write(90 - lValue);
    }
    return lValue;
}

Visual Studio code

C#
You run this code from your computer which is connected to the XBox Controller. This is basically a console program which processes the input of the controller and sends commands over the socket to the Photon
private static NetworkStream iStream;
private static StringBuilder iCommandBuilder;
private static bool iCommandFlush = true;
private static int iTestSpeedL = 30;
private static int iTestSpeedR = 30;
private static Controller iController;

static void Main(string[] args)
{
    Console.WriteLine("Looking for Xbox controller...");
    iController = new Controller(UserIndex.One);
    if (iController.IsConnected)
    {
        Console.WriteLine("Xbox controller connected!");
    }
         
    iCommandBuilder = new StringBuilder();

    string lAvogadro = "provide your photon key";
    string lToken = "provide your token";
    string lMethodName = "connect";
    string lUrl = @"https://api.particle.io/v1/devices/" + lAvogadro + @"/" + lMethodName + "?access_token=" + lToken;
    int lPort = 13914;

    Console.WriteLine("Start listener...");
    TcpListener lListener = new TcpListener(lPort);
    lListener.Start();

    Console.WriteLine("Sending IP '" + GetOwnIP() + "'...");
    WebClient lWebClient = new WebClient();
    var lValues = new NameValueCollection();
    lValues.Add("args", GetOwnIP() + "." + lPort.ToString());
    lWebClient.UploadValues(lUrl, lValues);

    Console.Write("Waiting for a connection... ");
    TcpClient lClient = lListener.AcceptTcpClient();
    Console.WriteLine("Connected!");

    iStream = lClient.GetStream();

    int lAngle = 90;

    short lOldLeft = -10000;
    short lOldRight = -10000;

    bool lLed0 = false;
    bool lLed1 = false;
    bool lLed2 = false;
    bool lA = false;

    if (iController != null)
    {
        while (true)
        {
            var lState = iController.GetState();
            var lLeft = lState.Gamepad.LeftThumbY;
            var lRight = lState.Gamepad.RightThumbY;

            Console.WriteLine("Left: " + lLeft + " - Right: " + lRight);

            if (Math.Abs(lOldLeft - lLeft) + Math.Abs(lOldRight - lRight) > 5000)
            {
                BeginCommand();
                WriteCommand(ObjectEnum.ServoLeft, Convert.ToInt32(Math.Round(lLeft / 1000.0)));
                WriteCommand(ObjectEnum.ServoRight, Convert.ToInt32(Math.Round(lRight / 1000.0)));
                EndCommand();

                lOldLeft = lLeft;
                lOldRight = lRight;
            }
            if (lState.Gamepad.LeftTrigger == 255 && !lLed0)
            {
                string lCommand = "L01#";
                WriteCommand(lCommand);
                lLed0 = true;
            }
            else if (lState.Gamepad.LeftTrigger == 0 && lLed0)
            {
                string lCommand = "L00#";
                WriteCommand(lCommand);
                lLed0 = false;
            }
            if (lState.Gamepad.RightTrigger == 255 && !lLed2)
            {
                string lCommand = "L21#";
                WriteCommand(lCommand);
                lLed2 = true;
            }
            else if (lState.Gamepad.RightTrigger == 0 && lLed2)
            {
                string lCommand = "L20#";
                WriteCommand(lCommand);
                lLed2 = false;
            }
            if (lState.Gamepad.Buttons == GamepadButtonFlags.A && !lA)
            {
                WriteCommand("L01#W100#L11#W100#L21#W100#L00#W100#L10#W100#L20#W100#"
                    + "L21#W100#L11#W100#L01#W100#L20#W100#L10#W100#L00#W100#");
                //lState.Gamepad.Buttons = GamepadButtonFlags.None;
                lA = true;
            }
            if (lState.Gamepad.Buttons != GamepadButtonFlags.A)
            {
                lA = false;
            }
        }
    }

    Console.WriteLine("Give input...");
    var lKey = Console.ReadKey();

      

    while (lKey.KeyChar != 'c')
    {
        if (lKey.KeyChar == 'a')
        {
            //WriteCommand("SR10#SL170#W300#SR90#SL90#");
            BeginCommand();
            WriteCommand(ObjectEnum.ServoBoth, 50);
            WriteCommand(ObjectEnum.Wait, 300);
            WriteCommand(ObjectEnum.ServoBoth, 0);
            EndCommand();
        }
        else if (lKey.KeyChar == 'z')
        {
            //WriteCommand("SR170#SL10#W300#SR90#SL90#");
            BeginCommand();
            WriteCommand(ObjectEnum.ServoBoth, -50);
            WriteCommand(ObjectEnum.Wait, 300);
            WriteCommand(ObjectEnum.ServoBoth, 0);
            EndCommand();
        }
        else if (lKey.KeyChar == 'o')
        {
            //WriteCommand("SR170#SL170#W100#SR90#SL90#");
            BeginCommand();
            WriteCommand(ObjectEnum.ServoLeft, -50);
            WriteCommand(ObjectEnum.ServoRight, 50);
            WriteCommand(ObjectEnum.Wait, 300);
            WriteCommand(ObjectEnum.ServoBoth, 0);
            EndCommand();
        }
        else if (lKey.KeyChar == 'p')
        {
            //WriteCommand("SR10#SL10#W100#SR90#SL90#");
            BeginCommand();
            WriteCommand(ObjectEnum.ServoLeft, 50);
            WriteCommand(ObjectEnum.ServoRight, -50);
            WriteCommand(ObjectEnum.Wait, 300);
            WriteCommand(ObjectEnum.ServoBoth, 0);
            EndCommand();
        }
        else if (lKey.KeyChar == '3')
        {
            string lCommand = "L0";
            if (lLed0) lCommand += "0#";
            else lCommand += "1#";
            WriteCommand(lCommand);
            lLed0 = !lLed0;
        }
        else if (lKey.KeyChar == '2')
        {
            string lCommand = "L1";
            if (lLed1) lCommand += "0#";
            else lCommand += "1#";
            WriteCommand(lCommand);
            lLed1 = !lLed1;
        }
        else if (lKey.KeyChar == '1')
        {
            string lCommand = "L2";
            if (lLed2) lCommand += "0#";
            else lCommand += "1#";
            WriteCommand(lCommand);
            lLed2 = !lLed2;
        }
        else if (lKey.KeyChar == '4')
        {
            WriteCommand("L01#W100#L11#W100#L21#W100#L00#W100#L10#W100#L20#W100#"
                + "L21#W100#L11#W100#L01#W100#L20#W100#L10#W100#L00#W100#");
        }
        else if (lKey.KeyChar == 'h')
        {
            iTestSpeedR += 1;
            Console.WriteLine("TestspeedR " + iTestSpeedR.ToString());
            WriteCommand(ObjectEnum.ServoRight, iTestSpeedR);
        }
        else if (lKey.KeyChar == 'n')
        {
            iTestSpeedR -= 1;
            Console.WriteLine("iTestSpeedR " + iTestSpeedR.ToString());
            WriteCommand(ObjectEnum.ServoRight, iTestSpeedR);
        }
        else if (lKey.KeyChar == 'g')
        {
            iTestSpeedL += 1;
            Console.WriteLine("iTestSpeedL " + iTestSpeedL.ToString());
            WriteCommand(ObjectEnum.ServoLeft, iTestSpeedL);
        }
        else if (lKey.KeyChar == 'b')
        {
            iTestSpeedL -= 1;
            Console.WriteLine("iTestSpeedL " + iTestSpeedL.ToString());
            WriteCommand(ObjectEnum.ServoLeft, iTestSpeedL);
        }

        lKey = Console.ReadKey();

        while (iStream.DataAvailable)
        {
            // Read the first batch of the TcpServer response bytes.
            Byte[] lDataBuffer = new Byte[256];
            Int32 lBytes = iStream.Read(lDataBuffer, 0, lDataBuffer.Length);
            var lData = System.Text.Encoding.ASCII.GetString(lDataBuffer, 0, lBytes);
            Console.WriteLine("Received: {0}", lData);
        }
    }

    // Close everything.
    iStream.Close();
    lClient.Close();
}

private static void EndCommand()
{
    iCommandFlush = true;
    FlushCommand();
}
private static void FlushCommand()
{
    if (iCommandFlush)
    {
        Byte[] lD = System.Text.Encoding.ASCII.GetBytes(iCommandBuilder.ToString());
        iStream.Write(lD, 0, lD.Length);
        iCommandBuilder.Clear();
    }
}

private static void BeginCommand()
{
    iCommandFlush = false;
}

private static void WriteCommand(ObjectEnum pObject, int pValue)
{
    switch (pObject)
    {
        case ObjectEnum.ServoLeft:
            WriteCommand("SL" + pValue.ToString());
            break;
        case ObjectEnum.ServoRight:
            WriteCommand("SR" + pValue.ToString());
            break;
        case ObjectEnum.ServoBoth:
            WriteCommand("SX" + pValue.ToString());
            break;
        /*case ObjectEnum.Led0:                    
        case ObjectEnum.Led1:                    
        case ObjectEnum.Led2:
        */
        case ObjectEnum.Wait:
            WriteCommand("W" + pValue.ToString());
            break;
        default:
            throw new Exception("Invalid command!");
    }
}

private static float iSpeedZero = -1.0F;
private static float iSpeedRange = 1.2F;

private static float iDirectionZero = 0.02F;
private static float iDirectionFactor = 10F;


private static void WriteCommand(string pCommand)
{
    if (iStream == null || !iStream.CanWrite) return;

    if (pCommand.EndsWith("#"))
    {
        iCommandBuilder.Append(pCommand);
    }
    else
    {
        iCommandBuilder.Append(pCommand + "#");
    }
    FlushCommand();
}

private static string GetOwnIP()
{
    IPHostEntry host;
    host = Dns.GetHostEntry(Dns.GetHostName());
    foreach (IPAddress ip in host.AddressList)
    {
        if (ip.AddressFamily.ToString() == "InterNetwork")
        {
            return ip.ToString();
        }
    }
    return null;
}

Credits

Joris Bijnens

Joris Bijnens

2 projects • 7 followers
.Net programmer by trade, but not picky to learn other stuff. Looking to build my own R2D2 as personal assistant and save the world doing it...

Comments