Android и Arduino. Привет Android из Arduino

В 3-ей части статьи нами был рассмотрен проект для передачи данных от Android-устройства в ADK-плату. Сейчас мы рассмотрим обратную задачу.

Но для того, чтобы немного усложнить наш пример, мы немного усложним нашу задачу. Передавать в Android Нужно будет 2 переменные: первая будет содержать случайное число, а вторая — состояние нажатия кнопки. На Android устройстве первая переменная будет отображаться простым текстом, а вторая будет влиять на переключатель. Т.е. если кнопка, подключенная к плате Arduino ADK нажата, то и переключатель на Android устройстве будет включен.

Программа для Arduino

Скетч для Arduino будет следующий:

 #include  #include   // Adb connection. Connection * connection; long lastTime; byte rndNum;    // Случайное число int buttonState = 0;  // Состояние кнопки uint16_t tosend;      // Данные для передачи в Android const int buttonPin = 2;  // PIN-подключенной кнопки  void setup() {   pinMode(buttonPin, INPUT);      // Инициализация подсистемы ADB.     ADB::init();    // Open an ADB stream to the phone's shell. Auto-reconnect. Use any unused port number eg:4568   connection = ADB::addConnection("tcp:4568", true, NULL);      //Serial.begin(115200);   randomSeed(analogRead(0));    // Для генератора случайных чисел (чтобы не повторялись значения) }  void loop() {   if ((millis() - lastTime) > 100)  // Условие выполняется каждые 100 мс   {     rndNum = random(1, 99);    // Генерируем случайное число от 1 до 99     buttonState = digitalRead(buttonPin);  // Считываем состояние цифр. входа          tosend = (rndNum << 8) | buttonState;  // Формируем слово из 2-х байт      Serial.println(tosend,BIN);     connection->write(2,(uint8_t*)&tosend);  // Отсылаем 2 байта      lastTime = millis();   }      // Poll the ADB subsystem.   ADB::poll(); } 

Код я постарался хорошо прокомментировать, поэтому расписывать подробно его не буду. Здесь используется основная функция connection->write(), которая отсылает данные по USB кабелю через TCP на Android устройство. В функции передается 2 параметра, первый — кол-во байт для пересылки, второй — сами данные. Обратите, внимание, что предварительно, мы упаковали 2 байта данных в 2-х байтовое слово.

Компилируем скетч и загружаем его в нашу плату.

Программа для Android

Создание проекта, настройку манифест файла вы можете посмотреть в предыдущей статье.

Немного про активити. В прошлый раз я говорил, что activity можно создавать при помощи графического интерфейса и при помощи XML файла. В этот раз я приведу XML-файл нашего активити (см. в файлах)

Ключевыми элементами здесь являются switch1 (переключатель) и textView2 (текстовое поле). Вот что должно получиться:

Graphical Layout

Теперь открываем файл /src/com/example/arduino53/MainActivity.java, все в нем удаляем и копируем следующие строки:

 package com.example.arduino53;  import java.io.IOException;  import org.microbridge.server.Server; import org.microbridge.server.AbstractServerListener;  import android.os.AsyncTask; import android.os.Bundle; import android.app.Activity; import android.util.Log; import android.widget.Switch; import android.widget.TextView;  public class MainActivity extends Activity {          private int Ard_data1 = 0;     private int Ard_data2 = 0;     public final String APP_NAME = "arduino53";          Server server = null;      @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);         // Создаем TCP сервер (на основе сервера MicroBridge LightWeight)       try       {           server = new Server(4568); //Этот же порт необходимо использовать и на ADK-плате           server.start();                   } catch (IOException e)       {           Log.e(APP_NAME, "Unable to start TCP server", e);           System.exit(-1);       }                    server.addListener(new AbstractServerListener() {              @Override             public void onReceive(org.microbridge.server.Client client, byte[] data)             {                 Log.d(APP_NAME, "data0:"+data[0]+"; data1:"+data[1]);                 if (data.length<2) Log.e(APP_NAME, "Размер данных менее 2-х байт:"+data.length);                  Ard_data1 = data[0];                 Ard_data2 = data[1];                                          //Any update to UI can not be carried out in a non UI thread like the one used                 //for Server. Hence runOnUIThread is used.                 runOnUiThread(new Runnable() {                     //@Override                     public void run() {                         new UpdateData().execute(Ard_data1,Ard_data2);                     }                 });             }         });          }          @Override     protected void onDestroy (){      super.onDestroy();      server.stop();     }          class UpdateData extends AsyncTask {         // Called to initiate the background activity         @Override         protected Integer[] doInBackground(Integer... ArdState) {             return (ArdState);  //Возвращаем в onPostExecute()         }                  @Override         protected void onProgressUpdate(Integer... values) {             super.onProgressUpdate(values);             // Not used in this case         }                  @Override         protected void onPostExecute(Integer... result) {      	             //Log.d(APP_NAME, "onPostExecute0:"+result[0]);             //Log.d(APP_NAME, "onPostExecute1:"+result[1]);                                     TextView txt_btn_Arduino = (TextView) findViewById(R.id.textView2);             txt_btn_Arduino.setText(String.valueOf(result[1]));    // Выводим на activity RND число полученное от ADK                          Switch switch1 = (Switch) findViewById(R.id.switch1);                          if(result[0] == 1){                 switch1.setChecked(true);             }             else switch1.setChecked(false);         }     } } 

Здесь мы для класса server определяем метод server.addListener(new AbstractServerListener() {}), а также onReceive() который вызывается каждый раз, при получении данных от сервера MicroBridge.

Затем мы создаем новый поток. Для этого используется класс AsyncTask с тремя методами (всего их 4, но мы один из них не используем):
doInBackground() — выполняется в фоновом потоке. Здесь необходимо производить сложные вычисления, но мы здесь ничего не делаем, а просто возвращаем объект в метод onPostExecute()
onProgressUpdate() — используется для ввода промежуточных результатов (в нашей программе не задействован)
onPostExecute() — выполняется после метода doInBackground(). В нем мы принимаем данные в массив result и отображаем данные на экране устройства в элементе TextView, а также двигаем переключатель Switch в зависимости от положения нажатой внешней кнопки.

Обращаю ваше внимание, что создание UI элементов в классе AsyncTask возможно только в методе onPostExecute().

Ниже вы можете скачать проекты для Arduino и Android, а также все необходимые библиотеки