Ofereço a vocês, queridos leitores do GeekTimes, outro artigo do ciclo sobre o uso do chip ESP8266 como uma ponte sem fio para microcontroladores AVR, usando a plataforma de hardware Arduino Uno (Nano) como exemplo. Desta vez,
para o vôo para a lua controlar a plataforma, usamos um dispositivo baseado no ANDROID em vez de um computador. Bem, por exemplo, um smartphone.

Detalhes sob o corte:
Em nosso trabalho, usaremos as ferramentas descritas no artigo anterior - o programador sem fio BABUINO e o módulo MPDE (módulo para troca de dados e programas) transmitidos no ESP8266.
Como resultou do feedback dos usuários, o próprio programador, em geral, compareceu ao tribunal e algumas pessoas o usaram com sucesso. Bem, basicamente, a coisa é realmente conveniente; lançou o aplicativo no Windows, selecionou o arquivo hexadecimal da pasta desejada e pronto - em poucos segundos o programa no dispositivo desejado sem fios. Outra coisa é que, em vão, com excesso de ardor, ataquei usuários de software ARDUINO que escrevem no Wiring C e usam o IDE ARDUINO com seus esboços e bibliotecas. Obviamente, todo mundo faz o que é conveniente para ele - seja no Arduin Wiring C ou C do estúdio AVR. Porém, no final, alguns usuários, por algum motivo, decidiram imediatamente que o programador não é de forma alguma compatível com o software ARDUINO.
De fato, não há problemas de compatibilidade, é claro. Absolutamente também compile seu esboço, onde for conveniente para você o estado de um arquivo hexadecimal e envie-o com a mesma facilidade através de um programador sem fio ao seu Arduino UNO ou NANO.
A troca de dados no software ARDUINO também não causa problemas. Escreva as linhas mágicas:
Serial.begin (9600);e então algo como:
receiveByte = Serial.read (); // lê o byte do buffer
ou:
Serial.write (receiveByte); // registro de bytes
E você pode trocar fluxos de bytes por WI-FI com bastante calma. Para o microcontrolador AVR envia para o ESP8266 e recebe bytes de lá via porta UART serial, que, como podemos ver, qualquer humanitário pode configurar o trabalho com o qual no Arduino.
Agora, de volta ao assunto deste artigo. Para controlar o robô por meio de um smartphone, é claro, você deve escrever o aplicativo apropriado para esse smartphone.
Como uma pessoa que apresentou esse processo há um mês de maneira extremamente vaga, agora posso declarar com toda a responsabilidade que o assunto não é essencialmente complicado. A menos, é claro, que você tenha pelo menos algum conhecimento básico no campo de Java.
Para escrever aplicativos Android, o código-fonte é escrito em Java e compilado no bytecode Java padrão usando as ferramentas Java tradicionais. Outras coisas interessantes acontecem no código, mas não precisamos desses detalhes aqui.
Parece que ainda há oportunidades para escrever aplicativos em C para notórios trabalhadores incondicionais que pontuam em nanossegundos e também em algum lugar há algum tipo de pacote para traduzir seu código do Python, mas não posso dizer nada aqui, porque fiz a escolha certa por um longo tempo .
Portanto, Java e o pacote de software Android Studio - um ambiente de desenvolvimento integrado (IDE) para trabalhar com a plataforma Android baseada no software IntelliJ IDEA da JetBrains, a ferramenta de desenvolvimento oficial para aplicativos Android.

Se você já estiver trabalhando com o software IntelliJ IDEA, ficará surpreso com a interface familiar.
Eu me familiarizei com os conceitos básicos de criação de aplicativos do livro de B. Phillips e C. Stewart - programação "ANDROID" para profissionais ". Pelo que entendi, os profissionais consideram os leitores que estão pelo menos um pouco familiarizados com o Java SE. Não encontrei e, para nossos propósitos, os dez primeiros capítulos do livro serão suficientes, felizmente, todos os exemplos de código são dados ao trabalhar com o Android Studio acima.
Os aplicativos de depuração podem ser executados em um emulador de software ou diretamente em um smartphone, alternando-o para o "modo de desenvolvedor".
Em um artigo anterior, o controle de carrinho através de um aplicativo Windows no Windows foi descrito. Ou seja, todo o código para criar conexões HTTP e UDP, bem como a lógica de controle, já está presente na teoria. Portanto, tendo adotado o slogan da Oracle "Ele está escrito em um só lugar, funciona em qualquer lugar", simplesmente transferimos essas classes para um novo programa para aplicativos Android. Mas a GUI é uma interface gráfica do usuário, por razões óbvias, você precisa sair de onde estava. Mas, por outro lado, no Android, tudo é feito de maneira muito semelhante e muito rápida, para que não fiquemos perdedores.
Coloque um dedo na tela
Então, criamos um novo projeto "FourWheelRobotControl" no Android Studio.
Esta é uma aplicação simples e consistirá em atividade
Classe MainActivity.javaimport android.content.Context; import android.hardware.Sensor; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private ImageButton mButtonUP; private ImageButton mButtonDOWN; private ImageButton mButtonLEFT; private ImageButton mButtonRIGHT; public static byte direction = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new HTTP_client(40000); new Udp_client(); mButtonUP = (ImageButton) findViewById(R.id.imageButtonUP); mButtonUP.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { direction = 3; } } if (event.getAction() == MotionEvent.ACTION_UP) { direction = 100; } return false; } }); mButtonDOWN = (ImageButton) findViewById(R.id.imageButtonDown); mButtonDOWN.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { direction = 4; Toast.makeText(MainActivity.this, " " + direction, Toast.LENGTH_SHORT).show(); } if (event.getAction() == MotionEvent.ACTION_UP) { direction = 100; } return false; } }); mButtonLEFT = (ImageButton) findViewById(R.id.imageButtonLeft); mButtonLEFT.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { direction = 1; Toast.makeText(MainActivity.this, " " + direction, Toast.LENGTH_SHORT).show(); } if (event.getAction() == MotionEvent.ACTION_UP) { direction = 100; } return false; } }); mButtonRIGHT = (ImageButton) findViewById(R.id.imageButtonRight); mButtonRIGHT.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { direction = 2; Toast.makeText(MainActivity.this, " " + direction, Toast.LENGTH_SHORT).show(); } if (event.getAction() == MotionEvent.ACTION_UP) { direction = 100; } return false; } }); }
e layout:
disposição /spoiler/ activity_main.xml <?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="mikhail_akhmetov.fourweelsrobotcontrol.MainActivity"> <ImageButton android:id="@+id/imageButtonRight" style="@android:style/Widget.Holo.ImageButton" android:layout_width="52dp" android:layout_height="52dp" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:background="@android:color/holo_orange_dark" app:layout_constraintBottom_toTopOf="@+id/linearLayout" app:layout_constraintHorizontal_bias="0.417" app:layout_constraintLeft_toRightOf="@+id/imageButtonLeft" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.513" app:srcCompat="@android:drawable/ic_media_ff"/> <ImageButton android:id="@+id/imageButtonUP" style="@android:style/Widget.ImageButton" android:layout_width="52dp" android:layout_height="52dp" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:background="@android:color/holo_orange_dark" app:layout_constraintBottom_toTopOf="@+id/imageButtonDown" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.486" app:srcCompat="@android:drawable/arrow_up_float"/> <ImageButton android:id="@+id/imageButtonDown" style="@android:style/Widget.ImageButton" android:layout_width="52dp" android:layout_height="52dp" android:layout_marginBottom="57dp" android:layout_marginLeft="8dp" android:background="@android:color/holo_orange_dark" android:fadingEdge="horizontal" app:layout_constraintBottom_toTopOf="@+id/linearLayout" app:layout_constraintHorizontal_bias="0.487" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:srcCompat="@android:drawable/arrow_down_float" android:layout_marginStart="8dp"/> <ImageButton android:id="@+id/imageButtonLeft" style="@android:style/Widget.ImageButton" android:layout_width="52dp" android:layout_height="52dp" android:layout_marginLeft="94dp" android:layout_marginStart="94dp" android:background="@android:color/holo_orange_dark" app:layout_constraintBottom_toTopOf="@+id/linearLayout" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.513" app:srcCompat="@android:drawable/ic_media_rew" /> <LinearLayout android:id="@+id/linearLayout" android:layout_width="368dp" android:layout_height="227dp" android:layout_marginBottom="11dp" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:orientation="vertical" android:weightSum="1" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"> <TextView android:id="@+id/textViewX" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0.28" /> <TextView android:id="@+id/textViewY" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0.28" /> <TextView android:id="@+id/textViewZ" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0.23" /> </LinearLayout> </android.support.constraint.ConstraintLayout>
Não é necessário escrever com canetas, ele é gerado automaticamente após as suas criações no editor.
Agora, apenas transferimos duas classes do programa fornecido no artigo anterior:
HTTP_client.java import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.net.Socket; public class HTTP_client extends Thread{ int port; String s; public static String host_address="192.168.1.138";
Udp_clent.java import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class Udp_client extends Thread { int i =0; byte [] data = {0}; int udp_port=50000; InetAddress addr; DatagramSocket ds; public Udp_client() { try { ds = new DatagramSocket(); addr = InetAddress.getByName(HTTP_client.host_address); } catch (Exception e) { } start(); } public void run() { while (true) { byte temp = MainActivity.direction; String s = "" + MainActivity.direction; data = s.getBytes(); if(temp!=100 ) { DatagramPacket pack = new DatagramPacket(data, data.length, addr, udp_port); try { ds.send(pack); i=0; Thread.sleep(200); } catch (Exception e) { } } else { if(i==0) { s = "" + 0; data = s.getBytes(); DatagramPacket pack = new DatagramPacket(data, data.length, addr, udp_port); try { ds.send(pack); Thread.sleep(200); } catch (Exception e) { } } i=1;
A essência do programa permanece a mesma. O MainActivity primeiro inicia os clientes HTTP e UDP e, em seguida, captura pressionamentos de botão e pressionamentos, enviando um código push de direção para formar um pacote UDP. E a partir daí tudo já está - “para frente”, “voltar”, “esquerda”, “direita” e, quando você pressiona “parar”, eles saem via WI-FI em um carrinho.
Além de tudo isso, precisamos modificar levemente o arquivo do chamado manifesto, que, novamente, é gerado principalmente por ele mesmo.
Um manifesto é um arquivo XML com metadados que descrevem seu aplicativo Android. O arquivo de manifesto é sempre chamado AndroidManifest.xml e está localizado no diretório app / manifest do seu projeto.
É verdade que há muito pouco trabalho para nós.
proibir a rotação da tela:
android:screenOrientation="portrait"
Permitimos trabalhos na Internet:
<uses-permission android:name="android.permission.INTERNET"/>
E aqui está tudo completamente.
AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="mikhail_akhmetov.fourweelsrobotcontrol"> <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=".MainActivity" android:screenOrientation="portrait" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-permission android:name="android.permission.INTERNET"/> </manifest>
Na inicialização, devemos ver algo como:

Isso é essencialmente tudo. Agora, o carrinho pode ser controlado a partir de um telefone celular. O aplicativo é extremamente simples, porque é um aplicativo de demonstração sem “apitos e fraudes”, como procurar um carrinho em uma rede doméstica, conectar, desconectar e outros recursos úteis que facilitam a vida, mas dificultam a compreensão do código.
Por outro lado, operar com os botões na tela é o mesmo
"Como beber apenas vodka, mesmo no pescoço - não há nada além de definhamento de espírito e confusão".
E Venya Erofeev estava certa nisso, é claro.
É muito mais interessante, por exemplo, dirigir um carrinho com a ajuda de acelerômetros padrão do mesmo smartphone.
Além disso, esse recurso também é implementado de maneira muito simples, por meio das chamadas intenções. É verdade que criar suas próprias intenções é mais difícil do que usar as já prontas. Felizmente, ninguém proíbe usar os já prontos.
ACELERÔMETRO EM MASSAS
Portanto, nosso código em MainActivity (e somente nele) mudará minimamente.
Adicione variáveis para o acelerômetro:
private SensorManager mSensorManager; private Sensor mOrientation; private float xy_angle; private float xz_angle;
Nós obtemos o próprio sensor do sistema e o registramos como ouvinte.
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Implementamos a própria interface do ouvinte:
public class MainActivity implements SensorEventListener
E escreveremos quatro métodos obrigatórios, dos quais usaremos apenas o último. Um método que funciona ao alterar as leituras do acelerômetro. Na verdade, eu mesmo queria interrogar o acelerômetro, como periféricos normais com um período de cerca de 100 ms, porque havia uma suspeita (devido ao nome onChanged) de que o método funcionava com muita frequência. Mas tudo é privado e figos para as interfaces pelas quais você passará.
@Override public void onAccuracyChanged(Sensor sensor, int accuracy) {
Como resultado, MainActivity.java assumirá o seguinte formato import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.content.Context; import android.hardware.Sensor; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.ImageButton; import android.widget.TextView; public class MainActivity extends AppCompatActivity implements SensorEventListener { private ImageButton mButtonUP; private ImageButton mButtonDOWN; private ImageButton mButtonLEFT; private ImageButton mButtonRIGHT; public static byte direction = 100; private SensorManager mSensorManager; private Sensor mOrientation; private float xy_angle; private float xz_angle; private int x; private int y; private TextView xyView; private TextView xzView; private TextView zyView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new HTTP_client(40000); new Udp_client(); mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
O programa exibe os sensores em dois eixos na tela do smartphone. Em vez do terceiro eixo, que não é usado, a variável de direção "direção" é exibida. E os mesmos dados são executados na forma de um fluxo de bytes no carrinho. No entanto, devido ao fato de o fluxo ser puramente byte, seria difícil determinar onde estaria o comando "forward" ou "stop". Portanto, eu agi simplesmente: cada direção e ângulo de inclinação tem seu próprio intervalo de números. Grosso modo, 1-20 está "para frente" com a velocidade correspondente, 21-40 está "para trás" e assim por diante. Obviamente, seria possível transmitir dados puros através de UDP, e os próprios comandos de controle deveriam ser definidos através do protocolo TCP, e isso certamente seria mais correto. Mas para isso você precisa editar o programa no próprio ESP8266, o que eu ainda não quero.
Então, o carrinho está rolando pelo apartamento, reagindo com sensibilidade às encostas do meu GalaxyS7, mas mesmo isso, como dizia o notório Venya, ainda não é isso.
“Agora eu ofereço a você o último e o melhor. "A coroa do trabalho, acima de todas as recompensas", disse o poeta. Em resumo, ofereço-lhe o coquetel "Offal Off", uma bebida que ofusca tudo. Isso não é mais uma bebida - é a música das esferas. „
Nesta era de Siri e Alexa, há algo para rodar suas mãos? Deixe o controle por voz obedecer!
E AGORA OUÇA-ME!
Cito:
Na verdade, trabalhar com reconhecimento e síntese de fala no Android é muito simples. Todos os cálculos complexos estão escondidos de nós em uma biblioteca bastante elegante com uma API simples. Você pode dominar esta lição, mesmo que tenha um conhecimento muito superficial da programação para Android.
De fato, tudo ficou muito desajeitado para mim, mas provavelmente porque usei a opção mais simples e não mergulhei nessa API adequadamente. Tudo também é feito através de intenções, com a ajuda da qual recorremos ao mecanismo de voz do Google, ou seja, à sua função de reconhecimento de fala. Portanto, você precisa de uma Internet funcional.
Novamente, apenas MainActivity.java muda, embora eu tenha mudado um pouco mais o layout (agora quatro botões são completamente inúteis, um é o suficiente).
O seguinte foi adicionado ao MainActivity.java:
A mesma intenção:
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); intent.putExtra(RecognizerIntent.EXTRA_PROMPT, ", , -??? "); startActivityForResult(intent, Print_Words);
E o que retorna:
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {
E ele retorna uma variedade de palavras semelhantes, e de uma forma "lixo", com todos os tipos de colchetes e vírgulas. E você só precisa escolher uma palavra semelhante à que você disse. Bem e, consequentemente, se a palavra "encaminhar" for capturada, seguiremos adiante, se "direita", então, para a direita e assim por diante. Obviamente, devemos levar em consideração onde, através de "E", onde a vírgula extra está anexada (cortei os colchetes, mas não tinha força suficiente para as vírgulas).
No total, o texto do MainActivity.java se tornou tão import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; import android.widget.ImageButton; import android.widget.TextView; import java.util.ArrayList; import android.content.Intent; import android.speech.RecognizerIntent; public class MainActivity extends AppCompatActivity { private ImageButton mButtonUP; public static byte direction = 100; public String stroka_otveta; private static final int Print_Words = 100; private TextView EnteredText1; private TextView EnteredText2; public static TextView EnteredText3; private boolean slovo_raspoznano =false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Udp_client(); new HTTP_client(40000); EnteredText1 = (TextView) findViewById(R.id.textViewX);
Bem, até o layout da pilha com um botão
activity_main.xml. <?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="mikhail_akhmetov.fourwheelsrobotandroidvoice.MainActivity"> <ImageButton android:id="@+id/imageButtonUP" style="@android:style/Widget.ImageButton" android:layout_width="160dp" android:layout_height="146dp" android:layout_marginTop="8dp" android:background="@android:color/holo_orange_dark" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@android:drawable/arrow_up_float" android:layout_marginBottom="8dp" app:layout_constraintBottom_toTopOf="@+id/linearLayout"/> <LinearLayout android:id="@+id/linearLayout" android:layout_width="368dp" android:layout_height="227dp" android:layout_marginBottom="11dp" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:orientation="vertical" android:weightSum="1" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintHorizontal_bias="1.0" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"> <TextView android:id="@+id/textViewTitle" android:layout_width="match_parent" android:layout_height="wrap_content" android:text=" " android:textColor="@android:color/holo_red_dark"/> <TextView android:id="@+id/textViewX" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0.28" /> <TextView android:id="@+id/textViewY" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0.28" /> <TextView android:id="@+id/textViewZ" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="0.23" /> </LinearLayout> </android.support.constraint.ConstraintLayout>
O mais surpreendente é que ele realmente monta e ouve a voz (em regra). Mas, claro, a interface do freio; enquanto você diz, até que seja reconhecido e retornará; Em resumo, escolha uma pequena velocidade de antecedência ou a sala é vice-versa mais ampla.
Por hoje é tudo, terei prazer em gostar.
