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

Предыстория


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

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

Компоненты


Скейт

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

Фото cruiserboard'а


Умные часы

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

Фото умных часов


Это умные часы компании SmartQ, называются Z-Watch. Делятся на старшую (Z1) и младшую (Z1-Lite) модель. Разница в том, что во младшей модели нету: модуля Wi-fi, флэш-памяти eMMC 512Mb (в старшей 4Gb), оперативной памяти 256Mb (в старшей 512Mb). Оснащены часы экраном 1.54-inch TFT LCD с разрешением 240x240 пикселей, процессором 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В, емкостью 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 за статью!
Видео работы будет немного позже.
Поделиться публикацией

Похожие публикации

Комментарии 66

    +55
    boolean yes = false;

    Ох.
      +5
      #define true false // а теперь поищите, редиски :)
        +6
        #define TRUE (random()>0.5)
      +11
      С таким диаметром колеса и местом расположения магнита погрешность измерений должна просто зашкаливать.
        0
        да, мне кажется зеркало + излучатель/приемник было бу лучше и точнее. и монитровать проще
          +4
          Да тут скорее проще купить дешёвый телефон, чтобы не жалко было в случае чего, завязать с часами и воспользоваться GPS для измерения скорости. За одно можно ещё и трек записывать где катался и на каком участке какая была скорость.
          • НЛО прилетело и опубликовало эту надпись здесь
              0
              Если мне не изменяет память, то у iPhone есть болезнь привязывать трек к собственной карте. Собственно вот пост по этой теме — shtosm.ru/all/s-ayfonom-gulyat-karty-ne-uvazhat/
              • НЛО прилетело и опубликовало эту надпись здесь
                  0
                  Кстати, я дешёвым андроид я тоже погорячился. У многих точность трека оставляет желать лучшего. Я себе для записей треков брал Samsung Galaxy S3.
                    0
                    У меня на Samsung sgs 4 mini в проге Sports Tracker через GPS скорость получается очень ступенчатая, так ехать невозможно, похоже, что GPS работает с большими интервалами и скорость постоянно усредняется.
                      0
                      В трекерах настраивается частота обновления координат. Обычно там выставлено 3 секунды. За это время можно много что сделать.
                  0
                  так вот почему он жрет GPRS-трафик, оправдываясь «Mapping services», стоит только включить мобильный интернет в какой-нибудь поездке. Хотя, наверное, он еще тащит кусочек базы локаций BSSID и Cell ID, но вряд ли это ~500 кб.
                  +1
                  Это совершенно не значит что на других телефонах тоже все плохо.
                  У меня сейчас moto razr maxx hd — идеально строит трек и аккума хватает надолго.
                +1
                И после проезда по луже/пыли показания прекратятся…
                Магнит — грязе/пыле/водо устойчивое решение.
                  0
                  возможно, но:
                  Можно крепить на ось, с нее грязь/вода отлично отбрасывается центробежной силой.
                    0
                    В идиеале — Да.
                    Вы это скажите моим хромированным дискам (про центробежную силу и т.д.)
                      0
                      У вас скейт на хромированных дисках???? Мы говорим о скейте, чистить который гораздо легче, чем автомобильные диски. Размеры несколько разные.
                        +3
                        Я вообще-то про хромированные зеркальные поверхности и очистку их от грязи центробежной силой имел ввиду…
                        А их я побыстрее чем скейт раскручиваю в разы (не смотря на радиус) :) и чего-то не блестят а то и вообще серые…

                        Вы каждый раз будете останавливаться и протирать колесико а точнее место крепление зеркального элемента тряпочкой???????
                        И еще и приемное и излучающее устройство??????

                        Магнит и геркон/датчик Холла (выбирайте что больше нравится) — наиправильнейшее решение на таком месте как колесо скейтборда, эксплуатирующегося на улице, в отличии от оптических систем.

                        И это проще нежели оптическая система со считыванием отражения. (пусть даже не обязательно зеркальной, а просто черной полоски на колесе которая может и будет загрязнятся, как и сама оптическая система)
                          0
                          На счет оптической системы подсчета оборотов колеса. У меня есть такой датчик, вместо магнита нужно просто сделать какой-нибудь
                          бугорок, например, в крутить болтик. Датчик будет реагировать на разницу: расстояния между колесом и датчиком и, соответственно, расстояния от датчика до болтика.
                          image
                            0
                            бугорок необязателен, достаточно наклеить что-нибудь белое или отражающее
                              +8
                              Да вы меня никто не слышите чтоли? или просто троллите? :)))))

                              Ключевое слово — "загрязнение" потому как «колесо» и ездит по "земле" — "лужам" — "пыли"

                              Из этого следует — оптическая система в данном, конкретном случае менее надежна.

                              P.S. а ваш датчик вероятнее всего еще нужно защитить от внешней засветки (от тех же. например, солнечных лучей)
                              Судя по цвету светодиода он в ИК диапазоне работает.
                                +3
                                Колесо не ездит по лужам. Если колесо ездит по лужам, то скейтер потом долго матерится пока вытряхивает песок из подшипников.
                                  +1
                                  Не ездит в сферическом городе в вакууме, ИРЛ по каким только говнам колёса не ездят. Хотя я роллер, может скейтеры не такие «грязнули» :)
                                  • НЛО прилетело и опубликовало эту надпись здесь
                                      0
                                      Покажите этого счастливчика который на велосипеде прокатился по ядерному реактору… на ВВЭР это невозможно принципиально, на РБМК размерчик и фактура поверхности верхней крышки позволяет. Но таких реакторов в мире мало осталось, надо бы не упустить шанс…
                  0
                  к диаметру колеса претензия понятна, но это данность.

                  А как расположение влияет?
                  • НЛО прилетело и опубликовало эту надпись здесь
                      +1
                      Ну он там с приличной частотой вертеться должен. Микроконтроллер может начать обороты пропускать.
                      • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          Что правда — то правда :)
                            +10
                            > на скорости 30 км/с

                            ракетный скейт однако!
                              +1
                              На лонгборде на холмах вполне нормальная скорость. На плоскости тоже можно выжать, но очень недолго.
                                +7
                                Если грубо, то это на два порядка выше скорости звука. Трением, о воздух не обжигает? :-)
                                  0
                                  Ой, не углядел секунды. Ну что ж, я ж говорю — очень недолго :)

                                  Вообще, конечно, интересно, что произошло бы при разгоне до такой скорости. Обжигать, кстати, будет больше не из-за трения, а из-за сжатия воздуха впереди. Надо бы написать Рэндаллу.

                                  Очевидно, падение произойдёт сразу, а после этого человек либо закувыркается по асфальту, либо просто будет скользить по нему, стираясь (на такой скорости сложно предположить). Но вот интересно, что будет в тот краткий промежуток времени до падения.
                                  • НЛО прилетело и опубликовало эту надпись здесь
                                      +2
                                      Вообще говоря, при такой скорости (ни много ни мало 87.5 Махов при температуре в 20°C) человек должен просто испариться от трения о воздух.

                                      Но если предположить, что этого не произойдет, то с первой же кочки он вместе со своим чудо-скейтбордом выйдет на гелиоцентрическую орбиту (а то и вовсе покинет солнечную систему, если будет покидать планету в направлении ее движения), поскольку эта скорость почти в три раза выше второй космической.
                                        0
                                        Логично, да. Что-то я продолжил мыслить с точки зрения «приземлённого» опыта, который тут совершенно не подходит.
                                          0
                                          только лишь за треть секунды он покинет плотные слои атмосферы…
                                  0
                                  А не лучше ли датчик магнитного поля вместо геркона?
                                    0
                                    Лучше. Как показала практика он дает дребезг на больших оборотах. В следующей версии будет с датчиком Холла.
                                      0
                                      Лучше комбинированный.
                                      Геркону не нужно питание, а с дребезгом можно успешно бороться программно.
                                      А когда скорость превысит предельную частоту для геркона — можно так же программно подать питание на датчик Холла и дальше считывать уже с него.
                                        0
                                        Усложнять систему из-за мизерного дополнительного потребления? контроллер один только 10-20мА хавать будет, а батарейки хватит на несколько суток непрерывной работы… куда уж там энергию экономить?
                                    0
                                    выберете частоту вращения и полагаете что геркон при этом будет под магнитным полем по меньшей мере половину оборота, но магнитик маленький… хоть частота небольшая, но время пролета магнитом над герконом очень мало. Герконы конечно быстрее кнопок, но при таких размерах магнита на этой частоте вращения время срабатывания геркона(1-10мс) может оказаться больше времени пролета магнита над ним. Поэтому в таких случаях все же предпочтительней использовать датчик холла, есть и такие что имеют сразу логический выход.
                                  0
                                  Меня тоже смутило… Но именно в таком положении перпендикулярно магниту геркон срабатывал как надо.
                                    +1
                                    что-то подсказывает, что все зависит от направления полюсов магнита
                                  0
                                  Для большей точности можно измерить расстояние, пройденное за 10 или 100 полных оборотов колеса — на сколько хватит терпения считать, чем больше тем лучше, а потом разделить на количество оборотов. Или не делить, а так и считать обороты десятками или сотнями — смотря какая точность нужна. Думаю, в метре будет как раз около 10 оборотов.
                                  И по мере изнашивания колеса проводить перекалибровку.
                                  –2
                                  Что-то я не могу понять — на отдельной фотке у вас Arduino Pro Mini без ног, а в проектировочной плате у него и снизу ноги, и сбоку.
                                    0
                                    Прошу прощения. Просто у меня старый цифровой фотоаппарат. Хорошие снимки сделать было трудно, по этому фотографии частично были взяты из интернета.
                                      0
                                      сбоку для USB-UART конвертера, для «заливки» скетчей
                                      • НЛО прилетело и опубликовало эту надпись здесь
                                          0
                                          Ну можно их напаять, просто из-за того, что фотки ардуино не совпадают с фоткой конечного продукта, я не мог понять, где там что.
                                        +1
                                        Я для отслеживания катания раньше пользовался Endomondo, сейчас — Polar Beat. Вполне подходят для данных целей, рекомендую.
                                          +2
                                          Напряжение на аккумуляторе не 4.11В, а является функцией от температуры и степени заряда. Я бы на вашем месте добавил бы супервизор питания, чтобы не допустить переразряда аккумулятора (они от этого быстро портятся), а также какой-нибудь стабилизатор напряжения. Есть микросхемы, которые все это совмещают, например, DC-DC преобразователь с компаратором для контроля разряда батареи — MAX1674
                                            +1
                                            Спасибо за замечание. Приму к сведению :)
                                            +3
                                            оптический датчик от мыши с объективом и подсветкой. будет движение асфальта под скейтом отслеживать. интерфейс простой. ну или gps
                                              0
                                              Оптический датчик от мышки будет возвращать вам массив 64 х 64 х 8 бит, в которых вам самостоятельно придётся отслеживать движение. Магнитный датчик, это просто, надёжно и дешёво.
                                                0
                                                Насчёт надёжности — не всегда так.
                                                Герконы в датчиках от велокомпа — стеклянные.
                                                В велосипедном случае он крепится с внутренней стороны вилки, потому достаточно хорошо защищен. А вот при ином положении, когда он доступен для физического воздействия (геркон «на ножке» для датчика заднего колеса — может попасть в спицы. Или как у автора — просто выступает и можно им на что-нибудь наехать) разбить его достаточно просто.
                                                  +1
                                                  А потому я и написал «магнитный датчик», потому что лучшим датчиком будет датчик холла, который не имеет механических частей и способен работать на значительно более высоких частотах, при гораздо меньшем размере и копеечной цене.
                                                    0
                                                    Мелкий геркон сам по себе выдерживает приличные ускорения, а в велодатчике он упакован в пластиковый корпус с герметиком, скорей скейт пополам разломаешь нежели геркон угробишь.
                                                    0
                                                    функция возвращения массива точек доступна не для всех датчиков, но все умеют возвращать дельту X и дельту Y
                                                      0
                                                      например ADNS-2610
                                                        0
                                                        Эм… Я как-то ковырялся с контроллерами мышей, никто не мешает тупо получать смещение по координате, без графических обработок.
                                                          0
                                                          так и я о том же. дельту все умеют давать. а картинку не все
                                                      +2
                                                      image
                                                      Не останавливайтесь, осталось ещё пару штук запилить.
                                                      • НЛО прилетело и опубликовало эту надпись здесь

                                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                      Самое читаемое