Обзор онлайн-курса по Arduino/робототехнике от МФТИ (вторая неделя)
Мы продолжаем публикацию обзора онлайн-курса «Строим роботов и другие устройства на Arduino», начало здесь.
Итак, долго ли коротко, закончилась вторая неделя онлайн-курса робототехники от МФТИ. Признаться, неделя оказалась очень насыщенная всевозможными темами.
Вот примерный перечень, который я выделил для себя:
- Делитель напряжения. Использование фоторезистора и термистора
- Аналоговый сигнал. Разрядность сигнала
- Обмен данными через последовательный порт. Среда Processing
- Цифровой сигнал. Кнопки и варианты подключения. Подтягивающий резистор
- Логические выражения, операторы if и else
- Зуммер, светодиодная шкала, семисегментный индикатор
- Микросхемы. Логический инвертор 74HC04, сдвиговый регистр 74HC595
- Отладка программ
- Внешние модули
- Вариант готовой системы мониторинга, отображающей температуру и уровень освещенности на светодиодной шкале, а также динамиком, срабатывающим при превышении определенной температуры
Успешно пройдя тест и собрав предложенные схемы из уроков, задумался, как можно улучшить ту или иную схему или собрать что-нибудь свое.
Первое что пришло на ум — модернизация датчика освещенности. Реализация, предложенная в уроке, просто снимала значение с фоторезистора и посылала его в последовательный порт.
Модернизированная версия должна использовать семисегментный индикатор для вывода чисел от 0 (минимальная освещенность) до 9 (максимальная освещенность). Индикатор должен быть подключен через сдвиговый регистр. С помощью двух кнопок должна осуществляться установка минимального и максимального уровня освещенности. Сдвиговый регистр нужен для того, чтобы не задействовать по пину Arduino на каждый сегмент, а вместо этого обойтись меньшим числом пинов. По сути, сдвиговый регистр преобразует последовательный вывод данных (по одному биту за единицу времени) в параллельный (несколько бит за единицу времени). В нашем случае вместо семи пинов Arduino нам понадобится лишь три.
В редакторе Fritzing у меня получилось такое устройство.
Таким образом оно выглядит вживую.
За основу были взяты схемы работы со сдвиговым регистром и фоторезистором.
Обратим внимание, что на плате фоторезистор подключен несколько иначе, чем в видеоуроке — там мы снимали значение напряжения на фоторезисторе относительно земли, а в схеме снимаем падение напряжения относительно питания. Так сделано, чтобы немного упростить программу — с увеличением уровня освещенности сопротивление фоторезистора падает. Следовательно, при том же токе уменьшается падение напряжения. Поэтому на аналоговом входе будет тем выше напряжение, чем выше уровень освещенности, и наоборот.
Теперь дело за малым — доработать исходный код. За основу была взята все та же программа для вывода значения на семисегментный индикатор.
// Пины, необходимые для работы сдвигового регистра
#define DATA_PIN 13
#define LATCH_PIN 12
#define CLOCK_PIN 11
// Пины кнопок, отвечающих за установку минимального и максимального значения
#define BTN_MIN 3
#define BTN_MAX 2
// Пин, с которого будем снимать значения фоторезистора
#define SENS_PIN A5
// Значения выводов регистра для индикатора, соответствующие цифрам
byte d0 = 0b01111101;
byte d1 = 0b00100100;
byte d2 = 0b01111010;
byte d3 = 0b01110110;
byte d4 = 0b00100111;
byte d5 = 0b01010111;
byte d6 = 0b01011111;
byte d7 = 0b01100100;
byte d8 = 0b01111111;
byte d9 = 0b01110111;
// Предопределенные значения минимального и максимального уровня освещенности
int min_light = 0;
int max_light = 1023;
// Текущее значение датчика
int value;
// Значение, ограниченное мин. и макс. уровнем
int output;
// Выводимая цифра
int digit;
void setup()
{
// Установка пинов сдвигового регистра
pinMode(DATA_PIN, OUTPUT);
pinMode(CLOCK_PIN, OUTPUT);
pinMode(LATCH_PIN, OUTPUT);
// Включаем порт для вывода отладочной информации
Serial.begin(9600);
// Установка пинов для кнопок
pinMode(BTN_MIN, INPUT_PULLUP);
pinMode(BTN_MAX, INPUT_PULLUP);
}
void loop()
{
// Получаем значение с фоторезистора
value = analogRead(SENS_PIN);
output = value;
// Если нажаты кнопки - устанавливаем пороговые значения
if (!digitalRead(BTN_MIN)) min_light = value;
if (!digitalRead(BTN_MAX)) max_light = value - 10;
// Применяем ограничения сверху и снизу
if (value < min_light) output = min_light;
if (value > max_light) output = max_light;
// Получаем цифру, которую должны вывести на индикатор
digit = map(value, min_light, max_light, 0, 9);
// Отладочная информация
Serial.println("Value: " + String(value) + " Output: " + String(output) + " Min: " + String(min_light) + " Max: " + String(max_light) + " Current : " + String(value) + " Digit: " + String(digit));
// Непосредственный вывод цифры на индикатор
if (digit == 0)
{
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, d0);
digitalWrite(LATCH_PIN, HIGH);
}
else if (digit == 1)
{
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, d1);
digitalWrite(LATCH_PIN, HIGH);
}
else if (digit == 2)
{
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, d2);
digitalWrite(LATCH_PIN, HIGH);
}
else if (digit == 3)
{
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, d3);
digitalWrite(LATCH_PIN, HIGH);
}
else if (digit == 4)
{
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, d4);
digitalWrite(LATCH_PIN, HIGH);
}
else if (digit == 5)
{
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, d5);
digitalWrite(LATCH_PIN, HIGH);
}
else if (digit == 6)
{
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, d6);
digitalWrite(LATCH_PIN, HIGH);
}
else if (digit == 7)
{
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, d7);
digitalWrite(LATCH_PIN, HIGH);
}
else if (digit == 8)
{
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, d8);
digitalWrite(LATCH_PIN, HIGH);
}
else if (digit == 9)
{
digitalWrite(LATCH_PIN, LOW);
shiftOut(DATA_PIN, CLOCK_PIN, LSBFIRST, d9);
digitalWrite(LATCH_PIN, HIGH);
}
// Задержка для более плавного вывода значения
delay(10);
}
Из особенностей — при установке максимального уровня освещенности пришлось вычитать некоторую константу (max_light = value — 10), подобранную эмпирическим путем. Это необходимо для того, чтобы избежать «дребезжания» при максимальном уровне освещенности, т. к. значение напряжения, снятого с фоторезисторе нестабильно.
Компилируем скетч, загружаем в Arduino и проверяем.
Сначала в мониторе порта…
А затем вживую
Как видим, устройство успешно работает в том виде, как его описывали. Конечно, еще есть куда его улучшать — например, можно добавить звуковой сигнал, когда освещение падает ниже определенного уровня — это будет означать, что нужно включить дополнительное освещение на рабочем месте. Также в будущем можно будет изменить саму программу, используя массивы и дополнительные функции.
В заключение еще раз повторю, что неделя получилась очень насыщенная на различные темы. Отметим, что с момента публикации курса в Arduino IDE появилась встроенная функция Serial Plotter, которая частично перекрывает рассматриваемые в уроках функции среды Processing. Также в конце недели авторы пришли к идее модульности, когда конечное устройство собирается из готовых элементов — модулей, например, кнопки с уже встроенным подтягивающим резистором, готового датчика света, где фоторезистор и обычный резистор уже собраны в делитель напряжения, и тому подобных. Однако, устройство можно без труда собрать на макетной плате, что и было сделано. Вопрос читателям, приходилось ли вам самостоятельно изготавливать корпус для ваших устройств? Какие материалы вы для этого использовали? Может быть, картон, фанеру, оргстекло или, что не редкость сегодня, печатали на 3D-принтере?