poniedziałek, 19 lutego 2018

[3] Xamarin - Sterowanie przekaźnikami w Arduino przez Bluetooth - V1.0

Ten post chciałbym poświęcić na przygotowanie aplikacji po bluetooth służącej do uruchamiania przekaźników na urządzeniu. Komunikacja będzie odbywać się z układem HC-05, który będzie podłączony do płytki Arduino.
[Źrodlo: https://en.wikipedia.org/wiki/Xamarin]

Komunikacja odbywa się poprzez wspomniany układ HC-05. Zapewnia on dwustronną komunikację z urządzeniem. Jego zaletą jest protokół UART jaki jest wykorzystany do przesyłania danych z mikrokontrolerem.

W wersji V1.0 do poprawy zostało mi odbieranie danych, które puki co działa jeszcze słabo, dane co prawda są odbierane, natomiast muszę odpowiednio przygotować funkcję obrabiającą odebraną ramkę. Drugim elementem do dodania jest kod dla ustawień czasowych dla Arduino. Dane odnośnie czasów załączania oraz wyłączania poszczególnych przekaźników zostały już zapisane w pamięci EEPROM, natomiast muszę dołożyć jeszcze część kodu odpowiadająca za ich ustawianie.


Wygląd aplikacji XAML:



Całą aplikacja prezentuje się następująco:


Poniżej przedstawię plik XAML z przygotowanym wyglądem graficznym dla aplikacji na Androida:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2.    android:orientation="vertical"
  3.    android:layout_width="match_parent"
  4.    android:layout_height="wrap_content"
  5.    android:background="#FFB404">
  6.     <Space
  7.        android:layout_width="match_parent"
  8.        android:layout_height="10dp"
  9.        android:id="@+id/space1" />
  10.     <TextView
  11.        android:text="1DOF"
  12.        android:layout_width="125dp"
  13.        android:layout_height="match_parent"
  14.        android:id="@+id/textView1" />
  15.     <ToggleButton
  16.        android:text="Connect"
  17.        android:layout_width="match_parent"
  18.        android:layout_height="wrap_content"
  19.        android:id="@+id/toggleButton1"
  20.        android:layout_marginLeft="50dp"
  21.        android:layout_marginRight="50dp"
  22.        android:textColor="#FFF"
  23.        android:background="#ed8404"
  24.        android:textOn="Connected"
  25.        android:textOff="Connect"
  26.        android:paddingBottom="10dp"
  27.        android:paddingEnd="10dp"
  28.        android:paddingLeft="10dp"
  29.        android:paddingRight="10dp"
  30.        android:paddingStart="10dp"
  31.        android:paddingTop="10dp" />
  32.     <Space
  33.        android:layout_width="match_parent"
  34.        android:layout_height="10dp"
  35.        android:id="@+id/space2" />
  36.     <ToggleButton
  37.        android:text="READ DATA FROM DEVICE"
  38.        android:id="@+id/toggleButton2"
  39.        android:layout_marginLeft="50dp"
  40.        android:layout_marginRight="50dp"
  41.        android:layout_width="match_parent"
  42.        android:layout_height="wrap_content"
  43.        android:textColor="#FFF"
  44.        android:background="#ed8404"
  45.        android:textOff="READ DATA FROM DEVICE"
  46.        android:textOn="WRITE DATA TO DEVICE" />
  47.     <Space
  48.        android:layout_width="match_parent"
  49.        android:layout_height="10dp"
  50.        android:id="@+id/space3" />
  51.     <TableLayout
  52.        android:minWidth="25px"
  53.        android:minHeight="25px"
  54.        android:layout_width="fill_parent"
  55.        android:layout_height="wrap_content"
  56.        android:stretchColumns="*"
  57.        android:id="@+id/tableLayout1">
  58.         <TableRow
  59.            android:id="@+id/tableRow1"
  60.            android:layout_width="0dp"
  61.            android:layout_height="0dp"
  62.            android:orientation="vertical"
  63.            android:layout_marginTop="2dp"
  64.            android:weightSum="2"
  65.            android:layout_marginRight="2dp">
  66.             <CheckBox
  67.                android:layout_width="0dp"
  68.                android:layout_height="40dp"
  69.                android:layout_weight="0"
  70.                android:gravity="center"
  71.                android:padding="5dp"
  72.                android:text="Relay 1"
  73.                android:textColor="#FFF"
  74.                android:background="#ed8404"
  75.                android:layout_column="0"
  76.                android:id="@+id/checkBox1"
  77.                android:textAlignment="center" />
  78.             <EditText
  79.                android:layout_width="0dp"
  80.                android:layout_height="40dp"
  81.                android:layout_weight="0"
  82.                android:gravity="center"
  83.                android:padding="5dp"
  84.                android:text="12:00"
  85.                android:textColor="#FFF"
  86.                android:background="#ed8404"
  87.                android:layout_column="1"
  88.                android:id="@+id/editText1"
  89.                android:layout_marginLeft="2dp"
  90.                android:layout_marginRight="2dp"
  91.                android:textAlignment="center"
  92.                android:layout_marginTop="1.5dp" />
  93.             <EditText
  94.                android:layout_width="0dp"
  95.                android:layout_height="40dp"
  96.                android:layout_marginTop="1.5dp"
  97.                android:layout_weight="0"
  98.                android:gravity="center"
  99.                android:padding="5dp"
  100.                android:text="13:00"
  101.                android:textColor="#FFF"
  102.                android:background="#ed8404"
  103.                android:layout_column="2"
  104.                android:id="@+id/editText2"
  105.                android:textAlignment="center" />
  106.         </TableRow>
  107.         <TableRow
  108.            android:id="@+id/tableRow2"
  109.            android:layout_width="0dp"
  110.            android:layout_height="0dp"
  111.            android:orientation="vertical"
  112.            android:layout_marginTop="2dp"
  113.            android:weightSum="2">
  114.             <CheckBox
  115.                android:layout_width="0dp"
  116.                android:layout_height="40dp"
  117.                android:layout_weight="0"
  118.                android:gravity="center"
  119.                android:padding="5dp"
  120.                android:text="Relay 2"
  121.                android:textColor="#FFF"
  122.                android:background="#ed8404"
  123.                android:layout_column="0"
  124.                android:id="@+id/checkBox2" />
  125.             <EditText
  126.                android:layout_width="0dp"
  127.                android:layout_height="40dp"
  128.                android:layout_marginTop="1.5dp"
  129.                android:layout_weight="0"
  130.                android:gravity="center"
  131.                android:padding="5dp"
  132.                android:text="12:00"
  133.                android:textColor="#FFF"
  134.                android:background="#ed8404"
  135.                android:layout_column="1"
  136.                android:id="@+id/editText3"
  137.                android:layout_marginLeft="2dp"
  138.                android:layout_marginRight="2dp" />
  139.             <EditText
  140.                android:layout_width="0dp"
  141.                android:layout_height="40dp"
  142.                android:layout_marginTop="1.5dp"
  143.                android:layout_weight="0"
  144.                android:gravity="center"
  145.                android:padding="5dp"
  146.                android:text="12:00"
  147.                android:textColor="#FFF"
  148.                android:background="#ed8404"
  149.                android:layout_column="2"
  150.                android:id="@+id/editText4" />
  151.         </TableRow>
  152.         <TableRow
  153.            android:id="@+id/tableRow3"
  154.            android:layout_width="0dp"
  155.            android:layout_height="0dp"
  156.            android:orientation="vertical"
  157.            android:layout_marginTop="2dp"
  158.            android:weightSum="2">
  159.             <CheckBox
  160.                android:layout_width="0dp"
  161.                android:layout_height="40dp"
  162.                android:layout_weight="0"
  163.                android:gravity="center"
  164.                android:padding="5dp"
  165.                android:text="Relay 3"
  166.                android:textColor="#FFF"
  167.                android:background="#ed8404"
  168.                android:layout_column="0"
  169.                android:id="@+id/checkBox3" />
  170.             <EditText
  171.                android:layout_width="0dp"
  172.                android:layout_height="40dp"
  173.                android:layout_marginTop="1.5dp"
  174.                android:layout_weight="0"
  175.                android:gravity="center"
  176.                android:padding="5dp"
  177.                android:text="12:00"
  178.                android:textColor="#FFF"
  179.                android:background="#ed8404"
  180.                android:layout_column="1"
  181.                android:id="@+id/editText5"
  182.                android:layout_marginLeft="2dp"
  183.                android:layout_marginRight="2dp" />
  184.             <EditText
  185.                android:layout_width="0dp"
  186.                android:layout_height="40dp"
  187.                android:layout_marginTop="1.5dp"
  188.                android:layout_weight="0"
  189.                android:gravity="center"
  190.                android:padding="5dp"
  191.                android:text="12:00"
  192.                android:textColor="#FFF"
  193.                android:background="#ed8404"
  194.                android:layout_column="2"
  195.                android:id="@+id/editText6" />
  196.         </TableRow>
  197.         <TableRow
  198.            android:id="@+id/tableRow4"
  199.            android:layout_width="0dp"
  200.            android:layout_height="0dp"
  201.            android:orientation="vertical"
  202.            android:layout_marginTop="2dp"
  203.            android:weightSum="2">
  204.             <CheckBox
  205.                android:layout_width="0dp"
  206.                android:layout_height="40dp"
  207.                android:layout_weight="0"
  208.                android:gravity="center"
  209.                android:padding="5dp"
  210.                android:text="Relay 4"
  211.                android:textColor="#FFF"
  212.                android:background="#ed8404"
  213.                android:layout_column="0"
  214.                android:id="@+id/checkBox4" />
  215.             <EditText
  216.                android:layout_width="0dp"
  217.                android:layout_height="40dp"
  218.                android:layout_marginTop="1.5dp"
  219.                android:layout_weight="0"
  220.                android:gravity="center"
  221.                android:padding="5dp"
  222.                android:text="12:00"
  223.                android:textColor="#FFF"
  224.                android:background="#ed8404"
  225.                android:layout_column="1"
  226.                android:id="@+id/editText7"
  227.                android:layout_marginLeft="2dp"
  228.                android:layout_marginRight="2dp" />
  229.             <EditText
  230.                android:layout_width="0dp"
  231.                android:layout_height="40dp"
  232.                android:layout_marginTop="1.5dp"
  233.                android:layout_weight="0"
  234.                android:gravity="center"
  235.                android:padding="5dp"
  236.                android:text="12:00"
  237.                android:textColor="#FFF"
  238.                android:background="#ed8404"
  239.                android:layout_column="2"
  240.                android:id="@+id/editText8" />
  241.         </TableRow>
  242.     </TableLayout>
  243.     <LinearLayout
  244.        android:orientation="vertical"
  245.        android:layout_width="match_parent"
  246.        android:layout_height="wrap_content"
  247.        android:id="@+id/linearLayout1">
  248.         <Space
  249.            android:layout_width="match_parent"
  250.            android:layout_height="2dp"
  251.            android:id="@+id/space3" />
  252.         <Space
  253.            android:layout_width="match_parent"
  254.            android:layout_height="2dp"
  255.            android:id="@+id/space3" />
  256.         <ToggleButton
  257.            android:text="Relay1 On"
  258.            android:id="@+id/toggleButton3"
  259.            android:layout_marginLeft="50dp"
  260.            android:layout_marginRight="50dp"
  261.            android:layout_width="match_parent"
  262.            android:layout_height="wrap_content"
  263.            android:textColor="#FFF"
  264.            android:background="#ed8404"
  265.            android:textOff="RELAY1 ON"
  266.            android:textOn="RELAY1 OFF" />
  267.         <Space
  268.            android:layout_width="match_parent"
  269.            android:layout_height="2dp"
  270.            android:id="@+id/space3" />
  271.         <ToggleButton
  272.            android:text="Relay2 On"
  273.            android:id="@+id/toggleButton4"
  274.            android:layout_marginLeft="50dp"
  275.            android:layout_marginRight="50dp"
  276.            android:layout_width="match_parent"
  277.            android:layout_height="wrap_content"
  278.            android:textColor="#FFF"
  279.            android:background="#ed8404"
  280.            android:textOff="RELAY2 ON"
  281.            android:textOn="RELAY2 OFF" />
  282.         <Space
  283.            android:layout_width="match_parent"
  284.            android:layout_height="2dp"
  285.            android:id="@+id/space3" />
  286.         <ToggleButton
  287.            android:text="Relay3 On"
  288.            android:id="@+id/toggleButton5"
  289.            android:layout_marginLeft="50dp"
  290.            android:layout_marginRight="50dp"
  291.            android:layout_width="match_parent"
  292.            android:layout_height="wrap_content"
  293.            android:textColor="#FFF"
  294.            android:background="#ed8404"
  295.            android:textOff="RELAY3 ON"
  296.            android:textOn="RELAY3 OFF" />
  297.         <Space
  298.            android:layout_width="match_parent"
  299.            android:layout_height="2dp"
  300.            android:id="@+id/space3" />
  301.         <ToggleButton
  302.            android:text="Relay4 On"
  303.            android:id="@+id/toggleButton6"
  304.            android:layout_marginLeft="50dp"
  305.            android:layout_marginRight="50dp"
  306.            android:layout_width="match_parent"
  307.            android:layout_height="wrap_content"
  308.            android:textColor="#FFF"
  309.            android:background="#ed8404"
  310.            android:textOff="RELAY4 ON"
  311.            android:textOn="RELAY4 OFF" />
  312.     </LinearLayout>
  313. </LinearLayout>

Cała aplikacja składa się z dwóch sekcji. Jedna pozwala na ustawienie czasu załączania na poszczególnych przekaźnikach. Druga natomiast pozwala na włączenie lub wyłączenie określonego wyprowadzenia. W celu sprawdzenia aktualnych ustawień należy uruchomić przycisk odczytu informacji. Wyświetli on w polach na ekranie aktualne stany wszystkich ustawień.

Proste włączanie oraz wyłączanie odbywa się przez kliknięcie na przycisk. Natomiast ustawienie czasu jest wykonywane dla przekaźnika którego checkbox zostanie zaznaczony. Pod przyciskiem Connect znajduje się przycisk Read Data From Device. 

W celu stworzenia całej aplikacji należy przekopiować pliki MainActivity oraz Main.axml.


Program Xamarin:



W tej części posta przedstawię program na Androida odpowiedzialny za ustawianie odpowiednich przekaźników oraz wysyłania czasu jego załączenia do urządzenia.

Funkcja uruchamiana w głównej pętli która odpowiada za nawiązanie połączeniu Bluetooth:

  1. /* Bluetooth veryfication, check if it enable */
  2. private void connectToBtDevice()
  3. {
  4.     /* Write bluetooth device with what there will be communication */
  5.     mBluetoothAdapter = BluetoothAdapter.DefaultAdapter;
  6.     //Check if device is enabled
  7.     if (!mBluetoothAdapter.Enable())
  8.     {
  9.         Toast.MakeText(this"OK: Bluetooth Disconnected", ToastLength.Short).Show();
  10.     }
  11.     else
  12.     {
  13.         Toast.MakeText(this"OK: Bluetooth Connected", ToastLength.Short).Show();
  14.     }
  15.     //check if data was write into device
  16.     if (mBluetoothAdapter == null)
  17.     {
  18.         Toast.MakeText(this,
  19.                     "ERROR: Bluetooth don't exist or it is busy", ToastLength.Short)
  20.                     .Show();
  21.     }
  22. }

Najpierw tworzony jest nowy adapter Bluetooth. Dalej sprawdzane jest czy układ jest podłączony. W przypadku błędu zwracana jest informacja informująca o problemach z połączeniem.

Aby utworzyć połączenie aplikacji z układem należy kliknąć przycisk Connect, który wywoła następującą funkcje:

  1. /* Event for change btn for establish/disable connection */
  2. void TgConnect_HandleCheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e)
  3. {
  4.     if (e.IsChecked)
  5.     {
  6.         /* After btn connect inicialize is connect method */
  7.         try
  8.         {
  9.             connectBlt();
  10.             Toast.MakeText(this"Device Connected", ToastLength.Short).Show();
  11.         }
  12.         catch (System.Exception ex)
  13.         {
  14.             Toast.MakeText(this"Connect Error: " + ex, ToastLength.Short).Show();
  15.         }
  16.      }
  17.      else
  18.      {
  19.         /* After disable btn clicked then disconnect */
  20.         if (btSocket.IsConnected)
  21.         {
  22.             try
  23.             {
  24.                 btSocket.Close();
  25.                 Toast.MakeText(this"Connection Closed", ToastLength.Short).Show();
  26.             }
  27.             catch (System.Exception ex)
  28.             {
  29.                 Toast.MakeText(this"Close Error" + ex, ToastLength.Short).Show();
  30.             }
  31.         }
  32.     }
  33. }

Najpierw sprawdzany jest czy przycisk jest kliknięty to wywoływana jest funkcja wykonująca połączenie. Gdy przycisk zostanie ponownie kliknięty to połączenie z układem Bluetooth zostanie przerwane.

Główna funkcja wykonująca połączenie wygląda następująco:

  1. public void connectBlt()
  2. {
  3.     BluetoothDevice device = mBluetoothAdapter.GetRemoteDevice(BLT_ADDRESS);    /* Start connection with HC-05 */
  4.     /* Tell adapter that he is not visible */
  5.     mBluetoothAdapter.CancelDiscovery();
  6.     try
  7.     {
  8.        /* Initialize communication socket with device */
  9.        btSocket = device.CreateRfcommSocketToServiceRecord(UUID_VAR);
  10.        /* Socket Connect */
  11.        btSocket.Connect();
  12.     }
  13.     catch (System.Exception e)
  14.     {
  15.         /* When error occure then close socket */
  16.         try
  17.         {
  18.             btSocket.Close();
  19.         }
  20.         catch (System.Exception) { }
  21.     }
  22.     /* After establish connection with blt device we will call method that generate thread
  23.        for receiving information */
  24.     BeginListenForData();
  25. }

Najpierw rozpoczyna się wykonanie połączenia z układem o podanym adresie. Następnie wyłączamy widoczność urządzenia w sieci. Następnie wykonywane jest połączenie z podanym numerm UUID.
W przypadku błędu następuje próba zamknięcia połączenia.

Cztery przyciski odpowiadają za osobne włączenia oraz wyłączanie przekaźników. Po kliknięciu wysyłana jest odpowiednia ramka danych do układu. Przykładowy kod do uruchomienia przekaźnika pierwszego:

  1. void Relay1OnOffBtn_HandleCheckedChange(object sender, CompoundButton.CheckedChangeEventArgs e)
  2. {
  3.     if(relay1OnOffBtn.Text == "RELAY1 ON")
  4.     {
  5.         dataToSend = new Java.Lang.String("b" + "R1On" + "xx" + "\n");
  6.         WriteData(dataToSend);
  7.     }
  8.     else if(relay1OnOffBtn.Text == "RELAY1 OFF")
  9.     {
  10.         dataToSend = new Java.Lang.String("b" + "R1Off" + "xx" + "\n");
  11.         WriteData(dataToSend);
  12.     }
  13. }

Powyżej w zależności od aktualnego tekstu na ekranie wysyłana jest komenda uruchamiająca bądź wyłączająca przekaźnik.

W celu przesłania danych do układu Bluetooth wykorzystuje się następującą funkcje:

  1. /* Method use for sending data to BLT device */
  2. private void WriteData(Java.Lang.String data)
  3. {
  4.     /* Extract output stream */
  5.     try
  6.     {
  7.        outStream = btSocket.OutputStream;
  8.     }
  9.     catch (System.Exception e)
  10.     {
  11.        Toast.MakeText(this"Sending error: " + e.Message, ToastLength.Short).Show();
  12.     }
  13.     /* Create string to send */
  14.     Java.Lang.String message = data;
  15.     /* Convert it into bytes */
  16.     byte[] msgBuffer = message.GetBytes();
  17.     try
  18.     {
  19.        /* write the arrangement that we just generated in the buffer */
  20.        outStream.Write(msgBuffer, 0, msgBuffer.Length);
  21.     }
  22.     catch (System.Exception e)
  23.     {
  24.        Toast.MakeText(this"Sending error" + e.Message, ToastLength.Short).Show();
  25.     }
  26. }

Funkcja powyżej otwiera strumień. W przypadku błędu zwracana jest kod błędu. Następnie wiadomość do przesłania zapisywana jest do zmiennej  message, która jest w formacie Java.Lang.String. Dalej wartość jest konwertowana na tablice bajtów, która zostaje wysłana do modułu HC-05.


Program Arduino:



Do odbierania danych wykorzystywany będzie układ HC-05 podłączony do Arduino Uno. Tutaj obsługiwana jest przesyłana przez układ ramka danych.

W celu komunikacji z układem wykorzystuje jeden systemowy UART oraz jeden sprzętowy, który służy do wyświetlenia odebranych danych. Dodatkowo do zapisu stanu wykorzystywany jest wbudowany EEPROM.

Dane w pamięci zapisane są w następujących miejscach.

  1. #define EEPROM_R1_STATE     0
  2. #define EEPROM_R2_STATE     1
  3. #define EEPROM_R3_STATE     2
  4. #define EEPROM_R4_STATE     3
  5. #define EEPROM_R1_TIME_STATE   4
  6. #define EEPROM_R1_TIME_ON_H    5
  7. #define EEPROM_R1_TIME_ON_M    6
  8. #define EEPROM_R1_TIME_OFF_H   7
  9. #define EEPROM_R1_TIME_OFF_M   8
  10. #define EEPROM_R2_TIME_STATE   9
  11. #define EEPROM_R2_TIME_ON_H    10
  12. #define EEPROM_R2_TIME_ON_M    11
  13. #define EEPROM_R2_TIME_OFF_H   12
  14. #define EEPROM_R2_TIME_OFF_M   13
  15. #define EEPROM_R3_TIME_STATE   14
  16. #define EEPROM_R3_TIME_ON_H    15
  17. #define EEPROM_R3_TIME_ON_M    16
  18. #define EEPROM_R3_TIME_OFF_H   17
  19. #define EEPROM_R3_TIME_OFF_M   18
  20. #define EEPROM_R4_TIME_STATE   19
  21. #define EEPROM_R4_TIME_ON_H    20
  22. #define EEPROM_R4_TIME_ON_M    21
  23. #define EEPROM_R4_TIME_OFF_H   22
  24. #define EEPROM_R4_TIME_OFF_M   23
  25. #define EEPROM_LAST_WRITED_ADDR 23

Konfiguracja pinów wygląda następująco:

  1. #define SOFT_UART_RX 11
  2. #define SOFT_UART_TX 10
  3. #define RELAY1_PIN 2
  4. #define RELAY2_PIN 3
  5. #define RELAY3_PIN 4
  6. #define RELAY4_PIN 5

Funkcja odpowiedzialna za uruchomienie poszczególnych komponentów:

  1. void setup()
  2. {
  3.   Serial.begin(9600);
  4.   I2CBT.begin(9600);
  5.   Serial.println("Uart Enabled");
  6.   pinMode(RELAY1_PIN, OUTPUT);
  7.   pinMode(RELAY2_PIN, OUTPUT);
  8.   pinMode(RELAY3_PIN, OUTPUT);
  9.   pinMode(RELAY4_PIN, OUTPUT);
  10.   digitalWrite(RELAY1_PIN, OFF_STATE);
  11.   digitalWrite(RELAY2_PIN, OFF_STATE);
  12.   digitalWrite(RELAY3_PIN, OFF_STATE);
  13.   digitalWrite(RELAY4_PIN, OFF_STATE);
  14.   Serial.println("Pinout Setted");
  15.   setRelayStateBaseOnEEPROMData();
  16. }

Powyżej ustawiane są przekaźniki oraz uruchamiane dwa UARTy. Jeden do komunikacji z układem Bluetooth, drugi natomiast do wyświetlania danych na ekranie.

Pętla:

  1. void loop()
  2. {
  3.   char recSingleData;
  4.   int insize = 0;
  5.   char character;
  6.   if ((insize=(I2CBT.available())) > 0)
  7.   {
  8.      Serial.write(recSingleData=char(I2CBT.read()));
  9.      recData[dataLoop] = recSingleData;
  10.      dataLoop++;
  11.   }
  12.   if(checkReceiveData(recSingleData))
  13.   {
  14.     dataLoop = 0;
  15.     setDataByBuffer(recData);
  16.   }
  17. }

W pętli głównej następuje odbieranie danych od UART'a oraz sprawdzanie otrzymanej ramki.

Pozostałe funkcje będą znajdować się plikach od projektu.

Cały projekt można pobrać z dysku Google pod tym linkiem w folderze Csharp - Xamarin.