Архив рубрики ‘Arduino’

Аудиоплеер на Arduino

Posted: Февраль 10, 2016 in Arduino

Мою статью опубликовали на сайте Паяльник. Она даже заняла утешительное 4 место и принесла мне 150 рубчиков;)

Реклама

Хочется ли вам сделать свой дом немного умнее? Всем привет! Есть простая вещь, дающая почувствовать, что вы живете в мире высоких технологий, а именно — управление бытовыми приборами через смартфоны. Делается это с помощью микроконтроллеров и, в нашем случаем, реле. Создадим простой скетч в Протеусе, где будем моделировать управление обычной лампой, питающейся из розетки 220В, с помощью Ардуино.

dispenser

Реле старо как мир, принцип его работы основан на магните, но он все так же хорошо делает свое дело — отделяет силовую часть от логической. Скетч чрезвычайно прост, сейчас я его немного поясню. Здесь в качестве смартфона выступает виртуальный терминал. В реальной жизни можно написать программку для айфона или андроида, которая будет подключаться к bluetooth модулю Ардуино и посылать команды. Команды могут быть любыми, в данном случае у меня «1» для включения, «2» для выключения. Программа просто слушает последовательный порт и посылает сигнал на транзистор, который открывается и подает напряжение на реле, которое в свою очередь срабатывает и включает/выключает лампу.

#define RELAY_PIN 13
int m_state = -1;

void setup() {
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW);

  Serial.begin(9600);
}

void loop() {
   while(m_state == -1){
    m_state = Serial.read();
  }
  switch(m_state){
    case '1' : 
      digitalWrite(RELAY_PIN, HIGH);
      break;
    case '2' : 
      digitalWrite(RELAY_PIN, LOW);
      break;
    default:
      break;
  }
 
  m_state = -1;
}

Напишем простенькое приложение, которое будет показывать нам температуру в комнате. В качестве аппаратной платформы возьмем Arduino и датчик влажности DHT11. Подключим библиотеку DHT и накидаем такой скетч

#include "DHT.h"

#define DHTPIN 2
#define DHTTYPE DHT11

DHT dht(DHTPIN, DHTTYPE);

void setup() {
  Serial.begin(9600);
  dht.begin();
}

void loop() {
  delay(2000);
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();
  Serial.println(t);
}

Он шлет температуру на последовательный порт. Теперь надо бы как-нибудь ее считывать и отображать. Но что-то не хочется ставить тяжеловесное решение вроде Visual Studio, писать программу на каком-нибудь WPF или еще круче — поднять веб сервер и отображать данные на сайте в реальном времени с помощью каких-нибудь веб сокетов… К счастью, существует замечательная платформа Processing. Более того, IDE для Arduino безбожно передирался с этого самого процессинга. В двух словах, это еще один С-образный язык, который позволяет легко делать несложные графики и анимации. Но самая интересная для нас фишка — это возможность взаимодействия с COM-портом. Можно сделать так, что цвет фона будет меняться в зависимости от температуры, но так как комнатная температура у меня не подвержена большим скачкам, что я решил просто выводить показание температуры на черном фоне. А вот и сама программа

import processing.serial.*; 

Serial port; 
float temperature =0; 

void setup() 
{ 
  size (200,200); 
  port = new Serial(this, "COM3", 9600); 
  port.bufferUntil('\n');
}

void draw () 
{ 
  background(0, 0, 0);  
  textSize(32);
  text(nf(temperature, 2, 1), 60, 100); 
} 

void serialEvent (Serial port) 
{ 
  temperature = float(port.readStringUntil('\n')); 
  println(temperature);
}

В конце увидим что-то похожее на это

Безымянный

Перейдем сразу к делу:

ArduinoIR

И машинка, и пульт дистанционного управления будут работать под управлением Arduino Uno. На схеме они обозначены Transmitter и Reciever. На передатчике у нас будет потенциометр для регулировки скорости движения, и кнопка для регулировки направления: вперед или назад. Пока что без поворотов. На приемнике у нас будет все тот же H-мост L293D и один моторчик, который будет крутить задние колеса. К сожаление, в Proteus нельзя добавить радио-приемник и передатчик, поэтому их будет симулировать провод, соединяющий 13-е порты Ардуин.
Перейдем к самой сложной части системы — шифрованию и дешифровке сигнала. Будем использовать термин «отсчет», который будет означать переполнение таймера. Начало посылки будет длиться 400 отсчетов, затем 255 отсчетов для регулировки скорости, 50 отсчетов на интервал между скоростью и направлением, и 255 отсчетов для регулировки направления. Скорость будет регулироваться ШИМ-сигналом. Насчет направления будем считать, что 255 означает вперед, 127 — назад, или наоборот.

сигнал

Для упрощения работы с таймером будем использовать библиотеку TimerOne. Далее приведен полный код приемника и передатчика с некоторыми комментариями:

// Передатчик
#include <TimerOne.h>

const int infra_red_pin = 13;
const int direction_pin = 3;
const int potentiometer_pin  = A0; 

int direction_of_motion;
int counter_cycles;
int m_speed = 0;

const int timer1_period = 1000; // Значение для переполнения мкс
const int package_start = 400;
const int package_gap = 50;

void setup() {
  pinMode(infra_red_pin, OUTPUT);
  pinMode(direction_pin, INPUT);
  pinMode(potentiometer_pin, INPUT);

  Timer1.initialize(timer1_period);
  Timer1.attachInterrupt(blinky);
}

void loop() {
  if (digitalRead(direction_pin)) {
    direction_of_motion = 127;
  }
  else {
    direction_of_motion = 255;
  }

  m_speed = map(analogRead(potentiometer_pin), 0, 1023, 0, 255);
}

// Обработка прерываний по таймеру
void blinky()
{
  if (counter_cycles == package_start) {
    digitalWrite(infra_red_pin, HIGH);
  }
  if (counter_cycles == (package_start + m_speed)) {
    digitalWrite(infra_red_pin, LOW);
  }

  if (counter_cycles == package_start + 255 + package_gap) {
    digitalWrite(infra_red_pin, HIGH);
  }
  if (counter_cycles == (package_start + 255 + package_gap + direction_of_motion)) {
    digitalWrite(infra_red_pin, LOW);
  }

  counter_cycles++;
  if (counter_cycles > package_start + (2 * 255) + package_gap) {
    counter_cycles = 0;
  }
}

// Приемник
#include <TimerOne.h>

const int infra_red_pin = 13;
const int motor_b1 = 10;
const int motor_b2 = 11;

int counter_cycles;
int m_speed = 200;

const int timer1_period = 1000; // Значение для переполнения мкс
const int package_start = 400;

void setup() {
  pinMode(infra_red_pin, INPUT);
  pinMode(motor_b1, OUTPUT);
  pinMode(motor_b2, OUTPUT);

  Timer1.initialize(timer1_period);
  Timer1.attachInterrupt(blinky);
}

void loop() {
  // ждем начала пакета
  while(digitalRead(infra_red_pin)!=0);
  counter_cycles = 0;
  while(digitalRead(infra_red_pin)!=1);

  if (counter_cycles < 400) return; //Если пауза не выдержана...

  // считываем скорость
  while(digitalRead(infra_red_pin)!=0);
  m_speed = counter_cycles;

  // считываем направление
  while(digitalRead(infra_red_pin)!=1);
  counter_cycles = 0;
  while(digitalRead(infra_red_pin)!=0);

  if(counter_cycles < 50){
    stop_car();
  }else if(counter_cycles < 150){
    forward();
  }else{
    backward();
  }

  counter_cycles = 0;
}

// Обработка прерываний по таймеру
void blinky()
{
  counter_cycles++;
}

// Остановиться
void stop_car(){
  analogWrite(motor_b1, LOW);
  analogWrite(motor_b2, LOW);
}

// Едем вперед
void backward(){
  analogWrite(motor_b1, m_speed);
  analogWrite(motor_b2, LOW);
}

// Едем назад
void forward(){
  analogWrite(motor_b1, LOW);
  analogWrite(motor_b2, m_speed);
}

В прошлой части было рассказано как сделать радиоуправляемую машинку с использованием ATtiny2313 на языке ассемблер. Теперь давайте посмотрим как ее можно сделать с использованием Arduino. Возьмем самую популярную модель Arduino Uno. Она хороша тем, что в ней есть АЦП. Его мы будем использовать для регулировки скорости движения. Сама регулировка будет осуществляться с помощью обычного потенциометра. Еще хотелось бы управлять такой машинкой дистанционно, например через последовательный порт компьютера, но тогда нам нужен будет провод, или через Bluetooth мобильного телефона. В Proteus есть замечательная вещь, которая называется «Виртуальный терминал», он нам заменит оба этих случая, достаточно только подключить RX с TX и наоборот. Теперь можно считать, что либо мы протянули длинный провод от компьютера, либо поставили на Arduino какой-нибудь Bluetooth shield или HC-05 модуль. Во втором случае можно даже написать простенькую программку для смартфона под управлением ОС Android или, зная команды, которые мы сейчас придумаем, просто скачать любой bluetooth терминал.
Схема в Proteus будет выглядеть следующим образом:
ArduinoBluetooth
В качестве H-моста используется все тот же L293D. По-хорошему, скорость тоже надо бы передавать через блютуз, но, чтобы продемонстрировать работу АЦП, сделаем такую химеру, где направление движения передается от пульта, а скорость регулируется на самой машинке. Жуть, но с такими примерами программисты сталкиваются практически каждый день 😉 Еще хорошо было бы добавить второй мотор для возможности поворачивать, но тут очень сильно зависит от конструкции самой машинки: либо как трактор будет управляться включением только одной гусеницы, либо серводвигатель будет поворачивать специальную конструкцию. Так что пока ограничимся одним моторчиком.
Система команд будет следующая:
«f» — вперед
«b» — назад
«s» — стоп
Простенький скетч для ардуино

const int pot_pin  = A0;
const int motor_b1 = 10;
const int motor_b2 = 11;

int m_potentiometer = 0;
int m_speed = 0;
int m_state = 0;

void setup() {
  pinMode(pot_pin, INPUT);
  pinMode(motor_b1, OUTPUT);
  pinMode(motor_b2, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  while(m_state == -1){
    m_state = Serial.read();
  }

  m_potentiometer = analogRead(pot_pin);
  m_speed = map(m_potentiometer, 0, 1023, 0, 255);

  switch(m_state){
    case 'f' :
      forward();
      break;
    case 'b' :
      backward();
      break;
    case 's' :
      stop_car();
      break;
    default:
      break;
  }

  m_state = -1;
}

void stop_car(){
  analogWrite(motor_b1, LOW);
  analogWrite(motor_b2, LOW);
}

void backward(){
  analogWrite(motor_b1, m_speed);
  analogWrite(motor_b2, LOW);
}

void forward(){
  analogWrite(motor_b1, LOW);
  analogWrite(motor_b2, m_speed);
}

Запустим симуляцию в протеусе и будем в виртуальном терминале посылать наши команды.
Безымянный