Создаем интерфейс для детской микроволновки из IKEA на Ардуино

В известном на весь мир магазине IKEA продается много интересных предметов для взрослых и детей. Сегодня мы поговорим о том, как можно с помощью Ардуино улучшить микроволновую печь от IKEA Duktig Kids. Мы не простArdо улучшим данную микроволновку, а придадим ей полную реалистичность.

Но сначала давайте посмотрим то, что мы хотим получить в итоге, очень классно:

Шаг 1. Комплектующие

Нам понадобится несколько деталей для реализации микроволновой печи:

Деталь Кол-во
1 4-значный 7-сегментный дисплей 1
2 Вращающийся энкодер с кнопкой 1
3 27-миллиметровый динамик 1
4 Резистор 3,3 кОм 3
5 Резистор 27 Ом 1
6 Резистор 56 Ом 1
7 Резистор 100 Ом 1
8 Транзистор NPN общего назначения 1
9 Arduino Nano R3 1

Также вам может понадобиться 3D-принтер. Но это необязательно, некоторые детали всегда можно сделать из дерева и покрасить их.

Родители часто покупают детям какие-то реальные аналоги вещей из взрослого мира, например кухню Duktig из ИКЕА. Многие разрисовывают и добавляют к таким вещам новые детали, но мы хотели пойти немного далее и добавить рабочую микроволновую панель.

Дети одержимы нажатием кнопок и поворотными ручками. За основу мы взяли микроволновую печь, которую можно контролировать с помощью одной большой ручки. Мы заказали 4-значный 7-сегментный дисплей, поворотный кодер и алюминиевую ручку, которые собрали с Arduino Nano.

Вращающиеся энкодеры

Вращающийся энкодер очень похож на потенциометр, однако они работают совсем по-другому. Во-первых, нет ограничителя, поэтому они вращаются на 360 градусов. Но главное отличие заключается в том, как вы с ними взаимодействуете: они выводят цифровой код под названием Код Грея, названный в честь Фрэнка Грея.

Энкодер, который мы использовали, также имеет кнопку, которую мы будем использовать для запуска и остановки таймера.

Дисплей

Четырехзначный семисегментный дисплей имеет встроенный чип для драйверов, что означает, что нам нужно всего два вывода для управления: clock и data. Опять же, есть удобная библиотека, которая занимается передачей данных на плату.

Светодиоды

В нашем случае было шесть желтых светодиодов, которые были взяты из ночного фонаря, чтобы сделать промежуточный ночной свет, поэтому мы перепрофилировали их для этого проекта. В общей сложности нужно около 120 мА, когда они включены, но они не могут управляться Arduino напрямую, поэтому мы используем транзистор BC547 для управления ими.

Динамик

Мы добавили дешевый 8-омный стикер, который работает напрямую от одного из цифровых выходов Arduino. Существует библиотека тонов, которая поворачивает GPIO в ON и OFF достаточно быстро, чтобы создать тон.

Режим экономии энергии

Этот микроволновый контроллер должен работать от батареи. Контроллер должен отключаться и включаться. К счастью, Arduino Nano поддерживает режим сна, который уменьшает потребление энергии. После чего Nano может быть разбужен импульсом на 2 или 3 контакте.

Подавая сигнал от поворотного датчика и переключателя на эти контакты, любое вращение нажатия кнопки пробуждает процессор.

Шаг 2. Схема проекта

Теперь переходим с схеме нашего проекта и к итоговому виду нашей макетной платы.

Шаг 3. Код

Вы можете скачать или скопировать код для нашего проекта ниже:

#include <avr/sleep.h>

#include <TM1637Display.h>
#include <ClickEncoder.h>
#include <TimerOne.h>

#define SLEEP_INTERRUPT_1 2
#define SLEEP_INTERRUPT_2 3
#define SPEAKER 5
#define LIGHT 6
#define ENCODER_BUTTON 2
#define ENCODER_A 3
#define ENCODER_B 4

#define MAX_SECONDS 10 * 60
#define SLEEP_TIMEOUT 60 * 1000;

#define CLK 12
#define DIO 11

ClickEncoder *encoder;
TM1637Display display(CLK, DIO);

uint8_t data[] = {0, 0, 0, 0};
int16_t last, left, milli, sleepTimer;
bool running, showEnd, lightOn;

void timerIsr() {
  encoder->service();
  if(running) {
    milli -= 1;
  }
  sleepTimer -= 1;
}

void feedSleepTimer() {
  sleepTimer = SLEEP_TIMEOUT;
}

void goToSleep() {
  displayOff();

  Timer1.detachInterrupt();

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  attachInterrupt(digitalPinToInterrupt(SLEEP_INTERRUPT_1), wakeUp, CHANGE);
  attachInterrupt(digitalPinToInterrupt(SLEEP_INTERRUPT_2), wakeUp, CHANGE);
  sleep_mode();
  // Zzzzz. When we wake, we resume here...
  sleep_disable();
  detachInterrupt(SLEEP_INTERRUPT_2);
  detachInterrupt(SLEEP_INTERRUPT_1);

  // Run setup again, so everything is back to the initial state
  setup();
}

void wakeUp() {
  // This happens after we wake up.
  feedSleepTimer();
}

void displayOff() {
  // We may need to turn off the display using hardware...
  display.setBrightness(0, false);
  display.setSegments(data);
}

void displayOn() {
  display.setBrightness(7, true);
  display.setSegments(data);
}

void displayEnd() {
//
//      A
//     ---
//  F |   | B
//     -G-
//  E |   | C
//     ---
//      D
// XGFEDCBA
  data[0] = 0;
  data[1] = 0b01111001;
  data[2] = 0b01010100;
  data[3] = 0b01011110;
}

void displayTime(void) {
  int8_t minutes = left / 60;
  int8_t seconds = left % 60;

  uint8_t dot = (!running || milli < 500) ? 0x80 : 0x0;
    
  data[0] = display.encodeDigit(minutes / 10);
  data[1] = display.encodeDigit(minutes % 10) | dot;
  data[2] = display.encodeDigit(seconds / 10);
  data[3] = display.encodeDigit(seconds % 10);
}

void updateTime(void) {
  if(showEnd) {
    displayEnd();
  } else {  
    displayTime();
  }
  
  display.setSegments(data);
}

void setup() {
  encoder = new ClickEncoder(ENCODER_A, ENCODER_B, ENCODER_BUTTON, 4, HIGH);
  encoder->setAccelerationEnabled(true);
    
  Timer1.initialize(1000);
  Timer1.attachInterrupt(timerIsr);

  pinMode(LIGHT, OUTPUT);

  running = false;
  showEnd = false;
  lightOn = false;
  
  left = 0;
  last = 0;
  milli = 1000;
  
  feedSleepTimer();
  displayOn();
  updateTime();
}

void checkButton() {
  ClickEncoder::Button b = encoder->getButton();
  if(b == ClickEncoder::Released) {
    running = !running;
  }
}

void checkLight() {
  if(running && !lightOn) {
    digitalWrite(LIGHT, HIGH);
    lightOn = true;
  } else if(!running && lightOn) {
    digitalWrite(LIGHT, LOW);
    lightOn = false;
  }
}

void done() {
  showEnd = true;
  tone(SPEAKER, 1500, 2000);
}

void loop() {
  checkButton();
  
  left += encoder->getValue();
  
  if(left > MAX_SECONDS) {
    left = MAX_SECONDS;
  }
  
  if(milli <= 0) {
    left--;
    milli = 1000;
  }

  if(left < 0) {
    left = 0;
  }

  // If the timer is running, the display needs to be updated every 500ms, otherwise it only needs updating if the encode value changes.
  if ((running && milli / 500 % 2 == 0) || left != last) {
    if(left != last) {
      last = left;
    }
    
    if(running && left == 0) {
      running = false;
      done();
    } else {
      showEnd = false;
    }
    
    updateTime();
    feedSleepTimer();
  }

  checkLight();

  if(sleepTimer == 0) {
    goToSleep();
  }
}

Цикл

Поскольку все входы обрабатываются через прерывания (interupts), цикл просто должен иметь дело с обновлением дисплея.

Левая переменная представляет собой количество оставшихся секунд. Первое, что нужно сделать, это отрегулировать это, если энкодер меняется. Мы просто добавляем к нему дельта. Обратите внимание, что его происходит независимо от того, работает ли таймер или нет - это означает, что вы можете добавлять или удалять время, даже когда микроволновая печь работает.

Если таймер работает и светодиод выключен, мы включаем его (и наоборот).

Если таймер работает, переменная автоматически уменьшается каждые 1000 миллисекунд.

Если переменная равна 0, таймер останавливается и подается звуковой сигнал. (Светодиоды также погаснут)

Процедура отображения на дисплее вызывается каждые 500 мс. Почему каждую половину секунды? Таким образом, мы можем мигать точкой, чтобы показать, что таймер работает!

Заключение

Во время тестирования стало понятно, что на Ардуино и на дисплее остались включенные светодиоды, независимо от того, спал Ардуино или нет. Мы измерили мощность в этот момент и увидели около 15 мА. Батарея AA имеет емкость около 2400 мАч, что хватит на 160 часов или 6 дней. Ясно, что это слишком.

Мы удалили светодиоды, и это привело к снижению до 2 мА, что дает 1200 часов или 50 дней. Лучше, но не идеально. К сожалению, Nano не очень хорош как устройство с более низким энергопотреблением.

Мы также заказали повышающий преобразователь, поэтому мы можем запустить плату от двух батарей с эффективностью 95%, а не с четырьмя (на данный момент), поэтому можно просто использовать пару C-накопителей с пропускной способностью 8000 мАч (хватит почти на полгода).

На этом пока всё! Хороших вам проектов.

Ардуино+