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