В этой статье мы рассмотрим создание портативного счетчика шагов (шагомера) на основе микроконтроллера AVR ATtiny85, акселерометра и гироскопа MPU6050, и OLED дисплея. Питание на шагомер будет подавать от простой батарейки на 3V, что позволяет сделать его достаточно компактным и удобным для переноски. Для изготовления данного шагомера потребуется сравнительно мало компонентов, код программы также будет достаточно простой.
В программе шагомера с помощью датчика MPU6050 производится измерение ускорения по 3-м осям (X, Y и Z). Затем производится вычисление разницы между текущими и предыдущими значениями ускорения. Если эта разница будет больше определенной величины (для ходьбы больше 6, для бега более 10), производится увеличение счетчика числа шагов. Общее число сделанных шагов отображается на экране OLED дисплея.
Чтобы сделать наш шагомер максимально компактным мы изготовили для него печатную плату с помощью сервиса PCBWay. При желании вы можете добавить в данный проект датчик частоты сердечных сокращений. Также на нашем сайте вы можете посмотреть проект шагомера на основе платы Arduino.
Необходимые компоненты
- Микроконтроллер ATtiny85 (купить на AliExpress).
- Модуль (датчик) MPU6050 (купить на AliExpress).
- Модуль OLED дисплея (купить на AliExpress).
- Резисторы (SMD) 10 кОм – 5 шт. (купить на AliExpress).
- Кнопки – 2 шт.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Гироскопический датчик (гироскоп) MPU-6050
MPU-6050 представляет собой 8-пиновый 3-осевой гироскоп, акселерометр и датчик температуры на едином чипе. По умолчанию данный модуль работает по интерфейсу I2C, но можно задействовать и интерфейс SPI. В нашем проекте мы будем использовать интерфейс (режим) I2C и в этом режиме нам понадобятся контакты SDA и SCL модуля.
Распиновка MPU-6050:
Vcc – контакт для подачи питающего напряжения постоянного тока;
GND – земля модуля;
SDA – это контакт используется для передачи данных между модулем mpu6050 и микроконтроллером;
SCL – вход синхронизации;
XDA – линия передачи данных (опциональная) по протоколу I2C для конфигурирования и считывания данных с внешних датчиков (не используется в нашем проекте);
XCL – вход синхронизации протокола I2C для конфигурирования и считывания данных с внешних датчиков (не используется в нашем проекте);
ADO – I2C Slave Address LSB (не используется в нашем проекте);
INT – контакт прерывания для индикации готовности данных.
На нашем сайте вы можете посмотреть следующие проекты на основе гироскопа MPU6050:
- подключение гироскопа MPU6050 к Arduino;
- измеритель уровня (уклономер) на основе Arduino и MPU6050;
- самобалансирующийся робот на Arduino Uno.
Работа схемы
Схема шагомера на ATtiny85 и акселерометре MPU6050 представлена на следующем рисунке.
Взаимодействие между MPU6050, OLED дисплеем м платой Arduino осуществляется с помощью протокола I2C. Поэтому контакт SCLPin (PB2) микроконтроллера ATtiny85 подключен к контактам SCLPin датчика MPU6050 и OLED дисплея. Аналогичным образом, контакт SDAPin (PB0) микроконтроллера ATtiny85 подключен к контактам SDAPin датчика MPU6050 и OLED дисплея. Две кнопки подключены к контактам PB3 и PB4 микроконтроллера ATtiny85. Эти кнопки используются для скроллинга или изменения текста на экране дисплея.
Изготовление печатной платы для шагомера на основе ATtiny85
Для проектирования печатной платы мы использовали редактор EasyEDA. 3D модель спроектированной нами печатной платы для этого проекта выглядит следующим образом:
Gerber файлы для изготовления печатной платы данного проекта можно скачать по следующей ссылке - Gerber file for ATtiny85 Step Counter.
Заказ печатной платы с сервиса PCBWay
Для заказа печатной платы с сервиса PCBWay (разумеется, вы можете использовать любой удобный вам способ заказа печатной платы) выполните следующую последовательность шагов.
Шаг 1. Перейдите на сайт https://www.pcbway.com, зарегистрируйтесь на нем если вы заходите на него в первый раз. На вкладке PCB Prototype (прототип печатной платы) укажите размеры печатной платы, число ее слоев и необходимое число плат.
Шаг 2. Нажмите на кнопку ‘Quote Now’ (заказать сейчас). После этого вас перебросит на страницу, на которой вам необходимо будет ввести дополнительные параметры печатной платы: ее тип, слои, материал, толщину и т.д. Большинство этих параметров можно оставить такими, какими их сервис предлагает по умолчанию.
Шаг 3. В заключение вам необходимо загрузить в сервис Gerber файлы и оплатить заказ. Перед переходом к процессу оплаты сервис PCBWAY проверяет ваши Gerber файлы на корректность.
Сборка шагомера на печатной плате
Нашу плату сервис изготовил за несколько дней, ее качество было на высоте. Низ и верх нашей печатной платы показаны на следующем рисунке.
После припаивания компонентов проекта к печатной плате у нас получилась конструкция следующего вида:
Объяснение программы для ATtiny85
Полный текст программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты. Поскольку здесь используется программирование микроконтроллера ATtiny85 с помощью Arduino IDE, то для этого можно использовать USB программатор для ATtiny85 на основе загрузчика Digispark.
В коде программы мы будем использовать библиотеки TinyWireM.h и TinyOzOLED.h. Библиотеку TinyWireM можно непосредственно скачать из менеджера библиотек (Library Manager) Arduino IDE и установить ее оттуда же. Для этого откройте в Arduino IDE пункт меню Sketch < Include Library < Manage Libraries. После этого запустите поиск TinyWireM.h и установите ее. Эта библиотека написана компанией Adafruit.
Библиотеку TinyOzOLED.h можно скачать по следующей ссылке.
После установки этих библиотек первым делом в программе нам необходимо их подключить.
1 2 |
#include "TinyWireM.h" #include "TinyOzOLED.h" |
После этого объявим переменные, в которых будем хранить данные, считываемые с акселерометра.
1 |
intaccelX, accelY, accelZ; |
Внутри функции setup() мы инициализируем связь по протоколу I2C, сбросим датчик MPU6050 при помощи регистра управления питанием. Также мы зададим ориентацию OLED дисплея и очистим его экран, а также введем адрес регистра для значений акселерометра и гироскопа.
1 2 3 4 5 6 7 8 9 10 |
TinyWireM.begin(); OzOled.init(); OzOled.clearDisplay(); OzOled.setNormalDisplay(); OzOled.sendCommand(0xA1); OzOled.sendCommand(0xC8); TinyWireM.beginTransmission(mpu); TinyWireM.write(0x6B); TinyWireM.write(0b00000000); TinyWireM.write(0x1B); |
В функции getAccel() мы начнем считывание данных акселерометра. Данные для каждой оси сохраняются в двух байтах (верхний и нижний) регистра. Чтобы считать их все начните с первого регистра, а затем используя функцию requiestFrom() мы считаем все 6 регистров для осей X, Y, Z. Затем мы считываем данные из каждого регистра, и поскольку значение каждой оси состоит из двух слагаемых, мы комбинируем (объединяем) их соответствующим образом чтобы получить полные значения данных с акселерометра.
1 2 3 4 5 6 7 8 9 |
voidgetAccel() { TinyWireM.beginTransmission(mpu); TinyWireM.write(0x3B); TinyWireM.endTransmission(); TinyWireM.requestFrom(mpu, 6); accelX = TinyWireM.read() << 8|TinyWireM.read(); accelY = TinyWireM.read() << 8|TinyWireM.read(); accelZ = TinyWireM.read() << 8|TinyWireM.read(); } |
Затем, внутри функции loop мы будем считывать значения осей X, Y и Z акселерометра и рассчитывать результирующий вектор ускорения при помощи вычисления квадратного корня из суммы квадратов значений осей. Затем мы будем рассчитывать разницу между текущим и предыдущим векторами ускорения и если значение этой разницы будет больше 6 мы будем инкрементировать счетчик шагов.
1 2 3 4 5 6 7 8 9 10 |
getAccel(); vector = sqrt( (accelX * accelX) + (accelY * accelY) + (accelZ * accelZ) ); totalvector = vector - vectorprevious; if (totalvector> 6){ Steps++; } OzOled.printString("Steps", 0, 4); OzOled.printNumber(Steps, 0, 8, 4); vectorprevious = vector; delay(600); |
Тестирование работы шагомера
После сборки конструкции проекта загрузите программу в микроконтроллер ATtiny85. После этого возьмите шагомер в руки и начните ходьбу – вы должны увидеть как будет увеличиваться число шагов, отображаемое на экране OLED дисплея.
Видео с работой созданного нами шагомера приведено в конце статьи.
Исходный код программы на С
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
#include "TinyWireM.h" #include "TinyOzOLED.h" int accelX, accelY, accelZ; char mpu = 0x68; float vectorprevious; float vector; float totalvector; int Steps = 0; void setup() { TinyWireM.begin(); OzOled.init(); OzOled.clearDisplay(); OzOled.setNormalDisplay(); OzOled.sendCommand(0xA1); // установки ориентации дисплея OzOled.sendCommand(0xC8); TinyWireM.beginTransmission(mpu); TinyWireM.write(0x6B); // установка регистра питания TinyWireM.write(0b00000000); // отключаем спящий режим TinyWireM.endTransmission(); TinyWireM.beginTransmission(mpu); TinyWireM.write(0x1B); // конфигурация регистров гироскопа TinyWireM.write(0x00000000); // 250° per second range (default) TinyWireM.endTransmission(); TinyWireM.beginTransmission(mpu); //адрес I2C гироскопа TinyWireM.write(0x1C); // конфигурация регистров акселерометра TinyWireM.write(0b00000000); // 2g range +/- (default) TinyWireM.endTransmission(); } void loop() { getAccel(); vector = sqrt( (accelX * accelX) + (accelY * accelY) + (accelZ * accelZ) ); //OzOled.printString("Vec:", 0, 2); //OzOled.printNumber(vector, 0, 5, 2); totalvector = vector - vectorprevious; //OzOled.printString("Pre:", 0, 4); //OzOled.printNumber(vectorprevious, 0, 5, 4); //OzOled.printString("Diff:", 0, 6); //OzOled.printNumber(totalvector, 0, 5, 6); if (totalvector > 6){ Steps++; } //String Step_count = String(Steps); //char data[2]; //Step_count.toCharArray(data, 2); //OzOled.printBigNumber(data, 6, 2, 3); OzOled.printString("Steps", 0, 4); OzOled.printNumber(Steps, 0, 8, 4); vectorprevious = vector; delay(600); //OzOled.clearDisplay(); } void getAccel() { TinyWireM.beginTransmission(mpu); //адрес I2C гироскопа TinyWireM.write(0x3B); // регистр данных акселерометра TinyWireM.endTransmission(); TinyWireM.requestFrom(mpu, 6); // считываем 6 байт, 2 for each DoF accelX = TinyWireM.read() << 8|TinyWireM.read(); accelY = TinyWireM.read() << 8|TinyWireM.read(); accelZ = TinyWireM.read() << 8|TinyWireM.read(); // OzOled.printNumber(accelX, 0, 0, 0); // OzOled.printNumber(accelY, 0, 0, 2); //OzOled.printNumber(accelZ, 0, 0, 4); } |