Skocz do zawartości

Komunikacja Raspberry Pi Pico z aplikacją na Androida poprzez przewód USB [C/Java]


matsobdev

Pomocna odpowiedź

Z konieczności utworzenia małego mobilnego systemu pomiarowego, pojawiła się potrzeba połączenia mikrokontrolera z telefonem pracującym na Androidzie, tak aby ten drugi otrzymywał dane z pierwszego. Wybór podstawowej platformy (Android) padł z uwagi na jej powszechność, a także brak konieczności zakupu płytek, czujników itp. Dlatego też chciałbym ośmielić tym artykułem osoby, które noszą się z zamiarem tworzenia projektów, które wymagają dużej ilości dostępnych w telefonach czujników, interfejsów komunikacyjnych i możliwości obliczeniowych, które czasem się marnują, a samo przedsięwzięcie nie przewiduje trwałego montażu telefonu w projektowanym urządzeniu (choć pewnie dużo osób i tak ponownie wykorzystuje te same mikrokontrolery/płytki z czujnikami itd. do kolejnych projektów). Jedną z wad takiego rozwiązania, jest brak zestawu pinów ogólnego przeznaczenia (GPIO) w telefonach.

Ten artykuł bierze udział w naszym konkursie! 🔥
Na zwycięzców czekają karty podarunkowe Allegro, m.in.: 2000 zł, 1000 zł i 500 zł.

konkurs_forbot_nagrody_1-350x147.png

Potrafisz napisać podobny poradnik? Opublikuj go na forum i zgłoś się do konkursu!
Czekamy na ciekawe teksty związane z elektroniką i programowaniem. Sprawdź szczegóły »

W kilku słowach niniejszy poradnik przeprowadzi przez proces tworzenia aplikacji na Androida (Java), która będzie komunikować się poprzez wirtualny port szeregowy za pomocą przewodu USB z całkiem nowym Raspberry Pi Pico. Oczywiście, omówiony zostanie również kod w języku C, który będzie sterował stanami logicznymi pinów mikrokontrolera (włączony/wyłączony) na podstawie odczytu wysłanych z telefonu danych. Ponadto będzie on również na żądanie telefonu odczytywał stan logiczny wybranych wejść i wysyłał te informacja, które wyświetlone zostaną w aplikacji mobilnej. Dodatkowo zaznajomi on z przygotowaniem środowiska do kompilacji programów w C/C++ na Raspberry Pi Pico jak i podpowie jak do pewnego stopnia zautomatyzować proces kompilacji i wgrywania programu do mikrokontrolera.

Przygotowanie aplikacji na telefon z Androidem

Po pierwsze należy pobrać i zainstalować zintegrowane środowisko programistyczne (IDE) Android Studio. Po utworzeniu nowego projektu z domyślną opcją "Empty Activity" i następnie wybranym "API21: Android 5.0 (Lollipop)" w pozycji "Minimum SKD" można zacząć od utworzenia pliku o nazwie "usb_devices.xml" o następującej zawartości (widoczne w pliku wartości wyjaśnione zostaną później):

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-device product-id="10" vendor-id="11914" />
</resources>

Należy skopiować go do katalogu "app\res\xml" tak jak na zrzucie poniżej. Albo skopiować plik w Eksploratorze Windows i wkleić w Android Studio (może być wymagane utworzenie katalogu "xml" - patrz opcje menu kontekstowego), albo utworzyć bezpośrednio w edytorze Android Studio, czy znaleźć jego lokalizację na dysku (domyślnie "Litera:\Users\Użytkownik\AndroidStudioProjects\NazwaProjektu\app\src\main\res\xml").

capture_11042021_165150.thumb.png.f375c5c83bd3a3e8d2397e3b5bba6aff.png

Następnie można przystąpić do konfiguracji pliku "AndroidManifest.xml" (do znalezienia jak na zdjęciu powyżej w katalogu "manifests"). Należy wkleić poniższe linijki kodu:

<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/usb_device" />

tak jak pokazano to na zrzucie ekranu:

capture_11042021_144107.thumb.png.163abb8e4624d1a80385afa16b1cc16b.png

Aplikacja będzie korzystała z zewnętrznej biblioteki usb-serial-for-android, którą należy podłączyć do aplikacji. Zrobić można to edytując najpierw plik "build.gradle (Project)", następnie - "build.gradle (Module)" tak, jak przedstawiono na zdjęciach poniżej:

capture_11042021_144144.thumb.png.fe32ffaef08bc145aa135440b5f5b6e6.pngfffffffffffffffff.thumb.png.b9d143c875b2185b5010d098b928777f.png

wklejając poniższe linijki:

maven { url 'https://jitpack.io' }

compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
}
implementation 'com.github.mik3y:usb-serial-for-android:3.3.0'

Teraz można zsynchronizować projekt klikając na "Synch Now" (ostatni rysunek powyżej). Teraz należy wkleić poniższy kod do pliku "app\res\layout\activity_main.xml". Będzie opisywał on wygląd ekranu aplikacji - pole tekstowe i dwa przyciski.

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

    <TextView
        android:id="@+id/tekst"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="vendorID productID"
        android:textSize="29sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/on"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="50dp"
        android:layout_marginTop="50dp"
        android:text="ON"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/off"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:layout_marginEnd="50dp"
        android:text="OFF"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>

Teraz przyszła pora na kod programu. Należy otworzyć plik "MainActivity.java" z katalogu "app\java" i zastąpić jego zawartość (poza pierwszą linijką rozpoczynającą się od "package...") tą podaną poniżej. Komentarz zawarte w kodzie opisują "co jest od czego" i ich przestudiowanie da pogląd na to, jak to wszystko działa.

// Zaimportowanie używanych przez aplikację klas
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

// Zaimportowanie zewnętrzenej biblioteki "usb-serial-for-android"
import com.hoho.android.usbserial.driver.CdcAcmSerialDriver;
import com.hoho.android.usbserial.driver.ProbeTable;
import com.hoho.android.usbserial.driver.UsbSerialDriver;
import com.hoho.android.usbserial.driver.UsbSerialPort;
import com.hoho.android.usbserial.driver.UsbSerialProber;
import com.hoho.android.usbserial.util.SerialInputOutputManager;

public class MainActivity extends AppCompatActivity {
    // Deklaracja zmiennych globalnych - opis w dalszej części kodu
    TextView poleTekstowe;
    BroadcastReceiver broadcastReceiver;
    UsbSerialPort usbSerialPort;
    SerialInputOutputManager serialInputOutputManager;
    ScheduledFuture co100Ms;
    ExecutorService rx;
    Button on;
    Button off;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Załadowanie pliku układu okna i elementów aplikacji
        setContentView(R.layout.activity_main);
        // Ekran będzie włączony tak długo, jak aplikacja będzie widoczna na głównym planie
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        // Przypisanie pola tekstowego z pliku układu do zmiennej w kodzie aplikacji
        poleTekstowe = findViewById(R.id.tekst);
        // Definicja obiektu, który będzie nasłuchiwał zdarzeń związanych z podłączeniem i odłączeniem
        // miktokontrolera do telefonu
        broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // Przełącznik "switch" - program nie musi sprawdzać po kolei wszystkich "if'ów" czy
                // "else if'ów", tylko kieruje się do właściwego miejsca za pierwszym podejściem
                switch (intent.getAction()) {
                    // Obsługa akcji podłączenia urządzeni USB do telefonu
                    case "android.hardware.usb.action.USB_DEVICE_ATTACHED":
                        UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
                        UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                        // Wartości, które należy umieścić we wspomnianym wcześniej pliku "usb_device.xml"
                        poleTekstowe.setText(usbDevice.getVendorId() + " " + usbDevice.getProductId());
                        ProbeTable probeTable = new ProbeTable();
                        // Przypisanie odpowiedniego dla Pico sterownika
                        probeTable.addProduct(usbDevice.getVendorId(), usbDevice.getProductId(), CdcAcmSerialDriver.class);
                        UsbSerialProber usbSerialProber = new UsbSerialProber(probeTable);
                        UsbSerialDriver usbSerialDriver = usbSerialProber.probeDevice(usbDevice);
                        UsbDeviceConnection usbDeviceConnection = usbManager.openDevice(usbSerialDriver.getDevice());
                        // Przypisanie wcześniej zdefiniowanego sterownika do wirtualnego portu
                        // szeregowego USB
                        usbSerialPort = usbSerialDriver.getPorts().get(0);
                        try {
                            // Próba otwarcia portu szeregowego dla mikrokontrolera
                            usbSerialPort.open(usbDeviceConnection);
                            // Zdefiniowanie parametrów komunikacji
                            usbSerialPort.setParameters(115200, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
                            // Sygnał Data Terminal Ready - Pico i Android rozpocznął komunikację
                            usbSerialPort.setDTR(true);
                            // Sygnał Request To Send - wymaga go np. Arduino do rozpoczęcia komunikacji z Androidem
                            usbSerialPort.setRTS(true);
                        } catch (Exception ignored) {}
                        // Obiekt nasłuchujący danych przychodzących
                        SerialInputOutputManager.Listener serialInputOutputListener = new SerialInputOutputManager.Listener() {
                            @Override
                            public void onRunError(Exception ignored) {}
                            @Override
                            public void onNewData(byte[] data) {
                                runOnUiThread(() -> poleTekstowe.setText(new String(data)));
                            }
                        };
                        serialInputOutputManager = new SerialInputOutputManager(usbSerialPort, serialInputOutputListener);
                        serialInputOutputManager.setReadTimeout(0);
                        // Definicja pozyższego obiektu jako oddzielnego wątku programu...
                        rx = Executors.newSingleThreadExecutor();
                        // ...i jego uruchomienie
                        rx.submit(serialInputOutputManager);
                        // Zdefiniowanie osobnego wątku, który będzie wywoływał się do 100 ms wysyłając
                        // porcję danych
                        co100Ms = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
                            try {
                                usbSerialPort.write("o".getBytes(), 0);
                            } catch (Exception ignored) {}
                        }, 0, 100, TimeUnit.MILLISECONDS);
                        break;
                    // Obsługa akcji podłączenia urządzeni USB do telefonu
                    case "android.hardware.usb.action.USB_DEVICE_DETACHED":
                        // Sprzątanie po ustanowionej wcześniej komunikacji do odłączeniu Pico od Androida
                        if (co100Ms != null && rx != null) {
                            co100Ms.cancel(false);
                            serialInputOutputManager.stop();
                            rx.shutdown();
                            poleTekstowe.setText("Odłączono");
                        }
                        break;
                }
            }
        };
        // Definicja filtrów podłączone/odłączone urządzenie USB
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
        intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
        // Uruchomienie nasłuchiwania podłączenia/odłączenia urządzenia USB
        this.registerReceiver(broadcastReceiver, intentFilter);

        // Definicja przycisków "ON" i "OFF"
        on = findViewById(R.id.on);
        on.setOnClickListener(v -> {
            try {
                // Wysłanie ciągu znaków do mikrokontrolera
                usbSerialPort.write("n".getBytes(), 0);
            } catch (Exception ignored) {}
        });
        off = findViewById(R.id.off);
        off.setOnClickListener(v -> {
            try {
                usbSerialPort.write("f".getBytes(), 0);
            } catch (Exception ignored) {}
        });
    }

    // Sprzątanie po ustanowionej komunikacji w momencie zamknięcia (strzałką wstecz) aplikacji
    @Override
    protected void onDestroy() {
        if (co100Ms != null && rx != null) {
            co100Ms.cancel(false);
            serialInputOutputManager.stop();
            rx.shutdown();
        }
        this.unregisterReceiver(broadcastReceiver);
        super.onDestroy();
    }
}

Ostatnim krokiem w tej części będzie skompilowanie pliku *.apk aplikacji, który to będzie można zainstalować w telefonie. Wybieramy z menu "Build" pozycję "Generate Signed APK...", wybrać "APK", utworzyć nowy klucz ("Create new..."), wybrać opcję "Build Variants" "release" oraz "Signature Versions" "V2..." i zakończyć kreatora przyciskiem "Finish" - zrzuty poniżej. W powiadomieniu wyświetli się lokalizacja do pliku *.apk. Za jego pomocą można zainstalować aplikację w telefonie. Gotowe! Przynajmniej androidowa aplikacja 😉

capture_11042021_194132.thumb.png.aaa7886c59ffa5d663f92593e73997f3.pngcapture_11042021_194133.thumb.png.8476f5fc62ed2a43360a9efcc49d2a1c.pngcapture_11042021_194137.thumb.png.ef0db98c4dfffbebf16d34e031a3b2e3.png

Pliki do znalezienia tutaj: Android.zip

Program dla Raspberry Pi Pico

Za wszelką cenę chciałem uruchomić Pico SDK, aby móc zaprogramować "Małą Malinkę" w C i finalnie kopiując plik *.uf2 do widocznej pamięci masowej po podłączeniu jej do komputera trzymając przyciśnięty przycisk "BOOTSEL". Było to zanim powstał skrypt dla Windowsa, a że na Windowsie 7 nie chciał współpracować żaden instalator Microsoftu, to rozwiązałem to w inny sposób. Jak to mówią "the hard way". Po pierwsze nigdy nie zrobiłem nic w C, co więcej oficjalna instrukcja do C/C++ mówiła np. o łatwej konfiguracji chociażby CLion'a. Nie wspominała tylko, że na Linuksie...

A więc do rzeczy. Pobieramy następujące składniki:

  1. ARM GCC Compiler
  2. CMake
  3. MinGW
  4. Python 3
  5. Raspberry Pi Pico SDK
  6. TinyUSB

Następnie wypakowujemy je w swojej ulubionej lokalizacji (ja utworzyłem oddzielny folder, gdzie przechowuję binarki programów do "devovania"). Nie ma instalatorów, więc otwarcie mogę się przyznać, że mam niechęć do nich, jak ja coś zrobię, to wiem, gdzie naśmieciłem i łatwiej później posprzątać. Można się przyznawać się, kto też tak ma 🤷‍♂️ Co zrobić... A nie, jest MinGW, instalujemy architekturę x86_64. Pierwsze trzy pozycje będą zawierały podkatalog "bin", których ścieżki należy umieścić w systemowej zmiennej "PATH" (Komputer>Właściwości>Zaawansowane ustawienia systemu>Zmienne środowiskowe>Zmienne systemowe) oddzielając je średnikiem. Python 3, tutaj należy podać ścieżkę katalogu głównego. Zawartość TinyUSB kopiujemy do podkatalogu "lib" folderu gdzie wypakowano Raspberry Pi Pico SDK. Można się też pokusić o utworzenie zmiennej użytkownika "PICO_SDK_PATH" zawierającej ścieżkę do wspomniajego w poprzednim zdaniu katalogu. Na zdjęciu tak to wygląda:

capture_11042021_202303.thumb.png.3af3e106a7c6e05623ee889e758735fc.png

Pora przygotować właściwe pliki. Najpierw "CMakeLists.txt", który będzie miał zawartość jak poniżej. Należy wspomnieć, że plik"pico_sdk_import.cmake" należy umieścić w katalogu projektu, a znaleźć można go w katalogu zawierającym Raspberry Pi Pico SDK w podfolderze "external".

cmake_minimum_required(VERSION 3.17)
include(pico_sdk_import.cmake)
project(project_name) # nazwa projektu
set(CMAKE_C_STANDARD 11)
pico_sdk_init()
add_executable(project_name file_name.c) # dodanie plików wykonywalnych projektu
target_link_libraries(project_name pico_stdlib)
pico_enable_stdio_usb(project_name 1) # włączenie połączenia szeregowego USB
pico_enable_stdio_uart(project_name 0) # wyłączenie UART
pico_add_extra_outputs(project_name)

Teraz tworzymy długo wyczekiwany plik *.c taki jak wskazany w "CMakeLists.txt" plik wykonywalny. Jego zawartość będzie jak poniżej. Komentarze rozjaśnią sprawę.

#include <stdio.h>
#include "pico/stdlib.h"

int main() {
    stdio_init_all();
    // Użycie pinu GP25 (dioda LED na płytce)
    gpio_init(25);
    // Ustawienie GP25 jako wyjścia
    gpio_set_dir(25, GPIO_OUT);
    gpio_init(0);
    // Ustawienie GP0 jako wejścia
    gpio_set_dir(0, GPIO_IN);
    gpio_init(1);
    gpio_set_dir(1, GPIO_IN);
    // Podciąga GP1 poprzez wbudowany w czip RP2040 opornik - stan wysoki (1), gdy nie podłączony do masy (GND)
    gpio_pull_up(1);
    // Bufor dwóch znaków. Efektywnie jednego, drugi to znak terminalny "\0"
    // Jeśli chcesz odczytać 5 znaków, ustaw należy ustawić bufor na 6
    char wiadomosc[2];
    while (true) {
        // Odczytuje dwa znaki (łącznie z terminalnym) w wejścia. Jeśli chcesz odczytywać wiadomości
        // ze zmienną ilością znaków, użyj "gets(wiadomosc)" zamiast "fgets(wiadomosc, 2, stdin)"
        // i zakończ wysyłane wiadomości znakiem kowej linii - "\n"
        fgets(wiadomosc, 2, stdin);
		// Odczytuje pierwszy znak (indeks [0]) wektora zawierającego wiadomość.
		// Java może porównać cały ciąg w instrukcji "switch" ;P
        switch(wiadomosc[0]) {
            case 'n':
                // Włącza diodę led przy wiadomości "n" ze standardowego wejścia
                gpio_put(25, 1);
                break;
            case 'f':
                // Wyłącza diodę led przy wiadomości "f" ze standardowego wejścia
                gpio_put(25, 0);
                break;
            case 'o':
                // Odczytuje wartości stanów GP0 i GP1 - wysoki (1) lub niski (0)
				// i wysyła je przez połączenie szeregowe
                printf("GP0: %d, GP1: %d", gpio_get(0), gpio_get(1));
                break;
        }
    }
}

Do kompilacji przygotujemy plik wsadowy *.bat, który ułatwi zautomatyzuje kompilację. Jego zawartość będzie następująca:

@echo off
mode con: cols=130 lines=32
for %%i in (%1) do (set sciezka=%%~pi)
for %%i in (%1) do (set litera=%%~di)
cd /d "%litera%%sciezka%"
cmake.exe -DCMAKE_BUILD_TYPE=Release -G "CodeBlocks - MinGW Makefiles" "%litera%%sciezka%"
cmake.exe --build "%litera%%sciezka%"
robocopy "%litera%%sciezka% " "G:\ " "*.uf2" /nfl /ndl /njh /njs /np
pause

Należy zmienić literę "G:\ " na tą, pod którą pojawi się dysk wymienny Pico. Spacje nie są błędem. Dziwna sprawa, ale inaczej nie chce działać, przynajmniej na Siódemce, a dodanie spacji na końcu było rozwiązaniem. Przygotowany plik chowamy przed wzrokiem i np. programem Default Programs Editor tworzymy pozycję w menu kontekstowym pliku *.c a jeszcze lepiej tworzymy nowe rozszerzenie dla plików C Pico. Albo kompilujemy wykorzystując dwie linijki zaczynające się od "cmake.exe", albo korzystamy ze szpanerskiego menu, a efekt kompilacji będzie następujący:

capture_11042021_212759.thumb.png.293bb25f0e4b2c7952b0461cbfc35020.pngcapture_11042021_212829.thumb.png.a18ff0d04f6db7e97ee70f997e8123d6.png

Pliki do znalezienia tutaj: Pico.zip

Pierwsze uruchomienie

Po uruchomieniu aplikacji na telefonie i podłączeniu Pico przez kabel USB i adapter OTG należy zgodzić się, aby aplikacja uruchamiała się wraz z podłączeniem mikrokontrolera do smartfona. Nada to uprawnienia do korzystania z połączenia USB aplikacji. Pola "vendorID" i "productID" zmienią swoje wartości na te, które trzeba było umieścić we wspomnianym wcześniej pliku "usb_devices.xml". Po odłączeniu i ponownym podłączeniu (w trakcie działania aplikacji mobilnej, gdyż w androidowej aplikacji odczyt od cykliczne nadawanie zakodowane jest po podłączeniu urządzenia, ale tym zdefiniowanym w pliku "MainActivity.java", który wykonuje się po jej uruchomieniu) Malinki będzie można włączyć i wyłączyć jej diodę LED (GP25) i co 100 ms (na żądanie telefonu) będzie odczytywany stan wejść GP0 i GP1. Jak to działa, na zdjęciach poniżej. Zwarcie pinów GP0 (1) i 3V3OUT (36) ustawi stan wysoki na wejściu GP0, a połączenie GP1 (2) z GND (3) przesteruje normalnie wysoki stan do niskiego.

P1000524.thumb.JPG.d6262e1759b48690335da190000d6b90.JPGScreenshot_20210411-213611_cvncvn.thumb.jpg.e8359f95ef8266490f1ac7c68aad0f1c.jpgScreenshot_20210411-213555_cvncvn.thumb.jpg.5e3828293ed8a52a61ebd3d5e4e7b04e.jpgScreenshot_20210411-213616_cvncvn.thumb.jpg.28349b8d28f733cfbb65937bde96b62f.jpgScreenshot_20210411-213634_cvncvn.thumb.jpg.69877017e0a9ae7ee67cc2b9f8b4bdec.jpgbitmap.thumb.png.9b53f7b06461e2c8d906fc8945fa3830.pngScreenshot_20210411-213725_cvncvn.thumb.jpg.364e3d03cf46c07eccb04dcfe8ba0ed6.jpg

Gotowe pliki wykonywalne tutaj: Binarki.zip

Podsumowanie

Niniejszy artykuł nie jest projektem od A do Z jakiegoś przedsięwzięcia, tylko przedstawia zagadnienie komunikacji portem szeregowym pomiędzy mikrokontrolerem (tutaj Raspberry Pi Pico, choć nie musi to być on) a telefonem komórkowym z Androidem wykorzystując natywne rozwiązania tworzenia programów na te platformy, tym samym nie będąc na "łasce" np. rozwiązań modułowych tworzenia aplikacji z klocków, wykorzystując wszystkie możliwości jakie daje Android, Pico, Java i C. Oczywiście to tylko zalążek, ale nie znalawszy gotowego rozwiązania, zmotywowało mnie to, żeby takie zrobić i zaprezentować, ktoś może mieć łatwiej. Ja już mam łatwiej, bo takiego potrzebuję 😁

PS. Nie spodziewałem się, że tak długo zajmie mi napisanie tego tekstu...
PS2. Nazwa aplikacji mobilnej to taka losowo wklepana na klawiaturze.
PS3. Pierwszy obrazek, drugi kod i trzy następne obrazki to trochę ludzik, trochę android 🤖

  • Lubię! 2
Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.