» » Машинка с пропорциональным управлением по Bluetooth из-под Android

 
 
 

Машинка с пропорциональным управлением по Bluetooth из-под Android

Разместил Discover 23 мая 2016. Просмотров: 3747

3 Не будем покупать плохие игрушки у Китайцев, а купим у них дешевый конструктор-шасси, несколько модулей и приложим руки!
Вот что у меня получилось в итоге: проходимое шасси, управляемое — ТА-ДА!!! — с моего смартфона на Андроиде.

«Я и прямо, я и боком,
     С поворотом, и с прискоком,
         И с разбега, и на месте,
               И двумя ногами вместе…»

Сегодня мы соберём забавную машинку с дистанционным управлением по Bluetooth. Исходники программы управления под Android в комплекте.

Достойный образец игрушки

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

Почему так? Наверное потому что эта пила продавалась в магазине инструментов «STIHL». Как я полагаю, «STIHL» сделал игрушечный аналог своей продукции небольшим рекламным тиражом. В результате появилась на свет вполне вменяемая игрушка, очень похожая на своего старшего брата. Цепь резиновая – крутится, процентов на 80 реализованы органы управления. Есть даже шнур с ручкой для завода пилы, выключатель, кнопка газа. В комплекте есть запасная цепь и инструмент для смены цепи.

Вот такая игрушечная пила


О чём это я? Ах, да, об архитектуре! Это я к тому, что при желании отличную игрушку сделать можно. И есть на что равняться.

Будем строить машинку с ДУ!

Практический и технический интерес вызывают игрушки с радиоуправлением. Однако, ребёнку в возрасте 4-6 лет не будут дарить игрушки со «взрослым» пропорциональным управлением. Скорее всего, игрушка будет сломана, а деньги выкинуты на ветер.
В итоге, обычно дарят что-то недорогое. Из всего этого – «недорогого» — машинки или шибко быстрые или тормозные; танки хилые; и прочие очевидные и скрытые недостатки. И уж конечно никакого пропорционального управления.

В один прекрасный день у одной из машинок перестало вращаться правое колесо. Разобрал, проверил моторчик – исправный.
На плате управления три микросхемы – Китай голимый, вменяемой документации найти не смог. Один чип – приёмник радиосигнала с логическими выходами и два мостовых драйвера двигателей. Один из драйверов вышел из строя. Сваять сходу мостовой драйвер двигателя из дискретных компонентов у меня не получилось.

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

Не откладывая в долгий ящик идею о своей машинке, я снова подался на Алиэкспресс для выбора основы — шасси будущей машинки. Шасси бывают разные, для наземного транспорта: гусеничные, колесные, с двумя, тремя, четырьмя колесами и т.п.

Как я выбирал шасси

Во-первых, я выбрал наземный вид транспорта, значит и шасси у меня будет наземное. Гусеничные шасси, как правило, дороже и не слишком быстроходные. Двух-трёх-колесные мне кажутся слабо проходимыми, такое шасси сможет ездить только по ровной поверхности.
Я остановился на 4-колесном шасси с приводом (мотором) на каждое колесо. По моему мнению, такое шасси будет обладать отличной проходимостью и скоростью.
Машинка с пропорциональным управлением по Bluetooth из-под Android

В комплекте поставки шасси:
• две пластины из акрила с кучей технологических отверстий для крепления все возможных датчиков, плат управления и прочих компонентов
• 4 колеса
• 4 привода в сборе (электродвигатель + редуктор)
• 4 диска с прорезями для датчиков скорости, по одному на каждое колесо
• крепеж
Да, это снова Китай. Да, дешёвый. Да, нешибко качественный. НО! Нам бы для начала попробовать. Ведь «взрослое» шасси и стоит по-взрослому, мы до него ещё не доросли.

Болото мыслей и Техзадание

Когда держишь в руках перспективную вещь, например, в плане возможностей по обвесу модели всевозможными датчиками, серво и пр., начинаешь тонуть в болоте мыслей и трясине перспектив. Но, скажем себе — СТОП! И составим себе мини-ТЗ на прототип с кратким описанием всех узлов.
У нас должна получиться RC-модель наземного траспортного средства с управлением по Bluetooth, с возможностью реверса и плавного регулирования скорости вращения колес.

Что нам потребуется для сборки машинки?

Все компоненты списком:
1. Шасси 4 WD с четырьмя колесами.
2. Bluetooth–модуль «HC-06» .
3. Драйвер двигателей TB6612FNG двухканальный.
4. Плата управления MOD-IO (Olimex) или другая МК-плата, например обычная платка Ардуино Нано с правкой исходников.

1. Шасси 4 WD с четырьмя колесами.
Машинка с пропорциональным управлением по Bluetooth из-под Android

Поворотные колеса отсутствуют, значит, управление поворотом будет как у гусеничного ТС. То есть, для прямого движения, вперед/назад, правая и левая сторона приводов вращаются с одинаковой скоростью. А для осуществления поворота, скорость вращения на одной из сторон должна быть меньше или больше.
Машинка с пропорциональным управлением по Bluetooth из-под Android


2. Bluetooth–модуль «HC-06» .
Машинка с пропорциональным управлением по Bluetooth из-под Android

Для дистанционного управления машинкой используем канал Bluetooth. Модуль «HC-06» — это мост Bluetooth, последовательный интерфейс, позволяющий передавать данные в обе стороны. На входе — TTL-сигналы последовательного интерфейса «RxD» и «TxD» для подключения к микроконтроллеру (целевой плате).
В качестве пульта дистанционного управления будет служить сотовый телефон с Android. Напишем свою программу!

3. Драйвер двигателей TB6612FNG двухканальный.
Машинка с пропорциональным управлением по Bluetooth из-под Android
Машинка с пропорциональным управлением по Bluetooth из-под Android

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

4. Плата управления MOD-IO (Olimex)
Машинка с пропорциональным управлением по Bluetooth из-под Android

Выбрана эта плата т.к. валялась в ящике стола и полностью подходит для нашей цели. Есть дискретные входы/выходы, выведены сигналы МК «RxD» и «TxD», куда будет подключен «HC-06“.
Забегая вперёд скажу, что продукт Олимекс MOD-IO — это жёсткий перебор. Прекрасно можно будет применить и обычную платку Ардуино Нано, о чём в продолжении истории!

Итого: шасси + плата управления + Bluetooth-модуль + программа управления под Android.

Общая схема подключения

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


Схема в Протеусе

Машинка с пропорциональным управлением по Bluetooth из-под Android

Расписывать, что и куда я подключил не буду. Скорее всего у вас будет другая плата управления. Исходники я прикладываю, так что прошивку сможете править. Ну, а если кто-то не в силах скомпилировать прошивку под свою плату, обращайтесь — помогу по мере свободного времени.

Программа микроконтроллера

Программа МК умеет принимать команды по последовательному интерфейсу с Bluetooth-модуля.
И, в соответствии с командами, управлять левой и правой парой приводов. Реверс и управление скоростью работают при помощи ШИМ.

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

Функции кольцевого буфера у меня вынесены в отдельную библиотеку состоящую из:
заголовочного файла ring_buffer.h и файла реализаций функций ring_buffer.с
Для использования библиотеки её нужно подключить в main.c
#include "ring_buffer.h"


Далее, в заголовочном файле необходимо настроить библиотеку. Для настройки необходимо задать всего четыре директивы:
#define RX_PACKET_SIZE		7	// Размер RxD пакета
#define BUFFER_SIZE			16	
// Размер приёмного буфера. Должен быть в два раза больше RX_PACKET_SIZE

#define START_BYTE			's'     // Стартовый байт
#define STOP_BYTE			'e'     // Стоповый байт


Собственно настраивать больше нечего.

Использование кода В main.c настраиваем USART микроконтроллера.
Вызываем функцию настройки USART
USART_Init(MYUBRR);


#define BAUD 9600
#define MYUBRR F_CPU/16/BAUD-1

void USART_Init( unsigned int ubrr )
{
	/* Set baud rate */
	UBRRH = (unsigned char)( ubrr >> 8 );
	UBRRL = (unsigned char)ubrr;
	/* Enable receiver and transmitter */
	UCSRB = (1 << TXCIE) | (1 << RXCIE)| (1 << TXEN) | (1 << RXEN);
	/* Set frame format: 8data, 2stop bit */
	UCSRC = (1 << URSEL) | (0 << USBS) | (3 << UCSZ0);
}


Приём пакета данных

В функции обработчика прерывания приема байта по USART, нам необходимо всего лишь положить принятый байт в кольцевой буфер. Разбор пакета будем делать потом.
ISR(USART_RXC_vect)
{
	uint8_t Data = UDR;
	RB_push_char( Data );	// Складываем принятый байт в кольцевой буфер
}

Да, я пока пренебрег всякими проверками фрейма.

Теперь нам остается время от времени проверять наш буфер. Для этого я инициировал таймер, в котором я устанавливаю флаг разрешающий проверку кольцевого буфера
ISR(TIMER0_OVF_vect)
{
	TCNT0 = 0x64;
	ReadRingBuffer = 1;
}


В основном цикле добавляем условие для проверки флага.
if ( ReadRingBuffer )
{
if ( RB_read_buffer((uint8_t*)&RxPacket) == 1 ) Чтение буфера
			{
                                          // Разбираем пакет, делаем что-нибудь
			}
			ReadRingBuffer = 0;
		}


Функция RB_read_buffer проверяет кольцевой буфер, если размер пакета, стартовый и стоповые байты находятся на своих местах — пакет считается валидным, функция возвращает "1». В качестве аргумента, функция принимает указатель, куда складывать принятый пакет.
Для пущей надежности, пакет можно снабдить контрольной суммой, в одном из своих коммерческих проектов я так и сделал. То есть к проверке размера, старт/стопового байтов прибавляется еще проверка контрольной суммы. Но пока обойдемся без неё.

Как я разбираю пакет

Теперь самое интересное, как я разбираю пакет. В пакете могут передаваться данные размерностью больше байта, знаковые данные, данные с плавающей точкой.
Объясню на примере пакета для управления шасси. Кроме стартового и стопового байтов, в своём пакете мне необходимо передавать одну команду, и два значения ШИМ, для левой и правой стороны приводов. Для команды мне достаточно одного байта, а для каждого значения ШИМ, я передаю int16 — 16 бит, знаковый тип. То есть, я не передаю флаг (байт/признак) направления. Для смены направления, я передаю положительное или отрицательное значение ШИМ.

Приёмный пакет у меня организован в виде структуры.
struct RxPacket
{
	uint8_t	StartByte;
	uint8_t      Command;
	int16_t      Left_PWM;
	int16_t      Right_PWM;
	uint8_t	StopByte;
} RxPacket;


Вызывая функцию RB_read_buffer ((uint8_t*)&RxPacket), в качестве аргумента передаем указатель на структуру приёмного пакета. То есть, при принятом пакете, будет все разложено по своим полочкам в структуре RxPacket. Дальше остается прочитать эти данные из структуры например так:
       lCmd = RxPacket.Command;
        lLPWM = RxPacket.Left_PWM;
        lRPWM = RxPacket.Right_PWM;


Передача пакета данных

Хоть в моей программе передача пока не используется, тем не менее, возможность передачи реализована. Передавать данные проще. Точно также, как и для приемного пакета, создадим структуру:
struct TxPacket
{
	uint8_t StartByte;
	uint8_t Rc5System;
	uint8_t Rc5Command;
	uint8_t StopByte;
} TxPacket;


Где, есть стартовый и стоповый байт и информационная часть. Мы уже инициализировали приемник USART.
Для инициирования передачи пакета — вызываем функцию
void send_packet()
{
	// Запись стартового байта в регистр UDR
	UDR = START_BYTE;
}

В данном примере, в этой функции, я записываю только стартовый байт в регистр UDR. Вроде бы немного, но в этой же функции можно реализовать подготовку пакета или еще что-то полезное. И это, на мой взгляд более логично. Логично в плане самодокументирования кода. То есть, если я в коде, просто запишу значение в регистр UDR это может быть воспринято, как передача всего одного байта, а вызывая самоговорящую функцию send_packet () — я говорю о том, что я отправляю пакет данных.

Далее, когда передатчик USART отправит весь байт из регистра UDR — будет вызван обработчик прерывания по передаче.
ISR(USART_TXC_vect)
{
	unsigned char *Pointer = (unsigned char *)&(TxPacket);

	static unsigned char TxIndex = 1;

	if ( TxIndex < sizeof(TxPacket) )
	{
		UDR = *(Pointer + TxIndex);
		TxIndex++;
	}
	else TxIndex = 1;
}


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

Условие
if ( TxIndex < sizeof(TxPacket) )
проверяет, что индекс меньше чем размер пакета. Если условие верно, то записываем байт в регистр UDR:
UDR = *(Pointer + TxIndex);

инкрементируем TxIndex. Когда USART передаст очередной байт, то мы снова попадём в обработчик, но будет передан уже следующий байт из структуры и так будут переданы все байты структуры. Когда TxIndex будет больше чем размер структуры — условие будет не верно и мы попадем в 
else TxIndex = 1;
Где будет проинициализирован TxIndex, но в регистр UDR уже ничего не записывается, соответственно обработчик больше не будет вызван до следующего инициирования передачи пакета. Таким образом, процесс передачи полностью автоматический, и даже, если мы изменим структуру пакета, то обработчик переписывать не придётся.

В рамках описания программы МК осталось рассказать про реализацию управления драйверами. Драйвер управляется тремя сигналами: A1 (B1), A2 (B2) и PWMA (PWMB). A1 и A2 предназначены для включения/выключения драйвера и для изменения полярности выхода. На вход PWMA подается ШИМ сигнал с МК — можно управлять скоростью вращения. Для ШИМ сигнала я задействовал два аппаратных ШИМа таймера 1.
#define _WGM13 0
#define _WGM12 1
#define _WGM11 0
#define _WGM10 1

// Timer 1 init
TCCR1A = ( 1 << COM1A1 ) | ( 0 << COM1A0 ) | ( 1 << COM1B1 ) |
 ( 0 << COM1B0 ) | ( _WGM11 << WGM11 ) | ( _WGM10 << WGM10 );
TCCR1B = ( 0 << CS12 ) | ( 0 << CS11 ) | ( 1 << CS10 ) |
			 ( _WGM13 << WGM13 ) | ( _WGM12 << WGM12 );

	TCNT1 =0x0000;
	OCR1A = 0;
	OCR1B = 0;


Таймер 16-битный, но ШИМ инициализирован на 8 бит. И как вы наверное уже заметили, в приёмном пакете у меня есть два значения для задания ШИМ, для левого и правого привода соответственно. Переменные знаковые 16-битные.
Объясню почему я так сделал.

Во-первых, это пошло от программы под Андроид. Дело в том что в Java нет без знаковых типов и я уже наступал на эти грабли. И для передачи числа от 0 до 255 мне пришлось бы как то извернуться. Я решил пойти более простым путем — отсылаю знаковое 16-бит число. При этом, 16 бит знакового типа это от -32786 до 32768, нам хватит.

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

И в-третьих, как не крути, для наших целей меньше чем в три байта не уложиться. Пожертвуем еще одним байтом, зато всё становится понятно, положительное значение ШИМ — прямое вращение, отрицательное значение — обратное вращение.

Для управления приводами я написал функцию drive (int leftPWM, int rightPWM);.
void drive(int leftPWM, int rightPWM)
{
	// Движение ВПЕРЁД левое колесо
	if ( leftPWM > 0 ){
		ClearBit( A2_PORT, A2_PIN );
		SetBit( A1_PORT, A1_PIN );
	}
	// Движение НАЗАД левое колесо
	if ( leftPWM < 0 ){
		ClearBit( A1_PORT, A1_PIN );
		SetBit( A2_PORT, A2_PIN );
	}
	// Движение ВПЕРЁД правое колесо
	if ( rightPWM > 0 ){
		ClearBit( B2_PORT, B2_PIN );
		SetBit( B1_PORT, B1_PIN );
	}
	// Движение НАЗАД правое колесо
	if ( rightPWM < 0 ){
		ClearBit( B1_PORT, B1_PIN );
		SetBit( B2_PORT, B2_PIN );
	}
	// Остановка
	if ( leftPWM == 0 ){
		ClearBit( A1_PORT, A1_PIN );
		ClearBit( A2_PORT, A2_PIN );
	}
	// Остановка
	if ( rightPWM == 0 ){
		ClearBit( B1_PORT, B1_PIN );
		ClearBit( B2_PORT, B2_PIN );
	}

	set_PWM( (uint8_t)(abs(leftPWM)), (uint8_t)(abs(rightPWM)) );
}

В соответствии со значением ШИМ, осуществляется управление сигналами A1 (B1), A2 (B2) и устанавливается значение ШИМ вызовом функции set_PWM (leftPWM, rightPWM).

Уфф, выдохся…
Подытожим: приняли пакет, разобрали, передали значение ШИМ в функцию drive.

Приложение под Android для машинки

Нет, так подробно, как программу для МК я разбирать не буду. В разработке ПО под Android я ещё новичок и не готов рассказывать достаточно компетентно и глубоко.

Основная функция программы - передача данных модулю HC-06 по каналу Bluetooth. Программа имеет не замысловатый интерфейс.
Машинка с пропорциональным управлением по Bluetooth из-под Android


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


Дальше кнопка «Выкл» — включает/отключает связь с «HC-06». Ниже слева направо: передаваемое значение ШИМ левого канала, тип датчика, значение правого канала. Ниже два слайдера регулировки чувствительности скорости и поворота.

В программе реализовано два типа управления машинкой. Для переключение типа датчика нужно коснуться надписи названия датчика: «Наклон» или «Шаркать».

1. Управление машинкой с помощью наклона телефона. Нулевое положение телефона — горизонтальное. При наклоне телефона вперед значение ШИМ увеличивается пропорционально наклону в диапазоне от 0 до 255. При наклоне телефона назад значение ШИМ уменьшается пропорционально наклону в диапазоне от 0 до -255
Машинка с пропорциональным управлением по Bluetooth из-под Android

Что бы повернуть влево или вправо — нужно наклонить телефон вперед или назад и, одновременно, влево или вправо соответственно. Да, как в настоящей машине, пока не поддашь газку поворот не осуществляется.

2. Управление касанием. Моё фирменное название для такого управления — «шаркать».
Машинка с пропорциональным управлением по Bluetooth из-под Android

Можно воспринимать как тачпад. При касании в сером квадрате, значение ШИМ увеличивается/уменьшается в зависимости от места касания, чем дальше от центра вниз или вверх тем больше/меньше значение.

Никаких «красивостей» и наворотов нет. Вот вроде бы и всё.

Немножко дёгтя на «лыжи»

Есть косяк у моего телефона. Ага, телефон «лыжа» — LG G2 mini. На нём соединение по Bluetooth устанавливается неадекватно. Соединение устанавливается нормально только, если Bluetooth был включен непосредственно перед запуском приложения.
Я сделал так: при запуске приложении проверяю включен ли Bluetooth, если выключен — делаю запрос на включении. А при «сворачивании», закрытии приложения — принудительно выключаю Bluetooth.
И еще один момент, при смене ориентации экрана, Bluetooth выключается и снова выдается запрос на включения, приходится выключать автоматическое переключение разворота экрана.

Резюме

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

Есть еще поле для деятельности, есть куда расти. Можно «наворачивать» шасси, можно модернизировать и улучшать ПО для телефона.
И об этом будет продолжение!

Файлы

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


Схема платы MOD-IO, PDF
Файловый сервис доступен только полноправным членам сообщества и подписчикам.
Пожалуйста, ознакомьтесь с условиями доступа.


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


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


Установочный файл для телефона, apk
Файловый сервис доступен только полноправным членам сообщества и подписчикам.
Пожалуйста, ознакомьтесь с условиями доступа.


Спасибо за внимание!

Об авторе

Иван Гаврилов (Discover)
Вологодская обл. Череповецкий р-н, п.Тоншалово
Дата рождения: 13.09.1977.
Работаю в компании "Нординкрафт" , электрик по совместительству в своем ТСЖ.
С паяльником с 12 лет.
Интересы: паяльник, работа, фото, лес.
C 2007г занимаюсь микроконтроллерами AVR.

Напаяно уже много чего, всего и не припомню.
 

Понравилось? Палец вверх!

  • всего лайков: 54

Поделись с друзьями!


Связанные материалы:


Kaspersky FREE. Бесплатная годовая лицензия для России, Белоруссии и Украины

Привет, друзья! Очередной подарок от kaspersky.ru — антивирус «Kaspersky FREE». Спасибо...

Блок управления вытяжным вентилятором. Наладка, результаты

Всем здоровья! Утюг починен. Хочу коротко отчитаться в проделанной работе и результатах...

Пусть всегда будет солнце!

Сегодня День Победы. Мы так долго живём без войны, что начали забывать, что это...

Спасибо деду за Победу 2015!

Датагорцы, сограждане, друзья! У кого деды-ветераны живы — успевайте, поздравляйте,...

17/01 киберсубботник на портале

Привет, дрУги! В ближайщую субботу на нашем портале и в магазине будет...

Coil32 v9.0 - программа для расчета катушек индуктивности

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

Сэмплы гитарного комбоусилителя U-96

Как и обещал, записал пару сэмплов на скорую руку с моего комбика...

Библиотеки Diptrace +3D. Разъёмы PBS (розетки)

Продолжая тему библиотек для Diptrace, хочу предложить розетки однорядные PBS для пайки...

Toshiba - это хорошо! Новейший каталог 4Q2012

Предлагаю вниманию сограждан новейший каталог Toshiba Bipolar Power Transistors — 4 квартал...

Датагорскiя Вѣдомости №1 (2012)

Здравствуйте, уважаемые сограждане-датагорцы и гости нашего электронного города! Спешу...

Transformer: программа расчета маломощного (до 500 Вт) силового трансформатора на

Предлагаю начинающим очень простую программулину для расчета силовых трансформаторов. Автор...

Датагорская Ярмарка электроники v.2 открылась!

Уважаемые граждане Датагории и гости нашего кибер-города! После обновления открылась...
<
  • Главный редактор
23 мая 2016 20:19

Игорь Петрович Котов / Datagor

  • Регистрация: 25.02.2011
  • Публикаций: 262
  • Комментариев: 1607
 
  • +1
Ваня, когда я увидел разворот на месте, я просто влюбился!
Спасибо за отличный проект и новую статью!
handshake

<
  • Гражданин
24 мая 2016 11:26

Владимир / vladimirm2

  • Регистрация: 5.01.2010
  • Публикаций: 14
  • Комментариев: 131
 
  • 0
Хорошая, достойная работа! Делал подобное, но гораздо проще, поэтому вижу этот объем работы, впечатляет.

Спасибо за статью, есть многое, что для себя выделил и взял на вооружение! )) Супер игрушка!

<
  • Гражданин
24 мая 2016 23:14

Антон / basilevscom

  • Регистрация: 9.01.2010
  • Публикаций: 15
  • Комментариев: 111
 
  • 0
Просто супер! Обязательно надо будет попробовать повторить )))

Информация
Вы не можете участвовать в комментировании. Вероятные причины:
— Администратор остановил комментирование этой статьи.
— Вы не авторизовались на сайте. Войдите с паролем.
— Вы не зарегистрированы у нас. Зарегистрируйтесь.
— Вы зарегистрированы, но имеете низкий уровень доступа. Получите полный доступ.