Велоспидометр. Версия 1.0

processort
Topic author, Рядовой
Рядовой
Avatar
processort
Topic author, Рядовой
Рядовой
Posts: 5
Joined: 23 Dec 2018
With us: 5 years 9 months

#1by processort » 18 Sep 2024, 10:33

Использованные компоненты:
1. Arduino Nano.
2. Дисплей TM1637.
3. Модуль заряда аккумуляторов TP4056.
4. DC-DC повышающий преобразователь MT3608.
5. Модуль датчика холла A3144.
6. Тактовые кнопки (2 шт.)
7. Выключатель клавишный.
8. Магнит неодимовый цилиндрической формы.

В качестве первоисточника использовался давний проект от AlexGyver: https://alexgyver.ru/speedometer/

Принципиальная схема устройства приведена на рисунке ниже.

Speedometer__scheme.jpg
Speedometer__scheme.jpg (67.54 KiB) Viewed 75 times


Индикация скорости производится на 7-сегментный дисплей TM1637 размером 0,54''. Сегменты большие – меня это совершенно устроило.
Модуль датчика Холла размещен на вилке переднего колеса изнутри при помощи пластиковых стяжек.
Напротив датчика на спицах колеса установлен держатель с неодимовым магнитом. Размещение магнита таково, чтобы в действие магнитного поля попадал датчик Холла.
Различные виды велоспидометра, смоделированного при помощи 3D, показана на рисунках.

Velospeedometer_de_face.jpg
Velospeedometer_de_face.jpg (86.64 KiB) Viewed 75 times


Velospeedometer_par_derriere_1.jpg
Velospeedometer_par_derriere_1.jpg (152.36 KiB) Viewed 75 times


Velospeedometer_par_derriere_2.jpg
Velospeedometer_par_derriere_2.jpg (80.04 KiB) Viewed 75 times


Velospeedometer_assemblage.jpg
Velospeedometer_assemblage.jpg (105.04 KiB) Viewed 75 times


Ниже реальные фотографии устройства.

Vl-Sp-01.jpg
Vl-Sp-01.jpg (145.39 KiB) Viewed 75 times


Vl-Sp-02.jpg
Vl-Sp-02.jpg (184.84 KiB) Viewed 75 times


Vl-Sp-03.jpg
Vl-Sp-03.jpg (194.32 KiB) Viewed 75 times


Vl-Sp-04.jpg
Vl-Sp-04.jpg (196.61 KiB) Viewed 75 times


Кратко по функционалу устройства.

Верхняя кнопка – кнопка режима «скорость / расстояние».
При включении велоспидометр находится в «состоянии измерения скорости».
Формат показа: километры и сотни метров.
При кратковременном нажатии на верхнюю кнопку устройство переходит в «состояние измерения расстояния», которое проехал велосипед:
- если расстояние менее 5 км, то оно показывается в метрах;
- если расстояние более 5 км, но менее 10 км, то формат показа в километрах, сотнях метров и десятках метров;
- если расстояние более 10 км – формат показа в километрах и сотнях метров.
Длительное (более 1,5 с) нажатие верхней кнопки в режиме «состояние измерения расстояния» обнуляет пройденное расстояние.

Нижняя кнопка - кнопка изменения яркости дисплея.
Однократное нажатие на нижнюю кнопку увеличивает яркость дисплея на один шаг. Величина яркости дисплея TM1637 в соответствии с их спецификацией – 0…7. При достижении максимальной яркости (7) и еще одном нажатии яркость сбрасывается на минимальную (0). Данная опция по регулировке яркости введена для экономии потребления энергии от имеющегося в составе устройства аккумулятора. При поездках в вечернее время максимальная яркость дисплея избыточна и ее можно снизить. А вот при ярком солнце максимальная яркость позволяет считывать значение (иначе говоря цифры) на дисплее без труда.

Корпусные детали устройства (корпус и крышка) распечатаны на 3D-принтере из PETG-пластика. Магнит установлен в держатель (также распечатан из PETG-пластика), состоящий из двух деталей, скрепленных между собой. Держатель закреплен на двух соседних спицах переднего колеса. Для присоединения датчика Холла с корпусом устройства был использован четырехжильный телефонный кабель (одна из жил не используется).

Ниже скетч:

Code: Select all

#include <EEPROM.h>   //библиотека для работы со внутренней памятью ардуино

//-----------дисплей-----------
#define CLK 7
#define DIO 6
#include "GyverTM1637.h"
GyverTM1637 disp(CLK, DIO);
//-----------дисплей-----------

unsigned long lastturn, time_press; // переменные хранения времени
float SPEED; // переменная хранения скорости в виде десятичной дроби
float DIST; // переменная хранения расстояния в виде десятичной дроби
float w_length=2.05; // длина окружности колеса в метрах
boolean flag; // флажок для хранения (что выводим на дисплее, скорость или расстояние)
              // 0 - показ скорости
              // 1 - показ дистации (пройденного расстояния)
boolean state, button; // флажки для обработчика нажатия кнопки
int lumin; // яркость отображения дисплея

uint16_t cel;
uint16_t edin, des, sot, tys, dest;

void setup() {
  Serial.begin(9600);  // открыть порт
  lumin = 5; // начальная яркость дисплея
  disp.brightness(lumin);  // яркость дисплея
  attachInterrupt(0,sens,RISING); // подключить прерывание на 2 пин при повышении сигнала
  pinMode(3, OUTPUT);   // 3 пин как выход
  digitalWrite(3, HIGH);  // подать 5 вольт на 3 пин
  pinMode(8, INPUT);   // подключена кнопка к D8 для управления режимами: скорость - дистанция
  pinMode(4, INPUT);   // подключена кнопка к D4 для изменения яркости
 
  //--------------
  byte val1 = EEPROM.read(0); //вспоминаем пройденное расстояние при запуске системы
  byte val2 = EEPROM.read(1);
  DIST = (float)((val1<<8)+val2);
}

void sens() {
  if (millis()-lastturn > 80) {  //защита от случайных измерений (основано на том, что велосипед не будет ехать быстрее 120 кмч)
    SPEED=w_length/((float)(millis()-lastturn)/1000)*3.6;  //расчет скорости, км/ч
    lastturn=millis();  //запомнить время последнего оборота
    DIST=DIST+w_length;  //прибавляем длину колеса к дистанции при каждом обороте
  }
}

void loop()
{
  disp.brightness(lumin);
  int cel_sp=floor(SPEED);
  int sot_sp=(((float)cel_sp/1000)-floor((float)cel_sp/1000))*10;
  int des_sp=(((float)cel_sp/100)-floor((float)cel_sp/100))*10;
  int ed_sp=(((float)cel_sp/10)-floor((float)cel_sp/10))*10;
  int dr_sp=(float)(SPEED-floor(SPEED))*10;
 
  if (flag==0) {
    //disp.set(LED_0F[sot_sp],3);    // вывод сотен скорости (для велосипеда не нужно =)
    disp.display(0, des_sp);         // вывод десятков скорости
    disp.display(1, ed_sp);          // вывод единиц скорости
    disp.displayByte(2, 0b00001000); // вывод разделителя скорости
    disp.display(3, dr_sp);          // вывод после разделителя
  }

  cel = (uint16_t)floor(DIST);
  edin = cel % 10;
  des = cel / 10 % 10;
  sot = cel / 100 % 10;
  tys = cel / 1000 % 10;
  dest = cel / 10000 % 10;
 
  if (flag==1) {
    if (cel < 5000)                   // если расстояние меньше 5000 м (5 км)
    { disp.display(0, tys);           // вывод тысяч метров расстояния
      disp.display(1, sot);           // вывод сотен метров расстояния
      disp.display(2, des);           // вывод десятков метров расстояния
      disp.display(3, edin);          // вывод единиц метров расстояния
    }
      else if (cel >=5000 && cel < 10000)    // если 5 км <= расстояние < 10 км
      { disp.display(0, tys);                // вывод тысяч метров расстояния
        disp.displayByte(1, 0b1000000);      // вывод разделителя расстояния
        disp.display(2, sot);                // вывод сотен метров расстояния
        disp.display(3, des);                // вывод десятков метров расстояния
      }
        else                                      // если расстояние > 10 км
        { disp.display(0, dest);                  // вывод десятков тысяч метров расстояния
          disp.display(1, tys);                   // вывод тысяч метров расстояния
          disp.displayByte(2, 0b1000000);         // вывод разделителя расстояния
          disp.display(3, sot);                   // вывод сотен метров расстояния
        }
  }

  if ((millis()-lastturn) > 2200){ //если сигнала нет больше 2,2 секунды
    SPEED=0;  //считаем что SPEED 0
    byte hi = highByte(cel);
    byte low = lowByte(cel);
    EEPROM.update(0, hi); //записываем DIST во внутреннюю память по двум ячейкам
    EEPROM.update(1, low);
  }

    if (digitalRead(8)==1 && state==0) //выбор режимов кнопкой. Если кнопка нажата
    { state=1;     
      button=1;
      time_press=millis();              //запомнить время нажатия
      while (millis()-time_press<1500)
        {                                   //выполнять, пока кнопка нажата не менее 1500 миллисекунд
          if (digitalRead(8)==0) {            //если кнопка отпущена, выйти из цикла и принять button=0
            button=0;
            break;
          }
        }       
                // если кнопка не отпущена 1500 мс, то button будет равен 1
      switch (button) {
        case 0:
          flag=!flag; state=0; disp.clear();  //просто переключение режима отображения скорости и расстояния
          break;
        case 1:
          if (flag==1)
            { DIST=0; state=0; disp.clear();}; //если мы в режиме отображения расстояния и кнопка удерживается, то обнулить расстояние
          break;
      }
    }

    if (digitalRead(4)==1 && state==0)
    { state=1;
      lumin++;
      if (lumin > 7) lumin = 0;
      time_press=millis();
      while (millis()-time_press < 250)  { };
      state=0;
    }
}


Не претендую на оптимальность работы скетча. Работа скетча и устройства в целом проверена в "полевых условиях".

Использованные библиотеки:
EEPROM.h
GyverTM1637.h

Библиотека EEPROM.h используется для записи обновления пройденного расстояния в энергонезависимой памяти каждый раз, когда скорость перемещения велосипеда (скорость вращения переднего колеса) равна нулю более 2 с. Это же ранее записанное расстояние достается из памяти после включения устройства.

У меня велосипед имеет колеса диаметром 26”. Все расчеты по нахождению скорости и расстояния завязаны через следующие формулы:
SPEED=w_length/((float)(millis()-lastturn)/1000)*3.6,
где
SPEED – текущая скорость велосипеда, км/ч;
w_length – длина окружности колеса, м (для 26” составляет примерно 2,05 м);
millis()-lastturn – промежуток времени между ближайшими активировании прерываний, мс.

DIST=DIST+w_length,
где
DIST – текущая пройденная дистанция (расстояние), м;
w_length – длина окружности колеса, м (для 26” составляет 2,05 м).

Датчик Холла подключен к пину D2 и изменение состояние датчика контролируется по прерыванию INT0 по изменению с LOW на HIGH (режим работы RISING – повышение уровня). При срабатывании прерывания производится вычисление значений SPEED и DIST по вышеперечисленным формулам, записанных в функции обработки прерывания sens.
В зависимости от выбранного режима работы производится вывод соответствующего значения на дисплей.

Я не заморачивался с креплением спидометра на руль велосипеда. Вырезал основание из OSB-плиты, установил на него спидометр и примотал это всё к рулю изолентой – достаточно надежно.

Еще раз ссылаюсь на Александр “AlexGyver” (https://alexgyver.ru/), как на первоисточник проекта, на основании которого я создал данный проект.


Дим M
Администратор
Администратор
Avatar
Дим M
Администратор
Администратор
Posts: 1612
Joined: 5 Apr 2013
With us: 11 years 5 months

#2by Дим » 19 Sep 2024, 9:54

Зачётно !!!
[center]i love you [s]mxIni[/s] Mysql[/center]


Return to “Программирование”

Who is online (over the past 5 minutes)

Users browsing this forum: 2 guests