Спидометр для скейта. Безысходность

от автора

Предыстория

Доброго времени суток.
В один прекрасный летний вечер, в поисках чего-нибудь давно забытого, но увлекательного, я перебирал вещи в ящиках. Заглянув в последний, уже слегка отчаявшись, я все-таки нашел интересную вещь. Это был китайский спидометр для велосипеда. Конечно, микрокомпьютера там не было — что не удивительно (в детстве, благодаря моему любопытству, большинство разобранных мною вещей не были собраны и просто напросто были выброшены). Но это не единственная была проблема — у меня нету велосипеда. Его забрал старший брат, а сам я — катаюсь на скейте. Так и возникла идея, чем себя занять!

Под катом много фотографий.

Компоненты

Скейт

Поскольку не выполняю никаких трюков на скейте, я катаюсь на cruiserboard’е в свое удовольствие и бывает интересно: «сколько я проехал?».

Фото cruiserboard'а

Умные часы

На Новый год приобрел себе у китайцев вот такие часики:

Фото часов

Это умные часы компании SmartQ, называются Z-Watch. Делятся на старшую (Z1) и младшую (Z1-Lite) модель. Разница в том, что во младшей модели нету: модуля Wi-fi, флэш-памяти eMMC 512Mb (в старшей 4Gb), оперативной памяти 256Mb (в старшей 512Mb). Оснащены часы экраном 1.54-inch TFT LCD с разрешением 240×240 пикселей, процессором Ingenic JZ4775 с частотой 1.0Ghz, Bluetooth 4.0 BLE, Wi-fi модулем IEEE 802.11 b/g/n, акселерометром, водонепроницаемые IP-X7 (3 АТМ), батарея Li-poly на 300мАч, операционная система OS Android 4.4 KitKat (упрощенная).

Arduino Mini Pro

Микроконтроллер Arduino Pro Mini:

Фото Arduino Pro Mini

Был выбран просто потому что был под рукой. Будет считать кол-во оборотов колеса.

Bluetooth HC-06

Bluetooth модуль с помощью которого мы будет связываться с нашими часами:

Фото Bluetooth модуля

Датчик с герконом

Датчик с китайского спидометра для велосипеда:

Фото датчика

Аккумулятор

Аккумулятор Samsung напряжением 3.7В (в действительности 4.11В), емкостью 1000мАч:

Фото аккумулятора

Сборка

Схема нашего устройства:

Схема спидометра

Думаю, в комментариях не нуждается. Конечно светодиод стоило подключить через транзистор — тогда света было бы больше, но это не столь важно. Прототип я собрал на макетной плате. Она имеет снизу кусок двухстороннего скотча, на который я прицепил аккумулятор. Обрезать крепление датчика к рулю велосипеда я не стал, так как в дальнейшем может обзаведусь, соответственно, велосипедом 🙂

Фото прототипа


Датчик с герконом я закрепил на подвеске при помощи жгутиков:

Фото нижней части скейта

Светодиод снизу был прикреплен для проверки передачи данных от часов к микроконтроллеру. Подойдет в качестве подсветки нижней части ночью.
Просверлил в колесе неглубокое отверстие и закрепил в нем неодимовый магнит (вытащил со старого дисковода):

Фото колеса с магнитом

Программная часть

В программировании не силен, но буду очень рад в советах по этому поводу!

Микроконтроллер

volatile long cntr; boolean flip; boolean yes = false; int rev = 0;  void setup() {   Serial.begin(9600);   pinMode(13, OUTPUT);   pinMode(12, INPUT);   TCCR2A = 0;    TCCR2B = 2;   TCNT2 = 59;   TIMSK2 |= (1 << TOIE2);   }  ISR(TIMER2_OVF_vect) {   TCNT2 = 59;//55;   cntr++;   if(cntr>9999)    {     flip = true;     cntr = 0;   } }  void loop()  {   if (flip)    {     Serial.println(String(rev)+';');     rev = 0;     flip = false;   }     else   {     if (digitalRead(12) == HIGH)      {       if (yes)       {         rev++;         yes = false;       }     } else yes = true;   }      if (Serial.available() > 0){     char command = Serial.read();     switch(command){       case '0': digitalWrite(13, LOW); break;       case '1': digitalWrite(13, HIGH); break;     }   } } 
Суть программы

Пока таймер тикает, в основном цикле подсчитывается кол-во оборотов и обрабатываются команды с часов, если таковые имеются. Через секунду контроллер отправляет кол-во оборотов на часы, т.е. мы имеем частоту (об/c).

Часы

Код был взят с этой статьи и немного дописан.

Код

AndroidManifest.xml

<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> 
FullscreenActivity.java

package com.example.admin.speedometer;  import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.UUID;  import com.example.admin.speedometer.R;  import android.os.Bundle; import android.os.Handler; import android.app.Activity; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; import android.bluetooth.*; import android.content.Intent;  public class FullscreenActivity extends Activity {     private static final int REQUEST_ENABLE_BT = 1;     final int ArduinoData = 1;     final String LOG_TAG = "myLogs";     private BluetoothAdapter btAdapter = null;     private BluetoothSocket btSocket = null;     private StringBuilder sb = new StringBuilder();     private static String MacAddress = "20:13:05:07:01:97"; // MAC-адрес БТ модуля     private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");     private ConnectedThred MyThred = null;     public TextView spdtext, distext, fromarduino;     public double Distance = 0;     Button b1, b2;     Handler h;      /*     Settings:      */     private static double Radius = 3.0; //радиус колеса в сантиметрах     private static double spdUnit = 3.6; //размерность скорости: 3.6 в км/ч, 1.0 в м/c      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_fullscreen);          btAdapter = BluetoothAdapter.getDefaultAdapter();         spdtext = (TextView) findViewById(R.id.textView1);         distext = (TextView) findViewById(R.id.textView2);         fromarduino = (TextView) findViewById(R.id.textView5);          if (btAdapter != null){             if (btAdapter.isEnabled()){                 //mytext.setText("Bluetooth включен. Все отлично.");             }else             {                 Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);                 startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);             }          }else         {             MyError("Fatal Error", "Bluetooth ОТСУТСТВУЕТ");         }         b1 = (Button) findViewById(R.id.button1);         b2 = (Button) findViewById(R.id.button2);          b1.setOnClickListener(new OnClickListener() {             public void onClick(View v) {                 MyThred.sendData("1");             }         });          b2.setOnClickListener(new OnClickListener() {             public void onClick(View v) {                 MyThred.sendData("0");             }         });          h = new Handler() {             public void handleMessage(android.os.Message msg) {                 switch (msg.what) {                     case ArduinoData:                         byte[] readBuf = (byte[]) msg.obj;                         String strIncom = new String(readBuf, 0, msg.arg1);                         sb.append(strIncom);                         int endOfLineIndex = sb.indexOf("\r\n");                         if (endOfLineIndex > 0) {                             String sbprint = sb.substring(0, endOfLineIndex);                             sb.delete(0, sb.length());                             String value = "";                             byte channel = 0; //используется если команд несколько: 0;0;0;                             fromarduino.setText("Arduino: " + sbprint);                             for (int i = 0; i < sbprint.length(); i++) {                                 if (sbprint.charAt(i) == ';')  {                                     if (!value.isEmpty()) {                                         switch (channel) {                                             case 0:                                                 double Dis = (Double.parseDouble(value) * (Radius * 6.28) )  / 100.0;                                                 double Speed = Dis * spdUnit;                                                 spdtext.setText(String.valueOf(Math.round(Speed)));                                                 Distance += Dis;                                                 distext.setText(String.valueOf(Math.round(Distance)));                                                 break;                                         }                                     }                                     value = "";                                     channel++;                                 } else value += sbprint.charAt(i);                             }                         }                         break;                 }             };         };      }      @Override     public void onResume() {         super.onResume();          BluetoothDevice device = btAdapter.getRemoteDevice(MacAddress);         Log.d(LOG_TAG, "***Получили удаленный Device***"+device.getName());          try {             btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);             Log.d(LOG_TAG, "...Создали сокет...");         } catch (IOException e) {             MyError("Fatal Error", "В onResume() Не могу создать сокет: " + e.getMessage() + ".");         }          btAdapter.cancelDiscovery();         Log.d(LOG_TAG, "***Отменили поиск других устройств***");          Log.d(LOG_TAG, "***Соединяемся...***");         try {             btSocket.connect();             Log.d(LOG_TAG, "***Соединение успешно установлено***");         } catch (IOException e) {             try {                 btSocket.close();             } catch (IOException e2) {                 MyError("Fatal Error", "В onResume() не могу закрыть сокет" + e2.getMessage() + ".");             }         }          MyThred = new ConnectedThred(btSocket);         MyThred.start();     }      @Override     public void onPause() {         super.onPause();          Log.d(LOG_TAG, "...In onPause()...");          if (MyThred.status_OutStrem() != null) {             MyThred.cancel();         }          try     {             btSocket.close();         } catch (IOException e2) {             MyError("Fatal Error", "В onPause() Не могу закрыть сокет" + e2.getMessage() + ".");         }     }      private void MyError(String title, String message){         Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();         finish();     }       //Отдельный поток для передачи данных     private class ConnectedThred extends Thread{         private final BluetoothSocket copyBtSocket;         private final OutputStream OutStrem;         private final InputStream InStrem;          public ConnectedThred(BluetoothSocket socket){             copyBtSocket = socket;             OutputStream tmpOut = null;             InputStream tmpIn = null;             try{                 tmpOut = socket.getOutputStream();                 tmpIn = socket.getInputStream();             } catch (IOException e){}              OutStrem = tmpOut;             InStrem = tmpIn;         }          public void run()         {             byte[] buffer = new byte[1024];             int bytes;              while(true){                 try{                     bytes = InStrem.read(buffer);                     h.obtainMessage(ArduinoData, bytes, -1, buffer).sendToTarget();                 }catch(IOException e){break;}              }          }          public void sendData(String message) {             byte[] msgBuffer = message.getBytes();             Log.d(LOG_TAG, "***Отправляем данные: " + message + "***"  );              try {                 OutStrem.write(msgBuffer);             } catch (IOException e) {}         }          public void cancel(){             try {                 copyBtSocket.close();             }catch(IOException e){}         }          public Object status_OutStrem(){             if (OutStrem == null){return null;             }else{return OutStrem;}         }     } } 

Суть программы

Программа принимает данные с микроконтроллера о частоте вращения колеса. Для того, чтобы получать корректную информацию, нужно измерить радиус колеса, тогда программа найдет его длину окружности и будет рассчитывать скорость и дистанцию. Нужно настроить радиус колеса и в каких единицах будет отображаться скорость:

/* Settings: */ private static double Radius = 3.0; //радиус колеса в сантиметрах private static double spdUnit = 3.6; //размерность скорости: 3.6 в км/ч, 1.0 в м/c 

N — кол-во оборотов;
l — длина окружности;
t — время (поскольку мы считаем раз в секунду — этим значением можно пренебречь);
l = 2пr — длина окружности;
S = V * t = (N * l) / 100 — расстояние которое мы проехали за 1 секунду (выражено в метрах);

double Dis = (Double.parseDouble(value) * (Radius * 6.28) )  / 100.0; 

V = S / t = S * 3.6 — скорость (выражена в км/ч).

double Speed = Dis * spdUnit; 

Так же имеются две кнопки вкл. и выкл. светодиода, который находится на нижней части скейта.

Вывод

Есть некоторые ошибки, но в целом результатом доволен. Спасибо sychidze за статью!
Видео работы будет немного позже.

ссылка на оригинал статьи http://habrahabr.ru/post/231461/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *