SULI COMPANY | Научно-технический сайт Сулико Асабашвили » Электроника » Arduino » Статьи по практическому программированию Arduino/CraftDuino
Информация к новости
  • Просмотров: 5690
  • Автор: sulicompany
  • Дата: 16-02-2013, 04:48
 (голосов: 0)
16-02-2013, 04:48

Статьи по практическому программированию Arduino/CraftDuino

Категория: Электроника » Arduino

 

Подготовка к работе с Arduino/CraftDuino

  •  
Итак, у вас уже есть  (Arduino/Freeduino). Что же нужно сделать чтобы начать работать на этой платформе?
Первым делом нужно скачать  для того чтобы иметь возможность писать свои программы и заливать их на Arduino.
Текущая версия (на момент написания статьи) IDE 0021 — её можно скачать по адресу:


В архиве содержится директория arduino-0021, которую желательно поместить в корень диска С.
(можно и в другое место — главное, чтобы в пути не было названий отличных от английского )
Теперь можно подключить плату к компьютеру, посредством USB-кабеля типа A-B, которым подключаются к ПК принтеры. 

Подключив Arduino к компьютеру — операционная система обнаружит новое устройство и предложит Вам установить драйвер.


Драйвер находится в директории Arduino IDE в папке \drivers\FTDI USB Drivers\
(C:\arduino-0021\drivers\FTDI USB Drivers\)


Произойдёт установка двух драйверов и в Вашей системе появится дополнительный COM-порт (USB Serial Port).

Остаётся запустить Arduino IDE – для этого нужно запустить программу
C:\arduino-0021\arduino.exe
, выбрать новый COM-порт (Tools — Serial port) 

и выбрать тип платы (Tools — Board): в нашем случае — это: 
Arduino Diecimila, Duemilanove, or Nano w/ ATmega168 
(если на плате используется МК ATMega168) или же:
Arduino Duemilanove or Nano w/ ATmega328 
(если на плате используется МК ATMega328) 


Вот и всё.
Можно начинать  :)

данная статья в 

Ссылки:


Ошибки Arduino

  •   
Возможные ошибки при работе с .

avrdude: stk500_getsync(): not in sync: resp=0x00


Если вы получаете сообщение об ошибке: "avrdude: stk500_getsync(): not in sync: resp=0x00", 
то это означает, что  не отвечает.
Существуют масса причин, почему это может происходить.


Попробуйте проверить следующее:
* Подаётся ли на плату питание (если контроллер подключается через USB, то джампером должен быть выбран соответствующий режим работы)
* Проверьте правильность и надёжность соединения контроллера Arduino с ПК
* Если у вас , ты нажмите кнопку сброса перед загрузкой скетча (нажатием Upload)
* Убедитесь, что в Arduino IDE у вас  правильный последовательный порт.
* Попробуйте .
* Верно ли установлен чип микроконтроллера (если вы , то (bootloader)?)
* Правильная ли версия загрузчика? (если вы )

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

java.lang.NullPointerException at processing.app.Serial.setDTR.



Если вы получаете сообщение об ошибке: "java.lang.NullPointerException at processing.app.Serial.setDTR(Serial.java:480)", то это означает, что неправильно  последовательный порт (меню Tools — Serial port)

avrdude: Expected signature for ATMEGA is ...



Если вы получаете сообщение об ошибке: "avrdude: Expected signature for ATMEGA is ...", то это означает, что либо неправильно  версия МК (меню Tools — Board) или на МК прошит неверный загрузчик.

ser_send(): write error: sorry no info avail


Неправильно  последовательный порт (меню Tools — Serial port).

Ссылки:

Практическое программирование Arduino/CraftDuino - начало

  •   
У нас уже была . Там мы рассмотрели структуру программы, константы и специфичные для Arduino функции, которые собственно и составляют язык .

Теперь же настала пора практических занятий :)

0. Начало

Пожалуй, всё же стоит обозначить элементы управления Arduino IDE


Итак, слева направо:

— компиляция (оригинальное название — проверка) кода
— стоп (остановка монитора COM-порта)
— новый скетч
— открыть скетч
— сохранить скетч
— загрузить скетч в микроконтроллер Arduino/Freeduino
— монитор последовательного (COM) порта

Самые важные для нас – первая и две последние кнопки :)

Т.о. цикл разработки скетча для Arduino можно представить так:


Написание кода – компиляция – загрузка в МК.

Кажется — всё просто :) 
Попробуем снова загрузить Blink :)
Открываем тестовый скетч из Examples — Digital — Blink
и нажмём кнопку компиляции


Компиляция прошла без ошибок о чём нам и сообщают – «Done compiling» :)

Остаётся подключить нашу ардуину к питанию и COM-порту и нажать кнопку выгрузки скетча на МК.
Во время выгрузки будут мигать светодиоды Rx и Тx – сигнализирующие приём и передачу сообщений через последовательный интерфейс ардуины :)
Если выгрузка прошла успешно – мы получим сообщение: «Done uploading.»



Т.к. джампер на плате установлен в Autoreset enable плата сама перезагрузится, произойдёт заливка скетча, снова перезагрузка и через 10 секунд ардуина начнёт весело мигать светодиодом :) 



Впрочем, светодиод даже не обязательно вставлять в разъём – на плате уже есть сигнальный светодиод, подключённый к 13-му цифровому порту ардуины через ограничительный резистор.



Используя провода, изготовленные из витой пары, 

схему можно перенести на макетную плату.



А если всё делать по-честному и подключать светодиод через ограничительный резистор, то получится такая простейшая схема:

, которая на макетной плате будет выглядеть так:



Теперь немножко переделаем скетч Blink т.о., чтобы плата сообщала нам через COM-порт когда светодиод горит, а когда нет.
Для этого нужно добавить всего три строчки кода:

int ledPin = 13;                

void setup()                  
{
  pinMode
(ledPin, OUTPUT);      
 
Serial.begin(9600);     // инициализация работы с COM-портом
}

void loop()                    
{
  digitalWrite
(ledPin, HIGH);  
 
Serial.print("H");      // светодиод горит – пишем H
  delay
(1000);                  
  digitalWrite
(ledPin, LOW);    
 
Serial.println("L");    // светодиод погасили – пишем L
  delay
(1000);                  
}


Компилируем и загружаем скетч в МК.


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



Ура! Работает :)
Сообщения из последовательного порта можно смотреть любой терминальной программой, например :

выбираем наш COM-порт и любуемся:


Но что это – при любой новой попытке просмотреть сообщения COM-порта наша плата перезагружается!
Всё дело в джампере Autoreset enable, который так удобен при загрузке новых скетчей :)
Если джампер снять, то теперь каждое новое подключение к последовательному порту не будет вызывать перезагрузку ардуины, но новые скетчи придётся загружать после ручного нажатия на кнопку Reset, расположенную на плате.


По теме:



книга на русском языке:  (PDF 1.3 Mb)

Практическое программирование Arduino/CraftDuino - цифровой ввод - кнопка

  •   


1. Цифровой ввод — кнопка

Настало время попробовать поработать с  :)
Для начала, возьмём обычную кнопку и соберём такую простую схему:

как видно – это просто дополненная схема -а :) 
Что же происходит на 12-м цифровом порту? Если к порту ничего не подключено – его состояние меняется между 0V и 5V ().
Подключая резистор между портом и землёй — мы тем самым «прижимаем» порт к 0 (LOW), а при нажатии кнопки происходит соединение с напряжением питания и тем самым порт «подтягивается» к 5V (HIGH).
Тем самым мы узнаём – нажата кнопка или нет – просто считывая значение на входе 12 цифрового порта и проверяя – HIGH это или LOW.

код скетча прост:

/*
 * LED with button
 */


int ledPin = 13;                // сетодиод
int btnPin = 12;                // кнопка
int val=0;

void setup()                    
{
  pinMode
(ledPin, OUTPUT);      // это выход - светодиод
  pinMode
(btnPin, INPUT);       // а это вход - кнопка
 
Serial.begin(9600);           // будем записывать в COM-порт
}

void loop()                      
{
  val
= digitalRead(btnPin);    // узнаём состояние кнопки
 
if(val==HIGH)                 // кнопка нажата
 
{
    digitalWrite
(ledPin, HIGH); // зажигаем светодиод
   
Serial.println("H");
 
}
 
else                          // кнопка не нажата
 
{
    digitalWrite
(ledPin, LOW);  // гасим светодиод
   
Serial.println("L");
 
}
  delay
(100);
}



а вот что увидим в мониторе последовательного порта:


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

Практическое программирование Arduino/CraftDuino - Аналоговый вывод - Fading



2. Аналоговый вывод — Fading

Рассмотрим скетч Fading (Sketchbook – Examples – Analog — Fading ), как пример работы с функцией  — аналоговым выводом сигнала :) 

Как мы помним, значение, которое можно передавать в analogWrite должно быть между 0 и 255, что соответствует напряжению от 0V до 5V на выходе порта. 
Т.о. постепенно увеличивая/уменьшая переданное в analogWrite значение можно добиться плавного роста/падения напряжения на выходе. 
Теперь, подключив к одному из PWM-портов ардуины (3, 5, 6, 9, 10, 11, а на платах на базе ATmega8 только —9, 10, 11 ) светодиод будет плавно загораться и затухать (fading).

Остаётся собрать простейшую схему 

и скомпилировать скетч :)

Код скетча:

// Fading LED
// by BARRAGAN <http://people.interaction-ivrea.it/h.barragan>

int value = 0;     // переменная для хранения нужного значения
int ledpin = 9;    // светодиод подключен к digital pin 9
 
void setup()
{
 
// Нет необходимости вызвать функцию pinMode, чтобы установить
 
// порт на вывод сигналов перед вызовом функции analogWrite
}
 
void loop()
{
 
for(value = 0 ; value <= 255; value+=5) // постепенно зажигаем светодиод (от min до max)
 
{
    analogWrite
(ledpin, value);           // устанавливаем значение вывода (от 0 до 255)
    delay
(30);                            // ждём :)
 
}
 
for(value = 255; value >=0; value-=5)   // постепенно гасим светодиод (от max до min)
 
{
    analogWrite
(ledpin, value);
    delay
(30);
 
}  
}


Вот и всё :) Пойду пить сок :)
3. Аналоговый ввод – потенциометр

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

Крайние выводы переменного резистора подключаются к земле и +5V, а со среднего снимаем напряжение, зависящее от положения отводного контакта. 
Т.е. если отводной контакт упирается в нижнее положение – то получаем 0V, а если в верхнее – то +5V.

В качестве примера можно воспользоваться стандартным скетчем AnalogInput (Sketchbook — Examples — Analog – AnalogInput)

принципиальная схема: 

Код скетча:

/*
 * AnalogInput
 * by DojoDave <http://www.0j0.org>
 *
 * включает и выключает светодиод, подключённый к digital  
* pin 13. Время задержки между включением и выключением
* светодиода зависит от величины, полученной от функции
* analogRead().
* В самом простом случае – получить аналоговое значение можно
* считывая напряжение с потенциометра, подключённого к
 * to analog pin 2.
 *
 * http://www.arduino.cc/en/Tutorial/AnalogInput
 */


int potPin = 2;    // потенциометр подключается к 2-му порту
int ledPin = 13;   // светодиод подключается к digital pin 13
int val = 0;       // переменная для хранения значения входного напряжения

void setup() {
  pinMode
(ledPin, OUTPUT);  // настраиваем ledPin как выход
}

void loop() {
  val
= analogRead(potPin);    // считываем значение с потенциометра
  digitalWrite
(ledPin, HIGH);  // зажигаем светодиод
  delay
(val);                  // задержка, зависит от значения
  digitalWrite
(ledPin, LOW);   // гасим светодиод
  delay
(val);                  // опять задержка
}

— как видно – это опять же несколько изменённый Blink, время задержки между включением/выключением светодиода зависит от уровня напряжения на входе analog input 2, к которому подключён средний вывод переменного резистора, включенного между землёй и +5V

Более наглядное представление можно получить используя не цифровой вывод -а, а аналоговый -а.

Для этого нужно изменить схему, подключив светодиод на один из -выводов – например, digital pin 9:

И соответствующим образом изменить скетч.
Однако, тут нужно учесть, что значение, получаемой от функции  представляет собой целое число от 0 до 1023, а функция  оперирует значениями между 0 и 255. Следовательно, нужно привести значение, полученное от analogRead к соответствующему уровню, например, просто разделив на 4.

/*
 * Dimmer_pot
 */


int potPin = 2;    // потенциометр подключается к 2-му порту
int ledPin = 9;    // светодиод подключается к digital pin 9
int val = 0;       // переменная для хранения значения входного напряжения

void setup()
{
 
}

void loop()
{
  val
= analogRead(potPin);    // считываем значение с потенциометра
  val
= val/4;                 // конвертируем из 0-1023 к 0-255
  analogWrite
(ledPin,val);     // устанавливаем значение
}



Работает :)
Насколько мы помним, у Arduino есть такая замечательная функция — , которая считывает значение с указанного аналогового порта.
А это значит мы можем получить простенький осциллограф :)

4. Аналоговый ввод – осциллограф

Для этого будем считывать данные с аналогового порта Arduino/Freeduino и записывать их в последовательный (COM) порт. А уже из последовательно порта их будет принимать наша программа и строить график сигнала :) 

Итак, скетч для Arduino/Freeduino будет совсем простой:


//
// Oscilloscope
//

#define ANALOGA_IN 0

void setup()
{
 
Serial.begin(38400); // указываем скорость работы с COM-портом
}

void loop()
{
 
int val;
  val
= analogRead(ANALOGA_IN); // считываем данные
 
Serial.println( val);         // записываем данные в порт
}

Подключившись к порту мы увидим что-то вроде:

403
401
384
361
346
341
341
358
376
386


Осталось только написать программу для превращения данных из последовательного порта в красивый график :)

На основе программы построения графиков функций 
Graph.pyw ()
я набросал программу для отображения данных из COM-порта.



Для работы потребуется  и библиотека: 
 – собственно сама библиотека для работы с COM-портом (для её работы под Windows требуется )

собственно работа с COM-портом из данной библиотеки крайне проста:


#
# для работы с COM-портом нужна библиотека
# pySerial, кроме того, под винду понадобится еще pyWin32
#
import serial
SERIAL_PORT
= 'COM1'
SERIAL_SPEED
= 38400

ser
= serial.Serial(SERIAL_PORT, SERIAL_SPEED)

while 1:
           
#s = ser.read()            # считывается один байт
            s
= ser.readline().strip() # считывается строка и убираются символы “\r\n”
           
print s                    # печатаем на экран


Весь остальной код нужен для работы с графикой Tkinter и отображения линий графика с помощью
create_line(x1,y1,x2,y2,….,xn,yn)

Однако, у меня пока не получилось заставить работать программу в системе real-time – 
поэтому бесконечный цикл заменил на конечный
for i in range(0,200):

В итоге, получается, что при нажатии на кнопку «Serial» скрипт считывает 200 значений с COM-порта и строит по ним график:


Другие примеры осциллографов на ардуине:

для проекта 

И самый интересный
 :)


 является простым скриптовым языком для создания визуализаций с помощью платформы Java Virtual Mashine. 
Processing использовался для создания некоторых реклам Nike, клипов Radiohead & R.E.M., инсталляций в музеях, а также входит в учебные курсы некоторых вузов США.

 — версия IDE для Windows


Код скетча для Arduino:

//
// Oscilloscope
// http://accrochages.drone.ws/en/node/90
//

#define ANALOGA_IN 0
#define ANALOGB_IN 1
#define ANALOGC_IN 2
#define ANALOGD_IN 3
#define ANALOGE_IN 4
#define ANALOGF_IN 5

int counter = 0;

void setup()
{
 
Serial.begin(38400);
}

void loop()
{
 
int val[5];

  val
[0] = analogRead(ANALOGA_IN);
  val
[1] = analogRead(ANALOGB_IN);
  val
[2] = analogRead(ANALOGC_IN);
  val
[3] = analogRead(ANALOGD_IN);
  val
[4] = analogRead(ANALOGE_IN);
  val
[5] = analogRead(ANALOGF_IN);


 
Serial.print( "A" );
 
Serial.print( val[0] );

 
Serial.print( "B" );
 
Serial.print( val[1] );
 
Serial.print( "C" );
 
Serial.print( val[2] );
 
Serial.print( "D" );
 
Serial.print( val[3] );
 
Serial.print( "E" );
 
Serial.print( val[4] );
 
Serial.print( "F" );
 
Serial.print( val[5] );
}

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

Подключившись к порту увидим следующее:

A129B131C132D133E134F131A129B131C133D133E134F131A132B134C136D137E138F133



Скетч для Processing-а (как видите IDE – почти такая же :):

Здесь так же используется функция setup(), а вот вместо loop() используется функция draw() 

Сам скетч отличается от авторского только явным указанием последовательного порта:
port = new Serial(this,"COM1", 38400); // Serial.list()[0]



import processing.serial.*;

Serial port; // Create object from Serial class
int valA;
int valB;
int valC;
int valD;
int valE;
int valF;
// this should have been some kind of 2-diminsional array, I guess, but it works.
int[] valuesA;
int[] valuesB;
int[] valuesC;
int[] valuesD;
int[] valuesE;
int[] valuesF;

PFont fontA;
PFont fontB;

void setup()
{

// make sure you have these fonts made for Processing. Use Tools...Create Font.
// "fontA" is a 48 pt font of some sort. It's what we use to show the "now" value.
  fontA
= loadFont("CourierNewPSMT-48.vlw");

// "fontB" is a 14 pt font of some sort. It's what we use to show the min and max values.
  fontB
= loadFont("CourierNewPSMT-14.vlw");

// I wouldn't change the size if I were you. There are some functions that don't use
// the actual sizes to figure out where to put things. Sorry about that.
  size
(550, 600);

// Open the port that the board is connected to and use the same speed
// anything faster than 38.4k seems faster than the ADC on the Arduino can keep up with.
// So, if you want it to be smooth, keep it at or below 38400. 28800 doesn't work at all,
// I do not know why. If you turn on smooth() you need to drop the rate to 19.2k or lower.
// You will probably have to adjust Serial.list()[1] to get your serial port.
  port
= new Serial(this,"COM1", 38400); // Serial.list()[0]

// These are 6 arrays for the 6 analog input channels.
// I'm sure it could have just as well been a 2d array, but I'm not that familiar
// with Processing yet and this was the easy way out.
  valuesA
= new int[width-150];
  valuesB
= new int[width-150];
  valuesC
= new int[width-150];
  valuesD
= new int[width-150];
  valuesE
= new int[width-150];
  valuesF
= new int[width-150];
// the -150 gives us room on the side for our text values.

// this turns on anti-aliasing. max bps is about 19.2k.
// uncomment out the next line to turn it on. Personally, I think it's better left off.
//smooth();
}

int getY(int val)
{
 
// I added -40 to this line to keep the lines from overlapping, to
 
// keep the values within their gray boxes.
 
return (int)(val / 1023.0f * (height-40)) - 1;
}

void draw()
{
 
String decoder = "";
 
while (port.available() >= 3)
 
{
   
// read serial until we get to an "A".
    decoder
= port.readStringUntil(65);
 
}
// sanity check. make sure the string we got from the Arduino has all the values inside.
if ((decoder.indexOf("B")>=1) & (decoder.indexOf("C")>=1) &
(decoder.indexOf("D")>=1) & (decoder.indexOf("E")>=1) &
(decoder.indexOf("F")>=1))
{
 
// decoder string doesn't contain an A at the beginning. it's at the end.
  valA
=int(decoder.substring(0,decoder.indexOf("B")));
 
//println("A" + str(valA));
  valB
=int(decoder.substring(decoder.indexOf("B")+1,decoder.indexOf("C")));
 
//println("B" + str(valB));
  valC
=int(decoder.substring(decoder.indexOf("C")+1,decoder.indexOf("D")));
 
//println("C" + str(valC));
  valD
=int(decoder.substring(decoder.indexOf("D")+1,decoder.indexOf("E")));
 
//println("D" + str(valD));
  valE
=int(decoder.substring(decoder.indexOf("E")+1,decoder.indexOf("F")));
 
//println("E" + str(valE));
  valF
=int(decoder.substring(decoder.indexOf("F")+1,decoder.indexOf("A")));
 
//println("F" + str(valF));
 
}

 
//shift the new values into the array, move everything else over by one
 
for (int i=0; i<width-151; i++)
 
{
    valuesA
[i] = valuesA[i+1];
    valuesB
[i] = valuesB[i+1];
    valuesC
[i] = valuesC[i+1];
    valuesD
[i] = valuesD[i+1];
    valuesE
[i] = valuesE[i+1];
    valuesF
[i] = valuesF[i+1];
 
}

 
// -151 because the array is 151 less than the width. remember we
 
// saved room on the side of the screen for the actual text values.
  valuesA
[width-151] = valA;
  valuesB
[width-151] = valB;
  valuesC
[width-151] = valC;
  valuesD
[width-151] = valD;
  valuesE
[width-151] = valE;
  valuesF
[width-151] = valF;

  background
(0);

  textFont
(fontA);

 
// I'm sure these c/should have been determined using height math, but I don't have the time really.
 
// Draw out the now values with the big font.
  text
(valA + 1, (width-140), 108-5);
  text
(valB + 1, (width-140), 206-5);
  text
(valC + 1, (width-140), 304-5);
  text
(valD + 1, (width-140), 402-5);
  text
(valE + 1, (width-140), 500-5);
  text
(valF + 1, (width-140), 598-5);

  textFont
(fontB);
 
// Draw out the min and max values with the small font.
 
// the h value (30,128,266,etc) is a function of height,
 
// but I didn't bother to actually do the math.
 
// I guess it's (98*n)+30 where n is 0,1,2,3,4,5, but I don't know
 
// exactly how height (600) relates to 98... ((h/6)-2??)
  drawdata
("0", width-90, 30, valuesA);
  drawdata
("1", width-90, 128, valuesB);
  drawdata
("2", width-90, 226, valuesC);
  drawdata
("3", width-90, 324, valuesD);
  drawdata
("4", width-90, 422, valuesE);
  drawdata
("5", width-90, 520, valuesF);

for (int x=150; x<width-1; x++)
{
 
// next line adjusts the color of the stroke depending on the x value. (fades out the end of the line)
  check
(x,255,0,0);

 
// next line draws the line needed to get this value in the array to the next value in the array.
 
// the offsets (6+ in the next line) were used to get the values where I wanted them without
 
// having to actually do real spacial math. There's a hack in getY that offsets a little, too.
  line
((width)-x,
 
6+((height/6)*0)+((height-1-getY(valuesA[x-150]))/6), (width)-1-x,
 
6+((height/6)*0)+((height-1-getY(valuesA[x-149]))/6));
  check
(x,0,255,0);
  line
((width)-x,
 
4+((height/6)*1)+((height-1-getY(valuesB[x-150]))/6), (width)-1-x,
 
4+((height/6)*1)+((height-1-getY(valuesB[x-149]))/6));
  check
(x,0,0,255);
  line
((width)-x,
 
2+((height/6)*2)+((height-1-getY(valuesC[x-150]))/6), (width)-1-x,
 
2+((height/6)*2)+((height-1-getY(valuesC[x-149]))/6));
  check
(x,255,255,0);
  line
((width)-x,
 
0+((height/6)*3)+((height-1-getY(valuesD[x-150]))/6), (width)-1-x,
 
0+((height/6)*3)+((height-1-getY(valuesD[x-149]))/6));
  check
(x,0,255,255);
  line
((width)-x,
 
-2+((height/6)*4)+((height-1-getY(valuesE[x-150]))/6), (width)-1-x,
 
-2+((height/6)*4)+((height-1-getY(valuesE[x-149]))/6));
  check
(x,255,0,255);
  line
((width)-x,
 
-4+((height/6)*5)+((height-1-getY(valuesF[x-150]))/6), (width)-1-x,
 
-4+((height/6)*5)+((height-1-getY(valuesF[x-149]))/6));
}

 
// draw the boxes in gray.
  stroke
(170,170,170);

 
// these 5 lines divide the 6 inputs
  line
(0,108,width-1,108);
  line
(0,206,width-1,206);
  line
(0,304,width-1,304);
  line
(0,402,width-1,402);
  line
(0,500,width-1,500);

 
// these four lines make up the outer box
  line
( 0, 0, width-1, 0); // along the top
  line
(width-1, 0, width-1, height-1); // down the right
  line
(width-1, height-1, 0, height-1); // along the bottom
  line
( 0, height-1, 0, 0); // up the left
}

void drawdata(String pin, int w, int h, int[] values)
{
  text
("pin: " + pin, w, h);
  text
("min: " + str(min(values) + 1), w, h + 14);
  text
("max: " + str(max(values) + 1), w, h + 28);
}

void check(int xx, int rr, int gg, int bb)
{

// floating point operations in Processing are expensive.
// only do the math for the float (fading out effect) if
// we have to. You can change 170 to 160 if you want it to
// fade faster, but be sure to change the other 170 to 160
// and the 20 to 10.
// (20 is the difference between 170 and 150)
 
if (xx<=170)
 
{
   
float kick = (parseFloat(170-xx)/20)*255;
   
// invert kick so the brighter parts are on the left side instead of the right.
    stroke
(rr,gg,bb,255-kick);
 
}
 
else
 
{
    stroke
(rr,gg,bb);
 
}
}

Запустив это приложение увидим красивую картинку :)

Собственно – остаётся: 

— научиться определять уровень напряжения сигнала
уровень напряжения можно получить из простой формулы
5V/1024 значений = 0,004883 В/значение (4,883 мВ).

— и определять частоту сигнала :)

NB
Если посмотреть код скетча — увидим, что Arduino работает с COM-портом на скорости 38400 бод (бит/сек)
для передачи байта по протоколу RS-232 используется 10 бит (стартовый бит, 8 бит данных, бит чётности (не используется), стоповый бит)
38400/10 = 3840 байт/сек
т.к. на один отсчёт идёт 3 байта (4-5) получаем
3840/3 = 1280 (960-768) отсчётов в секунду
5. Генерация звука – пьезоизлучатель

Самым простым вариантом генерации звука является использование пьезоизлучателя.

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

Прямой пьезоэффект:
в пьезозажигалках, для получения высокого напряжения на разряднике;

Обратный пьезоэлектрический эффект:
в пьезоизлучателях (эффективны на высоких частотах и имеют небольшие габариты);)

Пьезоизлучатели широко используются в различных электронных устройствах — часах-будильниках, телефонных аппаратах, электронных игрушках, бытовой технике.

Пьезокерамический излучатель состоит из металлической пластины, на которую нанесён слой пьезоэлектрической керамики, имеющий на внешней стороне токопроводящее напыление. Пластина и напыление являются двумя контактами.

Пьезоизлучатель также может использоваться в качестве пьезоэлектрического микрофона или датчика.


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

В качестве примера рассмотрим стандартный скетч Melody
(Sketchbook — Examples –Digital – Melody)

принципиальная схема:

— схема проста, один вывод от пьезоизлучателя подключается к земле, а второй (от центра пластинки/провод красного цвета) к выводу digital pin 9.

Код скетча:

/* Melody
 * (cleft) 2005 D. Cuartielles for K3
 *
 * Пример использования пьезоизлучателя для проигрывания
 * мелодии.  
 * Для этого на пьезоизлучатель подаётся сигнал
 * соответствующей частоты.
 *
 *      Расчет тонов производится следующим образом:
 *
 *       timeHigh = period / 2 = 1 / (2 * toneFrequency)
 *
 * таблица различных тонов:
 *
 * нота            частота               период timeHigh
 * c            261 Hz          3830    1915    
 * d            294 Hz          3400    1700    
 * e            329 Hz          3038    1519    
 * f            349 Hz          2864    1432    
 * g            392 Hz          2550    1275    
 * a            440 Hz          2272    1136    
 * b            493 Hz          2028    1014    
 * C            523 Hz          1912    956
 *
 * http://www.arduino.cc/en/Tutorial/Melody
 */

 
int speakerPin = 9;

int length = 15;                  // число нот
char notes[] = "ccggaagffeeddc "; // пробел - пауза
int beats[] = { 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 4 };
int tempo = 300;

void playTone(int tone, int duration) {
 
for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite
(speakerPin, HIGH);
    delayMicroseconds
(tone);
    digitalWrite
(speakerPin, LOW);
    delayMicroseconds
(tone);
 
}
}

void playNote(char note, int duration) {
 
char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
 
int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
 
 
// проиграть тон, соответствующий ноте
 
for (int i = 0; i < 8; i++) {
   
if (names[i] == note) {
      playTone
(tones[i], duration);
   
}
 
}
}

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

void loop() {
 
for (int i = 0; i < length; i++) {
   
if (notes[i] == ' ') {
      delay
(beats[i] * tempo); // пауза
   
} else {
      playNote
(notes[i], beats[i] * tempo);
   
}
   
   
// пауза между нотами
    delay
(tempo / 2);
 
}
}


Весёленькая мелодия :)

Генерация звука происходит в функции playTone(), быстрой подачей на пьезоизлучатель сигналов HIGH и LOW. Время между сменой уровней определяет — какой частоты сигнал мы получим на пьезоизлучателе.

А теперь изменим скетч так, чтобы мелодия считывалась не из памяти, а из последовательного порта :)

/* Sound Serial (aka Keyboard Serial)
 *
 * Расчет тонов производится следующим образом:
 *
 *       timeHigh = period / 2 = 1 / (2 * toneFrequency)
 *
 * таблица различных тонов:
 *
 * нота            частота               период timeHigh
 * c            261 Hz          3830    1915    
 * d            294 Hz          3400    1700    
 * e            329 Hz          3038    1519    
 * f            349 Hz          2864    1432    
 * g            392 Hz          2550    1275    
 * a            440 Hz          2272    1136    
 * b            493 Hz          2028    1014    
 * C            523 Hz          1912    956
 *
 * на основе примера проекта todbot.com (Tod E. Kurt <[email protected]>)
 */

 
int speakerPin = 9;
int serByte = -1;

char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' };
int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956 };
int tempo = 300;

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

void loop()
{
  digitalWrite
(speakerPin, LOW);
  serByte
= Serial.read();       // считываем байт
 
if (serByte != -1)
 
{
   
Serial.print(serByte,BYTE);
 
}
 
for (int j=0;j<=8;j++) {        // проверяем - это нота?
   
if (names[j] == serByte) {    // ага - нота!
     
for( int i=0; i<50; i++ ) { // проигрываем ноту 50 раз
        digitalWrite
(speakerPin, HIGH);
        delayMicroseconds
(tones[j]);
        digitalWrite
(speakerPin, LOW);
        delayMicroseconds
(tones[j]);
     
}
   
}
 
}
}

Однако, монитор последовательного порта Arduino IDE в данном случае не так удобен, т.к. нужно нажимать Enter, поэтому лучше воспользоваться . Здесь вполне можно поиграться :)

Например, так:
cdefffdc
6. Фоторезистор



Переменный резистор можно представить себе, как два последовательно соединённых резистора — т.н.  

но у потенциометра можно соответствующим образом менять их сопротивление.



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

Например, фоторезистор. 

 — полупроводниковый прибор, изменяющий величину своего сопротивления при облучении светом.


принципиальная схема:

Остаётся только подправить скетч 

/*
 * Photoresistor
 * тестовый скетч для работы с фоторезистором
 */


int photoPin = 0;  // фоторезистор подключен 0-му аналоговому входу
int ledPin = 9;    // светодиод подключается к digital pin 9
int val = 0;       // переменная для хранения значения входного напряжения

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

void loop()
{
  val
= analogRead(photoPin);  // считываем значение с фоторезистора
 
Serial.println(val);
  val
= val/4;                 // конвертируем из 0-1023 к 0-255
  analogWrite
(ledPin,val);     // устанавливаем значение
  delay
(200);
}

Уровень напряжения, зависящий от освещённости фоторезистора, считывается на аналоговом порту Analog Input 0 и записывается в COM-порт.
Далее соответствующая величина устанавливается на -порту digital pin 9, к которому подключен светодиод.
Получается, что яркость свечения светодиода будет зависеть от освещённости фоторезистора :)


Уважаемый  выдал мне для опытов фоторезисторы RPP131 — от старого проигрывателя пластинок :)
В темноте (если закрыть окошечко пальцем :) – сопротивление составляет около 300k, а при освещении – 20k.


Теперь усложним задачу :)
Берём второй фоторезистор и теперь попробуем подключить второй канал данных об освещённости.
Выход от второго фоторезистора заведём на Analog Input 1.

Принципиальная схема:

Вот так просто – мы получим простейшее зрение для робота :)
Пора это дело запрограммировать :)
Очевидный вариант – определять какой фоторезистор засвечен больше :)


/*
 * Photoresistor duo :)
 * работаем с двумя фоторезисторами
 * определяем - какой из них засвечен сильнее и зажигаем соответствующий светодиод
 * если освещённость одинаковая - зажигаем оба.
 */


int photoPin1 = 0;  // первый фоторезистор подключен 0-му аналоговому входу
int photoPin2 = 1;  // второй фоторезистор
int ledPin1 = 12;   // первый светодиод подключается к digital pin 9
int ledPin2 = 13;   // первый светодиод подключается к digital pin 10
int val1 = 0;       // переменные для хранения значения входного напряжения
int val2 = 0;       //

void setup()
{
 
Serial.begin(9600);
  pinMode
(ledPin1, OUTPUT);
  pinMode
(ledPin2, OUTPUT);
}

void loop()
{
  val1
= analogRead(photoPin1);  // считываем значение с фоторезисторов
  val2
= analogRead(photoPin2);
 
Serial.print("values: ");      // выводим их в COM-порт
 
Serial.print(val1);
 
Serial.print(" : ");
 
Serial.println(val2);
  val1
= val1/4;                 // конвертируем из 0-1023 к 0-255
  val2
= val2/4;                 //
 
if(val1==val2)                 // если равны - зажигаем оба светодиода
 
{
     digitalWrite
(ledPin1, HIGH);
     digitalWrite
(ledPin2, HIGH);
     
Serial.println("forward");
 
}
 
else
 
{
   
if(val1>val2)                 // если освещённость одного больше - зажигаем
   
{                             // соответствующий светодиод, а второй гасим
      digitalWrite
(ledPin1, HIGH);
      digitalWrite
(ledPin2, LOW);
     
Serial.println("left");
   
}
   
else
   
{
      digitalWrite
(ledPin2, HIGH);
      digitalWrite
(ledPin1, LOW);
     
Serial.println("right");
   
}
 
}
  delay
(200);
}



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

Например, у меня отыскался старый советский терморезистор :) Однако, в отличие, от фоторезистора – этот пример не так нагляден :)




Статья про фоторезистор от Ильи Данилова:
7. Сенсор на светодиоде

Мы уже умеем определять уровень освещённости с помощью , но, оказывается, в этом нам может помочь и наш старый знакомый — светодиод.

Идея по двойному использованию светодиода такова:

если приложить к светодиоду обратное напряжение, 

т.е. к катоду подключить – плюс (HIGH), а к аноду – минус (LOW))
на принципиальной схеме: pin 2 -> 1, а pin 3 -> 0


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

Чтобы не путаться с катодом и анодом у светодиода – нужно просто запомнить, что у светодиода ножка катода расположена со стороны плоского спила корпуса (так же ножка катода – короче)


Схема включения:

Если теперь переключить pin 2 на вход и отключить внутренний подтягивающий резистор,

чтобы этого добиться – нужно для порта, сконфигурированного, как ВХОД
командой 
pinMode(port,INPUT);

выполнить команду
digitalWrite(port,LOW);    // отключаем подтягивающий резистор

и наоборот — команда
digitalWrite(port, HIGH);  // подключаем подтягивающий резистор

— подключит подтягивающий резистор


то паразитная ёмкость будет разряжаться обратным током светодиода, который зависит от его освещенности. Через некоторое время нога переключится в логический 0. 
Вот это время разряда, зависящее от освещённости светодиода мы и должны измерить ;)
Паразитная ёмкость, разумеется, мала, но и обратный ток светодиода тоже — 
поэтому время разряда можно успешно измерить :)

Соберём эту простую схему:


Код скетча:

//
// Фотосенсор на светодиоде
//
// Пример исползования светодиода в качестве фотодатчика
// Схема подключения:
//
//           + digital2
//           |
//           <
//           > 100 ohm resistor
//           <
//           |
//           |
//         -----
//          / \  LED, maybe a 5mm, clear plastic is good
//         -----
//           |
//           |
//           + digital3
//
// мы будем подавать положительное напряжение на digital2 и
// низкое напряжение в digital3. Т.о. на светодиод будет подано обратное напряжение.
// Разумеется, светодиод светиться не будет, но будет заряжаться паразитная ёмкость
// соединения светодиода и ног микроконтроллера Arduino.
//
// Потом мы отключаем выход с digital2 и считаем
// за какой промежуток времени заряд разрядится через светодиод.
// Причём скорость разряда зависит от освещённости светодиода.
// Чем ярче свет, тем быстрее паразитная ёмкость будет разряжаться на Digital3.
//
// Т.о. сразу виден недостаток идеи - в темноте время разряда может быть довольно большим.
//
//
#define LED_N_SIDE 2
#define LED_P_SIDE 3

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

void loop()
{
 
unsigned int j;

 
// обеспечиваем обратное включение светодиода, заряжая паразитную ёмкость
 
// ног микроконтроллера и светодиода
  pinMode
(LED_N_SIDE,OUTPUT);
  pinMode
(LED_P_SIDE,OUTPUT);
  digitalWrite
(LED_N_SIDE,HIGH);
  digitalWrite
(LED_P_SIDE,LOW);

 
// изолируем pin 2 от светодиода
  pinMode
(LED_N_SIDE,INPUT);
  digitalWrite
(LED_N_SIDE,LOW);  // отключаем втроенный в МК подтягивающий резистор

 
// считаем сколько требуется времени на разряд до логического нуля
 
for ( j = 0; j < 100000; j++)
 
{
   
if ( digitalRead(LED_N_SIDE)==0)
     
break;
 
}
 
 
Serial.println(j, DEC); // Выводим значение счетчика в COM-порт
  delay
(100);             // Пауза, чтобы не переполнять буфер COM-порта

}

Т.о. получаем данные о времени разряда в виде данных на COM-порт:

Достоинством метода является то, что никто не мешает использовать тот же самый светодиод по своему прямому назначению :)

Для этого после строчки
Serial.println(j, DEC);

добавим код:
// а теперь зажгём светодиод :)
  digitalWrite
(LED_P_SIDE,HIGH);
  digitalWrite
(LED_N_SIDE,LOW);
  pinMode
(LED_P_SIDE,OUTPUT);
  pinMode
(LED_N_SIDE,OUTPUT);


так же можно изменить кусок считывая времени разряда на:
j=0;
 
while(digitalRead(LED_N_SIDE)!=0)
    j
++;


Т.о. получим:

#define LED_N_SIDE 2
#define LED_P_SIDE 3

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

void loop()
{
 
unsigned int j;

 
// обеспечиваем обратное включение светодиода, заряжая паразитную ёмкость
 
// ног микроконтроллера и светодиода
  pinMode
(LED_N_SIDE,OUTPUT);
  pinMode
(LED_P_SIDE,OUTPUT);
  digitalWrite
(LED_N_SIDE,HIGH);
  digitalWrite
(LED_P_SIDE,LOW);

 
// изолируем pin 2 от светодиода
  pinMode
(LED_N_SIDE,INPUT);
  digitalWrite
(LED_N_SIDE,LOW);  // отключаем втроенный в МК подтягивающий резистор

 
// считаем сколько требуется времени на разряд до логического нуля
  j
=0;
 
while(digitalRead(LED_N_SIDE)!=0)
    j
++;
 
 
Serial.println(j, DEC); // Выводим значение счетчика в COM-порт
 
// а теперь зажгём светодиод :)
  digitalWrite
(LED_P_SIDE,HIGH);
  digitalWrite
(LED_N_SIDE,LOW);
  pinMode
(LED_P_SIDE,OUTPUT);
  pinMode
(LED_N_SIDE,OUTPUT);
  delay
(50);             // Пауза, чтобы не переполнять буфер COM-порта
}


Есть заметка про использование обычного светодиода в качестве датчика и на 
, а применять можно, например, как  :)
 

Ссылки на использованные материалы:



дополнительное чтение:


8. Общение с Arduino — программирование работы с COM-портом.

Правильным местом с которого стоит начать своё изучение написание программ для взаимодействия с Arduino/Freeduino является официальный сайт:

— там приведены примеры работы с последовательным портом из разных языков и сред программирования. 

Ниже рассмотрим примеры работы с Arduino на С, С++, С#, PERL и Python.

Arduino + C


приводится в пример консольная POSIX C – программа, исходник которой можно скачать 
— программа может принимать и передавать данные на плату Arduino/Freeduino

Использование:

laptop
% gcc -o arduino-serial arduino-serial.c
laptop
% ./arduino-serial
Usage: arduino-serial -p <serialport> [OPTIONS]

Options:
-h, --help Print this help message
-p, --port=serialport Serial port Arduino is on
-b, --baud=baudrate Baudrate (bps) of Arduino
-s, --send=data Send data to Arduino
-r, --receive Receive data from Arduino & print it out
-n --num=num Send a number as a single byte
-d --delay=millis Delay for specified milliseconds

Примечание: 
Порядок параметров имеет значение! '-b' нужно устанавливать перед указыванием порта '-p'.
Можно устанавливать несколько флагов для выполнения последовательности действий: 
'-d 2000 -s hello -d 100 -r'
Означает: «ждать 2 секунды, отправить 'hello', ждать 100msec, считать ответ»

Примеры использования: 
1. Отправить в Arduino один ASCII-символ «6» 
laptop% ./arduino-serial -b 9600 -p /dev/tty.usbserial -s 6


2. Отправить в Arduino строчку “furby” 
laptop% ./arduino-serial -b 9600 -p /dev/cu.usbserial -s furby


3. Получить данные от Arduino 
laptop% ./arduino-serial -b 9600 -p /dev/cu.usbserial -r
read
: 15 Hello world!


Данный пример показывает вывод данных от скетча 
из проекта  (Зловещий Ардуино).

Отправить ASCII-строчку “get” в Arduino и получить результат:
laptop% ./arduino-serial -b 9600 -p /dev/cu.usbserial -s get -r
read
: d=0


И немножко о внутреннем устройстве программы:
Там всего три интересных функции, которые показывают пример работы с последовательным портом на C:

int serialport_init(const char* serialport, int baud)

— получает имя порта ("/dev/tty.usbserial",«COM1») и скорость работы, а возвращает файловый дескриптор открытого порта.

int serialport_write(int fd, const char* str)

– отправляет в порт (по файловому дескриптору) строчку данных

int serialport_read_until(int fd, char* buf, char until)

– считывает данные из последовательного порта в буфер, пока не встретит определённый символ (char until)

Arduino + C++ (using libSerial) 


Работа с последовательным портом с использованием библиотеки 

libSerial работает в POSIX-системах (на Windows работать не будет)

подключение библиотеки: 
#include < SerialStream.h >
#include < iostream >
#define PORT "/dev/ttyUSB0" // указывается порт, к которому подключена Arduino

SerialStream ardu;

using namespace std;
using namespace LibSerial;

примеры работы:

void open()
{
    ardu
.Open(PORT);
   
/*The arduino must be setup to use the same baud rate*/
    ardu
.SetBaudRate(SerialStreamBuf::BAUD_9600);
    ardu
.SetCharSize(SerialStreamBuf::CHAR_SIZE_8);
}

int get(char out)
{
   
int res;
   
char str[SOME_BIG_SIZE];
    ardu
<< out;
    ardu
>> str;
    sscanf
(str,"%d",&res);
   
return res;
}


Ссылки:


Arduino + C++ (for windows) 


по ссылке можно найти пример класса для работы с последовательным портом в системах на базе Windows:

SerialClass.h (header)

#ifndef SERIALCLASS_H_INCLUDED
#define SERIALCLASS_H_INCLUDED

#define ARDUINO_WAIT_TIME 2000

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

class Serial
{
   
private:
       
// дескриптор COM-порта
        HANDLE hSerial
;
       
// статус соединения
       
bool connected;
       
// различная информация о соединении
        COMSTAT status
;
       
// последняя ошибка
        DWORD errors
;

   
public:
       
// инициализация с указанным COM-портом
       
Serial(char *portName);
       
// закрытие соединения
       
//NOTA: по каким-то причинам не удаётся переподключиться
       
// нужно перезапускать программу
       
~Serial();
       
// считываем данные в буфер – если nbChar больше, чем
       
// число доступных байт – возвращает только
       
// доступные данные
       
// возвращает -1 при ошибке
       
int ReadData(char *buffer, unsigned int nbChar);
       
// записывает данные из буфера в порт
       
// возвращает true при удаче
       
bool WriteData(char *buffer, unsigned int nbChar);
       
// возвращает статус соединения
       
bool IsConnected();


};

#endif // SERIALCLASS_H_INCLUDED


Serial.cpp (source code file)

#include "SerialClass.h"

Serial::Serial(char *portName)
{
   
// пока ещё не подключились
   
this->connected = false;

   
// пытаемся подключиться к порту посредством CreateFile
   
this->hSerial = CreateFile(portName,
            GENERIC_READ
| GENERIC_WRITE,
           
0,
            NULL
,
            OPEN_EXISTING
,
            FILE_ATTRIBUTE_NORMAL
,
            NULL
);

   
// проверяем – было ли соединение успешным
   
if(this->hSerial==INVALID_HANDLE_VALUE)
   
{
       
//если не было – выводим ошибку
       
if(GetLastError()==ERROR_FILE_NOT_FOUND){

           
// печатаем ошибку
            printf
("ERROR: Handle was not attached. Reason: %s not available.\n", portName);

       
}
       
else
       
{
            printf
("ERROR!!!");
       
}
   
}
   
else
   
{
       
// подключились – теперь устанавливаем параметры
        DCB dcbSerialParams
= {0};

       
// считываем текущие параметры
       
if (!GetCommState(this->hSerial, &dcbSerialParams))
       
{
           
// если невозможно – говорим о неудаче
            printf
("failed to get current serial parameters!");
       
}
       
else
       
{
           
// устанавливаем параметры для соединения с Arduino
            dcbSerialParams
.BaudRate=CBR_9600;
            dcbSerialParams
.ByteSize=8;
            dcbSerialParams
.StopBits=ONESTOPBIT;
            dcbSerialParams
.Parity=NOPARITY;

             
// применяем параметры
             
if(!SetCommState(hSerial, &dcbSerialParams))
             
{
                printf
("ALERT: Could not set Serial Port parameters");
             
}
             
else
             
{
                 
// отлично! Мы успешно подлючились :)
                 
this->connected = true;
                 
//ждём пока Arduino перезагрузится
                 
Sleep(ARDUINO_WAIT_TIME);
             
}
       
}
   
}

}

Serial::~Serial()
{
   
// проверяем статус подключение
   
if(this->connected)
   
{
       
// отключаемся
       
this->connected = false;
       
// закрываем дескриптор порта
       
CloseHandle(this->hSerial);
   
}
}

int Serial::ReadData(char *buffer, unsigned int nbChar)
{
   
// число считываемых байт
    DWORD bytesRead
;
   
// число байт, которое мы действительно хотим считать
   
unsigned int toRead;

   
// используем ClearCommError для получения информации о статусе последовательного порта
   
ClearCommError(this->hSerial, &this->errors, &this->status);

   
// проверяем – есть ли информация для считывания
   
if(this->status.cbInQue>0)
   
{
       
// если количество данных меньше требуемого – считываем
       
// столько, сколько есть
       
if(this->status.cbInQue>nbChar)
       
{
            toRead
= nbChar;
       
}
       
else
       
{
            toRead
= this->status.cbInQue;
       
}

       
// Пытаемся считать нужное количество данных и
       
//возвращаем число считанных байт при удачном завершении.
       
if(ReadFile(this->hSerial, buffer, toRead, &bytesRead, NULL) && bytesRead != 0)
       
{
           
return bytesRead;
       
}

   
}

   
// ничего не считали или где-то была ошибка – возвращаем -1
   
return -1;

}


bool Serial::WriteData(char *buffer, unsigned int nbChar)
{
    DWORD bytesSend
;

   
// пытаемся записать значение буфера buffer в COM-порт
   
if(!WriteFile(this->hSerial, (void *)buffer, nbChar, &bytesSend, 0))
   
{
       
// если не получилось – получаем код ошибки и возвращаем false
       
ClearCommError(this->hSerial, &this->errors, &this->status);

       
return false;
   
}
   
else
       
return true;
}

bool Serial::IsConnected()
{
   
// просто возвращаем статус соединения :)
   
return this->connected;
}


Пример использования:

#include <iostream>

#include "SerialClass.h"

#define COM_PORT "COM1"

#define BUF_SIZE 256

int main()
{
       
Serial com1(COM_PORT);
       
// обнуляем буфер
       
char buf[BUF_SIZE];
       
for(int i=0;i<BUF_SIZE;i++)
                        buf
[i]='\0';
       
// считываем и выводим данные из порта
        com1
.ReadData(buf,BUF_SIZE-1);
        std
::cout <<buf<< std::endl;
       
return 0;
}

Данный пример тестировался в 
, но должен прекрасно работать и в Visual Studio :)

Arduino + C#


.Net 2.0 предоставляет возможность просто работать с System.IO.Ports 
— поэтому очень просто написать программу, например, которая будет принимать вводимую информацию с клавиатуры и перенаправлять её в порт.

using System;
using System.IO.Ports;

 
namespace test
 
{
   
class Program
   
{

       
static void Main(string[] args)
       
{
           
SerialPort port = new SerialPort("COM1", 9600);
            port
.Open();
           
while (true)
           
{
               
String s=Console.ReadLine();
               
if (s.Equals("exit"))
               
{
                   
break;
               
}
                port
.Write(s+'\n');
           
}
            port
.Close();

       
}

   
}
 
}

Скомпилировать данный пример можно даже без сред разработки, а просто имея установленный .NET Framework. Для этого нужно перейти в директорию Framework-а – обычно это что-то вроде:
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\
теперь нужно скопировать туда файл с программой на C# — пусть это будет 
com-cs.cs

Чтобы скомпилировать — нужно выполнить команду:
csc com-cs.cs



Компилятор отработает и в директории появится исполняемый файл
com-cs.exe

Примеры:



Arduino + PERL


требуется модуль 


# установка параметров COM-порта
use Device::SerialPort;
my $port = Device::SerialPort->new("/dev/tty.usbserial");

# 19200, 81N on the USB ftdi driver
$port
->baudrate(19200);
$port
->databits(8);
$port
->parity("none");
$port
->stopbits(1);


Передача данных в порт:
$port->write("Whatever you feel like sending");


Получение данных от Arduino:
while (1) { 
   
# пытаемся получить данные из порта
   
my $char = $port->lookfor();

   
# если данные есть – распечатываем :)
   
if ($char) {
       
print "Recieved character: $char \n";
   
}
}


Arduino + Python


работая в UNIX-подобных системах, с последовательным портом можно общаться, как с файлом – считывать и записывать данные. 
Но есть кросплатформенная библиотека – 
, которая делает эти операции более наглядными.

эта библиотека уже упоминалась в посте про 


Однако, для её работы под Windows понадобится ещё 
 


#
# для работы с COM-портом нужна библиотека
# pySerial, кроме того, под винду понадобится еще pyWin32
#
import serial
SERIAL_PORT
= 'COM1'
SERIAL_SPEED
= 38400

ser
= serial.Serial(SERIAL_PORT, SERIAL_SPEED)

while 1:
           
#s = ser.read()            # считывается один байт
            s
= ser.readline().strip() # считывается строка и убираются символы “\r\n”
           
print s                    # печатаем на экран

писать данные в порт так же просто: 

import serial
ser
= serial.Serial('/dev/tty.usbserial', 9600)
ser
.write('5')


Примеры:
если не хочется ничего ставить – можно посмотреть или использовать  

программы Tod E. Kurt-а 




Arduino + Processing
9. Аналоговый датчик температуры – LM335

Варианты измерения температуры:
1. термопара
2. терморезистор
3. аналоговый термодатчик
4. цифровой термодатчик 
подробнее можно почитать 


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

LM335 – это недорогой (~ 40 рублей) температурный чувствительный элемент с диапазоном от -40 °C до +100°Cи точностью в 1°C.

Даташит (datasheet – документация на элемент) на LM335 можно посмотреть 

Фактически, LM335 — это  с нормированным Температурным Коэффициентом Напряжения (ТКU =10 мВ/K). 

Т.е. изменение температуры датчика на 1 градус ведёт к изменению напряжения на 10mV.


Схема включения (соответствует типовой схеме включения стабилитрона):


Задавая ток через датчик в диапазоне от 0.45mA до 5mA (резистором R1), получаем напряжение на датчике, которое в десятках mV представляет абсолютную температуру в градусах .

Как видим, используются только вторая и третья ножки датчика (если повернуть датчик к себе плоской стороной – то нумерация ножек будет идти слева-направо)

То есть, схему можно представить так:



Сопоставим напряжение на датчике и температуру, припоминая, что 
0С = 273.15К
На датчике, при этом будет напряжение 2.7315V
ТКU =10 мВ/K => в 1V будет 100K

-40С = 233.15К
на датчике будет 2.3315V
+100С = 373.15К 
на датчике будет 3.7315V

Получается – нам нужно только снять это напряжение на аналоговом входе (например, analog input 0) :)
Однако, функция , возвращает значение от 0 до 1023, причём 1023 соответствует величине Опорного Напряжения, задаваемого функцией  и по умолчанию, составляющего 5V.
Т.о., чтобы узнать какое напряжение поступило к нам на вход – нужно выполнить простое преобразование:
double voltage = val*5.0/1024;

, где val – величина, полученная от analogRead
Далее остаётся только перевести это напряжение в градусы, а потом привести из градусов Кельвина в более привычные :
double temp = voltage*100 - 273.15;

в температуру переводим – просто умножая на 100 

Код скетча:

//
// работа с температурным датчиком LM335
//
//

int ledpin=13;
int lm335=0;

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

void loop()
{
 
double val = analogRead(lm335);
 
Serial.print("Analog 0: ");
 
Serial.print(int(val));
 
double voltage = val*5.0/1024;
 
Serial.print(" : ");
 
Serial.print(voltage);
 
double temp = voltage*100 - 273.15;
 
Serial.print(" : ");
 
Serial.println(temp);
  delay
(100);
}

В последнем столбце вывода получаем заветную температуру в градусах Цельсия :)



читать далее: 

Ссылки по теме:
1.  
2.  

10. Подключаем к Arduino мышку PS/2

На официальном сайте ардуино натолкнулся на  для работы с устройствами PS/2.

Это даёт шанс пообщаться с компьютерной мышкой напрямую :)
У меня как раз завалялась полудохлая мышка и я сразу решил проверить на ней работу библиотеки :)

Посмотрим, что собственно представляет из себя разъём PS/2 (Personal System)
Если посмотреть на разъём типа «мама» (female connector), выведенный на материнской плате ПК, то увидим шесть входов:

Распиновка (pinout) следующая: 
Pin 1   +DATA           Data
Pin 2   N/C             Not connected
Pin 3   GND             Ground
Pin 4   Vcc             +5 V DC (до 275 mA)
Pin 5   +CLK            Clock
Pin 6   N/C             Not connected

Подробнее о протоколе обмена PS/2 можно .

Таким образом, у мышки, имеем шесть выводов, два из которых не используются :) 
Т.к. на рисунке приведён пример распиновки «мамы» PS/2, то соответствующие выводы типа «папа» у разъёма компьютерной мышки можно получить зеркальным отображением разъёма :)

Я подпаял к кабелю мышки провода витой пары, которые так хорошо работают с макетной платой, и напрямую подключил их к соответствующим портам ардуины 

, как показано на страничке ардуиновской библиотки PS/2 :)

Далее нужно скачать файл 
, который содержит библиотеку для работы с PS/2.
Открыв архив, видим папку ps2, которую нужно поместить в библиотечную директорию Arduino IDE (\hardware\libraries\).
Перезапускаем IDE и видим, что у нас появилась не только новая библиотека ps2, но и новые примеры :)

компилируем скетч и загружаем в МК.
Теперь через монитор последовательного порта можем наблюдать, данные о перемещении мышки:

Здорово, правда? И так просто :)

Из документации известно, что X растёт слева-направо, а Y снизу-вверх.

Т.е. вполне обычная для урока математики система координат :)


Таким образом, о движении мышки вправо можно судить по положительной величине X, и наоборот – о движении мышки влево будет говорить отрицательная величина X. Аналогично и для Y.

Обязательно стоит внимательно изучить тестовый скетч ps2_mouse и обратить внимание, что работу с мышкой нужно инициализировать (команда 0xFF)

/*
 * скетч для работы с ps/2-мышкой
 */


#include <ps2.h>

/*
 * к Pin 5 -  подключена линия Data, а к pin 6 – линия Clock
 */

PS2 mouse
(6, 5);

/*
 * Инициализация работы с мышкой.
 * Посылается команда Сброса (Reset it)
 * затем мышка переводится режим remote mode
 * так, что мы получаем от неё данные по запросу
 */

void mouse_init()
{
  mouse
.write(0xff);  // reset
  mouse
.read();  // ack byte
  mouse
.read();  // blank */
  mouse
.read();  // blank */
  mouse
.write(0xf0);  // remote mode
  mouse
.read();  // ack
  delayMicroseconds
(100);
}

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

/*
 * считываем данные с мышки и выводим в COM-порт
 */

void loop()
{
 
char mstat;
 
char mx;
 
char my;

 
/* запрашиваем данные от мышки */
  mouse
.write(0xeb);  // команда на чтение данных
  mouse
.read();      //  игнорируем ack
  mstat
= mouse.read();
  mx
= mouse.read();
 
my = mouse.read();

 
/* выводим данные в COM-порт*/
 
Serial.print(mstat, BIN);
 
Serial.print("\tX=");
 
Serial.print(mx, DEC);
 
Serial.print("\tY=");
 
Serial.print(my, DEC);
 
Serial.println();
//  delay(20);  
}


Список команд, которые поддерживает мышка можно посмотреть в документации на конкретный контроллер, используемый в мышке.
Например, в данной мышке используется оптический сенсор PAW3401,
поддерживающий следующие команды:

а вот табличка — что за данные шлёт мышка в ответ на команду чтения данных (0xEB):

нулевой бит 1-го байта показывает нажата ли левая кнопка мышки, первый бит — правая;
второй байт содержит смещение по X: значение может быть от -127 (смещение влево) до 127 (вправо);
третий байт содержит смещение по Y: значение может быть от -127 (смещение вниз) до 127 (вверх).
Т.о. если мышку не трогать во втором и третьем байтах будут нули.
для мышки с колесом прокрутки будет 4-й байт — для оси Z

Для чего же мы можем использовать мышку?
Например, как датчик перемещения… ну или просто управлять с помощью мышки сервой или нашим роботом :)


Ссылки:
1. 
2. 
3. 
4. 

Практическое программирование Arduino/CraftDuino - протокол 1-Wire и iButton

  •   
Сегодня мы познакомимся с интересным протоколом  и даже сразу попробуем поработать с устройством, использующим этот протокол. «Как?» — спросите Вы – «мне не нужно будет покупать детали в радиомагазине?»
«Нет» — отвечу я :) С большой долей вероятности, Вы пользуетесь устройством 1-Wire и, возможно, даже не догадываетесь об этом :) Это всего-навсего – ключ-таблетка от домофона ()!

Неужели?
Достанем ключи из кармана и примемся их пристально разглядывать :)
У меня контактная площадка сильно затёрта, но какие-то надписи проглядывают.

Потёр площадку фломастером и становится видна заветная надпись:

iButton.com
00000F67CE41
1-Wire
0744 UB1 DS1990A#F5


Ага! 1-Wire! 
Так что же это за интерфейс? 
Это однопроводной интерфейс, разработан фирмой Dallas Semiconductor (ныне MAXIM) в конце 90-х годов.

Этот интерфейс интересен тем, что для двустороннего обмена требуется всего одна линия! (отсюда и название :)

Подробнее об интерфейсе .

Возвращаемся к нашему ключу :) На самой таблетке указан тип устройства и, похоже, уникальный номер микросхемы ;)

DS1990A – это и есть название нашего устройства.

Смотрим 
Наш ключ — это самая первая модель в списке :)
DS1990A — 64 Bit ID — уникальный серийный номер-ключ
Страничка этого ключика , а  можно скачать датащит ;)

Число после # определяет форм-фактор ключа – у меня F5.

Итак, наш ключ хранит 64 бита уникальной информации
Питается от 2.8V до 6.0V
К центральной контактной площадке подключается линия данных, а к боковой каёмке – землю.
Ключ может работать в диапазоне температур от -40°C до +85°C – впрочем, это нам не нужно :)

Читаем датащит далее :)

Величина подтягивающего резистора рекомендуется в 2.2k
Так… запомним :)

Каждый DS1990A прошивается уникальным 64-битным номером

первые 8 бит — номер серии устройства (01h)
следующие 48 бит — уникальный серийный номер
последние 8 бит — CRC-код предыдущих 56 бит информации :)

(Cyclic redundancy code,  — циклический избыточный код) — способ цифровой идентификации некоторой последовательности данных, который заключается в вычислении контрольного значения её циклического избыточного кода.


Далее в датащите перечислены команды, которые понимает ключ:
33h — считать ROM — команда позволяет считать заветные 64-бита данных ключа
F0h — поиск ROM — т.к. к сети 1-wire может быть подключено несколько устройств данная команда используется для идентификации подключённых устройств.
55h — соответствие ROM / CCh — пропуск ROM
— данные команды входят в минимальный набор необходимых команд 1-wire-устройств. Т.к. в ключе DS1990A эти команды не требуются — он на них не отвечает.

Arduino//Freeduino может работать с 1-Wire, используя библиотеку Jim Studt-а – .

Настало время пообщаться с нашим ключиком! :)

Схема подключения – крайне проста (это ведь 1-Wire! :)

Далее нужно  и поместить её в свою директорию libraries.

Код скетча:
#include <OneWire.h>

/*
 * тестируем работу по 1-Wire с ключём-таблеткой DS1990A
 */


OneWire  ds(10);  // на  digital pin 10

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

void loop(void) {
 
byte i;
 
byte present = 0;
 
byte data[12];
 
byte addr[8];
 
 
if ( !ds.search(addr)) {
     
Serial.print("No more addresses.\n");
      ds
.reset_search();
     
return;
 
}
 
 
Serial.print("R=");
 
for( i = 0; i < 8; i++) {
   
Serial.print(addr[i], HEX);
   
Serial.print(" ");
 
}

 
if ( OneWire::crc8( addr, 7) != addr[7]) {
     
Serial.print("CRC is not valid!\n");
     
return;
 
}
 
 
if ( addr[0] != 0x01) {
     
Serial.print("Device is not a DS1990A family device.\n");
     
return;
 
}
 
Serial.println();
  ds
.reset();
 
  delay
(1000);
}

Происходит постоянный поиск устройств на линии 1-Wire. Пока ключ не подключён – выдаётся: 
No more addresses.
При подключении ключа-таблетки – считывается её номер и выдаётся в COM-порт.
Например, вот что выдаётся при подключении ключа-таблетки, с которого начиналась эта статья:

Первая цифра – номер серии – 01h
Далее, как видим, действительно ключ хранит номер, нанесённый на своём корпусе ;)
Только этот номер выведен наоборот – как и положено по протоколу 1-Wire – начиная с младшего байта ;)
Последний байт –B6h — CRC-код предыдущих байт.

Ура! Вот мы и считали наш ключ :)

Теперь можно самостоятельно сделать замок для комнаты или разблокировать свой компьютер по ключу :) Фантазируем :)



Cсылки на материалы:
 — данная замечательная статья использовалась при написании заметки об интерфейсе 1-Wire











библиотека  – 
  •  


Если после прочтения , Вам в голову пришла мысль, что можно сделать и обратную комбинацию – значит мы мыслим в одинаковом направлении :)

Сразу хочу сказать, что эта идея далеко не новая, и воспользовавшись поиском можно найти различные решения – примеры можно найти в конце статьи в ссылках.

Итак, что же нам нужно?
А нужно нам всего-навсего произвести симуляцию 1-Ware slave-устройства, выдавая себя за iButton :)
Для этого нужно вспомнить, что происходит в линии 1-Wire:
Сначала происходит
1. инициализация – ведущее устройство (домофон) подаёт импульс RESET, после него ведомое устройство (это мы) должно дать ответ PRESENCE (прижать линию к земле на 60 — 240 микросекунд)

Далее происходит сам обмен информации:
2. домофон выдает команду на чтение ПЗУ (ROM) – это должно быть 33h.
Информация, как мы помним, передаётся побайтно, бит за битом.
При этом «0» передаётся прижиманием линии к земле в течении всего тайм-слота (60 — 120 микросекунд)
А «1» передаётся кратковременным прижиманием (на 1-15микросекунд) и последующим отпусканием линии.
3. домофон выдерживает некоторое время и начинает посылать импульсы приема информации.
Т.к. в ответ ожидается 8 байт информации – будет 64 импульса (по одному импульсу для передачи каждого бита информации от нас).
Если мы хотим передать «0» — мы удерживаем линию на логическом нуле, а если хотим передать «1», то можем ничего не делать :)
Эмулируя ключ, мы должны сначала передать номер серии устройства 01h.
Затем собственно 6 байт номера ключа (начиная с младшего байта) и в самом конце – байт CRC-кода предыдущей информации.
Для ключа, рассмотренного в статье про iButton – это будет последовательность байт:
01h
41h
CEh
67h
0Fh
00h
00h

B6h

Попробуем это дело запрограммировать :)
Чтобы не бегать постоянно к домофону – начальную проверку можно попробовать сделать в  :)
Поместим в  – в одну загрузим скетч работы с iButton, а во вторую будем грузить код нашего эмулятора :)


Кроме того, при симуляции схемы можно воспользоваться виртуальным инструментарием. Например, подключив в Протеусе на линию 1-Wire виртуальный осциллограф – можно вживую понаблюдать – как происходит обмен информацией :)


блок-схема скетча для эмуляции iButton будет такой


А если мы не знаем ключ – сможем ли мы его подобрать перебором?

Оценочно прикинем – сколько времени понадобится на подбор ключа :)
У моего ключа-таблетки номер 
00000F67CE41
, что даёт нам возможность предположить, что пока для нумерации iButton-ов используются первые 4 байта номера :)
Вне всяких сомнений — это меньше 281 биллиона, которые можно спрятать в шести байтах :)
И даёт нам всего 4 миллиарда вариантов :)
0xFFFFFFFF = 4294967295

Прикинем — сколько времени займёт проверка одного ключа:
PRESENCE ~ 480 мкс
8 бит команды 120 мкс * 8
64 бита данных 120 мкс *64

Итого ~ 10 мс => 100 ключей в секунду (в датащите упоминается про 75 ключей в секунду)
Получается, что на полный перебор потребуется 497 дней :( Что-то долго :) 

В интернете упоминается, что такой брутфорс (brute force) на домофонах не сработает и после трёх неправильных ключей домофон загудит и впадёт в ступор на 5 минут :)


Однако выход есть – дело в том, что на некоторых моделях домофонов в новой чистой памяти домофона (предназначенной для хранения ключей жильцов) все биты установлены в 1 (т.е. забиты FF-ами). 
И если ключ будет выдавать тоже единицы, то программа подумает, что в чистой памяти тоже хранятся коды ключей и они совпадают с тем, что записано в нашем универсальном ключе и дверь откроется :)))

 дыра в программном обеспечении контроллера и есть во всех дешёвых контроллерах. Причём простая до смеху. Контроллер проверяет ключ (причём обычно только последнии 4 байта — контроллер то дешёвый) с записаными в ЭСПЗУ. А как выглядят пустые ячейки? То есть если записать ключ вида FFFFFF......FFFFFF что будет? Правильно, он уже везде прописан :) Подверженны все домофоны типа Цифрал ТС, Визит, Метаком


А где же скетч эмулятора?

Дело в том, что мы решили устроить  :)
Первый приславший работоспособный ардуиновский скетч эмулятора iButton-а получит  в подарок!

Ниже приводится набросок такого скетча – вам остаётся только  и наполнить кодом функции:
void wire_send_byte(byte dsbyte)
byte wire_read_byte()
void wire_write0(void) 
void wire_write1(void) 
или же полностью переписать скетч самостоятельно ;)

Условия конкурса:
— скетч должен быть написан на Wiring-е
— код ключа должен храниться в статическом массиве
— скетч должен быть опробован в работе ;)

Ваши варианты скетча присылайте по адресу [email protected] с пометкой Конкурс iButton!

Набросок скетча (ВНИМАНИЕ – СКЕТЧ НЕ ДОПИСАН ДО КОНЦА):

/*
* Эмулируем работу ключа-таблетки iButton типа DS1990A
*
*
*
* http://robocraft.ru
*/


int onepinio = 10; // порт для подключения 1-Wire

// данные ключа  - номер серии, 6 байт номера, байт CRC
byte key_data[8] = {0x1, 0x41, 0xCE, 0x67, 0x0F, 0x00, 0x00, 0xB6};

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

void loop()
{
   
// считываем импульс RESET из линии
   
//
    wire_read_RESET
();
       
   
//
   
// выдаём в линию импульс PRESENCE
   
//
    wire_PRESENCE
();
       
       
   
//
   
// считываем команду от домофона
   
//
   
int b = wire_read_byte();
       
       
   
//
   
// что за команду считали? :)
   
//
   
if(b==0xF0) // SEARCH ROM
   
{
       
//
       
// начинаем двубитную передачу :(
       
//
        wire_send_duos_bits
();
   
}
   
else
   
{
       
if(b==0x33) // READ ROM
       
{
           
//
           
// отправляем данные iButton :)
           
//
            wire_send_data
();
       
}
   
}
    delay
(300);
}

//
// ждём пока линия не прижмётся к земле
//
void wire_wait()
{
    pinMode
(onepinio, INPUT);
   
while(digitalRead(onepinio))
   
{
        delayMicroseconds
(1);
   
}
}

//
// двубитное общение с ведущим устройством - см. AN187
//
void wire_send_duos_bits()
{
}

//
// считываем импульс RESET из линии
//
void wire_read_RESET()
{
    pinMode
(onepinio, INPUT);
   
while(digitalRead(onepinio))
   
{
        delayMicroseconds
(1);
   
}
   
while(!digitalRead(onepinio))
   
{
        delayMicroseconds
(1);
   
}
}

//
// выдаём в линию 1-Wire сигнал PRESENCE
// для этого нужно прижать линию к земле на 60-240 микросекунд, а затем отпустить
//
void wire_PRESENCE()
{
    pinMode
(onepinio, OUTPUT);
    digitalWrite
(onepinio,LOW);         // прижимаем линию к земле
    delayMicroseconds
(120);             // 60-240 микросекунд
    pinMode
(onepinio, INPUT);           // отключаемся
    delayMicroseconds
(450);
}

//
// выдача в линию данных iButton
//
void wire_send_data()
{
   
for(int i=0;i<8;i++)
        wire_send_byte
(key_data[i]);
}

//
// выдача в линию байта данных
//
void wire_send_byte(byte dsbyte)
{
}

//
// считываем байт данных из линии
//
byte wire_read_byte()
{
}

//
// пишем бит "0" в линию
//
void wire_write0(void)
{
}

//
// пишем бит "1" в линию
//
void wire_write1(void)
{
}



Ссылки:
 
 
В продолжение темы  рассмотрим температурный 1-Wire датчик — DS18S20 :)

DS18S20 – высокоточный цифровой термометр с интерфейсом  (High Precision 1-Wire Digital Thermometer) от DALLAS Semiconductor (Maxim).
Для подключения датчика достаточно двух проводов – линии данных и заземления; 
питание элемента в этом случае называется «паразитным»/фантомным (Parasite power mode) и осуществляется по линии данных за счёт накопления энергии во встроенном конденсаторе во время высокого уровня напряжения 
(не рекомендуется при температуре свыше +100 С из-за быстрого разряда конденсатора).


Нормальный режим питания (external supply), заключается в подключении к датчику источника питания (3V-5V).

В обоих случаях рекомендуется использовать подтягивающий резистор в 4.7k

Характеристики DS18S20:
Интерфейс 1-Wire
Измеряемая температура от -55 до +125 °C
Точность 0.5 °C в диапазоне от -10 до +85 °С
Температура считывается 9-ю битами данных
Время на конвертацию температуры — 750 ms (максимальное)

Как и любое 1-Wire устройство — каждый DS18S20 содержит уникальный 64-битный ROM
Первые 8 бит — код серии ( для DS18S20 код — 10h, а для DS18B20 — 28h ).
Затем 48 бит уникального номера, и в конце 8 бит CRC-кода.

Однако, самое интересное — данные о температуре хранятся в оперативной памяти(scratch-pad memory) датчика.

Память состоит из оперативной ROM и энергонезависимой EEPROM:

Первые два байта – содержат данные об измеренной температуре, 
Третий и четвёртый байты хранят верхний (TH) и нижний (TL) пределы температуры.
Пятый и шестой – не используются.
Седьмой и восьмой – байты – счётчики – могут использоваться для более точного измерения температуры.
Девятый байт хранит CRC-код предыдущих восьми :)

Теперь осталось разобраться с тем – какие команды может выполнять этот датчик.

Кроме уже знакомых нам:
Поиск адресов — Search ROM [F0h] 
Чтение адреса устройства — Read ROM [33h]
Выбор адреса — Match ROM [55h]
Игнорировать адрес — Skip ROM [CCh]

Добавились:

Поиск Тревоги — Alarm Search [ECh] – операция этой команды идентична операции поиска адресов [F0h], за исключением того, что в данном случае ответят только те датчики, у которых, при последнем измерении, температуры вышла за установленные пределы (выше TH или ниже TL).

И команды работы со scratch-pad memory:

Конвертировать температуру — Convert T [44h] – датчик произведёт измерение и запись данных о текущей температуре. Если ведущее устройство будет за этой командой слать тайм-слоты чтения, то пока конвертация не закончена — DS18S20 будет выдавать в линию «0», а после завершения конвертации «1». 
Если датчик работает в режиме паразитного питания, то не позже 10 мкс после подачи команды устройство управления должно установить высокий уровень на шине на время продолжительности преобразование (не менее 750 ms)

Запись в память — Write Scratchpad [4Eh] – эта команда позволяет записать 3 байта в память датчику. Первый байт запишется в TH, второй в TL, а третий байт запишется в пятый байт памяти – у DS18S20 – он не используется, а у DS18B20 – это байт конфигурации

Чтение памяти — Read Scratchpad [BEh] – позволяет нам считать память датчика. В ответ на эту команду датчик вернёт 9 байт своей памяти, начиная с 0-го байта TEMPERATURE LSB и заканчивая восьмым – CRC.

Копировать память — Copy Scratchpad [48h] – датчик скопирует содержимое ОЗУ — TH и TL в EEPROM 
Если датчик работает в режиме паразитного питания, то не позднее 10 мкс после подачи этой команды устройство управление должно установить высокий уровень на шине и поддерживать его в течении не менее 10ms.
Производители обещают, что EEPROM датчика DS18S20 должен выдержать минимум 50000 циклов перезаписи и будет хранить данные 10 лет при T = +55°C.


Повторная загрузка — Recall E2 [B8h] – загружает данные из EEPROM в ОЗУ. Эта операция выполняется автоматически, как только на датчик подаётся напряжение.

Вид электропитания датчика — Read Power Supply [B4h] – с помощью этой команды можно определить – какой вид питания использует датчик. Если датчик выставит на шине «0» — значит он использует паразитное питание. Если же датчик использует внешнее питание, то он ответит «1».

Теперь становится понятно, что нужно сделать, чтобы получить от датчика данные о температуре :)

После RESET-а и поиска устройств на линии 1-Wire, нужно выдать команду 
0x44, чтобы запустить конвертацию температуры датчиком.
Подождать не менее 750 ms и выдать команду
0xBE, чтобы считать ОЗУ датчика.
Данные о температуре будут в первых двух байтах.

Остаётся только сложить эти два байта, предварительно сдвинув старший байт на 8 бит влево:
TReading = (HighByte << 8) + LowByte;

Температуру получим просто разделив на 2 (шаг ведь в пол-градуса)
Tc_100 = TReading/2;


Однако, датчик ведь называется «высокоточный»! Поэтому предусмотрена процедура для получения более точных данных о температуре:
0. Считать данные с датчика: LSB, MSB, COUNT_REMAIN, COUNT_PER_C
1. Получить данные о температуре с точностью в пол-градуса. (Tc_100)
2. Уточнённые данные о температуре высчитываются по формуле:
Temperature = Tc_100  0.25 + (COUNT_PER_C - COUNT_REMAIN)/ COUNT_PER_C


Как помним, для работы с датчиками с интерфейсом 1-Wire можно использовать  –OneWire

Соответственно, скетч для получения температуры от DS18S20 будет таким:

#include <OneWire.h>

/*
 * получаем температуру от DS18S20
 
 */


OneWire  ds(10);  // линия 1-Wire будет на pin 10

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



void loop(void)
{
 
byte i;
 
byte present = 0;
 
byte data[12];
 
byte addr[8];
 
 
if ( !ds.search(addr)) {
     
Serial.print("No more addresses.\n");
      ds
.reset_search();
     
return;
 
}
 
 
Serial.print("R=");
 
for( i = 0; i < 8; i++) {
   
Serial.print(addr[i], HEX);
   
Serial.print(" ");
 
}

 
if ( OneWire::crc8( addr, 7) != addr[7]) {
     
Serial.print("CRC is not valid!\n");
     
return;
 
}
 
 
if ( addr[0] != 0x10) {
     
Serial.print("Device is not a DS18S20 family device.\n");
     
return;
 
}

  ds
.reset();
  ds
.select(addr);
  ds
.write(0x44,1);         // запускаем конвертацию
 
  delay
(1000);     // скорее всего достаточно 750ms
 
// we might do a ds.depower() here, but the reset will take care of it.
 
  present
= ds.reset();
  ds
.select(addr);    
  ds
.write(0xBE);         // считываем ОЗУ датчика

 
Serial.print("P=");
 
Serial.print(present,HEX);
 
Serial.print(" ");
 
for ( i = 0; i < 9; i++) {           // обрабатываем 9 байт
    data
[i] = ds.read();
   
Serial.print(data[i], HEX);
   
Serial.print(" ");
 
}
 
Serial.print(" CRC=");
 
Serial.print( OneWire::crc8( data, 8), HEX);
 
Serial.println();
 
 
// высчитываем температуру :)
 
int HighByte, LowByte, TReading, Tc_100;
 
LowByte = data[0];
 
Serial.print("LB= ");Serial.print(LowByte,HEX);
 
HighByte = data[1];
 
Serial.print(" HB= ");Serial.print(HighByte,HEX);
 
TReading = (HighByte << 8) + LowByte;
  Tc_100
= TReading/2;
 
Serial.print(" T = ");Serial.print(Tc_100);
 
Serial.println();
}


Этот скетч можно опробовать даже не собирая схемы :) 
Элемент DS18S20 есть в базе протеуса, поэтому можно набросать схему в нём:


Загрузив в МК полученный после компиляции hex и запустив симуляцию увидим в окошке терминала:

Соответствующая температура выставляется на виртуальном датчике кнопками со стрелочками вверх и вниз.

А вот что будет в порту, если попробуем подключить реальный датчик DS18S20


Ссылки:

Практическое программирование Arduino/CraftDuino - драйвер двигателей L293D

  •   
Настала пора Arduino поуправлять моторчиками :)

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

L293D содержит два драйвера для управления электродвигателями небольшой мощности. Имеет две пары входов для управляющих сигналов и две пары выходов для подключения электромоторов. Кроме того, у L293D есть два входа для включения каждого из драйверов. Эти входы используются для управления скоростью вращения электромоторов с помощью  (PWM). 

Рассмотрим блок-схему L293D, приведённую в datasheet-е (нумерация для SO-корпуса):


К выходам OUTPUT1 и OUTPUT2 подключается электромотор MOTOR1 (для микросхемы в DIP-корпусе – ножки 3 и 6).
Соответственно MOTOR2 подключается к выходам OUTPUT3 и OUTPUT4 (ножки 11 и 14).

Сигналы, подаваемые на ENABLE1(2) управляют соответствующим драйвером (ножки 1 и 9 соответственно).

Подавая на вход ENABLE1 сигнал HIGH ( или просто соединив с плюсом источника питания +5V) – включаем драйвер 1-го моторчика. 
Если при этом на входы INPUT1 и INPUT2 сигналы не подавать, то моторчик вращаться не будет.
Подавая HIGH на INPUT1 и LOW на INPUT2 мы заставим моторчик вращаться. А если теперь поменять сигналы местами и подавать на INPUT1 сигнал LOW, а на INPUT2 сигнал HIGH – мы заставим моторчик вращаться в другую сторону.

Аналогично для второго драйвера.


Выход Vss (ножка 16) отвечает за питание самой микросхемы, а выход Vs (ножка 8) отвечает за питание моторчиков – это обеспечивает разделение электропитания для микросхемы и для управляемых ею двигателей, что позволяет подключить электродвигатели с напряжением питания отличным от напряжения питания микросхемы. Разделение электропитания микросхем и электродвигателей также необходимо для уменьшения помех, вызванных бросками напряжения, связанными с работой моторов.

Четыре контакта GND (ножки 4, 5, 12,13) нужно соединить с землёй. Так же эти контакты обеспечивают теплоотвод от микросхемы.

Характеристики L293D 

напряжение питания двигателей (Vs)              от 4,5 до 36В 
напряжение питания микросхемы (Vss)             5В
допустимый ток нагрузки                         600мА (на каждый канал)
пиковый (максимальный) ток на выходе            1,2A (на каждый канал)
логический "0" входного напряжения              до 1,5В
логическая "1" входного напряжения              2,3...7В
скорость переключений                           до 5 кГц.


Если использовать микросхему L293E, то допустимый ток нагрузки на каждый канал уже будет 1А (а пиковый ток – 2А), но придётся использовать внешние защитные диоды, которые у L293D встроены в саму микросхему.

Попробуем смоделировать как Arduino справляется с управлением моторчиками :)

Запускаем Proteus и открываем проект с Arduino :)
Добавим на схему наш драйвер – L293D

Соединим выводы Vss и Vs с положительным полюсом батареи, выводы GND соединим с землёй, а к выводам 3,6 и 11,14 подсоединим моторчики – соответственно MOTOR1 и MOTOR2.

В реальной схеме — параллельно к моторчику нужно припаять конденсатор – он помогает справиться с наводками от работающего электродвигателя ( это распространённая практика — разберите любую игрушку с моторчиком и увидите, что прямо к моторчику припаян керамический конденсатор номиналом где-то в 0,1 мкф )


А как же соединить входы драйверов? Для начала, посмотрим – как вообще это работает на практике :) 
Загрузим в МК скетч Blink или Blink_HL, а далее, как показано на рисунке, соединим INPUT1 и ENABLE1 с digital pin 13, к которому подключен светодиод, а INPUT2 соединим с землёй. Запустим симуляцию и увидим, что моторчик одну секунду вращается в одну сторону, а потом останавливается на секунду. Т.е. получили  :)

Итак, получается, что для управления одним моторчиком требуется три порта(один из них — PWM).
Если скоростью вращения моторчика управлять не требуется, то можно сэкономить на PWM-портах (ENABLE1 и ENABLE2). Тогда, для управления одним моторчиком нужно будет задействовать два порта.

Итак, определимся с портами (в скобках – соответствующий номер ножки МК из  ):

MOTOR1
11              (17) PWM              ENABLE1
12              (18)                    INPUT2
13              (19)                    INPUT1

MOTOR2
7               (13)                    INPUT3
8               (14)                    INPUT4
9               (15) PWM              ENABLE2


Изменим наш проект в Proteus-е:


А теперь напишем скетч.

Для удобства – будем хранить номера портов не в обычных переменных типа int, а объединим их структурой:
struct MOTOR    // структура для хранения номеров pin-ов, к которым подключены моторчики
{
 
int in1;      // INPUT1
 
int in2;      // INPUT2
 
int enable;   // ENABLE1
};


однако, если просто объявить такую структуру в коде скетча, например так:
/*
 * тестовый скетч с L293
 */


struct MOTOR    // структура для хранения номеров pin-ов, к которым подключены моторчики
{
 
int in1;      // INPUT1
 
int in2;      // INPUT2
 
int enable;   // ENABLE1
};

// определяем порты, к которым подключены моторчики
MOTOR MOTOR1
= { 13, 12, 11 };
MOTOR MOTOR2
= { 7, 8, 9 };

void set_m_pins(MOTOR *m);

void setup()
{
  set_m_pins
(&MOTOR1);
  set_m_pins
(&MOTOR2);
 
Serial.begin(9600);
}

void loop()
{
  delay
(1000);
}

void set_m_pins(MOTOR *m)
{
 
if(m)
 
{
    pinMode
(m->in1, OUTPUT);
    pinMode
(m->in2, OUTPUT);
 
}
}


То при попытке скомпилировать — получим ошибку:

error: variable or field 'set_m_pins' declared void In function 'void setup()':
— дело в том, что дополнительные структуры данных должны объявляться в заголовочных (.h) файлах.
Т.е. нужно создать библиотечный файл и подключиться его к скетчу директивой #include

Однако, тут компилятору не нравится именно обращение к новому типу данных по указателю.

Изменим скетч:
/*
 * тестовый скетч с L293
 */


struct MOTOR    // структура для хранения номеров pin-ов, к которым подключены моторчики
{
 
int in1;      // INPUT1
 
int in2;      // INPUT2
 
int enable;   // ENABLE1
};

// определяем порты, к которым подключены моторчики
MOTOR MOTOR1
= { 13, 12, 11 };
MOTOR MOTOR2
= { 7, 8, 9 };

void setup()
{
 
Serial.begin(9600);
  pinMode
(MOTOR1.in1, OUTPUT); // настраиваем выводы
  pinMode
(MOTOR1.in2, OUTPUT); // на ВЫВОД
  pinMode
(MOTOR2.in1, OUTPUT);
  pinMode
(MOTOR2.in2, OUTPUT);
}

void loop()
{
  forward1
();   // вращаем оба моторчика вперёд
  forward2
();
  delay
(3000);
  back2
();      // вращаем второй моторчик назад
  delay
(500);
  forward2
();   // а теперь опять вращаем второй моторчик вперёд
}

void forward1() // первый вперёд
{
  digitalWrite
(MOTOR1.in1, HIGH);
  digitalWrite
(MOTOR1.in2, LOW);
  analogWrite
(MOTOR1.enable, 254);
}

void forward2() // второй вперёд
{
  digitalWrite
(MOTOR2.in1, HIGH);
  digitalWrite
(MOTOR2.in2, LOW);
  analogWrite
(MOTOR2.enable, 254);
}

void back1() // первый назад
{
  digitalWrite
(MOTOR1.in1, LOW);
  digitalWrite
(MOTOR1.in2, HIGH);
  analogWrite
(MOTOR1.enable, 254);
}

void back2() // второй назад
{
  digitalWrite
(MOTOR2.in1, LOW);
  digitalWrite
(MOTOR2.in2, HIGH);
  analogWrite
(MOTOR2.enable, 254);
}


Три секунды оба моторчика вращаются вперёд, затем пол-секунды второй моторчик вращается назад, а потом снова вперёд.


Перенесём структуру и функции в  .

/*
 * robocraft.h
 *
 * RoboCraft - library fo RoboCraft.ru project
 * RoboCraft - библиотека для проекта RoboCraft.ru
 *
 *
 * Written by noonv, August 2009.
*/


#ifndef robocraft_h
#define robocraft_h

#include "WProgram.h"

#define _RCDEBUG_ 1

struct MOTOR    // структура для хранения номеров pin-ов, к которым подключены моторчики
{
 
int in1;      // INPUT1
 
int in2;      // INPUT2
 
int enable;   // ENABLE1
};

class RoboCraft
{
       
public:
               
RoboCraft();
               
RoboCraft(MOTOR *m1, MOTOR *m2);
               
void hello();
               
void motor_forward(int m,int speed=254);
               
void motor_back(int m, int speed=254);
       
private:
               
void set_m_pins(MOTOR *m);
               
void m_forward(MOTOR *m,int speed=254);
               
void m_back(MOTOR *m,int speed=254);
                MOTOR MOTOR1
;
                MOTOR MOTOR2
;
};

#endif // #ifndef robocraft_h

А скетч тогда будет таким:

#include <robocraft.h>
//
// тестовый скетч с L293
// добавили пробные функции для работы с моторчиками через L293
//
//
// by noonv <http://www.robocraft.ru>
//

MOTOR MOTOR1
= { 13, 12, 11 };
MOTOR MOTOR2
= { 7, 8, 9 };

RoboCraft robot(&MOTOR1, &MOTOR2);     // создаём экземпляр нашего класса

void setup()
{
 
Serial.begin(9600);
  robot
.hello();    // говорим "Hello" :)
}

void loop()
{
 
Serial.println("loop");
  robot
.motor_forward(0,254); // оба полный вперёд :)
  delay
(3000);
  robot
.motor_back(2,254);    // крутим второй назад
  delay
(1000);
  robot
.motor_forward(2,254);  // и снова 2-й вперёд!
}


Функции вращения принимают два параметра – номер моторчика(1-2) и скорость вращения (0-255). Если номер отличается от 1-2, то вращение задаётся сразу обоим моторчикам.

У меня под рукой оказалась микросхема L293E – поэтому необходимо использовать защитные диоды (1N4007), которых для двух моторчиков нужно аж 8 штук :)


Так же у L293E не 16, а целых 20 ножек:


Впрочем, для тестирования можно собрать схему только для одного моторчика ;)

Соответственно нужно внести изменения в скетч:

MOTOR MOTOR1 = { 13, 12, 11 };
MOTOR MOTOR2
= { 7, 8, 9 };

RoboCraft robot(&MOTOR1, &MOTOR2);     // создаём экземпляр нашего класса

void setup()
{
 
Serial.begin(9600);
  robot
.hello();    // говорим "Hello" :)
}

void loop()
{
 
Serial.println("loop");
  robot
.motor_forward(0,254); // полный вперёд :)
  delay
(3000);
  robot
.motor_back(1,254);    // крутим назад
  delay
(1000);
}

— крутим наш моторчик 3 секунды вперёд, а затем одну секунду назад.

Крутится :)



Ссылки:

Простой мотор-шилд для Arduino/CraftDuino

  •   
Некогда, мы уже научились управлять моторчиками, .

Однако, согласитесь – отдавать целых три порта всего на один моторчик – крайне расточительно!
Здесь нам поможет простенький Мотор-шилд (Motor-shield) :)

Вот схема простого мотор-шилда: 

IC1 - L293D(E)
IC2
74HC00
Резисторы R1, R2, R3, R4 - 100к
Конденсаторы C1, C2, C3, C4, C6 - 0.1мкф
Электролиты C5, C7 -  10-100мкф
Диоды D1 D8 - 1N4007

Как видно – для L293E добавлены защитные диоды (если используется L293D – их можно выкинуть).


Главный нюанс этой схемы заключается в использовании инвертора IC2 на микросхеме 74HC00 (состоит из 4 логических элементов И-НЕ).

Рассмотрим, как происходит управление одним мотором – на схеме выводы для двиг2.

Для управления движком №2 используюся порты 7 и 9(), соответственно для движка №1 используются порты 8 и 10(PWM)
Видим, что PWM-порты на входы ENABLE драйвера L293 подключаются напрямую.
А порты 7 и 8 идут через инвертор. Это сделано с простой целью:
Если на пине 7 будет логическая 1 то на выходе IC2.1 будет 0, который подаётся на INPUT1 драйвера, затем этот 0 инвертируется IC2.2, на выходе которого будет 1, который подаётся на INPUT2 драйвера.
Теперь, если подать сигнал на вход ENABLE1, то моторчик начнёт вращаться.
А если нам потребуется вращать моторчик в обратную сторону – нужно будет, всего лишь, установить в порт 7 логический 0, что приведёт к 1 на выходе IC2.1 (и соответственно INPUT1) и 0 на выходе IC2.2 (и, соответственно, INPUT2).

Аналогично и для двигателя №1.
Т.о. добавление простого инвертора даёт возможность полноценного управления моторчиком с помощью всего двух портов!

По теме:



Ссылки:

Всем хороша  кроме тока — только 600мА на канал (у L293N — 1.2А, но со внешними диодами и только кратковременно). 
Но есть у неё старший брат — , надо сказать брат-близнец — представляет из себя те же четыре полумоста объединённые по-два сигналами разрешения работы(ENABLE), 

но пожилистей — до 4А.
Корпус тоже покрупнее — при необходимости его удобно прикрепить к радиатору.
Ну и тут уже без внешних диодов никуда — придётся ставить, причём желательно шоттки и желательно на ток не меньше чем ток нагрузки.
Принципы работы, методы управления, достоинства и недостатки совершенно те же что и у L293 — можно почитать  и .
А соберём ка мы на ней мотрошилд=)
Кроме рук и паяльных принадлежностей нам понадобятся: 
*собственно 
*
*три  (один для подключения питания, два — для моторов)
*немного обрезков витой пары и/или 

Собирать будем по такой схеме

Резисторы R1 и R2 —  для замера(и ограничения внешним контроллером) тока через каналы. Если мерить и ограничивать не собираетесь — можно поставить перемычки.
Получилось как-то вот так:

тут я смухлевал и воткнул конденсатор по-мельче на 16В(в комплекте идёт на 50В — чтоб перекрыть весь рабочий диапазон ЭЛЬ-ки) — влез бы и штатный еслиб я не запаял так близко друг к другу клемники для моторов=\
Нижняя каша:

Так как управление, решительно, ничем не отличается от L293 просто немного подправим   для неё:
/*
 * тестовый скетч с L293/L298
 */


struct MOTOR    // структура для хранения номеров pin-ов, к которым подключены моторчики
{
 
int in1;      // INPUT1
 
int in2;      // INPUT2
 
int enable;   // ENABLE1
};

// определяем порты, к которым подключены моторчики
MOTOR MOTOR1
= { 2, 4, 3 };
MOTOR MOTOR2
= { 6, 7, 5 };

void setup()
{
  pinMode
(MOTOR1.in1, OUTPUT); // настраиваем выводы
  pinMode
(MOTOR1.in2, OUTPUT); // на ВЫВОД
  pinMode
(MOTOR2.in1, OUTPUT);
  pinMode
(MOTOR2.in2, OUTPUT);
}

void loop()
{
  forward1
(50);   // вращаем оба моторчика вперёд на 50
  forward2
(50);
  delay
(3000);
  forward1
(250);   // вращаем оба моторчика вперёд на 250
  forward2
(250);
  delay
(3000);
  back2
(100);      // вращаем оба моторчика назад на 100
  back1
(100);
  delay
(5000);
  forward2
(40);   // а теперь опять вращаем второй моторчик вперёд
  delay
(1000)
}

void forward1(int pwm) // первый вперёд
{
  digitalWrite
(MOTOR1.in1, HIGH);
  digitalWrite
(MOTOR1.in2, LOW);
  analogWrite
(MOTOR1.enable, pwm);
}

void forward2(int pwm) // второй вперёд
{
  digitalWrite
(MOTOR2.in1, HIGH);
  digitalWrite
(MOTOR2.in2, LOW);
  analogWrite
(MOTOR2.enable, pwm);
}

void back1(int pwm) // первый назад
{
  digitalWrite
(MOTOR1.in1, LOW);
  digitalWrite
(MOTOR1.in2, HIGH);
  analogWrite
(MOTOR1.enable, pwm);
}

void back2(int pwm) // второй назад
{
  digitalWrite
(MOTOR2.in1, LOW);
  digitalWrite
(MOTOR2.in2, HIGH);
  analogWrite
(MOTOR2.enable, pwm);
}
Тестируем на недостойно-хилой нагрузке=)

Работает=)
Но тратить по три пина на управление каждым каналом — жуткое расточительство — поэтому дополним наш шилд парой инверторов (подробности про принцип работы этого решения можно почитать ) а точнее поставим одну  

— самая распространённая микросхема логики — 4 двухвходовых элемента И-НЕ — для моторшилдов довольно решение.
Схема у нас теперь будет выглядеть так:

Шилд сверху

снизу

Также немного переделаем скетч
/*
 * тестовый скетч с L293/L298 и 74hc00
 */


struct MOTOR    // структура для хранения номеров pin-ов, к которым подключены моторчики
{
 
int in;    // INVERTOR INPUT
 
int enable;   // ENABLE1
};

// определяем порты, к которым подключены моторчики
MOTOR MOTOR1
= { 4, 3 };
MOTOR MOTOR2
= { 6, 5 };

int FORWARD = HIGH;
int BACK = LOW;

void setup()
{
  pinMode
(MOTOR1.in, OUTPUT); // настраиваем выводы
  pinMode
(MOTOR2.in, OUTPUT);
}

void loop()
{
  motor1
(FORWARD, 50);   // вращаем оба моторчика вперёд на 50
  motor2
(FORWARD, 50);
  delay
(3000);
  motor1
(FORWARD, 250);   // вертимся в раздрай на 250 (поворачиваемся)
  motor2
(BACK, 250);
  delay
(3000);
  motor1
(BACK, 100);      // оба назад на 100
  motor2
(BACK, 100);
  delay
(5000);
  motor2
(FORWARD, 50);   // а теперь опять вращаем второй моторчик вперёд
  delay
(1000);
}

void motor1(int dir, int pwm) // первый
{
  digitalWrite
(MOTOR1.in, dir);
  analogWrite
(MOTOR1.enable, pwm);
}

void motor2(int dir, int pwm) // второй
{
  digitalWrite
(MOTOR2.in, dir);
  analogWrite
(MOTOR2.enable, pwm);
}

Проверяем уже на 

Работает!
Видео результата:

Сервы

  •   

Серва, рулевая машинка, сервомашинка — кому как больше нравится =)
По сути, это мотор-редуктор, способный поворачивать выходной вал строго в заданное положение (на угол) и удерживать его там, вопреки сопротивлениям и возмущениям недружелюбной среды. 
Нужно это было в первую очередь моделистам, для управления положениями закрылков всяких, рулей различных и лопастей вертолётных. Оттуда, из моделизма, и пришло в остальные сферы технического творчества и в робототехнику в частности =) 
На  “качалку” – пластиковый рычаг(реже металлический), коромысло, диск или крест, с отверстиями для закрепления тяг рулей высоты, глубины, элеронов каких-нибудь или ног робота =)
Чтобы чутко отслеживать имения положения вала и, собственно, воспринимать сигналы управления сервы имеют на борту “электронику”:
Вал с качалкой жёстко связан с движком переменного резистора. Резистор заткнут в схему контроля и сообщает ей своим текущим сопротивлением о текущем положении вала. На схему, в свою очередь, поступают сигналы управления, сообщающие в какое положение нужно повернуть выходной вал (и резистор соотвественно). Схема подаёт питание на моторчик и крутит им всё это дело до куда сказано, там замирает и если что-нибудь сдвинет качалку из нужной точки вернёт её на место, также передвинет её если придёт новая команда.

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

Управлять сервой очень просто — у неё есть три провода:

— земля (коричневый/черный),
— питание +5 вольт (красный),
— сигнальный (оранжевый/желтый/белый).

Управление импульсное — по сигнальному проводу. 
Особая прелесть состоит в том, что сигнальный провод слаботочный — импульсы можно давать непосредственно с ноги микроконтроллера, а вот по силовому «питанию» просасывается приличный ток.
Чтобы повернуть серву на нужный угол – нужно на сигнальный вход подавать импульс с нужной длительностью.
Чтобы удерживать определённую позицию – импульс должен повторяться.


Простой пример работы с сервой можно рассмотреть на примере этого демонстрационного скетча для , который заставляет серву делать поворот от 0 до 180 градусов и обратно.

/*
 * Servo Move Simple
 * -----------------
 * Move an R/C servo back and forth (0 to 180 degrees) using
 * delayMicroseconds() for pulse and delay() for time between pulses.
 *
 * Created 18 October 2006
 * copyleft 2006 Tod E. Kurt <[email protected]>
 * http://todbot.com/
 *
 * Adapted from Daniel @
 * http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1160470155/0
 * Rotates servo through 180 degrees, using "servoPulse" function  
 * adapted from "Temporary Servo Function"  by Tom Igoe and Jeff Gray
 */

 
 
int servoPin = 9;            // порт подключения сервы
int myAngle;                 // будет хранить угол поворота
int pulseWidth;              // длительность импульса
 
void servoPulse(int servoPin, int myAngle)
{
  pulseWidth
= (myAngle * 11) + 500;  // конвертируем угол в микросекунды
  digitalWrite
(servoPin, HIGH);       // устанавливаем серве высокий уровень
  delayMicroseconds
(pulseWidth);      // ждём
  digitalWrite
(servoPin, LOW);        // устанавливаем низкий уровень
  delay
(20);                          //
}

void setup()
{
  pinMode
(servoPin, OUTPUT);          // конфигурируем пин сервы, как выход
}

void loop()
{
 
// медленно поворачиваем серву от 0 до 180 градусов
 
for (myAngle=0; myAngle<=180; myAngle++) {  
    servoPulse
(servoPin, myAngle);
 
}
  delay
(300);
 
// а теперь обратно
 
for (myAngle=180; myAngle>=0; myAngle--) {  
    servoPulse
(servoPin, myAngle);
 
}
  delay
(300);
}  


Однако, как мы помним – работать через библиотеку намного удобнее :) 
В комплекте штатных библиотек Arduino IDE уже есть библиотека Servo.


Примечание:
В Arduino IDE 0017 библиотека Servo поддерживает до 12 серв (до 48 на Mega), а в IDE 0016 и ранее работает только на 9 и 10 пинах!


Рассмотрим пример
File – Examples — Servo — Sweep
который делает то же самое, что и первый.

// Sweep
// by BARRAGAN <http://barraganstudio.com>

#include <Servo.h>
 
Servo myservo;  // создаём объект для контроля сервы
               
// максимальное количество таких объектов - 8
 
int pos = 0;    // переменная для хранения позиции сервы
 
void setup()
{
  myservo
.attach(9);  // серва подключена к 9-му пину
}
 
 
void loop()
{
 
for(pos = 0; pos < 180; pos += 1)  // от 0 до 180 градусов
 
{                                  // с шагом в 1 градус
    myservo
.write(pos);              //
    delay
(15);                       // ждём 15ms пока серва займёт новое положение
 
}
 
for(pos = 180; pos>=1; pos-=1)     // от 180 до 0 градусов
 
{                                
    myservo
.write(pos);              
    delay
(15);                      
 
}
}


Существуют и другие библиотеки:
SoftwareServo —  — удобна одинаковым названием функций с системной библиотекой Servo
— она не ограничивает количество серв 8-ю, но требует для работы вызова метода 
SoftwareServo::refresh()

Пример:
#include <SoftwareServo.h> 

SoftwareServo myservo; // объект сервы

int potpin = 0;  // пин для подключения потенциометра
int val;        // переменная для хранения значения с аналогового входа

void setup()
{
  myservo
.attach(2);  // серва подключена ко 2-му пину
}

void loop()
{
  val
= analogRead(potpin);            // считываем значение с потенциометра (величина от 0 до 1023)
  val
= map(val, 0, 1023, 0, 179);     // переводим в значение от 0 до 180)
  myservo
.write(val);                  // устанавливаем угол сервы
  delay
(15);                           // ждём поворота

 
SoftwareServo::refresh();
}


MegaServo — 
— библиотека для более ранних версий IDE (в 0017 не подгрузится, т.к. она уже входит в состав IDE под именем Servo :) — позволяет контролировать до 12 серв

Для четырёхнога, вроде  (с двумя сервами на одну ногу), потребуется 8 серв.

Далее: 


По теме:


Cсылки:

 — статья ДиХальта – на примере сервомашинки HS-311.
Ну и так как придумали их моделисты/для моделистов лучшая статья у них:
 (о том какие бывают чем различаются и что внутри)

Как с помощью Arduino/CraftDuino можно управлять устройствами на 220В

  •   

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

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


Небольшое видео работы шилда:


автор схемы и ролика — уважаемый 

Подключаем LCD-дисплей на базе HD44780 к Arduino

  •   
При работе с Arduino иногда возникает необходимость вывести какие-либо данные на дисплей, но передавать для этого данные на ПК имеет смысл только если устройство будет использоваться в связке с ПК. А как же быть с автономными устройствами? Тут на помощь придут LCD-дисплеи.

Рассмотрим LCD-дисплеи на базе контроллера HD44780 на примере WH1602B-YYK-CTK.

Этот монохромный дисплей имеет опциональную подсветку и может отображать 2 строки по 16 символов. Разрешение символов — 5x8 точек. Есть поддержка кириллицы, но странная, об этом — в конце статьи.

Чем хороши такие дисплеи? Контроллер HD44780 — стандарт де-факто среди небольших монохромных LCD-дисплеев, поэтому библиотеку для работы с дисплеями на его базе не написал только ленивый. Ленивым на сей раз оказался я, а вот разработчики Arduino написали библиотеку для своей платформы, и называется она LiquidCrystal. Её мы и используем для работы с выбранным мной дисплеем.

Итак, прежде чем писать код, нужно подключить дисплей к Arduino. Вам понадобятся:
  •  или 
  • LCD-дисплей, например WH1602B-YYK-CTK
  • макетная плата
  • соединительные провода
  • потенциометр
Схема подключения в официальном руководстве на сайте Arduino выглядит так:



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

Кстати, дисплей имеет режим самотестирования, который можно включить, подсоединив выводы:
  • 1 — Vss, земля ⇨ GND
  • 2 — Vdd, питание ⇨ +5 В
  • 3 — Vo, управление контрастностью напряжением ⇨ выход потенциометра
  • 15 — A, питание для подсветки ⇨ +5 В
  • 16 — K, земля для подсветки ⇨ GND
Верхний ряд символов должен полностью заполниться тёмными прямоугольниками:



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

А для полноценной работы с дисплеем подключим 12 выводов:
  • 1 — Vss, земля ⇨ GND
  • 2 — Vdd, питание ⇨ +5 В
  • 3 — Vo, управление контрастностью напряжением ⇨ выход потенциометра
  • 4 — RS, выбор регистра ⇨ пин 12 Arduino
  • 5 — R/W, чтение/запись ⇨ земля (режим записи)
  • 6 — E, он же Enable, cтроб по спаду ⇨ пин 11 Arduino
  • 7-10 — DB0-DB3, младшие биты 8-битного интерфейса; не подключены
  • 11-14 — DB4-DB7, старшие биты интерфейса ⇨ пины 5-2 Arduino
  • 15 — A, питание для подсветки ⇨ +5 В
  • 16 — K, земля для подсветки ⇨ GND


Этот дисплей, как и прочие на контроллере HD44780, поддерживает два варианта параллельного интерфейса:
  • 8-битный, выводы DB0-DB7, за один такт передаётся 1 байт (8 бит)
  • 4-битный, выводы DB4-DB7, за один такт передаётся половина байта (4 бита)
Смысла использовать 8-битный вариант нет, потому что это требует больше ног, а выигрыша в скорости всё равно нет: частота обновления дисплея не больше 10 раз в секунду, так что мы всё равно не сможем увидеть часто обновляемые данные. Поэтому выводы DB0-DB3 оставляем неподключенными.

Ну что же, всё подключено — пора писать код. Начнём с классического «Hello, World», который доступен в Arduino IDE через пункт меню File ⇨ Examples ⇨ LiquidCrystal ⇨ HelloWorld:

/* Подключаем библиотеку для работы с LCD */
#include <LiquidCrystal.h>

/* Создаём объект LCD-дисплея, используя конструктор класса LiquidCrystal
* с 6ю аргументами. Библиотека по количеству аргументов сама определит,
* что нужно использовать 4-битный интерфейс.
* Указываем, к каким пинам Arduino подключены выводы дисплея:
*   RS, E, DB4, DB5, DB6, DB7
*/

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup()
{
 
/* Инициализируем дисплей: 2 строки по 16 символов */
 lcd
.begin(16, 2);
 
/* Выводим на дисплей традиционную фразу (: */
 lcd
.print("hello, world!");
}

void loop()
{
 
/* Устанавливаем курсор в 1 столбец 2й строки. Нумерация идёт с нуля,
  * первым аргументом идёт номер столбца.
  */

 lcd
.setCursor(0, 1);
 
/* Выводим на дисплей число секунд, прошедших с момента старта Arduino */
 lcd
.print(millis() / 1000);
}

Ничего сложного, как видите: основного кода набралось всего 4 строки.
Обратите внимание на инициализацию дисплея:

lcd.begin(16, 2);

Здесь мы задали размер экрана: 2 строки по 16 символов. Прелесть в том, что можно, ничего не меняя в схеме, подцепить другой дисплей — например, размером 16х4 символа, или сэкономить и взять 8х2. Достаточно изменить строку инициализации соответственно размеру подключенного дисплея.



Но банальными надписями сыт не будешь, так что изучим документацию на предмет более продвинутых возможностей. Нам доступные следующие полезные методы:
  • home() и clear()
    Первый метод возвращает курсор в начало экрана; clear() делает то же самое, заодно стирая всё, что было на дисплее до этого.
  • write(ch)
    Выводит одиночный символ ch на дисплей.
    Пример:
    lcd.write('z');
    lcd
    .write(0); // вывести символ с кодом 0; см. метод createChar()
  • cursor() и noCursor()
    Позволяют показать курсор на дисплее (символ подчёркивания) и скрыть его.
  • blink() и noBlink()
    Включить и выключить мигание курсора, если включено его отображение.
  • display() и noDisplay()
    Включить и выключить дисплей.
  • scrollDisplayLeft() и scrollDisplayRight()
    Прокрутить экран на один символ влево или вправо.
  • autoscroll() и noAutoscroll()
    Включить и выключить режим автопрокрутки. В этом режиме при выводе каждого следующего символа содержимое экрана будет смещено на один символ влево (или вправо, если включен режим вывода справа налево), а выводимый символ займёт место первого сдвинутого. Проще говоря, в этом режиме все последующие символы выводятся в одно и то же место, вытесняя текущее содержимое экрана.
  • leftToRight() и rightToLeft()
    Устанавливают направление вывода текста: слева направо и справа налево, соответственно.
  • createChar(ch, bitmap)
    Самая крутая функция: позволяет создать свой символ с кодом ch (от 0 до 7), пользуясь массивом битовых масок bitmap для задания тёмных и светлых точек.
Неплохо бы опробовать все эти функции, не правда ли? Сказано — сделано! Напишем большой и фаршированный функционалом скетч:

#include <LiquidCrystal.h>

/* Константа: высота символа в точках */
enum { SYMBOL_HEIGHT = 8 };

/* Описываем наши собственные символы в виде массивов битовых масок.
 * Каждый символ - это восемь масок по пять бит.
 */

byte arnie[SYMBOL_HEIGHT] =
{
  B10101
,
  B11111
,
  B10111
,
  B11111
,
  B11111
,
  B00111
,
  B11110
,
  B00000
,
};

byte skull_and_bones[SYMBOL_HEIGHT] =
{
  B01110
,
  B10101
,
  B11011
,
  B01110
,
  B00000
,
  B10001
,
  B01110
,
  B10001
,
};

byte lightning[SYMBOL_HEIGHT] =
{
  B00010
,
  B00100
,
  B01000
,
  B11111
,
  B00010
,
  B00100
,
  B01000
,
  B00000
,
};

/* Константы размеров экрана. Нужны для того, чтобы размер экрана
 * менять только в одном месте, а не по всему коду. Везде, где
 * результат вывода на дисплей должен зависеть от размера экрана,
 * следует использовать эти константы.
 */

enum { LCD_WIDTH = 16, LCD_HEIGHT = 2 };

/* Объявляем объект нашего дисплея */
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);


void setup()
{
 
/* Регистрируем собственные символы с кодами 0, 1 и 2 */
  lcd
.createChar(0, arnie);
  lcd
.createChar(1, skull_and_bones);
  lcd
.createChar(2, lightning);
 
/* Начинаем работу с дисплееем */
  lcd
.begin(LCD_WIDTH, LCD_HEIGHT);
}


typedef void (*LCD_demo)();

/* Это список демо-функций. Добавляйте свои или меняйте их порядок,
 * если хотите разнообразить этот скетч (:
 */

LCD_demo
const demos[] =
{
  showArnie
,
  showWarning
,
  showScrolling
,
  showAutoscroll
,
  showRTL
};

/* Этот макрос - обычный "синтаксический сахар". Мне просто влом писать
 * по 10 почти одинаковых циклов for. Конечно, в серьёзном коде такое лучше не писать.
 */

#define dotimes(n, code) for (int i = 0; i < (n); ++i) code;


void loop()
{
  dotimes
(sizeof(demos) / sizeof(demos[0]),
 
{
    demos
[i](); // запускаем очередную демонстрацию

   
/* Даём насладиться её последними "кадрами", после чего очищаем экран */
    delay
(2000);
    lcd
.clear();
    delay
(1000);
 
});
}


void showArnie()
{
  lcd
.cursor(); // показываем курсор
  lcd
.blink(); // заставляем его мигать
  delay
(1000);

  lcd
.write(0); // выводим наш собственный символ с кодом 0 (ноль)
  lcd
.write(' ');
  delay
(1000);

  lcd
.print("I'll be back"); // угадай, кто на экране (:
  delay
(1000);

  lcd
.noBlink(); // больше мигать не нужно
  lcd
.noCursor(); // прячем курсор
}


void showWarning()
{
 
/* Так как коды наших собственных символов - от 0 до 7,
   * можно смело использовать запись символов в строке при помощи
   * восьмеричных кодов: \1, \2 и т.д.
   * НО: код 0 (ноль) в C и C++ используется в качестве маркера конца строки,
   * так что используйте lcd.write(0) для вывода нулевого символа.
   */

  lcd
.print("Smoke kills \1"); // в конце строки будет черепок с костями
  delay
(2000);

  lcd
.setCursor(0, 1); // переходим на следующую строку
 
/* Можно вывести молнию как в начале, так и в середине */
  lcd
.print("\2 AC/DC \2  rocks");
}


void showScrolling()
{
  lcd
.print("I'm scrolling");
  delay
(1000);

 
/* Прокручиваем текст вправо за экран */
  dotimes
(16,
 
{
    lcd
.scrollDisplayRight();
    delay
(150);
 
});

 
/* И возвращаем на место, прокручивая влево */
  dotimes
(16,
 
{
    lcd
.scrollDisplayLeft();
    delay
(150);
 
});
}

void showAutoscroll()
{
  lcd
.setCursor(LCD_WIDTH - 2, 0);
  lcd
.autoscroll(); // включаем автопрокрутку

 
const char *text = "autoscroll";

 
/* Печатаем строку буква за буквой. Текст будет "выползать" справа. */
  dotimes
(strlen(text),
 
{
    lcd
.write(text[i]);
    delay
(200);
 
});

  lcd
.noAutoscroll(); // выключаем автопрокрутку
}


void showRTL()
{
  lcd
.setCursor(LCD_WIDTH - 1, 0);
  lcd
.rightToLeft(); // теперь пишем справа налево

 
const char *text = "tfel-ot-thgir"; // похоже на заклинание

 
/* Печатаем строку буква за буквой. Текст будет выводиться задом наперёд. */
  dotimes
(strlen(text),
 
{
    lcd
.write(text[i]);
    delay
(200);
 
});

  lcd
.leftToRight(); // пишем слева направо
}

Видеозапись процесса работы этого скетча:



Надеюсь, вас не испугал такой объём кода. Зато всё в одном скетче, а не в нескольких, как в примерах к библиотеке LiquidCrystal. Исходный код с комментариями на английском языке также доступен вот  и на GitHub в .

Теперь о кириллице. Дело в том, что китайцы решили, что поддержка кириллицы — это значит произвольным образом раскидать по таблице знаков кириллические символы, без соответствия какой-либо кодировке. Один добрый человек, , в далёком 2010 году написал библиотеку LiquidCrystalRus, которая умело справляется с китайской кириллицей, делая её поддержку прозрачной. Однако с тех пор она малость устарела, поэтому я, да не осудит меня автор, её освежил и выкладываю .

Напишем с её помощью русский вариант «Hello, World»:

#include <LineDriver.h>
#include <LiquidCrystalExt.h>
#include <LiquidCrystalRus.h>

LiquidCrystalRus lcd(12, 11, 5, 4, 3, 2);

void setup()
{
  lcd
.begin(16, 2);
  lcd
.print("Здравствуй, мир!");
}

void loop()
{
  lcd
.setCursor(0, 1);
  lcd
.print(millis() / 1000);
}



Теперь вы знаете всё о работе с библиотекой LiquidCrystal, и в ваших силах создать более функциональное устройство с LCD-дисплеем — нужно только проявить фантазию (:

Для написания статьи были использованы:
  •  по работе с LCD на официальном сайте Arduino
  •  к библиотеке LiquidCrystal
  • Arduino-совместимая плата 
  • LCD-дисплей 
  • Удобные соединительные 
  • Беспаечная макетная плата 
Выражаю всяческие благодарности  и  за железо и помощь в подготовке статьи, а также Илье Данилову за библиотеку с поддержкой кириллицы.

Графический экран WG12864B (и ему подобные на ks0107/ks0108)

  •   
Когда-то товарищ  рассказал нам как  с знакосинтезирующим lcd экраном, а также обучил его русской речи и даже основам графики – нарисовал несколько значков. Вот только примитивные значки это максимум, что можно получить от символьных дисплеев — и то с некоторым трудом, без проблем только буковки-циферки, строчные-заглавные. А хочется картинок. Размеров, шрифтов разных, курсивов, там, менюшек модных.
Для этого придумали графические дисплеи. 
Они, как и символьные, формируют изображение зажигая/гася точки-пиксели. 
Кстати, как-то мы обошли вниманием в прошлый раз тот факт, что работать с точечками напрямую было бы очень уныло — никаких выводов не напасёшься (80х16 точек у символьного и 128х64 у графического). Но добрые китайские духи электроники позаботились о нас и снабдили эти девайсы собственными контроллерами. Для символьных экранов стандартный контроллер HD44780 (ks0066) а для графических — ks0107 (ks0108).
В случае WG12864B их там целых два — на на правую и левую часть экрана. Это потому что ks0108 может обеспечить вывод только на 64х64точки, вот и получился двухядерный дисплей=)

Общаться с контроллерами дисплея предстоит по:

DB0-DB7 — шине данных 
R/W — указывая, передаём — 0 (или читаем — 1)
D/I — данные — 1 (или команду — 0)
CS1, CS2 — какому контроллеру
E – и проталкивая каждое действие синхроимпульсом
Также есть:
RST — сброс(резет)
Vdd — питание 5В
GND — земля
Vo — контраст
Vee — источник отрицательного напряжения для контраста(если экран инверсный)
A — анод и
K — катод светодиода подсветки

Поиграемся с , вот его распиновка: 

Итого минимум 13 линий дуины предстоит принести в жертву красоте индикации=\
Подключается дисплей довольно просто (если, конечно, не считать количества проводков=) Как и в случае с символьным экраном кроме линий связи с мк понадобятся потенциометр регулировки контраста (обратите внимание на оригинальное подключение)
и токоограничительный резистор для подсветки.
Хотя некоторые его не ставят. 
Напрасно, кстати — падение на светодиодах подсветки, как гласит , не более 4.6 вольт, и впихивая туда все 5, рискуем их пожечь безвозвратно.
В ИДЕ-шку не включена библиотека для работы с графическими дисплеями, а на плейграунде лежит старая версия, поэтому берём , или у .
Подключаем согласно описанию (GLCD_Documentation.pdf лежит в папке glcd\doc, можно взять  или почитать ), наш дисплей — “Panel A”(в доке ошибка, см далее, юзайте эту схему, она правильная)

Удивляясь столь извращённому методу разбрасывания проводов по дуине, читаем чуть внимательней и узнаём, что совсем не обязательно именно так плести этот клубок

можно поменять расположение линий поправив glcd\config\ks0108_Arduino.h. То есть можем расположить эти 13 выводов в произвольном порядке, оставляя свободными именно те пины (… все 7, ни в чём себе не отказывайте, ага=), которые понадобятся нам для подключения всего остального.
Но мы так делать не будем, а просто побыстрей закинем в arduino-xxx\hardware\libraries папку glcd из скачанного архива. Запустим ИДЕ-шку и выбрав File->Exampes->glcd>ks0108example, трясущимися руками зальём всё это дело в плату. Залилось, включилось, крутим контраст… и что-то фигово видно=\

Меряем питание и узнаём что наш экран ужасно прожорлив=) Просело аж до 4.2 В.
Подключаем внешнее питание.

Чтозаужоснах!
Экран перепутался пополам=\ Тут самое время вспомнить, что у нас именно два контроллера и запись в них осуществляется по очереди, посредством выбора одного из них еденичкой на линии CS1 или CS2. Они-то у нас и перепутались… как-то, а точнее положение их не соответствует, предложенной автором распиновке=\
Меняем местами.

Так гораздо лучше=)
Симпатично, можно позаливать ещё примеры. Там есть «игра» жизнь, с ужасной скоростью прокручиваемые шрифты и бегущие таймеры, всем своим видом показывающие не высокую скорость обновления данного дисплея=) Часы мне так и не удалось скомпилировать — ИДЕ-шка непрерывно ругается на отсутствие библиотек (а при их добавлении сыплет другими ошибками). Особого внимания стоит игра Rocket, добавив потенциометр и бузер можно немного по ностальгировать=)

Но пора уже слепить что-нибудь своё, пусть и не столь впечатляющее.
Для этого придётся почитать объёмистое описание этой .
А для более полного краштеста сделаем свой рисунок и свой шрифт=)

Шрифты генерятся утилиткой GLCDFontCreator2, качать , или у .
Запускается жмаканьем на start.bat, вот такой френдли интерфейс.

Кнопки Open Font/Save Font — это для внутреннего, понятного только для GLCDFontCreator2, формата шрифтов(рабочие файлы), открывать сами шрифты прога не умеет — ни сконвертированные ею самою в .h ни .ttf=(
Так что жмём NewFont, и уже там импортируем какой-нибудь шрифт из уже установленных у вас в системе. 

Стоит сразу как-нибудь осмысленно обозвать своё творение, т.к. именно под этим именем (newFont у меня=) он, впоследствии, будет доступен нашему скетчу. 
Можно что-нибудь подрисовать.

Давим кнопку Export, указывая желаемое имя файла и расположение (да и не забудьте к имени файла дописать ".h", программа не считает, что это нужно=)
Всё это круто, за исключением одной мелочи — русских букв программа не знает=( 
Если указать диапазон символов помимо стандартного (Start Index и Char Count), то на месте родной кириллицы открытого шрифта видны только пустые шедевры Малевича. 
Можно попробовать вместо них намалевать чего-нибудь, но мне жутко лень=)
К тому же товарищ  уже  соответствующие исследования и работы. Правда он пошёл несколько иным путём — не через GLCDFontCreator2 а используя  (если с narod.ru что-то случится то файл можно взять у ).
В результате у него получился шрифт  его то я и использую=)

Так, накреативили шрифты, теперь займёмся картинками.

Для этого нам прям в архив с библиотекой засунули папку bitmaps, которая хорошо видна в виде тестового скетча из примеров glcd в Arduino IDE, но почему-то не компилится=)))
А всё дело в том, что там лежит скетч для Processig-а, и даже готовый к запуску файлик<