Miten tehdä Android sovellus langattomalle tiedonkeruulaitteelle

Liittynyt
08.07.2020
Viestejä
139
Puuhailen parhaillaan harrasteprojektia, joka on langaton (WiFI) tiedonkeruulaite ajoneuvon diagnostiikkaväylään. Perinteisemmissä OBD-lukijoissa signaaleja luetaan yksi kerrallaan vuoronperään ilman mahdollisuutta priorisoida signaaleja. Lisäksi OBD-lukijat ovat yleensä rajoittuneita näytteenottotajuudeltaan, joissain tapauksissa ne voivat jopa toimia näytteenottotaajuutta rajoittavana tekijänä. Haluan myös mahdollisuuden lisätä ulkoisia antureita. Tiedonkeruulaite lähettää reaaliaikaisen datan langattomasti esim. puhelimelle, samaan tyyliin kuin ELM327 OBD2-lukijat.

"Akuutein" tarve tiedonkeruulle on omassa moottoripyörässäni (KTM 690 vm08), jossa diagnostiikka hoidetaan K-linen kautta ja proprietary komennoin. Valitsin tiedonkeruulaitteen pohjaksi ESP32-WROOM:n ja lisäsin siihen vanhasta OBD2-kaapelista pelastetun l9637d IC:n. Tein reverse engineeringin käyttäen logiikka-analysaattoria (Saelae logic8 -klooni) ja TuneECu diagnostiikkalaitetta.

ESP32:lla pyörii yksinkertainen html sivu, jossa asetetaan tiedonkeruupalvelimen IP ja portti ja käynnistetään tiedonkeruu. Sivun ladanneen laitteen IP tarjotaan oletusarvoisesti IP-kenttään. Tarkoitus olisi että tätä voi käyttää helposti seuraavissa skenaarioissa: 1) ESP32 luo WiFi access pointin 2) Puhelin luo Wifi access pointin 3) ESP32 ja puhelin yhdistetään kiinteään langattomaan verkkoon

Alunperin ajattelin emuloida ELM327 kommunikaatiota ja hoitaa datan liikuttelun bluetoothilla, silloin olisin voinut käyttää jotain geneeristä OBD2-softaa, kuten Torque. Hyvin pian totesin sen haitat isommiksi kuin hyödyt ja päätin että hoidan kommunikoinnin TCP:llä. Olen jo kirjoittanut toimivan python-koodin, jolla avaan portin, vastaanotan tiedonkeruulaitteelta tulevat paketit ja kirjoitan ne lokitiedostoon. Kerään signaalit ESP32:n muistiin 100ms välein ja lähetän mitta-arvot kerran sekunnissa vastaanottimelle.

Kysymykseni kuuluukin: Miten minun kannattaisi lähteä tekemään Android-softaa, jolla voisin hallita tiedonkeruuta, lähettää ja vastaanottaa TCP-paketteja ja tallentaa lokitiedostoja puhelimen tallennustilaan? Tarkoitus ei ole hieroa puhelinsovellusta kuukausikaupalla, vaan nyt haetaan sellaista MVP-toteutusta mahdollisimman vähällä vaivalla.

1721375898630.png
1721376209937.png


Android-sovelluksen kehittäminen tiedonkeruulaitteesi hallintaan ja TCP-pakettien lähettämiseen/vastaanottamiseen ei välttämättä ole monimutkaista. Tässä on perusohjeet, joiden avulla pääset alkuun:

1. Asenna Android Studio
Lataa ja asenna Android Studio virallisilta sivuilta.

2. Luo uusi projekti
  1. Avaa Android Studio.
  2. Valitse "Start a new Android Studio project".
  3. Valitse "Empty Activity" ja anna projektille nimi.
3. Lisää käyttöoikeudet AndroidManifest.xml-tiedostoon
Lisää seuraavat rivit AndroidManifest.xml-tiedoston <manifest>-elementtiin, jotta sovelluksella on tarvittavat oikeudet käyttää internetiä ja kirjoittaa tiedostoja.

XML:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
4. Suunnittele yksinkertainen käyttöliittymä
Muokkaa activity_main.xml-tiedostoa ja lisää käyttöliittymäelementit, kuten tekstikentät IP-osoitteen ja portin syöttämistä varten, sekä napit tiedonkeruun aloittamiseksi ja lopettamiseksi.

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

    <EditText
        android:id="@+id/ipEditText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="Server IP or Hostname"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="50dp"
        android:inputType="text" />

    <EditText
        android:id="@+id/portEditText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="Port"
        android:layout_below="@id/ipEditText"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="20dp"
        android:inputType="number" />

    <Button
        android:id="@+id/startButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Start Logging"
        android:layout_below="@id/portEditText"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="20dp" />

    <Button
        android:id="@+id/stopButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop Logging"
        android:layout_below="@id/startButton"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="20dp" />
</RelativeLayout>
5. Lisää TCP-yhteys ja tiedonkeruulogiikka
Muokkaa MainActivity.java-tiedostoa lisätäksesi toiminnallisuudet nappeihin ja TCP-yhteyden hallinnan.

Java:
package com.example.tcpdatacollector;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainActivity extends AppCompatActivity {

    private EditText ipEditText, portEditText;
    private Button startButton, stopButton;
    private boolean isLogging = false;
    private Socket socket;
    private Thread loggingThread;

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

        ipEditText = findViewById(R.id.ipEditText);
        portEditText = findViewById(R.id.portEditText);
        startButton = findViewById(R.id.startButton);
        stopButton = findViewById(R.id.stopButton);

        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startLogging();
            }
        });

        stopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopLogging();
            }
        });
    }

    private void startLogging() {
        if (!isLogging) {
            final String ip = ipEditText.getText().toString();
            final int port = Integer.parseInt(portEditText.getText().toString());
            loggingThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        socket = new Socket(ip, port);
                        BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                        File logFile = new File(getExternalFilesDir(null), "data.log");
                        FileOutputStream fos = new FileOutputStream(logFile, true);
                        OutputStreamWriter writer = new OutputStreamWriter(fos);

                        isLogging = true;
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(MainActivity.this, "Logging started", Toast.LENGTH_SHORT).show();
                            }
                        });

                        String line;
                        while (isLogging && (line = input.readLine()) != null) {
                            writer.write(line + "\n");
                            writer.flush();
                        }

                        writer.close();
                        input.close();
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
            loggingThread.start();
        } else {
            Toast.makeText(this, "Logging is already running", Toast.LENGTH_SHORT).show();
        }
    }

    private void stopLogging() {
        if (isLogging) {
            isLogging = false;
            loggingThread.interrupt();
            try {
                if (socket != null && !socket.isClosed()) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            Toast.makeText(this, "Logging stopped", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "Logging is not running", Toast.LENGTH_SHORT).show();
        }
    }
}
6. Testaa sovellusta
  • Kytke Android-laitteesi tietokoneeseen ja varmista, että laite on kehittäjätilassa.
  • Valitse "Run" -> "Run 'app'" Android Studiossa ja valitse laitteesi.
  • Testaa sovellusta antamalla oikea IP-osoite ja portti, ja paina "Start Logging".
Lisävinkkejä
  • Voit käyttää AsyncTask tai Handler-luokkaa tehdäksesi verkko-operaatioista asynkronisia.
  • Jos haluat lisätä käyttöliittymäelementtejä, kuten graafisia esityksiä kerätyistä tiedoista, voit käyttää GraphView-kirjastoa.
  • Voit käyttää SharedPreferences-luokkaa tallentaaksesi IP-osoitteen ja portin pysyvästi sovelluksen asetuksiin.
Tällä ohjeella saat luotua perussovelluksen, joka kykenee hallitsemaan tiedonkeruuta ja TCP-pakettien lähettämistä/vastaanottamista. Voit laajentaa sovellusta tarpeidesi mukaan, kun perustoiminnot ovat valmiina.
 
Toggle Sidebar

Statistiikka

Viestiketjut
247 343
Viestejä
4 311 487
Jäsenet
72 020
Uusin jäsen
Viinaa

Hinta.fi

Ylös Bottom