Предыстория
Доброго времени суток.
В один прекрасный летний вечер, в поисках чего-нибудь давно забытого, но увлекательного, я перебирал вещи в ящиках. Заглянув в последний, уже слегка отчаявшись, я все-таки нашел интересную вещь. Это был китайский спидометр для велосипеда. Конечно, микрокомпьютера там не было — что не удивительно (в детстве, благодаря моему любопытству, большинство разобранных мною вещей не были собраны и просто напросто были выброшены). Но это не единственная была проблема — у меня нету велосипеда. Его забрал старший брат, а сам я — катаюсь на скейте. Так и возникла идея, чем себя занять!
Под катом много фотографий.
Компоненты
Скейт
Поскольку не выполняю никаких трюков на скейте, я катаюсь на 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:
Был выбран просто потому что был под рукой. Будет считать кол-во оборотов колеса.
Bluetooth HC-06
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/
Добавить комментарий