Общие сведения
Набор Сделай сам №10 - «Механический циферблат» — набор для сборки электромеханической семисегментной цифры. Может быть использован как в одиночном экземпляре, так и в составе из нескольких штук. В наборе есть всё необходимое для сборки одной семисегментной цифры, которую можно подключить либо напрямую к Aruduino, либо при помощи модулей Расширителей выводов
Для подключения сервоприводов дополнительно понадобится:
Оборудование:
- 1x Piranha UNO
- 2x Trema-модуль Расширитель выводов, FLASH-I2C
- 1x i2c hub
- Провода мама-мама
Библиотеки:
Подключение сервоприводов
Установим Trema Shield на Piranha UNO

Подключим модули
Подключим сервоприводы согласно следующей таблице:
| Сегмент | Расширитель | Вывод |
|---|---|---|
| A | нечет | 1 |
| B | нечет | 2 |
| C | чет | 0 |
| D | чет | 1 |
| E | чет | 2 |
| F | нечет | 0 |
| G | нечет | 3 |

Для удобства сборки рекомендуем подписать сервоприводы соответствующими буквами
Сборка механики
Перед установкой сервоприводов необходимо запустить калибровочный скетч.
После запуска скетча все качалки сервоприводов встанут в положение соответствующее положению "включено" сегментов цифры. Теперь можно вставить качалки сервоприводов в основания сегментов и установить их на сервоприводы, а сервоприводы в раму и начать калибровку.
// Подключаем библиотеку для работы с аппаратной шиной I2C, до подключения библиотеки iarduino_I2C_Expander.
#include <Wire.h>
// Подключаем библиотеку для работы с модулями расширителей выводов.
#include <iarduino_I2C_Expander.h>
// Подключаем файл с объектом механических часов.
#include <MechaClock.h>
// Создаём массив объектов библиотеки расширителя выводов, указывая адреса
// (не забудьте устонавить адреса расширителей)
iarduino_I2C_Expander gpio[2]{9, 10};
/* Сегменты
_a
f|_|b
e|g|c
d^
*/
/* Калибровка сервоприводов */
//!!! Сегменты E и B работают в обратном направлении !!!
// Положение ВКЛ. сегментов
uint8_t ON[DIGITS][SEGMENTS] {
{
// g f e d c b a
90, 10, 90, 10, 10, 90, 10
}
};
// Положение ВЫКЛ. сегментов
uint8_t OFF[DIGITS][SEGMENTS] {
{
// g f e d c b a
10, 90, 10, 90, 90, 10, 90
}
};
Digit myDigit;
// Функция очистки буфера последовательного порта
void discardSerial()
{
while(Serial.available())
Serial.read();
}
void setup()
{
Serial.begin(9600);
delay(500);
Serial.println(F("Скетч калибровки сервоприводов механических часов."));
Serial.println();
Serial.println(F("Адреса расширителей выводов в десятеричной системе:"));
bool flag = false;
for (auto& i:gpio) {
if(i.begin()) {
Serial.print(i.getAddress());
Serial.print("\t");
}
else {
Serial.print("failed");
Serial.print("\t");
flag = true;
}
}
Serial.println();
if (flag) {
Serial.println(
F("Один из расширителей не обнаружен."
" Проверьте подключение и адрес "
"модуля и запустите скетч заново.")
);
goto exit;
}
myDigit = Digit(gpio, 0);
myDigit.set('8');
Serial.println(
F("Все сервоприводы сегментов установлены в положение\r\n"
"ВКЛ. Если вы уже выполняли это шаг, пропустите его\r\n"
", если нет, отключите питание и установите качалки\r\n"
"сервоприводов и сегменты цифр. Качалки должны\r\n"
"\"смотреть\" на сколько это возможно вверх от \r\n"
"оснований (цифры показывают восьмёрки)")
);
delay(1000);
Serial.println();
Serial.println(F("Карта сегментов:"));
Serial.println();
Serial.println(F(" A"));
Serial.println(F(" #####"));
Serial.println(F(" # #"));
Serial.println(F(" F # # B"));
Serial.println(F(" # G #"));
Serial.println(F(" #####"));
Serial.println(F(" # #"));
Serial.println(F(" E # # C"));
Serial.println(F(" # D #"));
Serial.println(F(" #####"));
Serial.println();
Serial.println(
F("Для продолжения введите любой символ в поле ввода"
" и нажмите \"Отправить\" или <Enter>.")
);
while(!Serial.available());
Serial.println(F("Начинаем процесс калибровки. Для выхода из калибровки"
" введите \"exit\" и нажмите <Enter> или \"Отправить\""));
Serial.println(F("Для дострочного вывода углов сервоприводов и выхода"
" введите \"printout\" и нажмите <Enter> или \"Отправить\""));
delay(2000);
uint8_t dig = 0;
uint8_t seg = SEGMENTS;
char current_seg = 'A';
do {
Serial.println("Калибруем сегмент"
+ String(current_seg) + " "
+ String(dig+1)
+ "-й цифры");
uint8_t on_deg = 0;
if (current_seg == 'B' || current_seg == 'E')
Serial.println(F("Угол сервопривода этого сегмента должен"
"быть ближе к 90 градусам в положении ВКЛ."));
else
Serial.println(F("Угол сервопривода этого сегмента должен"
"быть ближе к 0 градусам в положении ВКЛ."));
Serial.print(F("текущее значение: "));
Serial.println(ON[dig][seg-1]);
Serial.println(
F("Введите новое значение... (если калибровка"
" текущего сегмента закончена, введите \"n\""
" и нажмите <Enter> или \"Отправить\")")
);
// Опустошаем буфер последовательного порта
discardSerial();
// Ждём ввода пользователя
while(!Serial.available());
// Читаем введённые данные
String s = Serial.readStringUntil('\n');
// Удаляем непечатные символы
s.trim();
// Выходим из калибровки по желанию
if (s == "exit")
goto exit;
// Переходим к следующему сегменту
else if (s == "n") {
seg--;
current_seg++;
}
else if (s == "printout")
goto printout;
// Записываем новые показания в массив
else {
on_deg = uint8_t(s.toInt());
ON[dig][seg-1] = on_deg;
myDigit.set('8');
}
if (seg == 0) {
dig++;
seg = SEGMENTS;
current_seg = 'A';
}
} while(dig != 1);
printout:
Serial.println();
Serial.println(F("Калибровка завершена."));
Serial.println();
Serial.println(F("Замените следующие данные в скетче"
" управления одной цифрой:"));
Serial.println();
Serial.println(F("uint8_t ON[DIGITS][SEGMENTS] {"));
Serial.println(F("\t{"));
//int k = 1;
Serial.println(F("\t//\tg f e d c b a"));
Serial.print("\t\t");
bool first_seg = true;
for (auto& j:ON[0]) {
if (!first_seg)
Serial.print(", ");
first_seg = false;
Serial.print(j);
}
Serial.println();
Serial.println("\t}");
Serial.println("};");
exit:
Serial.println();
Serial.println(F("Выходим из калибровки."));
// "Отпускаем все серво"
myDigit.release();
}
void loop()
{
delay(1000);
}
Сборка корпуса
Установите все сервоприводы в раму, как показано на рисунке:

После установки сервоприводов, установите раму на основание и соберите корпус:

Примеры
Скетч для одной цифры
Данный скетч выводит цифры от 0 до 9 на электромеханическую цифру.
// Подключаем библиотеку для работы с аппаратной шиной I2C, до подключения библиотеки iarduino_I2C_Expander.
#include <Wire.h>
// Подключаем библиотеку расширителей выводов
// (Она должна быть подключена до бибилотеки механических часов!)
#include <iarduino_I2C_Expander.h>
// Подключаем библоитеку механических часов
#include <MechaClock.h>
// Создаём массив объектов расширителей выводов, указывая их адреса
iarduino_I2C_Expander gpio[]{9, 10};
// Создаём объект цифры
Digit myDigit;
// Положение ВКЛ. сегментов. Сюда необходимо вставить результат калибровки
uint8_t ON[DIGITS][SEGMENTS] {
{
// g f e d c b a
90, 5, 92, 2, 0, 98, 10
}
};
// Положение ВЫКЛ. сегментов
uint8_t OFF[DIGITS][SEGMENTS] {
{
// g f e d c b a
20, 90, 10, 90, 90, 10, 90
}
};
void setup()
{
// Инициируем расширители выводов
for (auto& i:gpio) {
i.begin();
}
/* Инициируем объект цифры передавая указатель на массив объектов
* расширителей выводов в первом параметре и номер цифры. В данном
* случае у нас одна цифра, поэтому её порядковый номер 0.
*/
myDigit = Digit(gpio, 0);
}
void loop()
{
for (size_t i = 0; i < 10; i++) {
myDigit.set('0' + i);
delay(1000);
}
}
Скетч для нескольких цифр
Скетч выводит секунды до 99 с момента перезагрузки микроконтроллера
#include <Wire.h>
#include <iarduino_I2C_Expander.h>
#include <MechaClock.h>
// Интервал переключения сегментов в мс
#define INTERVAL 0
// количество цифр
#define DIG_NUM 2
// Создаём массив объектов расширитерей выводов, указывая адреса в десятиричной
// системе счисления
iarduino_I2C_Expander gpio[]{9, 10, 11, 12};
// Если у вас три цифры - установите соответсвующие адреса на дополнительных расширителях выводов
// и укажите их при создании объектов, например:
// iarduino_I2C_Expander gpio[]{9, 10, 11, 12, 13, 14};
// Положение ВКЛ. сегментов
uint8_t ON[DIGITS][SEGMENTS] {
{ // Углы сервоприводов первой цифры
// g f e d c b a
10, 10, 90, 10, 10, 90, 10
},
{ // Углы сервоприводов второй цифры
// g f e d c b a
10, 10, 90, 10, 10, 90, 10
}
};
// Положение ВЫКЛ. сегментов
uint8_t OFF[DIGITS][SEGMENTS] {
{
// g f e d c b a
90, 90, 10, 90, 90, 10, 90
},
{
// g f e d c b a
90, 90, 10, 90, 90, 10, 90
}
};
Display myDisp(gpio, INTERVAL, DIG_NUM);
void setup()
{
Serial.begin(9600);
while(!Serial);
for (auto& i:gpio) {
i.begin();
}
}
void loop()
{
// Выводим цифры раз в секунду...
for (size_t i = 0; i < 100; i++) {
/* Если у Вас меньше 4-х цифр, в функцию print необходимо
передать второй параметр. В случае с двумя цифрами это
POS2, в случае с тремя - POS3. Если Вы используете 4
цифры, то можно не передавать второй параметр. Если
Вам необходимо использовать больше 4-х цифр -
свяжитесь с нами: [email protected] */
myDisp.print(i, POS2);
delay(1000);
}
}

Обсуждение