ADXL345 — это цифровой трёхосевой акселерометр, разработанный компанией Analog Devices. Этот датчик измеряет статическое (сила тяжести) и динамическое (движение, вибрация, удар) ускорение по трём взаимно перпендикулярным осям (X, Y и Z) и широко применяется в самых разных устройствах — от смартфонов и фитнес-браслетов до робототехнических комплексов и промышленных систем мониторинга. В рамках данной статьи мы подробно рассмотрим как устроен датчик и его принцип работы, а также соберём в качестве эксперимента шагомер на базе ESP32.

ADXL345 — это микросхема акселерометра, построенная по технологии MEMS (Micro-Electro-Mechanical Systems, микроэлектромеханическая система). Акселерометр измеряет линейное ускорение, то есть изменение скорости объекта во времени, в одном или нескольких направлениях. Единица измерения — g, где 1g ≈ 9.8 м/с² — ускорение свободного падения на Земле. Обращаем внимание:
— даже в состоянии покоя сенсор «видит» гравитацию как ускорение 1g, направленное вниз;
— при движении он регистрирует инерционные силы — например, при разгоне машины или ударе;
— он не измеряет вращение (для этого нужен гироскоп), но по изменению проекции силы тяжести на оси можно определить наклон и ориентацию прибора.

Устройство и принцип работы акселерометра
Как уже было сказано, в основе датчика лежит MEMS-технология. Упрощённо принцип её работы можно представить так:
— микромеханическая структура: внутри чипа для каждой из трёх осей находится груз в виде крошечной инерционной подвижной массы, подвешенный на упругих подвесах («пружинках»);
— емкостной принцип измерения: при увеличении скорости по закону инерции масса смещается относительно корпуса чипа. То есть, когда устройство неравномерно движется (разгон, торможение, наклон или вибрация), массы смещаются из-за инерции (закон Ньютона: тело сопротивляется изменению движения). Смещение изменяет расстояние между массами и неподвижными пластинами, образуя конденсаторы с переменной ёмкостью;

— преобразование сигнала: изменение ёмкости преобразуется в изменение напряжения. Этот аналоговый сигнал усиливается, фильтруется от шумов и поступает на встроенный аналого-цифровой преобразователь (АЦП);
— цифровая обработка: оцифрованные данные обрабатываются встроенным процессором, который масштабирует их, применяет калибровку и может детектировать заранее запрограммированные события (падение, удар, вибрация);
— выход данных: готовые цифровые значения по осям X, Y и Z сохраняются в регистрах чипа, откуда их можно считать через цифровые интерфейсы.
В качестве простой аналогии можно представить шарик в коробке, ко всем стенкам которого прикреплены пружины. Если резко толкнуть коробку, шарик сместится и сожмёт пружины на противоположной стороне. По степени сжатия пружин можно определить силу и направление толчка. ADXL345 работает похожим образом, но в миниатюрном масштабе.
Для наглядности ниже представлена из официальной документации функциональная схема датчика:

Сенсор состоит из следующих блоков:
— трёхосевой MEMS чувствительный элемент (3-axis sensor);
— схема eмкостно-цифрового преобразования (sense electronics);
— 16-битный АЦП на каждую ось;
— блок обработки данных (цифровой сигнальный процессор DSP);
— регистры конфигурации и данных;
— интерфейс I²C/SPI;
— блок управления питанием и режимами сна;
— встроенные функции:
— обнаружение движения/бездействия;
— обнаружение одиночного/двойного тапа;
— регистрация свободного падения (free-fall detection);
— FIFO-буфер (32 выборки);
— автоматическая калибровка смещения (offset calibration).
Параметры и характеристики акселерометра ADXL345

Выделим основные особенности датчика:
— выбор диапазона ±2/4/8/16g позволяет адаптировать прибор под задачи от измерения мелких вибраций до сильных ударов;
— высокая частота обновления до 3200 Гц подходит для динамических измерений;
— низкое энергопотребление — отлично для портативных и энергоэффективных устройств;
— встроенный FIFO буфер на 32 уровня для хранения данных, что снижает нагрузку на процессор;
— прерывания по различным событиям: свободное падение, активность, неактивность, превышение порога и другое;
— калибровка и компенсация смещения встроены.
У сенсора ADXL345 есть функциональный аналог — младшая модель ADXL335, которая является аналоговым датчиком с меньшим фиксированным диапазоном (±3g).
Другой, более совершенный аналог — MPU-6050, который является цифровым акселерометром и гироскопом в одном чипе. Этот датчик несколько дороже, но на практике более эффективен, поскольку гироскоп позволяет измерять угловую скорость, а это повышает качество расчёта ускорения и также даёт возможность вычислять ориентацию в пространстве.
Близкий аналог MPU-6050 — комбинированный сенсор GY-BMI160.
Схема подключения ADXL345 к ESP32
Для построения шагомера воспользуемся отладочной платой NodeMCU-32S (38 pin), которая построена на базе модуля ESP-WROOM-32. Для отображения количества подсчитанных шагов применим OLED-дисплей на базе контроллера SSD1306. Интерфейс подключения для обоих модулей — I2C. Ниже представлена таблица подключений сенсора и дисплея к отладочной плате.


Программный код (скетч) для шагомера
Проект создавался в среде программирования Arduino IDE. О том, как настроить эту среду разработки для программирования ESP32 можно подробно ознакомиться в статье про микрофон INMP441.
Для работы с OLED-дисплеем воспользуемся библиотекой Adafruit_SSD1306 (опционально можно добавить Adafruit_GFX для отображения графики).
Что касается работы с датчиком, то применим популярную и простую библиотеку Adafruit_ADXL345. Она обеспечивает оптимальный набор функций и достаточно проста для понимания новичкам. Также для успешной компиляции может понадобиться библиотека Adafruit_Sensor. Ниже представлен очень подробно прокомментированный листинг кода:
// Подключение необходимых библиотек
#include <Wire.h> // Библиотека для работы с I2C интерфейсом
#include <Adafruit_GFX.h> // Графическая библиотека для дисплеев
#include <Adafruit_SSD1306.h> // Библиотека для работы с OLED дисплеем SSD1306
#include <Adafruit_ADXL345_U.h> // Библиотека для работы с акселерометром ADXL345
// НАСТРОЙКИ ДИСПЛЕЯ
#define SCREEN_WIDTH 128 // Ширина дисплея в пикселях
#define SCREEN_HEIGHT 64 // Высота дисплея в пикселях
#define OLED_RESET -1 // Пин сброса дисплея (-1 означает отсутствие пина сброса)
// Создание объекта дисплея с указанными параметрами
// Параметры: ширина, высота, указатель на объект Wire, пин сброса
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// СОЗДАНИЕ ОБЪЕКТА АКСЕЛЕРОМЕТРА
// Параметр 12345 - произвольный идентификатор датчика
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);
// ПЕРЕМЕННЫЕ ДЛЯ ШАГОМЕРА
int stepCount = 0; // Счетчик шагов (начинается с 0)
unsigned long lastStepTime = 0; // Время последнего обнаруженного шага (в миллисекундах)
const unsigned long stepDelay = 300; // Минимальное время между шагами (мс) для избежания двойного счета
// ПЕРЕМЕННЫЕ ДЛЯ ФИЛЬТРАЦИИ И КАЛИБРОВКИ ДАННЫХ
float alpha = 0.8; // Коэффициент фильтрации (0-1) для скользящего среднего
// 0.8 = сильная фильтрация (медленный отклик), 0.1 = слабая фильтрация (быстрый отклик)
float filteredX = 0; // Отфильтрованное значение ускорения по оси X
float filteredY = 0; // Отфильтрованное значение ускорения по оси Y
float filteredZ = 0; // Отфильтрованное значение ускорения по оси Z
float magnitude = 0; // Величина общего ускорения (модуль вектора)
float lastMagnitude = 0; // Величина ускорения на предыдущем цикле
// КАЛИБРОВОЧНЫЕ СМЕЩЕНИЯ ДЛЯ КОМПЕНСАЦИИ ПОГРЕШНОСТЕЙ ДАТЧИКА
float offsetX = 0; // Смещение по оси X
float offsetY = 0; // Смещение по оси Y
float offsetZ = 0; // Смещение по оси Z
const int calibrationSamples = 50; // Количество samples для калибровки (чем больше, тем точнее)
// ПОРОГИ ОБНАРУЖЕНИЯ ШАГОВ
float stepThreshold = 1.2; // Порог обнаружения шага (единица измерения g) - чувствительность к изменениям ускорения
float idleThreshold = 9.5; // Порог покоя (единица измерения g) - базовое значение ускорения в состоянии покоя
// 9.81 m/s² = 1g (гравитация Земли)
// СТАТИСТИКА ДЛЯ АВТОМАТИЧЕСКОЙ НАСТРОЙКИ ПОРОГОВ
float minMagnitude = 10.0; // Минимальное зарегистрированное значение величины ускорения
float maxMagnitude = 0.0; // Максимальное зарегистрированное значение величины ускорения
// ФУНКЦИЯ НАСТРОЙКИ (ВЫПОЛНЯЕТСЯ ОДИН РАЗ ПРИ ЗАПУСКЕ)
void setup() {
// Инициализация последовательного порта для отладки
// 115200 - скорость передачи данных в бодах
Serial.begin(115200);
// ИНИЦИАЛИЗАЦИЯ ДИСПЛЕЯ
// SSD1306_SWITCHCAPVCC - тип питания дисплея
// 0x3C - I2C адрес дисплея (может быть 0x3C или 0x3D)
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
// Если инициализация не удалась, программа останавливается в бесконечном цикле
for(;;);
}
// НАСТРОЙКА ПАРАМЕТРОВ ДИСПЛЕЯ
display.clearDisplay(); // Очистка дисплея
display.setTextSize(1); // Установка размера текста (1 = стандартный)
display.setTextColor(SSD1306_WHITE); // Установка цвета текста (белый на черном фоне)
display.setCursor(0,0); // Установка курсора в начальную позицию (0,0)
display.display(); // Обновление дисплея для отображения изменений
// ИНИЦИАЛИЗАЦИЯ АКСЕЛЕРОМЕТРА
if(!accel.begin()) {
// Если акселерометр не найден, программа останавливается
while(1);
}
// НАСТРОЙКА АКСЕЛЕРОМЕТРА
accel.setRange(ADXL345_RANGE_4_G); // Установка диапазона измерений (±4g)
accel.setDataRate(ADXL345_DATARATE_100_HZ); // Установка частоты обновления данных (100 Гц)
// КАЛИБРОВКА АКСЕЛЕРОМЕТРА
calibrateAccelerometer();
// ОЧИСТКА ДИСПЛЕЯ ПЕРЕД НАЧАЛОМ ОСНОВНОЙ РАБОТЫ
display.clearDisplay();
}
// ОСНОВНОЙ ЦИКЛ ПРОГРАММЫ (ВЫПОЛНЯЕТСЯ БЕСКОНЕЧНО)
void loop() {
// СОЗДАНИЕ ПЕРЕМЕННОЙ ДЛЯ ХРАНЕНИЯ ДАННЫХ С АКСЕЛЕРОМЕТРА
sensors_event_t event;
// ПОЛУЧЕНИЕ ДАННЫХ С АКСЕЛЕРОМЕТРА
// Данные записываются в структуру event
accel.getEvent(&event);
// ПРИМЕНЕНИЕ КАЛИБРОВОЧНЫХ СМЕЩЕНИЙ
// Вычитаем смещения из сырых данных для компенсации погрешностей датчика
float x = event.acceleration.x - offsetX;
float y = event.acceleration.y - offsetY;
float z = event.acceleration.z - offsetZ;
// ФИЛЬТРАЦИЯ ДАННЫХ МЕТОДОМ СКОЛЬЗЯЩЕГО СРЕДНЕГО (ЭКСПОНЕНЦИАЛЬНОЕ СГЛАЖИВАНИЕ)
// Формула: filtered = alpha * previous + (1 - alpha) * current
// alpha = 0.8 означает 80% предыдущего значения + 20% нового значения
filteredX = alpha * filteredX + (1 - alpha) * x;
filteredY = alpha * filteredY + (1 - alpha) * y;
filteredZ = alpha * filteredZ + (1 - alpha) * z;
// ВЫЧИСЛЕНИЕ ВЕЛИЧИНЫ ОБЩЕГО УСКОРЕНИЯ (МОДУЛЯ ВЕКТОРА)
// Формула: magnitude = sqrt(x² + y² + z²)
// Эта величина показывает общую силу ускорения независимо от направления
magnitude = sqrt(filteredX * filteredX + filteredY * filteredY + filteredZ * filteredZ);
// ОБНАРУЖЕНИЕ ШАГА
detectStepSimple();
// ОБНОВЛЕНИЕ ИНФОРМАЦИИ НА ДИСПЛЕЕ
updateDisplay();
// ЗАДЕРЖКА ДЛЯ СТАБИЛЬНОСТИ РАБОТЫ И ЭКОНОМИИ ЭНЕРГИИ
// 20 мс = 50 обновлений в секунду (достаточно для шагомера)
delay(20);
}
// ФУНКЦИЯ КАЛИБРОВКИ АКСЕЛЕРОМЕТРА
void calibrateAccelerometer() {
// ВЫВОД ИНСТРУКЦИЙ НА ДИСПЛЕЙ
display.clearDisplay();
display.setCursor(0,0);
display.println("Calibration..."); // "Калибровка..."
display.println("Put it flat"); // "Положите устройство на ровную поверхность"
display.display(); // Обновление дисплея
// ЗАДЕРЖКА ДЛЯ ТОГО, ЧТОБЫ ПОЛЬЗОВАТЕЛЬ УСПЕЛ ПОЛОЖИТЬ УСТРОЙСТВО
delay(3000);
// ПЕРЕМЕННЫЕ ДЛЯ НАКОПЛЕНИЯ СУММ
float sumX = 0;
float sumY = 0;
float sumZ = 0;
// СБОР КАЛИБРОВОЧНЫХ ДАННЫХ
for (int i = 0; i < calibrationSamples; i++) {
// Получение данных с акселерометра
sensors_event_t event;
accel.getEvent(&event);
// Накопление сумм по осям
sumX += event.acceleration.x;
sumY += event.acceleration.y;
sumZ += event.acceleration.z;
// Небольшая задержка между измерениями
delay(20);
}
// ВЫЧИСЛЕНИЕ СРЕДНИХ ЗНАЧЕНИЙ (СМЕЩЕНИЙ)
offsetX = sumX / calibrationSamples; // Смещение по оси X
offsetY = sumY / calibrationSamples; // Смещение по оси Y
offsetZ = (sumZ / calibrationSamples) - 9.81; // Смещение по оси Z с учетом гравитации
// 9.81 m/s² - ускорение свободного падения на Земле
// Когда устройство лежит ровно, ось Z должна показывать ≈9.81 m/s²
}
// ФУНКЦИЯ ОБНАРУЖЕНИЯ ШАГА ПРОСТЫМ МЕТОДОМ
void detectStepSimple() {
// ПОЛУЧЕНИЕ ТЕКУЩЕГО ВРЕМЕНИ
unsigned long currentTime = millis(); // millis() возвращает время в мс с начала работы программы
// АЛГОРИТМ ОБНАРУЖЕНИЯ ШАГА:
// 1. Текущая величина ускорения превышает порог (idleThreshold + stepThreshold)
// 2. Предыдущая величина ускорения была ниже порога
// 3. Прошло достаточно времени с последнего шага (stepDelay)
if (magnitude > idleThreshold + stepThreshold &&
lastMagnitude < idleThreshold + stepThreshold &&
currentTime - lastStepTime > stepDelay) {
// ИНКРЕМЕНТ СЧЕТЧИКА ШАГОВ
stepCount++;
// ОБНОВЛЕНИЕ ВРЕМЕНИ ПОСЛЕДНЕГО ШАГА
lastStepTime = currentTime;
}
// СОХРАНЕНИЕ ТЕКУЩЕЙ ВЕЛИЧИНЫ УСКОРЕНИЯ ДЛЯ СЛЕДУЮЩЕГО ЦИКЛА
lastMagnitude = magnitude;
}
// ФУНКЦИЯ ОБНОВЛЕНИЯ ИНФОРМАЦИИ НА ДИСПЛЕЕ
void updateDisplay() {
// ОЧИСТКА ДИСПЛЕЯ (ЗАЛИВКА ЧЕРНЫМ ЦВЕТОМ)
display.clearDisplay();
// ОТОБРАЖЕНИЕ ЗАГОЛОВКА "STEPS:"
display.setCursor(0, 0); // Позиция: x=0, y=0 пикселей
display.setTextSize(2); // Размер текста: 2 (крупный)
display.print("Steps:"); // Вывод текста
// ОТОБРАЖЕНИЕ КОЛИЧЕСТВА ШАГОВ (ОСНОВНАЯ ИНФОРМАЦИЯ)
display.setCursor(0, 35); // Позиция: x=0, y=35 пикселей (ниже заголовка)
display.setTextSize(3); // Размер текста: 3 (очень крупный)
display.print(stepCount); // Вывод значения счетчика шагов
// ОБНОВЛЕНИЕ ДИСПЛЕЯ ДЛЯ ОТОБРАЖЕНИЯ ВСЕХ ИЗМЕНЕНИЙ
display.display();
}
Алгоритм работы шагомера
Для полноценного тестирования шагомера обеспечьте питание девайса при помощи аккумулятора или батареек. Рекомендуется подавать питание на вход «VIN», чтобы напряжение стабилизировалось до безопасного уровня 3,3 В.
Но перед тем, как подавать питание, нужно устройство положить на ровную поверхность, чтобы произвести калибровку. Калибровка производится каждый раз при инициализации микроконтроллера после подачи питания или нажатия кнопки сброса «Reset». На дисплее при калибровке будет отображена соответствующая надпись: «Calibration… Put it flat» (Калибровка… Положите устройство на ровную поверхность). Этот процесс необходим, чтобы система оценила уровень смещения показаний по каждой из осей, чтобы в процессе работы из поступающих данных вычитать значение смещения.
После того, как будет завершена калибровка, прибор переходит в режим подсчёта шагов. Для тестирования шагомер можно положить в карман или зафиксировать на поясе.
В основном цикле программы loop() сенсор выдаёт данные по трём осям, и к этим данным применяется корректировка значениями, которые были получены при калибровке (вычитаются величины смещения).
Далее применяется фильтр скользящего среднего (экспоненциальное сглаживание):
filtered = alpha * previous + (1 - alpha) * current;
Значение alpha = 0.8 означает, что 80% старых данных + 20% новых. Это сглаживает шум.
После этого вычисляется общая величина ускорения (magnitude):
magnitude = sqrt(filteredX * filteredX + filteredY * filteredY + filteredZ * filteredZ);
Это модуль вектора ускорения. Он показывает, насколько сильно девайс трясётся независимо от направления.
Данные обновляются постоянно, каждые 20 мс для стабильности работы.
Для корректировки чувствительности шагомера особое значение имеют две константы, представляющие собой пороговые значения:
— idleThreshold = 9,5 (базовое ускорение в состоянии покоя, близко к учёту с гравитацией);
— stepThreshold = 1,2 (минимум изменения скорости).
Если magnitude > idleThreshold + stepThreshold (т.е. > 10.7), это может быть шаг.
Функция detectStepSimple(), которая отвечает за обнаружение движения.
Сравниваются текущая и предыдущая величины magnitude. При этом 3 условия должны одновременно соблюдаться:
— текущая magnitude > порог
— предыдущая < порога
— прошло как минимум 300 мс с последнего шага (чтобы избежать двойного подсчёта — stepDelay = 300 мс).
Если все 3 условия «ДА», то шаг засчитывается. Прошлое значение сохраняется для следующего цикла.
Грубо говоря, когда человек начинает шагать (поднимаете ногу), тело немного «падает» вниз — показания по осям меняется, что увеличивает переменную magnitude. Алгоритм ловит этот пик и засчитывает шаг, только если пик достаточно большой и не слишком частый.
Подсчёт шагов производится следующим образом:
— переменная stepCount начинается с 0;
— каждый раз, когда условие в detectStepSimple() выполняется, счетчик увеличивается: stepCount++;
— также обновляется время последнего движения (lastStepTime = currentTime), чтобы предотвратить ложные срабатывания;
— подсчитанное количество шагов хранится в stepCount и сразу отображается на дисплее.
Заключение
В этой статье мы познакомились с цифровым трёхосевым акселерометром ADXL345, на базе которого можно строить различные системы контроля движения: фитнес-трекеры (шагомеры), смартфоны (автоповорот изображения на экране), стабилизация полёта квадрокоптера, регистратор вибраций и ударов, система управления при помощи жестов.
В качестве примера мы подробно рассмотрели процесс создания простого шагомера, собрав макет на базе отладочной платы ESP32. Подробно описанный алгоритм работы прибора позволит адаптировать его для других задач, что открывает большой простор для самостоятельных проектов с использованием рассмотренного сенсора.
