Pull to refresh

Пилим Pan/Tilt вебкамеру (это которая крутится) + возможность подключать всякие датчики за ~15$

Reading time 14 min
Views 84K


В связи с тем что предыдущая поделка уехала «в закат» появилась идея сделать несложную поворотную (по двум осям — Pan/Tilt) вебкамеру для инсталляции ее на потолок около сервера, который весит на стенке и на пару с роутером неоправданно кушает ценное электричество, причем круглосуточно. Пусть займется чем-то полезным.


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



«Самое простое кажется тебе сложным до тех пор, пока ты боишься.»


Как обычно началось все с лени. Вертеть вебкамеру руками оказалось сверхсложной задачей особенно если для этого нужно вставать из кресла. Возник закономерный вопрос — почему она сама не может поворачиваться? Сверился с календарем — все верно 21 век, попробовал поискать готовые поворотный вебки и был впечатлен ценами. Начал думать чего там такого дорогого в ней может быть чтобы стоить таких денег и не придумал. (Борьба лени с жабой это то еще зрелище.) Попробовал прикинуть бюджет и получалось что нужную мне вещь можно соорудить за довольно смешную цену и быстро. (Помогло то что после предыдущих хобби-проектов осталось много информации где и почем можно не дорого достать «комплектуху». Если нужно могу помочь ссылками желающим. Все приобреталось в Украине.)

Итак идея проста как мычание — берем обычную вебку и приклеиваем ее к хитрой поворотной конструкции из двух сервоприводов. Сервоприводами управляем с помощью Atmega8 (микроконтроллер) в который добрый человек уже прошил bootloader от Arduino (куда ж без него). Микроконтроллер принимает команды с компьютера и вертит камерой посредством сервоприводов. Чтоб не проходить увлекательный квест — «найди СОМ порт у себя на компьютере» микроконтроллер подключим сразу по USB, но так как мы не настолько круты чтоб залить в микроконтроллер сразу программную эмуляцию USB, будем использовать USBtoUART конвертер на базе pl2303 благо их везде полно и дрова от них, уже с коробки, есть во всяких там линуксах и прочих рассадниках инакомыслия.

Небольшое отступление прежде чем начать:

1. У меня всегда все выглядит так коряво потому что я руководствуюсь обычно принципом — «пусть оно заработает хоть как-то, но прям сейчас, чем возможно никогда». Давно заметил что если я не вижу результат то быстро теряю интерес и проект превращается скорее в бремя чем в то чем хочется заниматься. А вот если текущий проект будет реально полезен то уже никуда не денешься от того чтобы постепенно сделать “рефакторинг” или как оно там называется в DIY. Вообще самообман штука хитрая, иногда можно его применить даже на пользу.

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

3. И еще одно, последнее. Однажды в средних классах школы моя учительница русского языка откровенно сказала что учить меня грамотности это бессмысленная трата времени (только указку тупить). И это было очень мудрое решение, которое я полностью поддерживаю. Посему люди которые в неравных боях, с превосходящим по численности противником, самоотверженно сражаются за грамматику, пунктуацию и правописание могут мне в личку писать о моих невежественных надруганиях над великим и могучим, буду править. Но очень прошу не разводите холливары в комментариях!





Компоненты:


1. Вебкамера — 4$
2. Микроконтроллер Atmega8 + сопутствующее — 3$
3. USB хаб — от 3$
4. Сервоприводы — 7$
5. USBtoUART — 2-3$
6. ОтносительноПрямыеРуки и ЧистыйРазум — увы, но наверно бесценно.

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



Итак начинаем готовить кашу из топора.



1. Сервоприводы.

Сервопривод умеет поворачиваться на 180 градусов, а реально немного меньше. (Вы это можете заметить на втором видео когда камера проходит 0->360 градусов по горизонтали.) Подбирайте сервы исходя из веса части которой будете «вертеть». (То есть если планируете ставить туда единый пулемет то рассчитывайте соответственно на 20 кг. Также учитывайте тряску и откат, естественно что нейлоновые шестеренки долго так не протянут хотя конечно зависит от настрела.) У меня же в наличии уже были два дешевых микросервопривода, их и применил. Там стоят двигатели которые не вызывают подозрений в лишней прожорливости.


Собственно вот такие сервоприводы у меня.

Если устанавливать камеру под потолком то две сервы смогут почти полностью перекрыть нижнюю полусферу (и незаметно противник уже не подберется никак). Чтоб грамотно соединить две сервы нужно изготовить хитрое (не так чтобы сильно) крепление которое будет позволять двигаться двум сервам и не ограничивать их возможные углы повороты (также не забывайте о проводах идущих от серв).

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


Вот как то так оно выглядит. (Стремно вообще-то выглядит, но работает.)


2. Вебкамера.

Это особая тема. Сначала я думал использовать обычную вебкамеру, но потом меня посетила очень удачная идея которая в дальнейшем очень облегчила бюджет и упростила конструкцию. Все дело в том что однажды я для интереса подключил к USB вебкамеру добытую из мертвого ноутбука. Посмотрел на цены ноутбучных вебкамер и был немало удивлен когда оказалось что они гораздо дешевле обычных. Видимо все дело в том что это очень не ходовая запчасть для ноутбука и цены на них задирать не получается. И еще они подключаются к ПК совсем не очевидным способом.


Россыпь вебок

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

Еще есть один момент что некоторые камеры вроде как питаются не от 5, а от 3.3 вольт. Я подключал их по всякому, одна камера работала от 5 вольт как минимум год без видимых последствий. Все остальные тоже работали от 5 но проверить их временем не успел еще. Просто включил в цепь питания 1-2 диода, понизив таким кустарным способом напряжение. Распиновка вебкамеры ноутбука ниже. Землю обычно «вызвонить» не проблема и от нее уже плясать.


Распиновка вебкамера ноутбука.


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


Флажок-переключатель переворачивающий картинку на 180 градусов..

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


Размеры камеры.

Если вы пойдете путем использования вебкамеры из ноутбука то я предлагаю список вебкамер которые использовал сам и уже проверил некоторые моменты совместимости с линуксом. Это важно если в дальнейшем планируется использовать mjpeg_streamer. К примеру камера почти такая же как использовалась, но другой модели (174f:6a31) ни в какую не хотела выдавать картинку под линуксом, даже когда компилировал ей дрова персонально. Остальные камеры не требовали никаких танцев с бубном.
— Камера от Acer TravelMate 3020 Series (UVC+ JPEG-)
P/N: 57.TCYV7.001
USB\VID_046D&PID_0896&REV_0100
— Logitech (UVC+ JPEG- WIN+)
Камера Logitech 1.3 M Acer Aspire 9410 9420
Камера Logitech 1.3 M Acer Aspire 9410 9420 TravelMate: 5620 Series
P/N: 57.AWZ01.001
USB\VID_046D&PID_09B0&REV_0006&MI_00
— NettopCamera (UVC+ JPEG-)
5986:0241
— Камера от старых ноутбуков Asus F8s, F5RL (UVC+ JPEG-)
174f:5a31
Это та камера что использовал я.

Расшифровка пометок:
UVC — поддерживает ли UVC конкретная камера (Вот список UVC камер)
JPEG — поддерживается ли аппаратное сжатие потока от вебки
WIN — запускается ли камера сама в Windows 7 (чтоб самому не охотится за дровами)

Естественно что камеры для ноутбуков скорее всего будут продаваться лишь с указанием модели ноутбука и все. Поэтому я выбирал камеры следующим образом — “гуглил” на предмет что за вебка установленная в конкретном ноуте и пытался найти информацию о совместимости с Линуксом. Большинство информации можно почерпнуть в разделе с драйверами на сайте производителя ноутбука. А под виндой обычно дрова найти не проблема. Мне было важно наличие драйверов конкретной вебки в современных ядрах линукса.


3. Преобразователь USB to UART.

Сейчас продаются уже готовые модули преобразователей, с одной стороны USB, а с другой «гребенка» UART. Делают их на разных микросхемах, но лучше всего чтоб в нем стояла именно микросхема, а не китайский “жук-капля-эпоксидка” (dead bug) которые вы могли видеть почти в любой дешевой электронике. Дешевле всего попадаются микросхема pl2303. Но тут тоже есть хитрость — например кабель для Samsung С100 или Siemens C55 это такие же самые преобразователи, но дешевле и в них часто стоят pl2303. Знаменитые кабели от Nokia типа DCU-5 тоже можно применять, но они более известны и цены на них выше.


Вот так выглядят обычные телефонные шнурки которые нам интересны. И распиновка микросхемы pl2303


Типичная плата извлеченная из такого шнурка


А вот такие платы продаются отдельно.

Собственно наверно любой кабель для телефона у которого нет USB выхода, но который, тем не менее, подключается им к ПК по USB представляет собой подобный преобразователь. Драйвера для всех более-менее известных микросхем обычно уже есть почти во всех современных ОС. Если вы подключили преобразователь и у вас появился в системе COM порт значит драйвер есть. Далее можно замкнуть контакты RX и TX и попробовать в обычном терменале передавать что-то. Если в ответ приходит то что отослали значит все работает и можно двигаться дальше. Можно использовать программу hercules, нашел ее случайно, оказалась очень полезной, хотя и предназначена совсем для другого.

Если у вас есть выбор то лучше остановится на телефонном кабеле у которого плата преобразователя расположена посередине и ее можно легко извлечь. Далее будет понятно зачем. Также нужно заметить что свет клином не сошелся на pl2303 и есть переходники например на микросхемах FTDI или CP2101, но они дороже и более серьезные, их лучше приберечь для другого применения.


4. USB hub.

Вы наверно подумаете — он тут зачем? Все просто — мы таким образом очень легко сможем организовать подключение итоговой камеры к ПК используя всего один кабель вместо нескольких и на хабе останутся еще свободные порты, туда можно подключить что угодно. Нужен дешевый хаб, но гарантированно версии 2.0. Поэтому лучше протестируйте его. Это можно сделать например копируя файлы с флешки и по скорости копирования определить версию хаба или подключить вебкамеру. Можно конечно посмотреть в диспетчере устройств, но хитрые китайцы могут все и там замаскировать. Я взял один из самый распространенных хабов и за одно использовал его как основание для монтирование сервоприводов увенчанных камерой. (Из самого хаба были добыты четыре отличных голубых светодиода и зеркальная поверхность, которые ему больше не понадобятся.)


Хаб

В нем забыли установить конденсаторы, хотя места для них хорошо различимы, а так как у нас есть сервоприводы и они скорее всего будут работать и просаживать напряжение лучше конденсаторов не жалеть. Думаю подойдут любые с рабочим напряжением >=6 вольт. Сильно гигантские по емкости не ставьте, подозреваю что USB контроллер матери может решить что произошло короткое замыкание в момент зарядки большой емкости большим током и послать вас куда подальше с вашим DIY.


Хаб уже с конденсатором и pl2303

Вероятнее всего вам попадется хаб на микросхеме FE1.1s. Вроде нормальная микросхема, но был у меня с ней печальный опыт. Я расширял количество USB портов на Raspberry Pi rev.A и долго не мог понять почему малина постоянно виснет, потом оказалось что виснет хаб. Поэтому перед тем как использовать конкретный хаб перегоните непрерывно через него 10-20 гигабайт чтоб убедится что конкретный экземпляр работает стабильно. Если нет то можете попробовать понизить напряжение питания с 5 до 3.3 Вольт, у меня такой номер прокатил.


Микросхема FE1.1s.

К одному из портов хаба мы припаиваем USBtoUART преобразователь, разъем-маму USB ответственно выпаиваем, чтобы потом случайно не подключить туда еще одно устройство. Далее преобразователь можно спрятать в корпус хаба, а наружу вывести только три провода RX,TX и DTR. От самой платы хаба можно еще вывести два провода питания которые мы потом припаяем на плату с микроконтроллером.


5. Микроконтроллер.

Это самый объемный участок работ, но интересный. Если вы не желаете парится с преобразователем и подключением самого микроконтроллера то вы конечно можете взять одну их готовых плат микро Arduino сразу с USB выходом, но сами понимаете что это будет дороже. Да и жалко хоронить такую плату навсегда в хабе.

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


Минимальный Arduino из атмега8

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

Сам многократно проходил этот путь и теперь если инструкция сильно «пространная» незамедлительно ищу что-то для чайников. (В этом моменте наверно и заключается самый основной минус нашего образования.)

Дорогие автора всего обучающего, вы поймите пожалуйста одну простую вещь — человек который чему-то учится он как бы этому учится именно потому, что он этого еще не знает. Нужно стараться все подавать МАКСИМАЛЬНО просто, не опираться на то что учащийся типа должны обладать некими «сферическими в вакууме» базовыми знаниями.

«Высшая степень уважения к читателям — считать их идиотами.»


Однажды эту цитату заминусовали. Следует понимать что имеется ввиду не то что читатели идиоты. Имеется ввиду то что если ты своим мануалом сможешь обучить идиота значит цель достигнута.


Каким образом в микроконтроллер поместить загрузчик Arduino я не буду рассматривать в рамках данной статьи, но хочу отметить что это совсем не сложно и делается минимумом оборудования (наличие LPT порта). Зато потом такой подход неизменно окупится. Гугл вам в помощь.

В общем у нас есть Atmega8 с прошитым бутлодером Arduino. Теперь он умеет скетчи и вся работа к микроконтроллером у нас сводится к написанию модных Arduino скетчей. У микроконтроллера мы задействуем только два вывода, а все остальное вы можете использовать на свое усмотрение. Обвешать датчиками нашу камеру теперь вообще не проблема. Для простоты монтажа лучше всего взять макетную плату с дырками и намертво припаять туда микроконтроллер (можно и не намертво, а цивилизованно если использовать dip панельку).


Пока еще голая макетная плата, микроконтроллер, камера и сервоприводы.

Поскольку сервоприводы управляются сигналом форма которого меняется во времени и это самое время микроконтроллеру нужно адекватно “ощущать”, нам все же понадобится кварц. В общем распаиваем питание и кварц, а так же подключаем преобразователь USBtoUART к микроконтроллеру по предложенной схеме. Не забывайте что когда вы переворачиваете микроконтроллер в пространстве то назначение выводов тоже переворачивается, в пайке ориентируйтесь на первый вывода который обычно помечен. (Опять таки не считаю всех идиотами, но запариться с этим лично у меня получалось запросто и не раз. Может конечно идиот я.)


Схема подключения питания, кварца и reset.

К выводам RX, TX и DTR подпаиваем наш преобразователь интерфейсов USBtoUART. DTR подключается к пину сброса микроконтроллера (reset) через конденсатор для того чтоб скетчи заливались автоматически без необходимость нажимать reset.
Если все подключено верно то вы можете уже подключить микроконтроллер к ПК и залить туда скетч. (Странно но конденсаторы, которые стоят после кварца, мешали нормальной работе микроконтроллера, поэтому я их убрал.)

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

Сам скетч.
#include "Servo.h"

Servo servo1,servo2;
int press_xdown= 0x61;	// код клавиши 'a'
int press_xup = 0x64;	// код клавиши 'd'
int press_yup = 0x77;	// код клавиши 'w' 
int press_ydown = 0x73;	// код клавиши 's'
int light =  0x6C;		// код клавиши 'l'
boolean laight = 0;
int chassis_light = 13; // пин магрательного светодиода
int ServoPower = 11; 	// пин куда подключен КМОП транзистор для 
						// управления питанием серв (если такого у вас нет то пофиг)
int incomingByte = 0;	// байт который считываем из последовательного порта

int X = 90;
int Y = 90;
int step = 5;
int xmax = 180;
int xmin = 0;
int ymax = 180;
int ymin = 0;
unsigned long time_servo;	// Тут будем сохранять время старта работы серв


void setup() {
  Serial.begin(9600);
  servo1.attach(10);
  servo2.attach(9);
  pinMode(chassis_light, OUTPUT);
  pinMode(ServoPower, OUTPUT);
  }

void loop() { 
  
  if (Serial.available() > 0)  //если есть доступные данные считываем байт
  {  
        incomingByte = Serial.read();
        time_servo = millis();
        digitalWrite(ServoPower, HIGH); 
        Serial.flush();
  }
  
   if ((millis()- time_servo)> 2000 )               // если прошло две секунды после последней команды то откл. питание серв		
		digitalWrite(ServoPower, LOW);  // если вы не добавляли такую возможность то это никак не отразится
    
       //------------------------------------------------------ 
       
        if(incomingByte == press_xup)
          if(X <=(xmax-step))
			X = X + step;
             
        if(incomingByte == press_xdown)
          if(X >=(xmin+step))
            X = X - step;    
          
        if(incomingByte == press_yup)
          if(Y <=(ymax-step))
            Y = Y + step;
   
        if(incomingByte == press_ydown)
          if(Y >=(ymin+step))
            Y = Y - step;     
    
         if(incomingByte>0)	
         {
            servo1.write(Y);
            servo2.write(X);            
            Serial.print(X,DEC); 
            Serial.print(":"); 
            Serial.println(Y,DEC);
          }
        incomingByte = 0;  
}  



Второй вариант скетча. Теперь камера может горизонтально перемещаться на 360 градусов, а по вертикали на 90. Это сделано с целью упростить удаленное управление. Как именно это происходит смотрите на втором видео.

Скетч посложнее.
#include "Servo.h"

Servo servo1,servo2;
int press_xdown= 0x61;	// код клавиши 'a'
int press_xup = 0x64;	// код клавиши 'd'
int press_ydown = 0x77;	// код клавиши 'w' 
int press_yup = 0x73;	// код клавиши 's'
int light =  0x6C;		// код клавиши 'l'
boolean laight = 0;
int chassis_light = 13; // пин магрательного светодиода
int ServoPower = 11; 	// пин куда подключен КМОП транзистор для 
                        // управления питанием серв (если такого у вас нет то пофиг)
int incomingByte = 0;	// байт который считываем из последовательного порта

int X = 90;
int Y = 90;
int step = 5;
int xmax = 360;
int xmin = 0;
int ymax = 90;
int ymin = 0;
unsigned long time_servo;	// Тут будем сохранять время старта работы серв


void setup() {
  Serial.begin(9600);
  servo1.attach(10);
  servo2.attach(9);
  pinMode(chassis_light, OUTPUT);
  pinMode(ServoPower, OUTPUT);
  }

void loop() { 
  
  if (Serial.available() > 0) {  //если есть доступные данные считываем байт
        incomingByte = Serial.read();
        time_servo = millis();
         digitalWrite(ServoPower, HIGH); 
       Serial.flush();
  }
  
   if ((millis()- time_servo)> 1000 ) // если прошло две секунды после последней команды то откл. питание серв		
        digitalWrite(ServoPower, LOW);// если вы не добавляли такую возможность то это никак не отразится
    
       //------------------------------------------------------ 
       
        if(incomingByte == press_xup)
         {
            X = X + step;
			if(X>360)
				X = 0;
          }   
        if(incomingByte == press_xdown)
          {
			X = X - step;
			if(X <0)
				X = 360;    
          }
		  
        if(incomingByte == press_yup)
          if(Y <=(ymax-step))
            Y = Y + step;
   
        if(incomingByte == press_ydown)
          if(Y >=(ymin+step))
            Y = Y - step;     
    
         if(incomingByte>0)	
         {
            if(X>180)
	    {
	      servo1.write(180-Y);
              servo2.write((180-X)*-1);
             }
	    else
            {		
	      servo1.write(Y);
              servo2.write(X);            
            }
			
	    Serial.print(X,DEC); 
            Serial.print(":"); 
            Serial.println(Y,DEC);
          }
        incomingByte = 0;  
}  



На 15, 16 вывод (или 9,10 по Arduino распиновке) подключаются сигнальные контакты сервоприводов, а питание сервоприводов подключается к общему питанию. (Знаю что надо разносить питание логики и силовой части, но решил немного побезпредельничать, возможно буду за это наказан суровыми глюками в будущем.)

Камерой поворачивать можно с помощью всем хорошо известной комбинации клавиш a,s,d,w. Естественно что коды этих клавиш надо посылать в СОМ порт (который появляется когда в ПК втыкается преобразователь USBtoUART) на котором у вас висит микроконтроллер.

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



6. Свет (опционально).

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


Вот так можно добавить светодиодов по вкусу.



7. Усиление кабеля хаба(опционально).

Так как хаб мы очень сильно «модифицировали» (уверен он сам в шоке) то и потребление тока у нас выросло. На весь этот огород + свет мне уже не хватило стандартных 500 мА c одного USB. Поэтому советую заменить кабель хаба на более качественный с раздвоенным штекером USB на конце. Или допаять второй штекер самостоятельно, задействовав только контакты питания (красный и черный). Сделать это можно если недалеко от штекера аккуратно снять оплетку и подпаять к проводам питания еще одни штекер (только красный и черный!!!). Делайте это осторожно потому как спалить замыканием USB контроллер матери это раз плюнуть. Хорошо все изолируйте.


Схематически показал как усилить кабель.

Можно также вместо второго USB штекера допаять стандартный molex разъем и получить тем самым «неограниченное» (почти) питание, но к ноутбуку такую конструкцию не подключишь.



8. Экономная экономия(опционально).

Чтобы не питать сервы в состоянии покоя их питание можно отключать используя тот-же метод что и для отключения света. В скетче этот вариант предусмотрен. Если команды не поступают две секунды питание сервоприводов выключается. Еще такой трюк избавит от противного дребезжания серв в простое. Это происходит из-за особенностей конструкции бюджетных сервоприводов.



Совсем не собирался писать так много но оно само.
Понятно что все выше написанное вы просто прокрутили чтоб добраться сразу до видео и картинок. А их тут есть!)


Видео:



По просьбам трудящихся в сжатые сроки попытался изобразить видео в котором можно оценить плавность изображения с самой камеры.
Шаг сервопривода равен 1, сначала команда подается раз в 50мс, потом раз в 100мс.


И шаг 5, команды через каждые 100мс.


Картинки:
Длиннющая пеленка с фото, трафик!




















P.S.
1. Если кто подскажет как отцентровать картинки в статье буду благодарен. С наскока что-то не получилось ничего, а выглядит не очень.
Tags:
Hubs:
+47
Comments 20
Comments Comments 20

Articles