Часы для любимой тещи.

Жили-были часы. Обычные, стрелочные. И постоянно с ними что-то случалось. То они отставали, то наоборот вперед убегали. И тикали. И ночью их было не видно.

Но у нас же на дворе 21 век! Меняем часы на цифровые. С большими цифрами! Пробег по магазинам показал, что хоть часы с большими цифрами и присутствуют на прилавках, но все поголовно имеют один большой минус: они светятся одной и той же яркостью. В итоге солнечным днем их не видно, а ночью они подобно прожектору слепят всех. Не годится.

Одновременно все магазинные часы требуют полочки или какой-либо ровной горизонтальной поверхности. Опять минус для отдельно взятого дома.

В общем, было решено взять и сделать часы самому.

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

Далее была взята Arduino Leonardo (можно взять абсолютно любую ардуинку, её возможности будут использованы процентов на 5). Почему леонарда? Просто потому что  была под рукой.

В качестве хранилки времени был взят наборчик от мастеркита, рядом нашлось пара кнопок и фотодиод. Как их подключать, можно прочитать в предидущем тут https://blog.kiltum.tech/2013/09/22/Многофункциональный-термометропока/

Внимание! Индикатор подключается абсолютно точно так же, как и тут: http://www.lucadentella.it/en/category/ledmatrix_ht1632c/ Расположенные в сети библиотеки с похожими названиями не работают, так как рассчитаны на другую схему подключения.

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

Все это долгое время валялось у меня на столе разобранным, пока я не взял себя за шкирку и не собрал все в одну кучу. Так как схема не просто простая, а очень простая, то я тупо собрал все навесным монтажем.

20131113_141210

Заодно опробовал и клеевой пистолет: вещь! Особенно для монтажа такого уровня.

Итак, что и как соединено по фотографии. Слева – направо.

Снаружи подходит обычный micro-usb кабель. Лично мне оказалось проще всего  обеспечить схему питанием +5В – у меня валяется много зарядок от телефонов.

Далее две кнопки, посаженные на analog in и фотодиод, выведенный на верхнюю поверхность часов.

С батарейкой – это часы реального времени от мастер-кита.

Далее сама ардуинка и провода, уходящие на индикатор.

Внимание! Индикатор в режиме “все включено на максимум” потребляет примерно 300мА. Встроенный в ардуинку стабилизатор попросту не выдержит. В данной схеме я воспользовался грязным хаком и подцепил питание индикатора на VIN. По схеме он зацеплен прямо на +5В от USB, а там предел 500мА. В общем, не повторяйте этого, если не знаете что делаете.

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

Ну и как же без кода.

#include “Wire.h”
#include “RTClib.h”
#include <avr/pgmspace.h>

// HT1632C PINs

#define DISPLAY_CS 5
#define DISPLAY_WR 6
#define DISPLAY_DATA 7

// HT1632C Commands

#define HT1632C_READ B00000110
#define HT1632C_WRITE B00000101
#define HT1632C_CMD B00000100

#define HT1632_CMD_SYSON 0x01
#define HT1632_CMD_LEDON 0x03
#define HT1632_CMD_PWM 0xA0

// where PIN button is
#define BUTT_H A8
#define BUTT_M A10

int photoRPin = 0; // where photo is
int minLight;
int maxLight;
int lightLevel;
int adjustedLightLevel;
byte display_buffer[32];
long previous_millis;

RTC_DS1307 RTC;

// 7 width 8 – height
PROGMEM char font[] = {
B01111110, // 0
B11111111,
B10000001,
B10000001,
B11111111,
B01111110,

// 1
B01000001,
B11111111,
B11111111,
B00000001,
B00000000,
B00000000,

B01100001,
B11100011, // 2
B10000111,
B10001101,
B11111001,
B01110001,

B01100110, // 3
B11100111,
B10000001,
B10010001,
B11111111,
B01101110,

B11110000,
B11111000, // 4
B00001000,
B00001000,
B11111111,
B11111111,

B11110010,
B11110011, // 5
B10010001,
B10010001,
B10011111,
B10001110,

B01111110,
B11111111, // 6
B10010001,
B10010001,
B11011111,
B01001110,

B10000000, // 7
B10000111,
B10001111,
B10011000,
B11110000,
B11100000,
B01101110,
B11111111, // 8
B10010001,
B10010001,
B11111111,
B01101110,
B01110010,
B11111011, // 9
B10001001,
B10001001,
B11111111,
B01111110,
};

PROGMEM int fontwidth[] = {
6,4,6,6,6,6,6,6,6,6
};

void show_string(String st)
{
ht1632c_clear_display();

int middle=15-1;

show_digit(middle-pgm_read_byte_near(fontwidth+st.charAt(1)-‘0’)-pgm_read_byte_near(fontwidth+st.charAt(0)-‘0’)-1,st.charAt(0)-‘0’);

show_digit(middle-pgm_read_byte_near(fontwidth+st.charAt(1)-‘0’),st.charAt(1)-‘0’);

middle=17+1;

show_digit(middle,st.charAt(2)-‘0’);

show_digit(middle+pgm_read_byte_near(fontwidth+st.charAt(2)-‘0’)+1,st.charAt(3)-‘0’);

}

 

void show_digit(int pos, int digit)
{
for(int i=0;i<pgm_read_byte_near(fontwidth+digit);i++) display_buffer[pos+i]=pgm_read_byte_near(font+digit*6 + i);
}

void show_dots(int on)
{
if(on==1)
{
display_buffer[15]=B01100110;
display_buffer[16]=B01100110;
}
else
{
display_buffer[15]=0;
display_buffer[16]=0;
}
}
// —————————————-
// HT1632C functions
// —————————————-

void ht1632c_clear_display() {
for(int i = 0; i < 32; i++) display_buffer[i] = 0x00;
//ht1632c_display_buffer();
}

void ht1632c_display_buffer() {

digitalWrite(DISPLAY_CS, LOW);
ht1632c_send_bits(HT1632C_WRITE, 1 << 2);
ht1632c_send_bits(0x00, 1 << 6);
for(int i = 0; i < 32; i++) ht1632c_send_bits(display_buffer[i], 1<<7);
digitalWrite(DISPLAY_CS, HIGH);
}

void ht1632c_send_command(byte command) {

digitalWrite(DISPLAY_CS, LOW);
ht1632c_send_bits(HT1632C_CMD, 1 << 2);
ht1632c_send_bits(command, 1 << 7);
ht1632c_send_bits(0, 1);
digitalWrite(DISPLAY_CS, HIGH);
}

void ht1632c_send_bits(byte bits, byte firstbit) {

while(firstbit) {
digitalWrite(DISPLAY_WR, LOW);
if (bits & firstbit) digitalWrite(DISPLAY_DATA, HIGH);
else digitalWrite(DISPLAY_DATA, LOW);
digitalWrite(DISPLAY_WR, HIGH);
firstbit >>= 1;
}
}

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

pinMode(DISPLAY_CS, OUTPUT);
pinMode(DISPLAY_WR, OUTPUT);
pinMode(DISPLAY_DATA, OUTPUT);

pinMode(BUTT_H,INPUT);
pinMode(BUTT_M,INPUT);
digitalWrite(BUTT_H,HIGH);
digitalWrite(BUTT_M,HIGH);

// enable System oscillator and LED duty cycle generator
ht1632c_send_command(HT1632_CMD_SYSON);
ht1632c_send_command(HT1632_CMD_LEDON);
ht1632c_send_command(HT1632_CMD_PWM+15); // full light!

ht1632c_clear_display();
previous_millis=0;
Wire.begin();
RTC.begin();
RTC.adjust(DateTime(__DATE__, __TIME__));

minLight=200;
maxLight=800;
}

int dig=1000;

String c_to(int dig)
{
String ret(dig);
if(dig<10)
return “0”+ret;
else
return ret;
}

char s;
String txtMsg;

void loop ()
{

while (Serial.available() > 0) {
s=(char)Serial.read();
if (s == ‘n’) {
if(txtMsg.charAt(2)==’:’)
{
DateTime nw = RTC.now();
DateTime nq = DateTime(nw.year(),nw.month(),nw.day(),String(txtMsg.substring(0,2)).toInt(),String(txtMsg.substring(3,5)).toInt(),0);
RTC.adjust(nq);
Serial.println(“Time is changed”);
}
else
{
Serial.println(“To change time, just print current time like ’12:24′”);
}
txtMsg=””;
} else {
txtMsg +=s;
}
}

if(analogRead(BUTT_H)<500)
{
delay(150);
if(analogRead(BUTT_H)<500) // if still pressed
{
DateTime nw = RTC.now();
DateTime nq = DateTime(nw.year(),nw.month(),nw.day(),nw.hour()+1,nw.minute(),nw.second());
RTC.adjust(nq);
previous_millis=millis()-1100;
//delay(100);

}}

if(analogRead(BUTT_M)<500)
{
delay(150);
if(analogRead(BUTT_M)<500) // if still pressed
{
DateTime nw = RTC.now();
DateTime nq = DateTime(nw.year(),nw.month(),nw.day(),nw.hour(),nw.minute()+1,nw.second());
RTC.adjust(nq);
previous_millis=millis()-1100;
//delay(100);

}}

long current_millis = millis();

if(current_millis – previous_millis > 1000) {
previous_millis = current_millis;
// do it

DateTime now = RTC.now();

show_string(String(c_to(now.hour())+c_to(now.minute())));

if((now.second() % 2) == 0)
show_dots(1);
else
show_dots(0);

Serial.print(now.day());
Serial.print(“/”);
Serial.print(now.month(), DEC);
Serial.print(“/”);
Serial.print(now.year(), DEC);
Serial.print(” “);

Serial.print(now.hour(), DEC);
Serial.print(“:”);
Serial.print(now.minute(), DEC);
Serial.print(“:”);
Serial.print(now.second(), DEC);
Serial.print(” l:”);

ht1632c_display_buffer();

// now ajust brightness

lightLevel=analogRead(photoRPin);
// Serial.println(lightLevel);
if(minLight>lightLevel)
{
minLight=lightLevel;
}
if(maxLight<lightLevel)
{
maxLight=lightLevel;
}

//Serial.println(maxLight-minLight);
adjustedLightLevel = map(lightLevel, minLight, maxLight, 0, 15);
Serial.println(adjustedLightLevel);

ht1632c_send_command(HT1632_CMD_PWM+adjustedLightLevel);

}
}

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

20131113_165232

 

Здесь часы собраны и расположены на испытательном стенде – обычной маркерной доске.

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

Многофункциональный термометропоказометр

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

– Иметь вход для ACC с замка зажигания.
Пока зажигание выключено: мигать светодиодом, как будто это сигналка.
При включении зажигания: первые 10 секунд показывать  показания заранее выбранного датчика температуры. Потом показывать время.
– Иметь вход для CarPC. Плата должна уметь рассказывать о себе, сообщать что к ней подключено и показывать то, что попросят.

– Иметь возможность обрабатывать 4 “ветки” датчиков. Скорость обработки 1 секунда на один датчик на каждой ветке.
– Иметь возможность регулировки яркости.
– Все светящееся должно светить зеленым светом. За синее – расстрел на месте.
– Работать должно на Arduino Micro.

В общем, ничего сложного, все описано в интернете не раз и не два, но по кусочкам.  Моя задача собрать все эти кусочки в один и “смазать” работой с CarPC. Ну и как обещал, параллельно выкладываю пошаговую инструкцию с краткими пояснениями.

Для начала решил выяснить, чем отличается Arduino Micro от CarDuino

– Первый итальянский, второй наш. Без разницы.
– Первый дороже второго почти на 500 рублей. 1200 супротив 700. Плохо, но терпимо.
– У micro по другому сделано общение с компьютером. Драйвера стандартные, при подключении компьютера сброса по умолчанию нет, можно спокойно пользовать RX и TX ножки. Это очень хорошо.
– У micro стандартные значения питания. Его, в отличии от CarDuino, напрямую от бортсети не запитаешь. Мелочь, но потребует как минимум одного лишнего элемента в схеме.
– У micro нет ножек SPK и HV12. Беда, печаль и огорчения. Абсолютно несущественно.

Теперь к индикаторам. Беглый поиск показал, что хотя у меня есть зеленый светодиодик, но вот индикатора с таким светом нет. Гугл дал ссылку на http://pacpac.ru/product/com-11440-7-segment-serial-display-green/ , он же COM-11440. Зелененький, 4 цифры, умеет дофига всего. Беру.

Для определения освещенности нужен фоторезистор. Опять же, у меня есть в закромах, но пойдет любой. Вот ссылка на тот же ПАКПАК http://pacpac.ru/product/sen-09088-mini-photocell/

Про часы и прочее можно прочитать в предыдущем посту.

Итак, собираем схему.

Первыми идут часы. Подключаем GND к GND, VCC к +5V, SDA к D2, SCL к D3.

Проверяем работоспособность

#include "Wire.h"
#include "RTClib.h"

RTC_DS1307 RTC;

void setup () {
Serial.begin(57600);
Wire.begin();
RTC.begin();

if (! RTC.isrunning()) {
Serial.println(“RTC is NOT running!”);
}
//RTC.adjust(DateTime(__DATE__, __TIME__));
}

void loop () {
DateTime now = RTC.now();

Serial.print(now.day());
Serial.print(‘/’);
Serial.print(now.month(), DEC);
Serial.print(‘/’);
Serial.print(now.year(), DEC);
Serial.print(‘ ‘);

Serial.print(now.hour(), DEC);
Serial.print(‘:’);
Serial.print(now.minute(), DEC);
Serial.print(‘:’);
Serial.print(now.second(), DEC);
Serial.println();

delay(1000);
}

В выводе serial monitor должны увидеть строчки с текущим временем.

Теперь подключаем светодиод.  К 5й ножке, затем резистор 1кОм, затем земля. Почему 5я ножка? Просто это первый PWM выход на микре. Проще будет красиво мигать светодиодиком. Проверяем


int led = 5;
int brightness = 0;
int fadeAmount = 10;

void setup() {
pinMode(led, OUTPUT);
}

void loop() {
analogWrite(led, brightness);

brightness = brightness + fadeAmount;

if (brightness == 0 || brightness == 255) {
fadeAmount = -fadeAmount ;
}
delay(30);
}

Светодиодик должен начать “взмаргивать”.

Теперь следующий шаг: фотодиод. Подключаем одной ножкой к А0, второй к +5В. И “опускаем” А0 через резистор в 10кОм на землю. То есть должно получиться А0-фотодиод-5В и А0-резистор-GND.

Проверяем


int photoRPin = 0;
int minLight;
int maxLight;
int lightLevel;
int adjustedLightLevel;

int led = 5;

void setup() {
Serial.begin(9600);
pinMode(led, OUTPUT);

lightLevel=analogRead(photoRPin);
minLight=lightLevel-20;
maxLight=lightLevel;
}

void loop(){
lightLevel=analogRead(photoRPin);
if(minLight>lightLevel){
minLight=lightLevel;
}
if(maxLight

В результате у нас должена получиться супер-пупер адаптивная подсветка из одного светодиода. Чем больше падает света на фотодиод, тем сильнее светится светодиод. При этом яркость подсветки адаптируется и самое яркое свечение светодиода будет при самом ярком свете. Вывод текущего значения подсветки в serial monitor - маленький бонус.

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

Соединяем VCC с +5В, GND с GND, а rx индикатора с tx ардуинки. Всё, всего 3 проводка (сравните с предидущим индикатором). Если просто так все включить, то индикатор должен загореться с 0000 на экране. Проверяем остальное.


#include "SoftwareSerial.h"

int ar_tx=1;
int ar_rx=0;

SoftwareSerial sp(ar_rx,ar_tx);

void setup() {
pinMode(ar_tx,OUTPUT);
pinMode(ar_rx,INPUT);
sp.begin(9600);
sp.print("v");
sp.print("8888");
}

void loop() {
// put your main code here, to run repeatedly:
sp.print("1234");
sp.print("w");
char s=16;
sp.write(s);
delay(500);
sp.print("4321");
sp.print("w");
s=0;
sp.write(s);
delay(500);
}

На экране должны меняться 1234 и 4321 с мигающим двоеточием. Более подробное объяснение команд дисплейчика можно найти в его документации. Или тут http://www.arunet.co.uk/tkboyd/ec/ec1led4x7ser.htm

Следующим сделаем вход для сигнала ACC. Так как в машине напряжение может скакать от 9 до 15-16 вольт (в крайних случаях), а ардуинка понимает только 5В, то я не долго думая, сделал обычный делитель из двух сопротивлений: 1кОм и 4,7кОм.

Что бы не мучаться, воспользуемся сервисом http://www.bezkz.su/index/delitel/0-9. R1=4700, R2=1000, U1 - то, что "входит", U2 - то, что выходит.

Сопротивление 1кОм включаем между GND и A1, а сопротивление 4,7кОм, между А1 и "+" измеряемого. "-" измеряемого соединяем с GND.

Теперь по появлению чего-либо на A1 можно судить, включено ли зажигание. Но это как-то не функционально. В общем, надо превратить этот вход еще и в вольметр. Судя по вышеприведенному сервису, с данными номиналами можно будет измерять напряжение в диапазоне от 0 до 28 вольт. Для машины более чем достаточно. Но тут есть одна большая проблема: резисторы вообще-то не идеального номинала и поэтому ожидать от полученного вольтметра точности прямо с нуля не стоит.

Берем вот такой вот маленький скетч

void setup()
{
Serial.begin(115200);
}
void loop()
{

Serial.println(analogRead(1));
delay(100);
}

Он просто выводит значения измеренного с порта А1. Значения могут колебаться от 0 (ноль) до 1023 (5В). Я подключился к лабораторному блоку питания и при напряжении 12В оно мне выдало 430, 9В - 320, 6В - 215, 5В - 176. То есть теоретическая точность (и шаг измерения) данного показометра будет ...

12/430 = 0.027
9/320 = 0.028
6/215 = 0.027
5/176 = 0.028

Ну, цельных 3 сотых вольта. Можно в принципе до 15 тысячных догнать, (снизив диапазон измеряемого напряжения) но зачем? Нам и десятых хватит за глаза.

Модифицируем программу вольметра


void setup()
{
Serial.begin(115200);
}
void loop()
{

Serial.println(analogRead(1)/35.8); // 430/12=
delay(100);
}

Теперь у нас в SerialMonitor показывается текущее напряжение с точностью до сотых. Немного изменив скетч проверки дисплея, мы получим вольметр


#include "SoftwareSerial.h"

int ar_tx=1;
int ar_rx=0;

SoftwareSerial sp(ar_rx,ar_tx);

void setup() {
Serial.begin(115200);
pinMode(ar_tx,OUTPUT);
pinMode(ar_rx,INPUT);
sp.begin(9600);
sp.print("v");
sp.print("8888");
sp.print("w");
char t=2;
sp.print(t);
}

void loop() {
int napr=int(analogRead(1)/35.8*100); // 430/12=
Serial.println(napr);
if(napr<1000) { sp.print(" "); } sp.print(napr); delay(100); }

При этом уже полученная штука может мерять напряжения еще в куче мест, ибо ножек свободных у нас дофига, даже если учесть, что нам надо куда-то подключить еще 4 ветки для датчиков температур. Ну разве это не прекрасно?

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

И наконец, почти последний шаг: измерение температуры. Я использую в своих проектах DS18B20. Это совершенно шикарный цифровой датчик, который с точностью 0,5 градуса измеряет свою температуру.

У этого датчика есть две схемы подключения: нормальная и с паразитным питанием. Если коротко, то нормальная использует 3 провода до датчика, а с паразитным питанием 2. Но (как обычно, без НО не обойтись) схема с паразитным питанием нормально работает только на короткие расстояния и в обычном, уличном, диапазоне температур. При увеличении расстояния от датчика до контроллера начинаются "глюки" и чем дальше, тем больше. И более того, ни одна схема с паразитным питанием и расстоянием по проводу до датчика порядка 5м (обычный домашний термометр) не выживала более года-полутора: попросту дохли "контроллеры". А трехпроводные живут. В общем, решать вам.

С картинками разницу между схемами можно прочитать тут http://openenergymonitor.org/emon/buildingblocks/DS18B20-temperature-sensing

Я лишь сопру картинку с нормальным питанием

normal power conection diagram

И схему подключения

temp sensors connection diagram 3 wire

Как видно, датчики подключаются гирляндой, один за одним, к одним и тем же проводам. Максимальное количество датчиков на одной гирлянде по-моему 127. Или 64. В общем, дофига. Каждый датчик имеет свой уникальный адрес, поэтому вероятность, что они "перепутаются", нет. Правда, совершенно не факт, что первый физически по проводу будет первым и по адресу ...

Для сборки датчиков надо следующее: 3 провода (желательно разных цветов, многожильных), сам датчик и 2 разных термоусадочных трубочки. Одна чуть больше диаметром, чем сам провод, другая на 4,3мм.

IMG_0107

Тут я показал процесс изготовления датчика: сначала припаиваем провода, затем изолируем выводы датчика первой термоусадочной трубкой, а затем собираем все "в кучу" с помощью второй. Данная конструкция показала свою надежность с 2004 года: до сих пор все датчики работают (а среди них есть и уличные, и домашние и те, которые все время в воде).

Для начала подключаем один датчик. VCC +5В, GND-GND, DQ-D6 и DQ-R4K7-+5В


#include "OneWire.h"
#include "DallasTemperature.h"

// Data wire is plugged into pin 6 on the Arduino
#define ONE_WIRE_BUS 6

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

void setup(void)
{
// start serial port
Serial.begin(9600);

// Start up the library
sensors.begin(); // IC Default 9 bit.
}

void loop(void)
{
// call sensors.requestTemperatures() to issue a global temperature
// request to all devices on the bus
Serial.print("Requesting temperatures...");
sensors.requestTemperatures(); // Send the command to get temperatures
Serial.println("DONE");

Serial.print("Temperature for Device 1 is: ");
Serial.println(sensors.getTempCByIndex(0));

}

И в Serial Monitor вы увидите похожее на следующее

Requesting temperatures...DONE
Temperature for Device 1 is: 21.69

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

Ну и совершенно аналогично поступаем, подключая датчики в D7, D8 и D9.

Да, опять не могу удержаться

#include "SoftwareSerial.h"
#include "OneWire.h"
#include "DallasTemperature.h"

// Data wire is plugged into pin 6 on the Arduino
#define ONE_WIRE_BUS 6

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

int ar_tx=1;
int ar_rx=0;

SoftwareSerial sp(ar_rx,ar_tx);

void setup() {
Serial.begin(115200);
pinMode(ar_tx,OUTPUT);
pinMode(ar_rx,INPUT);
sp.begin(9600);
sp.print("v");
sp.print("8888");
sp.print("w");
char t=2;
sp.print(t);
sensors.begin(); // IC Default 9 bit.
}

void loop() {
sensors.requestTemperatures();
int temp=sensors.getTempCByIndex(0)*100;
Serial.println(temp);
sp.print(temp);
}

И у нас готовый термометр с цифровой индикацией температуры.

IMG_0134

В принципе всё, можно с макетной платы переносить в "железо" и собирать все программки вместе ... но заказчик, после пары литров пива и объяснений, чего может и чего не может микропроцессорная техника, немного изменил техническое задание. Но об этом в следующем посту.

Часовой показометр

Итак, хватит разговоров, надо сделать что-либо полезное и приятное. А начнем мы с часов, совмещенных с показометром чего-нибудь. Чего именно и конкретно – пока я не определился.

В общем, сгребаем всё, что есть в предидущем посту и складываем на стол. Первым делом подключаем кабелем ардуинку к компьютеру и скачиваем весь необходимый для нее софт. Я останавливаться на этом не буду, ибо в интернете этих описаний – тьма и на любом языке. Точно так же я не буду останавливаться на том, что делает каждая конкретная функция.

Проверка для перехода к следующему шагу – стандартная “мигалка” компилируется, заливается в ардуинку и мигает светодиодиком, как положено.

Теперь подсоединяем индикатор к ардуинке. Ножку индикатора VCC цепляем к выводу +3.3V ардуинки. Не стоит занимать 0 и 1 ножку (там работа с компьютером) и 13й – там светодиодик.

IMG_0061

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

int pinA=2;
int pinB=3;
int pinC=4;
int pinD=5;
int pinE=6;
int pinF=7;
int pinG=8;
int pinDP=9;
int pinPP=10;
int pinDIG1=11;
int pinDIG2=12;
int pinDIG3=A0;
int pinDIG4=A1;

Затем я делаю определение, в каком режиме должны работать порты ардуинки (у нас все работают на вывод)

pinMode(pinA, OUTPUT); //A
pinMode(pinB, OUTPUT); //B
pinMode(pinC, OUTPUT); //C
pinMode(pinD, OUTPUT); //D
pinMode(pinE, OUTPUT); //E
pinMode(pinF, OUTPUT); //F
pinMode(pinG, OUTPUT); // G
pinMode(pinDP, OUTPUT); // DP
pinMode(pinPP, OUTPUT); // PP

pinMode(pinDIG1, OUTPUT); // DIG 1
pinMode(pinDIG2, OUTPUT);
pinMode(pinDIG3, OUTPUT);
pinMode(pinDIG4, OUTPUT); // DIG 4

И переключаю эти порты в соответствующее состояние. По нашей схеме, что бы светодиод загорелся, надо порт включить в режим LOW.
Что бы погас – в HIGH.

Ну и что бы добавить “оживляжа”, зажгем первую и третью цифру

digitalWrite(pinDIG1,LOW);
digitalWrite(pinDIG2,HIGH);
digitalWrite(pinDIG3,LOW);
digitalWrite(pinDIG4,HIGH);

… восьмерками
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
digitalWrite(pinDP,HIGH);
digitalWrite(pinPP,HIGH);

и начнем включать/выключать точки с интервалом в одну секунду
digitalWrite(pinPP,LOW);
delay(1000);
digitalWrite(pinPP,HIGH);
delay(1000);

В итоге в ардуинке должен залиться вот такой вот скетч
int pinA=2;
int pinB=3;
int pinC=4;
int pinD=5;
int pinE=6;
int pinF=7;
int pinG=8;
int pinDP=9;
int pinPP=10;

int pinDIG1=11;
int pinDIG2=12;
int pinDIG3=A0;
int pinDIG4=A1;

void setup() {
Serial.begin(9600);
pinMode(pinA, OUTPUT); //A
pinMode(pinB, OUTPUT); //B
pinMode(pinC, OUTPUT); //C
pinMode(pinD, OUTPUT); //D
pinMode(pinE, OUTPUT); //E
pinMode(pinF, OUTPUT); //F
pinMode(pinG, OUTPUT); // G
pinMode(pinDP, OUTPUT); // DP
pinMode(pinPP, OUTPUT); // PP

pinMode(pinDIG1, OUTPUT); // DIG 1
pinMode(pinDIG2, OUTPUT);
pinMode(pinDIG3, OUTPUT);
pinMode(pinDIG4, OUTPUT); // DIG 4

digitalWrite(pinDIG1,LOW);
digitalWrite(pinDIG2,HIGH);
digitalWrite(pinDIG3,LOW);
digitalWrite(pinDIG4,HIGH);

digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
digitalWrite(pinDP,HIGH);
digitalWrite(pinPP,HIGH);
}

// the loop routine runs over and over again forever:
void loop() {

digitalWrite(pinPP,LOW);
delay(1000);
digitalWrite(pinPP,HIGH);
delay(1000);
}


И этот скетч должен произвести такой результат:

Если получилось, можно радостно ухмыльнуться и перейти к следующему шагу – сделаем другие циферки.

Для начала добавим функцию, которая будет рисовать нам циферки
void showDigit(int digit)
{
switch(digit) {
case 0:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,HIGH);
break;
case 1:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
case 2:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,HIGH);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,LOW);
break;
case 3:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,LOW);
break;
case 4:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 5:
digitalWrite(pinA,LOW);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 6:
digitalWrite(pinA,LOW);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 7:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
case 8:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 9:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
}
}

Суть её простая: просто зажигаем или гасим нужные сегменты в зависимости от того, какую циферку нам надо нарисовать.

И просто перебираем циферки по порядку:

void loop() {
showDigit(count);
count++;
if(count>9)
{
count=0;
}
digitalWrite(pinPP,LOW);
delay(500);
digitalWrite(pinPP,HIGH);
delay(500);
}

Все просто: зажигаем циферку, затем увеличиваем ее и если она превышает 9, сбрасываем на 0. И затем с меньшим интервалом мигаем. В результате должен получиться вот такой вот скетч
int pinA=2;
int pinB=3;
int pinC=4;
int pinD=5;
int pinE=6;
int pinF=7;
int pinG=8;
int pinDP=9;
int pinPP=10;

int pinDIG1=11;
int pinDIG2=12;
int pinDIG3=A0;
int pinDIG4=A1;

void showDigit(int digit)
{
switch(digit) {
case 0:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,HIGH);
break;
case 1:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
case 2:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,HIGH);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,LOW);
break;
case 3:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,LOW);
break;
case 4:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 5:
digitalWrite(pinA,LOW);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 6:
digitalWrite(pinA,LOW);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 7:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
case 8:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 9:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
}
}

void setup() {
Serial.begin(9600);
pinMode(pinA, OUTPUT); //A
pinMode(pinB, OUTPUT); //B
pinMode(pinC, OUTPUT); //C
pinMode(pinD, OUTPUT); //D
pinMode(pinE, OUTPUT); //E
pinMode(pinF, OUTPUT); //F
pinMode(pinG, OUTPUT); // G
pinMode(pinDP, OUTPUT); // DP
pinMode(pinPP, OUTPUT); // PP

pinMode(pinDIG1, OUTPUT); // DIG 1
pinMode(pinDIG2, OUTPUT);
pinMode(pinDIG3, OUTPUT);
pinMode(pinDIG4, OUTPUT); // DIG 4

digitalWrite(pinDIG1,LOW);
digitalWrite(pinDIG2,HIGH);
digitalWrite(pinDIG3,LOW);
digitalWrite(pinDIG4,HIGH);

digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
digitalWrite(pinDP,HIGH);
digitalWrite(pinPP,HIGH);
}

int count=0;

// the loop routine runs over and over again forever:
void loop() {
showDigit(count);
count++;
if(count>9)
{
count=0;
}
digitalWrite(pinPP,LOW);
delay(500);
digitalWrite(pinPP,HIGH);
delay(500);
}

С вот таким вот результатом

Ура? Ура конечно. Следующим шагом надо вывести какое-нибудь число. Я взял 1234.

В таких штуках механизм вывода числа простой: зажигаем первую цифру, затем вторую, затем третью и четверную.
За счет инерции нам будет казаться, что все цифры светятся одновременно.

Добавляем функцию, которая будет разбивать число на циферки и показывать их по порядку
void showNumber(int num)
{
int divide=0;
for(int c=1;c<5;c++) { switch(c) { case 1: digitalWrite(pinDIG1,LOW); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,HIGH); divide=1000; break; case 2: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,LOW); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,HIGH); divide=100; break; case 3: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,LOW); digitalWrite(pinDIG4,HIGH); divide=10; break; case 4: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,LOW); divide=1; break; } // switch String s(int(num/divide)); char c=s.charAt(s.length()-1); showDigit(c-'0'); delay(100); } }

Самым сложным тут является кусок кода
String s(int(num/divide));
char c=s.charAt(s.length()-1);
showDigit(c-'0');

Вся его функция - "выкусить" нужную цифру из числа и затем показать.

скажем, нам нужна цифра 2 из 1234.

int(num/divide) - это "взять целую часть из деления num на divide". В нашем случае 1234/100=12

String(s - это "превратить данное число в строку"

s.length() - узнать длину строки в символах

s.charAt - взять Нный символ из строки

В итоге s.charAt(s.length()-1) значит "взять последний символ из строки". -1 нужен из-за того, что ардуинка считает, что 1й символ имеет "адрес" 0

c-'0' - превращаем символ назад в число. Отнеситесь к этому как к магии, иначе мне надо будет рассказывать вам про то, что такое ASCII

И заставляем ардуинку показать нам циферку

void loop() {
showNumber(1234);
}

Как обычно, скетч целиком

int pinA=2;
int pinB=3;
int pinC=4;
int pinD=5;
int pinE=6;
int pinF=7;
int pinG=8;
int pinDP=9;
int pinPP=10;

int pinDIG1=11;
int pinDIG2=12;
int pinDIG3=A0;
int pinDIG4=A1;

void showDigit(int digit)
{
switch(digit) {
case 0:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,HIGH);
break;
case 1:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
case 2:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,HIGH);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,LOW);
break;
case 3:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,LOW);
break;
case 4:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 5:
digitalWrite(pinA,LOW);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 6:
digitalWrite(pinA,LOW);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 7:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
case 8:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 9:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
}
}

void setup() {
Serial.begin(9600);
pinMode(pinA, OUTPUT); //A
pinMode(pinB, OUTPUT); //B
pinMode(pinC, OUTPUT); //C
pinMode(pinD, OUTPUT); //D
pinMode(pinE, OUTPUT); //E
pinMode(pinF, OUTPUT); //F
pinMode(pinG, OUTPUT); // G
pinMode(pinDP, OUTPUT); // DP
pinMode(pinPP, OUTPUT); // PP

pinMode(pinDIG1, OUTPUT); // DIG 1
pinMode(pinDIG2, OUTPUT);
pinMode(pinDIG3, OUTPUT);
pinMode(pinDIG4, OUTPUT); // DIG 4

digitalWrite(pinDIG1,HIGH);
digitalWrite(pinDIG2,HIGH);
digitalWrite(pinDIG3,HIGH);
digitalWrite(pinDIG4,HIGH);

digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
digitalWrite(pinDP,HIGH);
digitalWrite(pinPP,HIGH);
}

void showNumber(int num)
{
int divide=0;
for(int c=1;c<5;c++) { switch(c) { case 1: digitalWrite(pinDIG1,LOW); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,HIGH); divide=1000; break; case 2: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,LOW); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,HIGH); divide=100; break; case 3: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,LOW); digitalWrite(pinDIG4,HIGH); divide=10; break; case 4: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,LOW); divide=1; break; } // switch String s(int(num/divide)); char c=s.charAt(s.length()-1); showDigit(c-'0'); delay(100); } } // the loop routine runs over and over again forever: void loop() { showNumber(1234); }

Превращается в ...

А теперь уменьшаем задержку, меняя delay(100) на delay(1) и получаем ...

... циферку! Такую, какую я попросил! Очередное ура!

Но если вы приглядитесь к результату ...

IMG_0070

Какая-то фигня, не правда ли? Почему-то вокруг циферок светятся лишние сегменты. Хоть и тускло, но некрасиво же. Почему так?

Дело в скорости работы ардуинки. Скажем, откуда появились лишние сегменты у цифры 1?

Если посмотрите на логику работы скетча, у нас получится следующее

(много пропущено)
1. Включить 4й индикатор
2. Показать цифру 4
3. Включить 1й индикатор
4. Показать цифру 1
(опять пропускаем)

И вот обратите внимание, во время между шагами 3 и 4 на индикаторе горит 4! Да, это время невелико, но хватает, что бы мы заметили это.

Вывод? Надо между шагами 2 и 3 выключить показ циферок.

Добавляем вывод спец-цифры "10"

case 10:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,HIGH);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;

Она просто будет гасить все сегменты. И вставим ее вызов самое начало ShowNumber

for(int c=1;c<5;c++) { showDigit(10);

IMG_0073

Как видим, все получилось. Все красиво и никаких засветов нет. Можно побаловаться с разными циферками, поделать секундомеры.

Но нам нужны часы. Выключаем ардуинку, и вставляем на плату блок часиков. Предварительно вставьте туда батарейку, иначе чуда не будет. Соединения простые:

GND к GND,
VCC к +5V (НЕ к 3.3!)
SDA к A4
SCL к A5

И снова включаем, что бы удостовериться, что ничего не поломали и не оторвали.

IMG_0077

Теперь для проверки уже блока часов скачиваем с https://github.com/adafruit/RTClib библиотечку RTC, добавляем ее в проект и заливаем вот такой вот скетч:

#include
#include "RTClib.h"

RTC_DS1307 RTC;

void setup () {
Serial.begin(57600);
Wire.begin();
RTC.begin();

if (! RTC.isrunning()) {
Serial.println("RTC is NOT running!");
// following line sets the RTC to the date & time this sketch was compiled
//RTC.adjust(DateTime(__DATE__, __TIME__));
}

}

void loop () {
DateTime now = RTC.now();

Serial.print(now.year(), DEC);
Serial.print('/');
Serial.print(now.month(), DEC);
Serial.print('/');
Serial.print(now.day(), DEC);
Serial.print(' ');
Serial.print(now.hour(), DEC);
Serial.print(':');
Serial.print(now.minute(), DEC);
Serial.print(':');
Serial.print(now.second(), DEC);
Serial.println();

Serial.print(" since 1970 = ");
Serial.print(now.unixtime());
Serial.print("s = ");
Serial.print(now.unixtime() / 86400L);
Serial.println("d");

// calculate a date which is 7 days and 30 seconds into the future
DateTime future (now.unixtime() + 7 * 86400L + 30);

Serial.print(" now + 7d + 30s: ");
Serial.print(future.year(), DEC);
Serial.print('/');
Serial.print(future.month(), DEC);
Serial.print('/');
Serial.print(future.day(), DEC);
Serial.print(' ');
Serial.print(future.hour(), DEC);
Serial.print(':');
Serial.print(future.minute(), DEC);
Serial.print(':');
Serial.print(future.second(), DEC);
Serial.println();

Serial.println();
delay(3000);
}

Если все в порядке, то в выводе Serial Monitor вы должны увидеть ужас, похожий на

2000/61/120 0:81:4
since 1970 = 1023758464s = 11849d
now + 7d + 30s: 2002/6/18 1:21:34

Теперь меняем кусочек скетча в начале на

if (! RTC.isrunning()) {
Serial.println("RTC is NOT running!");
// following line sets the RTC to the date & time this sketch was compiled

}
RTC.adjust(DateTime(__DATE__, __TIME__));
}

(если кто не понял, строчку одну перенес) И снова заливаем.

Теперь в выводе должно появиться реальное время. Ибо компьютер подставил их вместо __DATE__ и __TIME__, а функция
RTC.ajust "поставила часы" как надо.

Теперь выдыхаем и модифицируем скетч вот так:

#include

#include

int pinA=2;
int pinB=3;
int pinC=4;
int pinD=5;
int pinE=6;
int pinF=7;
int pinG=8;
int pinDP=9;
int pinPP=10;

int pinDIG1=11;
int pinDIG2=12;
int pinDIG3=A0;
int pinDIG4=A1;

RTC_DS1307 RTC;

void showDigit(int digit)
{
switch(digit) {
case 0:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,HIGH);
break;
case 1:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
case 2:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,HIGH);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,LOW);
break;
case 3:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,LOW);
break;
case 4:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 5:
digitalWrite(pinA,LOW);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 6:
digitalWrite(pinA,LOW);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 7:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
case 8:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 9:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 10:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,HIGH);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
}
}

void showNumber(int num)
{
int divide=0;
float z;
for(int c=1;c<5;c++) { showDigit(10); switch(c) { case 1: digitalWrite(pinDIG1,LOW); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,HIGH); divide=1000; break; case 2: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,LOW); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,HIGH); divide=100; break; case 3: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,LOW); digitalWrite(pinDIG4,HIGH); divide=10; break; case 4: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,LOW); divide=1; break; } // switch String s(int(num/divide)); char b=s.charAt(s.length()-1); showDigit(b-'0'); delay(5); } } void setup() { Serial.begin(115200); Wire.begin(); RTC.begin(); pinMode(pinA, OUTPUT); //A pinMode(pinB, OUTPUT); //B pinMode(pinC, OUTPUT); //C pinMode(pinD, OUTPUT); //D pinMode(pinE, OUTPUT); //E pinMode(pinF, OUTPUT); //F pinMode(pinG, OUTPUT); // G pinMode(pinDP, OUTPUT); // DP pinMode(pinPP, OUTPUT); // PP pinMode(pinDIG1, OUTPUT); // DIG 1 pinMode(pinDIG2, OUTPUT); pinMode(pinDIG3, OUTPUT); pinMode(pinDIG4, OUTPUT); // DIG 4 digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,HIGH); digitalWrite(pinA,LOW); digitalWrite(pinB,LOW); digitalWrite(pinC,LOW); digitalWrite(pinD,LOW); digitalWrite(pinE,LOW); digitalWrite(pinF,LOW); digitalWrite(pinG,LOW); digitalWrite(pinDP,HIGH); digitalWrite(pinPP,HIGH); } long previousMillis = 0; long interval = 1000; DateTime now; // the loop routine runs over and over again forever: void loop() { unsigned long currentMillis = millis(); if(currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
now = RTC.now();
if((now.second() % 2) == 0)
{
digitalWrite(pinPP,LOW);
}
else
{
digitalWrite(pinPP,HIGH);
}
}

showNumber(now.hour()*100+now.minute());

}

Кака работает код, в принципе понятно из него самого. Но все-таки прокомментирую.

Система просто смотрит, не прошел ли уже заданный интервал и если прошел, то считывает время из часов точного времени и мигает двоеточием.

Почему я просто не поставил посередине что-нибудь типа delay(995)? Ответ опять же в том, что разные ардуинки имеют разную скорость работы. И код, который выполняется между delay,
выполняется за разный промежуток времени. А значит, в реальности между delay проходит разное количество времени. И чего, мне после каждого изменения кода или логики подбирать
задержку? Бессмысленное занятие при наличии под боком нормальных часов.

Код "now.second() % 2" означает "дробная часть при делении на два". Попросту говоря, я зажигаю светодиод каждую четную секунду (14/2=7.0), а гашу - нечетную (15/2=7.5).

Ну и now.hour()*100+now.minute() из часов и минут делает большое число.

Запускаем!

Ура! Работает! Теперь у нас есть часики. Свои, персональные! Можем танцевать и бросать в воздух чепчики!

Но мне же надо еще и показометр ...

Добавляем в loop

while (Serial.available() > 0) {
s=(char)Serial.read();
if (s == 'n') {
char ca[5];
txtMsg.toCharArray(ca, 5);
number_to_show = atoi(ca);
time_to_show=2;
digitalWrite(pinPP,HIGH); // we do to need dots
txtMsg = "";
} else {
txtMsg +=s;
}
}

if(currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
if(time_to_show > 0)
{
time_to_show--;
}
else
{
now = RTC.now();

Суть кода простая: если компьютер послал что-либо в ардуинку, то тупо собираем все, пока не попадется спецсимвол n - это Enter или Return.
Затем превращаем полученное в число и на две секунды показываем вместо часов. Думаю, что если вы одолели предидущее, то этот будет просто понять.

Вот он целиком (для тех, кто не просто копипастит, внутри сюрприз):

#include

#include

int pinA=2;
int pinB=3;
int pinC=4;
int pinD=5;
int pinE=6;
int pinF=7;
int pinG=8;
int pinDP=9;
int pinPP=10;

int pinDIG1=11;
int pinDIG2=12;
int pinDIG3=A0;
int pinDIG4=A1;

RTC_DS1307 RTC;

void showDigit(int digit)
{
switch(digit) {
case 0:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,HIGH);
break;
case 1:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
case 2:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,HIGH);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,LOW);
break;
case 3:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,LOW);
break;
case 4:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 5:
digitalWrite(pinA,LOW);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 6:
digitalWrite(pinA,LOW);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 7:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
case 8:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,LOW);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 9:
digitalWrite(pinA,LOW);
digitalWrite(pinB,LOW);
digitalWrite(pinC,LOW);
digitalWrite(pinD,LOW);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,LOW);
digitalWrite(pinG,LOW);
break;
case 10:
digitalWrite(pinA,HIGH);
digitalWrite(pinB,HIGH);
digitalWrite(pinC,HIGH);
digitalWrite(pinD,HIGH);
digitalWrite(pinE,HIGH);
digitalWrite(pinF,HIGH);
digitalWrite(pinG,HIGH);
break;
}
}

void showNumber(int num)
{
int divide=0;
float z;
for(int c=1;c<5;c++) { showDigit(10); switch(c) { case 1: digitalWrite(pinDIG1,LOW); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,HIGH); divide=1000; break; case 2: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,LOW); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,HIGH); divide=100; break; case 3: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,LOW); digitalWrite(pinDIG4,HIGH); divide=10; break; case 4: digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,LOW); divide=1; break; } // switch String s(int(num/divide)); char b=s.charAt(s.length()-1); showDigit(b-'0'); delay(5); } } void setup() { Serial.begin(115200); Wire.begin(); RTC.begin(); pinMode(pinA, OUTPUT); //A pinMode(pinB, OUTPUT); //B pinMode(pinC, OUTPUT); //C pinMode(pinD, OUTPUT); //D pinMode(pinE, OUTPUT); //E pinMode(pinF, OUTPUT); //F pinMode(pinG, OUTPUT); // G pinMode(pinDP, OUTPUT); // DP pinMode(pinPP, OUTPUT); // PP pinMode(pinDIG1, OUTPUT); // DIG 1 pinMode(pinDIG2, OUTPUT); pinMode(pinDIG3, OUTPUT); pinMode(pinDIG4, OUTPUT); // DIG 4 digitalWrite(pinDIG1,HIGH); digitalWrite(pinDIG2,HIGH); digitalWrite(pinDIG3,HIGH); digitalWrite(pinDIG4,HIGH); digitalWrite(pinA,LOW); digitalWrite(pinB,LOW); digitalWrite(pinC,LOW); digitalWrite(pinD,LOW); digitalWrite(pinE,LOW); digitalWrite(pinF,LOW); digitalWrite(pinG,LOW); digitalWrite(pinDP,HIGH); digitalWrite(pinPP,HIGH); } long previousMillis = 0; long interval = 1000; DateTime now; String txtMsg = ""; char s; int number_to_show=0; int time_to_show=0; // the loop routine runs over and over again forever: void loop() { unsigned long currentMillis = millis(); while (Serial.available() > 0) {
s=(char)Serial.read();
if (s == 'n') {
//if(txtMsg=="HIGH") { digitalWrite(13, HIGH); }
//if(txtMsg=="LOW") { digitalWrite(13, LOW); }
//Serial.println(txtMsg.charAt(0));

char ca[5];
txtMsg.toCharArray(ca, 5);
number_to_show = atoi(ca);
time_to_show=2;
digitalWrite(pinPP,HIGH); // we do to need dots
txtMsg = "";
} else {
txtMsg +=s;
}
}

if(currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
if(time_to_show > 0)
{
time_to_show--;
}
else
{
now = RTC.now();
if((now.second() % 2) == 0)
{
digitalWrite(pinPP,LOW);
}
else
{
digitalWrite(pinPP,HIGH);
}
number_to_show=now.hour()*100+now.minute();
}
}

showNumber(number_to_show);

}

И в реальности:

Все закадровые звуки принадлежат игрушке на айпадике, в который играет сын.

Ну вот и все. Первый этап завершен. Часики тикают и показывают то, что в них плюнет компьютер.

Но это еще не финал, поэтому паять проводки и собирать полученное в одну кучку я бы не спешил. Хотя дело ваше 🙂

Что и для чего надо взять.

Итак, основа получения результата – это отсутствие заморочек.

Всё, что я использую, можно легко купить в любом магазине, занимающимся электроникой. ЧипИДип, Терраэлектроника, Вольтмастер, Амперка, Электронщик … в общем, поищите, наверняка рядом с вами есть фирмы, которые обеспечивают радиолюбителей полезными штуками.

Повторюсь, для повторения не надо будет даже паяльника. Но лучше с ним.

Итак, первое, что необходимо взять, это макетная плата с набором проводков к ней. Выглядит она как доска с кучей дырочек.

Wisher_WB-102_J_big

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

Главная цель макетной платы это позволить быстро и более-менее надежно собрать схему для проверки работоспособности. И так же быстро ее поправить или разобрать.

Вторым пунктом идет сам микроконтроллер. Я выбрал наиболее широко распространенный: arduino. Этих контроллеров и их клонов в магазинах как грязи по самым разным ценам. Для наших целей подойдет абсолютно любой.

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

Итак, в качестве микроконтроллера я взял CarDuino. Он же Arduino Nano или Nano Duo. Процессор ATmega328, документации очень мало, но есть русскоязычный форум. Использовать всякие “фишки carduino”, типа порта HV12 или SPK не буду (нет документации, не совместимы с другими ардуинками).

carduinov7_mid1

Для показометра я взял набор MP1091 от МастерКит. Четыре семисегментных индикатора, объединенных в один блок. Из документации только одна схема и черезжопу написанная  библиотека.

mp1091

Ну и венцом всего будет MP1095, в который спрятали уже готовую схему часов с батарейкой.

DOC000867115

Отмечу, что хотя везде написано, что это наборы для Arduino, они прекрасно подойдут для других микроконтроллеров и компов.

Думаю, что обычный USB-miniUSB кабель, как и компьютер, вы найдете уж как-нибудь без моих подсказок.

Примерная стоимость набора в самом дорогом магазине Москвы:

Макетная плата с набором проводов – 1400 рублей.

CarDuino – 1180 рублей.

Индикатор MP1091 – 450 рублей

Часы MP1095 – 440 рублей

Итого 3500 рублей, из которых половину можно сэкономить, просто поспрашивав на форумах: многие отдают “макетки” и остальное за пиво-соки.

В дальнейшем в качестве “центрального компьютера” я буду использовать Raspberry Pi. Он точно так же доступен, обладает приличными характеристиками и при желании на нем можно собрать что угодно – от новогодней гирлянды до мультимедийного центра. Стоит ли вам его покупать сейчас, решать вам, но “часики/показометр” можно повторить за пару-тройку вечеров под пиво.

pi1l

 

Опять же, к нему (хотя бы на начальном этапе) надо USB-клавиатуру, мышку, монитор с HDMI или RCA входом и еще один USB-miniUSB шнурок.

В общем, по ходу дела расскажу с фотографиями, что зачем надо и куда что во что включать.