Arduino

arduino

Model auta řízený arduinem ovládaný přes bluetooth z mobilu

Úvodem

Na počátku všeho byl nepojízdný model RC autíčka, starý přes 30 let a chuť zkusit se naučit trochu programovat Arduino. Když jsem dokončil své první auto, tak jsem najednou chtěl předělat další, no a tehdy se to zvrhlo v současný stav, kdy kupuju nefunkční RC auta a „oživuju“ je Arduinem.

Google Android ovládací aplikace



Protože jsem nechtěl stavět nějaký vlastní ovládací modul a protože vlastním chytrý telefon se systémem Google Android (dále jen GA), tak jsem se rozhodl že budu auto ovládat pomocí mobilu. Takže první úkol bylo naprogramovat aplikaci pro GA, která bude přes BlueTooth (dále jen BT) ovládat Arduino v autíčku. Moje představa byla, že komunikace bude jednosměrná, tzn. že mobil bude pouze vysílat krátké příkazy, ty bude BT modul připojený k arduinu v autě přijímat, arduino je rozpozná a vykoná požádovanou akci.
Pro BT technologii jsem se rozhodl z několika důvodů:

  • Pro Arduino existuje hotový BT modul
  • Velmi jednoduché programování jak na straně GA tak na straně Arduina
  • BT frekvence je mimo běžně používané frekvence klasických RC aut, takže nehrozí zarušení od někoho jiného
  • BT komunikace je v podstatě „dvoubodový spoj“ mezi dvěma zařízeními, takže jakmile se spáruje auto s telefonem, je nepravděpodobné, že by někdo další ovládal moje auto a současně je možné že budou dva a více lidí současně ovládat svá auta a nezaruší se navzájem
  • Celkem rozumný dosah minimálně na úrovni běžných 27MHz Rc aut, většinou lepší.

Když jsem aplikaci vytvářel, měla být původně určena jen pro „to první“ autíčko. Když jsem pak začal přidávat další auta, tak jsem se rozhodl aplikaci předělat na „univerzální“ pro všechna má auta. Představím tedy svou druhou verzi GA aplikace pro ovládání aut.

Pro tvorbu android aplikace jsem použil Android studio

Protože projekt v android studiu generuje velké množství souborů, popíšu jen ty ty které jsem upravoval, nebo vytvořil.

AndroidManifest.xml

Každá GA aplikace musí mít soubor AndroidManifest.xml umístěný v root adresáři aplikace. Tento soubor obsahuje, mimo jiné, základní informace o aplikaci samotné.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="johanok.bt_car_rc">
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".BT_Search_Activity"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:screenOrientation="portrait">
        </activity>
    </application>
</manifest>

V podstatě vše v tomto souboru je generované, kromě názvu aplikace, který odkazuje do zdrojů na řetězec app_name. Dále jsou ještě přidané dva řádky: uses-permission, které říkají že aplikace bude mít právo zapnout a používat BT v azřízení. Poslední co jsem přidal je řádek: android:screenOrientation, který říká že aplikace bude vždy orientovaná na výšku a nebude se otáčet podle toho jak uživatel drží telefon.

strings.xml

Doporučovaným postupem při programování GA aplikací, je nepsat textové popisky „natvrdo“ do kódu, ale vypsat je do zdrojů jako řetězcové konstanty.

<resources>
    <string name="app_name">BT_Car_RC</string>
    <string name="tv_btsearch">Vyberte BT zařízení</string>
    <string name="title_activity_main">MainActivity</string>
    <string name="btn_forward">FORWARD</string>
    <string name="btn_left">LEFT</string>
    <string name="btn_brake">BRAKE</string>
    <string name="btn_right">RIGHT</string>
    <string name="btn_backward">BACKWARD</string>
    <string name="btn_cmdA">A</string>
    <string name="btn_cmdB">B</string>
    <string name="btn_cmdC">C</string>
    <string name="btn_cmdD">D</string>
    <string name="btn_cmdE">E</string>
    <string name="btn_cmdF">F</string>
    <string name="btn_cmdG">G</string>
    <string name="btn_cmdH">H</string>
    <string name="btn_cmdI">I</string>
    <string name="btn_disconnect">DISCONNECT</string>
</resources>

V tomto souboru tedy najdete konstanty pro:

  • Název aplikace
  • Nadpisy aktivit
  • Popisky na tlačítkách

Na tyto řetězce se poté v xml můžete odkázat pomocí: “@string/<jmeno_stringu>“

dimens.xml

V tomto souboru jsou zakódovány rozměry používané v layoutech aplikací, opět ze stejného důvodu jako výše, ani rozměry by neměly být kódovány přimo v aplikaci.

<resources>
    <dimen name="fab_margin">16dp</dimen>
    <dimen name="header_font_size">20dp</dimen>
    <dimen name="main_table_margin">10dp</dimen>
</resources>

Jediný můj rozměr je main_table_margin, který určuje okraje tlačítek v ovládací aplikaci, aby neležela moc „natěsno“ u sebe.

activity_bt_search.xml

Aplikace se skládá ze dvou aktivit. Ta první, activity_bt_search se spouští po startu aplikace a jejím úkolem je zobrazit seznam spárovaných BT zařízení a po kliknutí na jedno z nich se k němu připojit. Zde je potřeba upozornit na to že má aplikace neumožňuje vyhledávání a spárování nových BT zařízení. Ona pouze zobrazuje již spárovaná zařízení a po zvolení některého z nich, se k němu pokusí připojit. Přidat nové zařízení je nutné přes stadardní nastavení BT v GA.

Vzhled activity_bt_search vypadá takto:

layout

A xml kód tohoto vzhledu je zde:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <TextView
        android:id="@+id/tv_btsearch"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/tv_btsearch"
        android:textAlignment="center"
        android:textSize="@dimen/header_font_size"
        />

    <ListView
        android:id="@+id/lv_btsearch"
        android:layout_below="@+id/tv_btsearch"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</RelativeLayout>

Celý vzhled je vytvořen v relative layoutu, tedy prvky v něm jsou uspořádány relativně vůči sobě. Samotný obsah je jednoduchý, jako první prvek je TextView, který je statický (neměný) a obsahuje pouze textový popisek vyzívající uživatele k výběru nějakého BT zařízení. Zbytek layoutu tvoří ListView, což je komponenta která je tvořena seznamem spárovaných zařízení. V tomto seznamu zobrazuji textový název zařízení (lepší čitelné pro člověka) a MAC adresu (kvuli identifikaci zařízení).

BT_Search_Activity.java

Toto je java kód třídy který obstarává funkčnost této části aplikace.

package johanok.bt_car_rc;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Set;

public class BT_Search_Activity extends AppCompatActivity {

    //Widgets
    ListView lv_DeviceList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_bt__search_);

        //Calling widgets
        lv_DeviceList = (ListView)findViewById(R.id.lv_btsearch);

        if(BT_Connection.isBTAvailable() == false) {
            //Show a mesag. that the device has no bluetooth adapter
            Globals.showMsg(BT_Search_Activity.this, "Bluetooth Device Not Available");
            //finish apk
            finish();
        }

        //if the bluetoth is not enabled
        if(BT_Connection.isBTEnabled() == false) {
            //Ask to the user turn the bluetooth on
            Intent turnBTon = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(turnBTon, 1);
        }

        ArrayList list = BT_Connection.getPairedDevices();

        //show paired devices
        if (list.size()>0) {
            final ArrayAdapter adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1, list);
            lv_DeviceList.setAdapter(adapter);
            lv_DeviceList.setOnItemClickListener(deviceListClickListener); //Method called when the device from the list is clicked
        }
        else {
            Globals.showMsg(BT_Search_Activity.this, "No Paired Bluetooth Devices Found");
        }
    }

    private AdapterView.OnItemClickListener deviceListClickListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            //showMsg("Device Selected");
            // Get the device MAC address, the last 17 chars in the View
            String info = ((TextView) view).getText().toString();
            String address = info.substring(info.length() - 17);
            //create new Intent - main screen
            Intent screen_main = new Intent(BT_Search_Activity.this, MainActivity.class);
            //pass adress of selected device to the main screen
            screen_main.putExtra("bt_address", address);
            //start new activity
            startActivity(screen_main);
        }
    };
}

Kromě nezbytný importů tato třída obsahuje pouze dvě mětody. První metoda OnCreate je metoda, která se spouští ihned po startu aktivity. Metoda nejprve natáhne svůj vzhled z XML souboru a poté namapuje podle id komponentu ListView, kam bude vypisovat spárovaná zařízení. Než vůbec začneme cokoliv s BT dělat je třeba zjistit zda vůbec zařízení nějaký BT modul má (každý chytrý telefon co znám má, ale pro jistotu). Toto obstarává první podmínka a pokud nenajde BT modul na zařízení, ohlásí to a celá aplikace končí. V další podmínce aplikace zjistí zda je BT modul zapnutý a pokud není nabídne jeho zapnutí, což vyvolá klasické dialogové okno.

layout

Následuje už jen vytažení seznamu spárovaných zařízení do ArrayListu a následné naplnění komponenty ListView tímto seznamem. Současně každé položce v ListView nastavím posluchače události OnItemClick. Tedy metodu, která se spustí po kliknutí na některou z položek. To je druhá metoda, OnItemClick a jejím úkolem je vzít MAC adresu zvolené BT zařízení, spustit hlavní (ovládací aktivitu) a té předat jí onu MAC adresu.

V této třídě si můžete povšimnout že v některých místech volám metody ze „záhadných“ tříd: BT_Connection a Globals. To jsou mé třídy, které jsem si napsal pro svou potřebu a které obsahují pomocné metody.

BT_Connection.java

Jedná se o třídu, která obsahuje metody pro práci s BT modulem v GA zařízení.

package johanok.bt_car_rc;

import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.AsyncTask;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;

public class BT_Connection extends AsyncTask<Void, Void, Void> {

    private static BluetoothAdapter myBluetooth = null;
    BluetoothSocket btSocket = null;
    String address = null;
    static final UUID myUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    ProgressDialog pd_progress;
    Context context;
    private boolean ConnectSuccess = true;
    private boolean isBtConnected = false;

    public BT_Connection(Context context, String address) {
        this.context = context;
        this.address = address;
        execute();
    }

    public static boolean isBTAvailable() {
        myBluetooth = BluetoothAdapter.getDefaultAdapter();
        if(myBluetooth != null) {
            return true;
        } else {
            return false;
        }
    }

    public static boolean isBTEnabled() {
        if(myBluetooth.isEnabled()) {
            return true;
        } else {
            return false;
        }
    }

    public static ArrayList getPairedDevices() {
        Set<BluetoothDevice> pairedDevices;
        do {
            pairedDevices = myBluetooth.getBondedDevices();
        }while(pairedDevices.size() == 0);
        ArrayList list = new ArrayList();
        for(BluetoothDevice bt : pairedDevices) {
            list.add(bt.getName() + " - " + bt.getAddress()); //Get the device's name and the address
        }
        return list;
    }

    @Override
    protected void onPreExecute() {
        pd_progress = ProgressDialog.show(context, "Connecting...", "Please wait!!!");  //show a progress dialog
    }

    @Override
    protected Void doInBackground(Void... voids) {
        try {
            if (btSocket == null || !isBtConnected) {
                BluetoothAdapter myBluetooth = BluetoothAdapter.getDefaultAdapter();
                //connects to the device's address and checks if it's available
                BluetoothDevice device = myBluetooth.getRemoteDevice(address);
                //create a RFCOMM (SPP) connection
                btSocket = device.createInsecureRfcommSocketToServiceRecord(myUUID);
                myBluetooth.cancelDiscovery();
                btSocket.connect();//start connection
            }
        }
        catch (IOException e) {
            //if the try failed, you can check the exception here
            ConnectSuccess = false;
        }
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        if (!ConnectSuccess) {
            Globals.showMsg(context, "Connection Failed. Try Again");
        } else {
            Globals.showMsg(context, "Connected");
            isBtConnected = true;
        }
        pd_progress.dismiss();
    }

    protected void sendCmdViaBT(String cmd) {
        if (btSocket!=null) {
            try {
                btSocket.getOutputStream().write(cmd.getBytes());
            }
            catch (IOException e) {
                Globals.showMsg(context, "Error");
            }
        }
    }

    protected void disconnect() {
        if (btSocket != null) {
            try {
                //close connection
                btSocket.close();
                btSocket = null;
                Globals.showMsg(context, "Disconnected");
            } catch (IOException e) {
                Globals.showMsg(context, "Error");
            }
        } else {
            Globals.showMsg(context, "Allready Disconnected");
        }
    }
}

Kromě již zmiňovaných statických metod:

  • isBTAvailable - která zjistí zda zařízení vůbec má nějaký BT modul
  • isBTEnabled - která zjistí jestli ten modul je zapnutý
  • getPairedDevices - která vrátí ArrayList spárovaných zařízení

Obsahuje třída ještě další metody:

  • Konstruktor - který převezme MAC adresu zařízení, ke kterému se budeme připojovat a aktuální context (abychom věděli, kdo konstruktor spustil) a spustí metodu execute, co ž uvede do chodu další metody: onPreExecute, doInBackground a onPostExecute, které se pokusí připojit k BT zařízení se zadanou MAC adresou. Během toho je zobrazena komponenta ProgressDialog, vyzívající uživatele k čekání.

layout

  • sendCmdViaBT - metoda která slouží k odeslání textové řetězce ze zařízení, pomocí této metody poté odesílám krátké příkazy do BT modulu v autíčku.
  • disconnect - metoda sloužící k ukončení spojení mezi zařízením a BT modulem v autíčku.

Globals.java

Třída, která měla původně obsahovat pomocné metody používané v celé aplikaci, ale nakonec v ní zbyla jen jedna metoda.

package johanok.bt_car_rc;

import android.content.Context;
import android.widget.Toast;

public class Globals {
    public static void showMsg(Context context, String msg) {
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
    }
}

Metoda showMsg slouží pro zobrazování hlášení formou tzv. Toastu, tedy malého nerušivého okna. Jednak s ní zobrazuji hlášení uživateli, jednak jsem jí používal při ladění aplikace.

activity_main.xml

Hlavní (ovládací) část aplikace. Aktivita převezme do BT_Search_Activity MAC adresu zarřízení a pokusí se k zařízení připojit. Poté stiskem tlačítek odesílá krátké textové příkazy do BT modulu v autíčku. Její vzhled popisuje tento XML soubor.

<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:stretchColumns="*"
    >
    <TableRow
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        >
        <Button
            android:id="@+id/btn_forward"
            android:layout_height="80dp"
            android:layout_span="3"
            android:text="@string/btn_forward"
            android:onClick="forward"
            />
    </TableRow>
    <TableRow
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        >
        <Button
            android:id="@+id/btn_left"
            android:layout_height="80dp"
            android:text="@string/btn_left"
            android:layout_marginRight="@dimen/main_table_margin"
            android:onClick="left"
            />
        <Button
            android:id="@+id/btn_brake"
            android:layout_height="80dp"
            android:text="@string/btn_brake"
            android:layout_marginLeft="@dimen/main_table_margin"
            android:layout_marginRight="@dimen/main_table_margin"
            android:onClick="brake"
            />
        <Button
            android:id="@+id/btn_right"
            android:layout_height="80dp"
            android:text="@string/btn_right"
            android:layout_marginLeft="@dimen/main_table_margin"
            android:onClick="right"
            />
    </TableRow>
    <TableRow
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        >
        <Button
            android:id="@+id/btn_backward"
            android:layout_height="80dp"
            android:layout_span="3"
            android:text="@string/btn_backward"
            android:onClick="backward"
            />
    </TableRow>
    <TableRow
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        >
        <SeekBar
            android:layout_height="40dp"
            android:layout_span="3"
            android:id="@+id/sb_speed"
            android:max="9"
            />
    </TableRow>
    <TableRow
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        >
        <Button
            android:id="@+id/btn_cmdA"
            android:text="@string/btn_cmdA"
            android:onClick="cmdA"
            />
        <Button
            android:id="@+id/btn_cmdB"
            android:text="@string/btn_cmdB"
            android:onClick="cmdB"
            />
        <Button
            android:id="@+id/btn_cmdC"
            android:text="@string/btn_cmdC"
            android:onClick="cmdC"
            />
    </TableRow>
    <TableRow
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        >
        <Button
            android:id="@+id/btn_cmdD"
            android:text="@string/btn_cmdD"
            android:onClick="cmdD"
            />
        <Button
            android:id="@+id/btn_cmdE"
            android:text="@string/btn_cmdE"
            android:onClick="cmdE"
            />
        <Button
            android:id="@+id/btn_cmdF"
            android:text="@string/btn_cmdF"
            android:onClick="cmdF"
            />
    </TableRow>
    <TableRow
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        >
        <Button
            android:id="@+id/btn_cmdG"
            android:text="@string/btn_cmdG"
            android:onClick="cmdG"
            />
        <Button
            android:id="@+id/btn_cmdH"
            android:text="@string/btn_cmdH"
            android:onClick="cmdH"
            />
        <Button
            android:id="@+id/btn_cmdI"
            android:text="@string/btn_cmdI"
            android:onClick="cmdI"
            />
    </TableRow>
    <TableRow
        android:layout_height="wrap_content"
        android:layout_width="fill_parent"
        >
        <Button
            android:id="@+id/btn_disconnect"
            android:layout_span="3"
            android:text="@string/btn_disconnect"
            android:onClick="disconnect"
            />
    </TableRow>
</TableLayout>

Pro tento vzhled jsem použil tabulkový layout. Layout má osm řádků a tři sloupce. V prvním řádku je přes všechny tři sloupce roztažené tlačítko pro jízdu vpřed. V druhém řádku jsou tlačítka pro zatočení vlevo, brždění a zatočení vpravo. V dalším řádku je tlačítko pro jízdu vzad, opět roztažené přes všechny tři sloupce. Následuje komponenta SeekBar sloužící k zadávání rychlosti. SeekBar má deset pozic, tedy deset možných rychlostí. Následují tři řádky s třeti tlačítky na každém pro „univerzální“ příkazy. Každé auto může mít na daný příkaz jinou funkčnost (např. zapínání světel, troubení klaksonem, apod.). Poslední řádek obsahuje tlačítko pro ukončení spojení s autíčkem a návrat zpět do vyhledávací aktivity. Každé tlačítko má k sobě navázánu metodu na událost: onClick, která se spustí po kliknutí na dané tlačítko.

Vzhled activity_main popsaný XML výše vypadá takto:

layout

activity_main.xml

No a konečně Java kód hlavní aktivity vypadá takto:

package johanok.bt_car_rc;

import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;
import android.widget.Toast;

import java.io.IOException;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {

    //Bluetooth
    BT_Connection bt_connection = null;

    //Widgets
    Button btn_forward;
    Button btn_left;
    Button btn_brake;
    Button btn_right;
    Button btn_backward;
    Button btn_cmdA;
    Button btn_cmdB;
    Button btn_cmdC;
    Button btn_cmdD;
    Button btn_cmdE;
    Button btn_cmdF;
    Button btn_cmdG;
    Button btn_cmdH;
    Button btn_cmdI;
    Button btn_disconnect;
    SeekBar sb_speed;
    ProgressDialog pd_progress;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //calling widgets
        btn_forward = (Button) findViewById(R.id.btn_forward);
        btn_left = (Button) findViewById(R.id.btn_left);
        btn_brake = (Button) findViewById(R.id.btn_brake);
        btn_right = (Button) findViewById(R.id.btn_right);
        btn_backward = (Button) findViewById(R.id.btn_backward);
        sb_speed = (SeekBar) findViewById(R.id.sb_speed);
        btn_cmdA = (Button) findViewById(R.id.btn_cmdA);
        btn_cmdB = (Button) findViewById(R.id.btn_cmdB);
        btn_cmdC = (Button) findViewById(R.id.btn_cmdC);
        btn_cmdD = (Button) findViewById(R.id.btn_cmdD);
        btn_cmdE = (Button) findViewById(R.id.btn_cmdE);
        btn_cmdF = (Button) findViewById(R.id.btn_cmdF);
        btn_cmdG = (Button) findViewById(R.id.btn_cmdG);
        btn_cmdH = (Button) findViewById(R.id.btn_cmdH);
        btn_cmdI = (Button) findViewById(R.id.btn_cmdI);
        btn_disconnect = (Button) findViewById(R.id.btn_disconnect);

        //get adress of selected device in the bt_search_activity
        String address = getIntent().getStringExtra("bt_address");

        //Call the class to connect
        bt_connection = new BT_Connection(MainActivity.this, address);
    }

    public void forward(View view) {
        bt_connection.sendCmdViaBT("FOR" + Integer.toString(sb_speed.getProgress()) + ";");
    }

    public void backward(View view) {
        bt_connection.sendCmdViaBT("BAC" + Integer.toString(sb_speed.getProgress()) + ";");
    }

    public void left(View view) {
        bt_connection.sendCmdViaBT("LEFT;");
    }

    public void brake(View view) {
        bt_connection.sendCmdViaBT("BRAK;");
    }

    public void right(View view) {
        bt_connection.sendCmdViaBT("RIGH;");
    }

    public void cmdA(View view) {
        bt_connection.sendCmdViaBT("CMDA;");
    }

    public void cmdB(View view) {
        bt_connection.sendCmdViaBT("CMDB;");
    }

    public void cmdC(View view) {
        bt_connection.sendCmdViaBT("CMDC;");
    }

    public void cmdD(View view) {
        bt_connection.sendCmdViaBT("CMDD;");
    }

    public void cmdE(View view) {
        bt_connection.sendCmdViaBT("CMDE;");
    }

    public void cmdF(View view) {
        bt_connection.sendCmdViaBT("CMDF;");
    }

    public void cmdG(View view) {
        bt_connection.sendCmdViaBT("CMDG;");
    }

    public void cmdH(View view) {
        bt_connection.sendCmdViaBT("CMDH;");
    }

    public void cmdI(View view) {
        bt_connection.sendCmdViaBT("CMDI;");
    }

    public void disconnect(View view) {
        new AlertDialog.Builder(MainActivity.this)
                .setTitle("Disconnect")
                .setMessage("Do you really want to disconnect?")
                .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                        bt_connection.disconnect();
                        finish();
                    }
                })
                .setNegativeButton(android.R.string.no, null).show();
    }
}

Aktivita v metodě onCreate natáhne svůj vzhled, podle id naváže komponenty (tlačítka a seekbar) a poté co si vytáhne MAC adresu zařízení, kterou jí poslal BT_Search_Activity, vytvoří novou instanci třídy BT_Connection. Následují malé metody, které se spouštějí k kliknutí na některé z tlačítek a odesílají krátké textové příkazy do autíčka. Já jsem se rozhodl, že příkazy budou mít vždy pět znaků a že budou vždy zakončeny středníkem. Zbývají tedy čtyři znaky pro příkaz. Příkazy pro jízdu vpřed a vzad se trochu liší, protože jako součást příkazu se ještě posílá rychlost která je dána nastavením seekbaru. Jak již bylo řečeno, seekbar má 10 pozic, tedy rychlost je v rozsahu 0 - 9, takže proto příkazy pro jízdu vpřed a vzad jsou tvořeny pouze třemi znaky. Metoda disconnect neodesílá žádný text přes BT, ale slouží k odpojení zařízení. Aby se uživatel neodpojil při řízení auta, kdyby omylem klikl na odpojení, je zde implementován ještě klasický potrvzovací dialog.

layout

Auta

Červený brouk (To úplně první autíčko)

Jak jsem již napsal, mé úplně první autíčko byl cca. 30 let starý RC model, připomínající VW Brouk, kterého našel tchán při úklidu hraček. Původní elektronika byla zcela nefunkční, ale motory po připojení napájení točily. Takže jsem vyndal původní elektroniku a pájkou „nahrubo“ vytavil podvozek, aby šel osadit Arduinem.

Fotogalerie - původní stav

Potřebné komponenty

Obrázek Název Počet kusů Poznámka
Arduino UNO R3 ATmega328P CH340G ArduinoUno 1ks Hlavní řídící jednotka.
Arduino bluetooth modul HC-05 HC05 1ks Na propojení arduina přes bluetooth.
Nepájivé kontaktní pole ZY-170 W Nepajive pole 1ks Nejmenší nepájivé pole, tak abych tam vešel všechny komponenty a současně aby se vešlo pod kapotu auta.
IO L293D pro řízení motorů IOL293D 1ks Pro řízení motorů, protože arduino přes svoje piny nedodá potřebný proud (max 40mA na pin) aby roztočil motory. Tento obvod je dvojitý H-můstek, který je schopný ovládat až dva stejnosměrné motory napájecím napětím až 36V.
LED dioda červená 5mm Led_red 2ks Zadní a současně brzdová světla - rozdíl mezi zadními světly a brzdovými svělty bude v intezitě svícení.
LED dioda žlutá 5mm Led_yellow 2ks Levý a pravý blinkr - blinkry jsou pouze zepředu.
LED dioda bílá 5mm Led_white 4ks Přední světla a couvací světla.
LED dioda RGB 5mm Led_rgb 1ks V podstatě navíc jenom pro efekt.
Rezistor 200 Ohm 0.25 W 1% Rezistor200r 11ks Předřadné odpory k led diodám.
Bzučák 5V 2.3 KHz Buzzer 1ks Klakson auta.
Distanční sloupek DA5M3X20 nikl VIGAN DistSloup 2ks Pro přišroubování arduina k podvozku auta.
Vodiče samec-samec M-M DuPont 40 kusů pro Arduino DupontKabely 1ks Na propojení.
DuPont 40Pin 2,54 mm pinový pás rovný DupontPinPas 1ks Na výrobu více-pinových konektorů, drží lépe než jednopinové kabely.

arduino.txt · Poslední úprava: 2019/02/02 09:26 autor: johanovsky