Показаны сообщения с ярлыком проект. Показать все сообщения
Показаны сообщения с ярлыком проект. Показать все сообщения

воскресенье, 11 августа 2013 г.

Еще одни часы на Ардуине. Часть 2. Выводим время на экран

В прошлой части Еще одни часы на Ардуине. Часть 1. Запускаем часы мы запустили модуль часов DS1307 и научились выводить время в Serial. Но пользоваться такими часами, конечно невозможно. Открывать Serial монитор для что-бы узнать "который час" :)
Так что, сегодня, мы

Подключаем экран:

Мне повезло. В случае Arduino Mega подключение экрана сводится к простому "нахлобучиванию" его на плату (главное, при первом втыкании  не торопится и не погнуть пины). У Меги SDA/SLC не закрываются шилдом. А 5V и GND дополнительно выведено возле D23 и D25. Так что LCD шилд ничуть не мешает подключению RTC шилда.

В случае же UNO - придется чуть-чуть взять в руки паяльник.
Вам потребуется:


Гнезда:

  •  PBS-5 - для выведения вверх SDA/SLC
  • PBS-6 - для выведения земли и питания
  • PBS-7 - что-бы вывести цифровые пины (пока мы их не используем, но что-бы два раза не вставать).

Впаиваем их сюда:


Там специально, производителем, именно для этих целей, оставлены дырочки-площадки.

В результате мы получим что-то такое:

[тут-будет-картинка]

Тут для примера впаяна PBD-5 и в которую воткнут проводок.

Заливаем в дуину такой скетч проверки экрана:
 
#include "LiquidCrystal.h" // библиотека экрана

// select the pins used on the LCD panel
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup(){
   lcd.begin(16, 2); // запускаем библиотеку
   lcd.setCursor(0,0); // курсов в левый верхний угол экрана
   lcd.print("Hello from LCD!!");
}
void loop(){}


Видим на экрнае такое:

[тут-будет-картинка]

И радуемся правильно подключенному экрану. Если необходимо, плохо видно буквы или, наоборот сквозь буквы проступают "квадратики" фоновые, берем тонкую плоскую отвертку (или кончиком ножа) и крутим регулятор контрастности (винтик в верхнем левом углу шилда).


Который час?

Теперь у нас все готово к выводу времени на экран.  Подключаем RTC модуль (если мы его отключали), берем финальный скетч из  первой части и добавляем в него инициализацию экрана. Строки 1,4,7 из "скетч проверки экрана" прошлого раздела (выделенные строки).

После чего у нас все готово, что-бы написать функцию вывода времени на экран.

Делаем все практически аналогично тому как в прошлой части выводили в Serial. Только функцию называем printTimeToLCD()

 
void printTimeToLCD(){
    byte static prevSecond=0; // тут будем хранить, сколько секунд было при выводе
    
    if(RTC.second!=prevSecond){ // что-то делаем только если секунды поменялись
       lcd.setCursor(0,0); // устанавливаем позицию курсора
       
      lcd.print(RTC.hour); // часы
    
      lcd.print(":"); // разделитель
      lcd.print(RTC.minute);
    
      lcd.print(":"); 
      lcd.print(RTC.second);
    
    prevSecond=RTC.second; // запомнили когда мы "отчитались"
  }
}

Добавляем вызов этой функции в loop(), заливаем в дуину и... мы сделали свои, пусть и неказистые, но первые электронные часы!!! :) На экране видно текущие время и "меняющие секунды".

А причесать?

Попробуем добавить чуть-чуть "эстетичности". Что мне не нравится в текущем варианте

  1. Хочется выводить время "по центру", а не в углу
  2. Убрать секунды. Мне они не нужны. Пусть лучше мигает двоеточие между часами и минутами (что-бы было видно что "секунды идут").
  3. Привести время к более привычному формату "16:03",  а не как сейчас "16:3"
  4. Если присмотрется в момент смены секунд видно легкое "помаргивание" надписи, она как-бы "пригасает".
Первый пункт - самый простой. Что-бы подвинуть время, нам достаточно сделать

lcd.setCursor(5,0).

Секунды - просто выкидываем :) А разделитель между часами и минутой показываем либо пробелом либо двоеточием. В зависимости от того четная или не четная у нас секунда

lcd.print( (RTC.second %2 )?" ":":");

Что осталось? Дописать "0" перед минутами если они меньше 10-ти

if(RTC.minute<10)lcd.print(0);

Аналогично для часов. Только добавлять впереди будем пробел, а не "0".  Ноль, вперди часов - выглядит некрасиво, а "что-то дописать" - нужно. Что-бы позиция времени не смещалась влево на малых значениях часа.

Наша функция вывода времени приняла вид:

 

void printTimeToLCD(){
    byte static prevSecond=0; // тут будем хранить, сколько секунд было при выводе
    
    if(RTC.second!=prevSecond){ // что-то делаем только если секунды поменялись
       lcd.setCursor(5,0); // устанавливаем позицию курсора
       
      if(RTC.hour<10)lcd.print(" ");
      lcd.print(RTC.hour); // часы
    
      lcd.print( (RTC.second % 2)?" ":":"); // разделитель моргает
      
      if(RTC.minute<10)lcd.print(0); // лидирующий ноль, если нужен
      lcd.print(RTC.minute);
    
    prevSecond=RTC.second; // запомнили когда мы "отчитались"
  }
}

В принципе, 4-тый пункт, "помаргивание" - уже не виден на глаз. Так что усложнять код, для борьбы с ним - мы пока не будем.

Сегодняшний наш финальный скетч таков:

 

// Author: alxarduino@gmail.com
// Sample Clock App for http://alxarduino.blogspot.com

// Библиотеки необходимые для работы модуля часов
#include "Wire.h"
#include "DS1307new.h"

// библиотека экрана
#include "LiquidCrystal.h" 
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup(){
  Serial.begin(9600);
  if(RTC.isPresent()){ // обнаружен ли модуль?
    Serial.println("RTC Ready"); // все хорошо
  } else {
    Serial.println("Error!!!! RTC Module not found"); // сообщаем о проблеме
    while(1); // и останавливаем скетч
  }
  
  lcd.begin(16, 2); // запускаем библиотеку экрана
}

void loop(){
  RTC.getTime();// получить время от модуля
  printTimeToSerial(); // выводим полученное время в лог
  printTimeToLCD();; // выводи время на экран
  doSerialCommands(); // слушаем и выполняем команды из Serial
}

// Выводит текущие время в Serial
void printTimeToSerial(){

  byte static prevSecond=0; // тут будем хранить, сколько секунд было при прошлом отчете
  
  if(RTC.second!=prevSecond){ // что-то делаем только если секунды поменялись
    Serial.print(RTC.hour); // часы
  
    Serial.print(":"); // разделитель
    Serial.print(RTC.minute);
  
    Serial.print(":"); 
    Serial.println(RTC.second);
    
    prevSecond=RTC.second; // запомнили когда мы "отчитались"
  }
}

// Выводит текущие время на LCD

void printTimeToLCD(){
    byte static prevSecond=0; // тут будем хранить, сколько секунд было при выводе
    
    if(RTC.second!=prevSecond){ // что-то делаем только если секунды поменялись
       lcd.setCursor(5,0); // устанавливаем позицию курсора
       
      if(RTC.hour<10)lcd.print(" ");
      lcd.print(RTC.hour); // часы
    
      lcd.print( (RTC.second % 2)?" ":":"); // разделитель моргает
      
      if(RTC.minute<10)lcd.print(0); // лидирующий ноль, если нужен
      lcd.print(RTC.minute);
    
    prevSecond=RTC.second; // запомнили когда мы "отчитались"
  }
}

// устанавливает часы модуля на какое-то заранее определенное время
void setSomeTime(){
  RTC.stopClock();// останавливаем часы
  RTC.fillByHMS(19,15,0); // "подкручиваем стрелки на 19:15:00
  RTC.setTime();// отправляем "подкрученное время" самому модулю
  RTC.startClock(); // и опять запускаем часы
}

// слушает из Serial команды и выполняет их. Каждая команда - один символ.
// доступны команды:
//  s - установить время указанное в функции setSomeTime()
void doSerialCommands(){
  if(Serial.available()){ // что-нибудь пришло?
    char ch=Serial.read(); // читаем что пришло
    
    switch(ch){
      case 's': // команда установки времени
           setSomeTime(); // устанавливаем
           break;
           
       // тут, в будущем, мы можем добавлять дополнительные команды
      default:;
           // на неизвестную команду - ничего не делаем
    };
  }
}

На LCD Это выглядит примерно так:
В В реальности, конечно все симпатичней, использовать фотоаппарат я умею только "в автоматическом режиме".

В следующей части мы будем учится работать с кнопками шилда. С учетом дребезга, настройкой чувствительности, отличать "короткое" и "длинное" нажатие и т.п.

суббота, 10 августа 2013 г.

Еще одни часы на Ардуине. Часть 1. Запускаем часы


Для следующего проекта, попались мне в руки два таких шилда:

Arduino LCD KeyPad Shield (SKU: DFR0009) - Robot Wiki
Текстовый экран 16x2 плюс четыре кнопки с помощью делителя напряжения заведенные на пин A0
И шилд часов реального времени: Real Time Clock Module (DS1307) V1.1 (SKU:DFR0151) - Robot Wiki


Тут могла быть ваша реклама вашего магазина шилдов :)

Руки зачесались...
Понятно, что это 1001 проект  "часы на ардуине" и можно просто взять что-то готовое. Но у меня две цели: поиграться с модулями и показать как "вырастает проект". Правильный, с моей точки зрения, процесс. В противовес тому, как многие новички пытаются "накупить железо", составить 1000 "хотелок" и запустить все это сразу.

Итак, раз мы будем идти мелкими шагами - откладываем пока наш экран в сторону. Для начала освоимся с часами.

Железная составляющая


Подключаем их в соотвествии с wiki-производителя:
Линию DS - можно, пока, не подключать (пунктирная линия). С ней будем разбиратся позже.

Но у меня плата - Arduino Mega. У нее шина I2C (по ней передает данные модуль часов) расположены не на A4,A5, а на D20,D21
Поэтому я подключал SDA->D20 ,  SLC->D21

Если у вас другая плата - можете сверится с этой табличкой:
ПлатаI2C / TWI pins
Uno, EthernetA4 (SDA), A5 (SCL)
Mega256020 (SDA), 21 (SCL)
Leonardo2 (SDA), 3 (SCL)
Due20 (SDA), 21 (SCL)

Щупаем програмно

Что-бы комфортно работать с часами, на закапываясь в даташиты нам потребуется скачать и установить библиотеку для DS1307. Производитель модуля - предоставляет ее, но мне больше понравилась альтернативная. Вот эта: ds1307new - DS1307 RTC Library with NV-RAM support - Google Project Hosting
Скачиваем ее, распаковываем, забрасываем в папку libraries и пишем первый наш код, проверяющий видит ли ардуина наши часы:
   // Библиотеки необходимые для работы модуля часов
#include <Wire.h>
#include <DS1307new.h>

void setup(){
  Serial.begin(9600);
  if(RTC.isPresent()){ // обнаружен ли модуль?
    Serial.println("RTC Ready"); // все хорошо
  } else {
    Serial.println("Error!!!! RTC Module not found"); // сообщаем о проблеме
    while(1); // и останавливаем скетч
  }
}

void loop(){
}

Заливаем, открываем Serial монитор и надпись:


Для перепроверки, отсединяем, скажем, линю SLC. Закрываем, открываем монитор (перезапускаем скетч) и видим что надпись у нас сменилась на "Error!!!! RTC Module not found".
Возвращаем проводок на место и опять видим "RTC Ready". Наш "детектор проблем" - работает.

А который час?

На данный момент, мы знаем что "модуль часов работает", но хочется увидить "хотя-бы время" (работу с датой я опущу, для сокращения размера статьи - там все аналогично).

Ну что-же давай попробуем вывести время. Что-бы сразу привыкать к "хорошим манерам", не будем сильно захламлять loop(), вынесем вывод времени в отдельную функцию и назовем ее printTimeToSerial()  и будем ее вызвать из loop()

void loop(){
  RTC.getTime();// получить время от модуля
  printTimeToSerial(); // выводим полученное время
}
 
void printTimeToSerial(){
  Serial.print(RTC.hour); // часы
  
  Serial.print(":"); // разделитель
  Serial.print(RTC.minute);
  
  Serial.print(":"); 
  Serial.println(RTC.second);
}
В логе (сериал мониторе видим) что-то типа:
17:53:35
17:53:35
17:53:35
17:53:35
17:53:35

Много, много раз...
C одной стороны - хорошо. Модуль посылает нам время и мы научились отправлять его в Serial. С другой... он старается делать это как можно чаще. Насколько хватает пропускной способности Serial. А нам - это не нужно :) Столько дублирования. Поэтому давайте отправлять время в Serial только если, скажем секунды, изменились с прошлого вызова функции.
// Выводит текущие время в Serial
void printTimeToSerial(){

  byte static prevSecond=0; // тут будем хранить, сколько секунд было при прошлом отчете
  
  if(RTC.second!=prevSecond){ // что-то делаем только если секунды поменялись
    Serial.print(RTC.hour); // часы
  
    Serial.print(":"); // разделитель
    Serial.print(RTC.minute);
  
    Serial.print(":"); 
    Serial.println(RTC.second);
    
    prevSecond=RTC.second; // запомнили когда мы "отчитались"
  }
}


Теперь наш лог, примет такой вид:

RTC Ready
18:0:26
18:0:27
18:0:28
18:0:29

Каждая надпись - появляется раз в секунду. Можно дествительно смотреть как "часы идут".

 Кстати, возможно вы заметили что время выглядит "не очень красиво", вмето привычных 18:00:26, у нас получилось 18:0:26. Но так как работа с Serial у нас "вспомогательная", мы не будет отвлекатся на решение этой проблемы. Но вернемся к ней и будем "наводить красоту", когда время будет показываться уже на LCD экране.


А точнее?

Получать время - это хорошо. Но его же нужно еще уметь устанавливать. В конечном итоге - будем делать это с помощью кнопок, но пока... сделаем временное решение. Самое простейшие: забъем нужное нам время прямо в скетч. И будем его посылать модулю, если из Serial нам пришел символ 's'

Функция установки времени:

void setSomeTime(){
    RTC.stopClock();// останавливаем часы
    RTC.fillByHMs(19,15,0); // "подкручиваем стрелки на 19:15:00
    RTC.setTime();// отправляем "подкрученное время" самому модулю
    RTC.startClock(); // и опять запускаем часы
}

Функция которая слушает команды из Serial

 void doSerialCommands(){
  if(Serial.available()){ // что-нибудь пришло?
    char ch=Serial.read(); // читаем что пришло
    
    switch(ch){
      case 's': // команда устновки времени
           setSomeTime(); // устанавливаем
           break;
           
       // тут, в будущем, мы можем добавлять дополнительные команды
      default:;
           // на неизвестну команду - ничего не делаем
    };
  }
} 

Все. Нам осталось только вставить вызов doSerialCommand() в loop()  для регулярного опроса команда из Serial. После чего наш скетч принимает вид:

// Author: alxarduino@gmail.com
// Sample Clock App for http://alxarduino.blogspot.com

// Библиотеки необходимые для работы модуля часов
#include "Wire.h"
#include "DS1307new.h"

void setup(){
  Serial.begin(9600);
  if(RTC.isPresent()){ // обнаружен ли модуль?
    Serial.println("RTC Ready"); // все хорошо
  } else {
    Serial.println("Error!!!! RTC Module not found"); // сообщаем о проблеме
    while(1); // и останавливаем скетч
  }
}

void loop(){
  RTC.getTime();// получить время от модуля
  printTimeToSerial(); // выводим полученное время
  doSerialCommands(); // слушаем и выполняем команды из Serial
}

// Выводит текущие время в Serial
void printTimeToSerial(){

  byte static prevSecond=0; // тут будем хранить, сколько секунд было при прошлом отчете
  
  if(RTC.second!=prevSecond){ // что-то делаем только если секунды поменялись
    Serial.print(RTC.hour); // часы
  
    Serial.print(":"); // разделитель
    Serial.print(RTC.minute);
  
    Serial.print(":"); 
    Serial.println(RTC.second);
    
    prevSecond=RTC.second; // запомнили когда мы "отчитались"
  }
}

// устанавливает часы модуля на какое-то заранее определенное время
void setSomeTime(){
  RTC.stopClock();// останавливаем часы
  RTC.fillByHMS(19,15,0); // "подкручиваем стрелки на 19:15:00
  RTC.setTime();// отправляем "подкрученное время" самому модулю
  RTC.startClock(); // и опять запускаем часы
}

// слушает из Serial команды и выполняет их. Каждая команда - один символ.
// доступны команды:
//  s - установить время указанное в функции setSomeTime()
void doSerialCommands(){
  if(Serial.available()){ // что-нибудь пришло?
    char ch=Serial.read(); // читаем что пришло
    
    switch(ch){
      case 's': // команда установки времени
           setSomeTime(); // устанавливаем
           break;
           
       // тут, в будущем, мы можем добавлять дополнительные команды
      default:;
           // на неизвестную команду - ничего не делаем
    };
  }
}


Заливаем.Открываем Serial Монитор. Набираем s (в нижнем регистре) и нажимает "отправить"

Итого:

  • Мы подключили модуль часов DS1307. 
  • Убедились что Ардуина "его видит" (и сообщает нам если не видит). 
  • Научились получать от модуля время и отправлять его в Serial.
  • Научились устанавливать время модуля по команде из Serial

В следующей части: мы подключим уже LCD и выведем время на него

суббота, 12 января 2013 г.

Тахометр для мотоцикла. Часть 1. Знакомство с задачей

Тахометр для мотоцикла. Часть 1 . Знакомство с  задачей

На дня, ко мне обратился знакомый с просьбой оказать помощь сделать тахометр для мотоцикла. Кроме "самого тахометра" его интересовало "познакомится с Ардуиной", которой я ему все уши прожужжал :)

В постановке задачи тахометр должен быть "стрелочный", а обороты двигателя приходить Ардуине в виде "импульсов".   Так же, конечной целью было желание что-бы стрелка могла пробегать весь диапазон за секунду максимум (или намного быстрее :)), но при этом не было видно "ее шагов". Все должно было быть "плавно". При этом - не должно сбиваться/дрожать от внешних вибраций (на мотоцикле их намного больше чем в машине).

И вишенкой на всем этом пироге "хотелок" было "положено" - недорого, из подручного хлама. То есть о покупке качественных, дорогих приводов, микросхем и проч. - речи не идет (как и покупке готового). В случае удачи - опыт будет распространен на другие приборы. В итоге будет новая приборная панель для Viper F5.

Привод стрелки планировалось сделать на шаговике (он уже был в наличии, из старого FDD). Это и предопределило мое согласие. В живую, держа в руках, я с шаговыми двигателями еще не игрался (хотя, конечно уже был знаком с ними "удаленно"), мне это было интересно.

Поэтому я решил "потратить на это время", поиграется и попробовать описать эти игры  в виде серии статей блога (в блоговедении - у меня тоже ограниченный опыт). Поэтому не стоит их рассматривать как "туториал". Я просто разбираюсь и описываю то что понял. Проект не закончен, он в процессе и "куда вывезет" - пока не понятно и мне самому.


Любое решение подобных задач начинается с "дробления на подзадачи" (типичная ошибка новичков - пытаются написать "сразу готовый скетч"). Нужно  решать их по отдельности, а потом  "соединяем все вместе".

Дробим на подзадачи:


Обычная архитектура для скетчей более сложных чем "мигаем диодом" является три блока/слоя:

Типичный блоки
  1. Блок чтения данных (кнопки, датчики)
  2. Блок принимающий решения основываясь на данных полученных на предыдущем шаге. Основная "логика" программы.
  3. Управление исполнительными устройствами. Исполнение того что "решил предыдущий блок". Включение моторов, реле, серв и т.п.

В случае тахометра "принимать решение" нам не нужно. Что прочитали, то и показали. Соответственно одна часть у нас "выпала" и остались две:
  1. Замер оборотов
  2. Управление стрелкой

Измерение оборотов

Мой товарищ был готов был сделать внешнюю обвязку которая будет "импульсы" конвертировать в "напряжение", которое очень просто прочитать Ардуиной через analogRead  (в отличие от меня - "схемотехники" у него опыта больше, и "железные решения" - не пугают), но я его отговорил. По старой программистской привычке: если что-то можно сделать программно (относительно просто) - будем делать программно, а не "железно". Не может сломаться та деталь которой нет :)
Итак решено: будем считать импульсы от датчика холла.

Управление стрелочкой

Проще всего стрелочку было-бы крутить с помощью сервы. В Ардуине есть встроенная библиотека для этого, серва умеет "помнить свою позицию".

Но мы решили не искать легкий путей. Серва - относительно дорога. К тому же, как правило, свою позицию серва определяет с помощью внутреннего потенциометра (переменного резистора, как "крутилка" у старых телевизоров  что имеет фатальный недостаток - при активном "кручении"  потенциометр начинает изнашиваться и серва "дрожит".
А так как при езде, обороты прыгают постоянно, стрелка постоянно "мечется", мы поняли что решение на серве хоть и просто, но будет очень не долговечным.

Заменой серве был выбран шаговый двигатель. Он менее удобен так как не умеет "помнить свою позицию", придется самому об этом заботится. Но нас этим не испугать :) . Шаговый двигатель был выдран из старого 3.5' дисковода (он отвечал за перемещение карретки) и....мы приступили (продолжение будет...)