Arduino Keypad Shield или Знакосинтезирующий ЖКИ


Добрый день (опционально вечер/ночь).

Сегодня расскажу Вам о китайском ЖКИ. Брался в качестве баловства, возможно создания какого-либо проекта, получения навыков работы с ЖКИ, прошу под кат:

ПРЕДИСЛОВИЕ

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

ОПИСАНИЕ





Размер дисплея: 80 мм х 58 мм;
Рабочее напряжение: 4.5-5В;
Дисплей: 16х2;
Совместимость HD44780;
Подсветка: синий фон, белые символы;
Имеется переменный резистор, для регулировки яркости;
Русского языка нет.

Вообще исходя из цены, дисплей стоит там крайне бюджетный, такие в целом продаются за 80-100р. на али.

Фотографии специально делались на ардуине, что бы можно было оценить, то как дисплей устанавливается в разъемы. (попадались шилды, которые из-за совсем кривой пайки вставали с перекосом).

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









ТЕСТИРОВАНИЕ

В качестве тестирования был взят старый скетч:







Каков был функционал, такой и получил. Все работает исправно, кнопки все нажимаются, регулировка яркости работает в полном объеме:

Минимальная:


Средняя:


Максимальная:


Видео тест шилда с запущенной игрой:


ВЫВОДЫ

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

За скетчем сюда:
Скетчик
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7 );

const int FIELD_WIDTH = 16;
const float BAR_PROBABILITY = 1;

int button;
const int BUTTON_NONE = 0;
const int BUTTON_RIGHT = 1;
const int BUTTON_UP = 2;
const int BUTTON_DOWN = 3;
const int BUTTON_LEFT = 4;
const int BUTTON_SELECT = 5;

int gameStatus;
const int MAIN_MENU = 0;
const int IN_GAME = 1;
const int PAUSE = 2;
const int ENDING = 3;

char gameFields[32];
const char SPACE = ' ';
const char BAR = '*';
const char PLAYER = '>';

int gameSpeed;
int SPEED_HIGH = 70;
int SPEED_NORMAL = 300;
int changeSpeed = 5000;

long startGameTime;
long lastMovementTime;
long lastChangeSpeedTime;
long endGameTime;
long bestGameTime;
int topQueue = true;

int getPressedButton()
{
int buttonValue = analogRead(0);
if (buttonValue < 100) {
return BUTTON_RIGHT;
}
else if (buttonValue < 200) {
return BUTTON_UP;
}
else if (buttonValue < 400){
return BUTTON_DOWN;
}
else if (buttonValue < 600){
return BUTTON_LEFT;
}
else if (buttonValue < 800){
return BUTTON_SELECT;
}
return BUTTON_NONE;
}

void startGame()
{
SPEED_NORMAL = 250;
startGameTime = millis();
lastMovementTime = millis();
lastChangeSpeedTime = millis();
endGameTime = 0;
// Очищаем игровое поле
for (int i = 0; i < 2*FIELD_WIDTH; i++)
{
gameFields[i] = SPACE;
}
gameFields[0] = PLAYER;
gameStatus = IN_GAME;
printGameField();
}

void moveGameField()
{
if (gameFields[0] == PLAYER && gameFields[1] == BAR ||
gameFields[FIELD_WIDTH] == PLAYER && gameFields[FIELD_WIDTH+1] == BAR)
{
gameOver();
return;
}
int playerPos = 0;
if (gameFields[FIELD_WIDTH] == PLAYER)
{
playerPos = FIELD_WIDTH;
}
for (int i = 0; i < FIELD_WIDTH*2; i++)
{
if (i != FIELD_WIDTH-1)
{
gameFields[i] = gameFields[i+1];
}
}
gameFields[playerPos] = PLAYER;
int topBarsCount = 0;
int bottomBarsCount = 0;
for (int i = 0; i < FIELD_WIDTH; i++)
{
if (gameFields[i] == BAR)
{
topBarsCount++;
}
}
for (int i = FIELD_WIDTH; i < FIELD_WIDTH*2; i++)
{
if (gameFields[i] == BAR)
{
bottomBarsCount++;
}
}
if (topQueue)
{
if (gameFields[FIELD_WIDTH-1] != BAR &&
gameFields[FIELD_WIDTH-2] != BAR &&
gameFields[FIELD_WIDTH-3] != BAR &&
bottomBarsCount < FIELD_WIDTH/2 &&
random(9) < BAR_PROBABILITY)
{
gameFields[2*FIELD_WIDTH-1] = BAR;
} else
{
gameFields[2*FIELD_WIDTH-1] = SPACE;
}
if (gameFields[2*FIELD_WIDTH-1] != BAR &&
gameFields[2*FIELD_WIDTH-2] != BAR &&
gameFields[2*FIELD_WIDTH-3] != BAR &&
topBarsCount < FIELD_WIDTH/2 &&
random(9) < BAR_PROBABILITY)
{
gameFields[FIELD_WIDTH-1] = BAR;
} else
{
gameFields[FIELD_WIDTH-1] = SPACE;
}
topQueue = false;
} else
{
if (gameFields[2*FIELD_WIDTH-1] != BAR &&
gameFields[2*FIELD_WIDTH-2] != BAR &&
gameFields[2*FIELD_WIDTH-3] != BAR &&
topBarsCount < FIELD_WIDTH/2 &&
random(9) < BAR_PROBABILITY)
{
gameFields[FIELD_WIDTH-1] = BAR;
} else
{
gameFields[FIELD_WIDTH-1] = SPACE;
}
if (gameFields[FIELD_WIDTH-1] != BAR &&
gameFields[FIELD_WIDTH-2] != BAR &&
gameFields[FIELD_WIDTH-3] != BAR &&
bottomBarsCount < FIELD_WIDTH/2 &&
random(9) < BAR_PROBABILITY)
{
gameFields[2*FIELD_WIDTH-1] = BAR;
} else
{
gameFields[2*FIELD_WIDTH-1] = SPACE;
}
topQueue = true;
}
}

void gameOver()
{
gameStatus = ENDING;
endGameTime += (millis() — startGameTime);
if (endGameTime > bestGameTime)
{
bestGameTime = endGameTime;
}
int playerPos = 0;
if (gameFields[FIELD_WIDTH] == PLAYER)
{
playerPos = FIELD_WIDTH;
}
lcd.setCursor(playerPos%FIELD_WIDTH, playerPos/FIELD_WIDTH);
lcd.print('x');
delay(300);
lcd.setCursor(playerPos%FIELD_WIDTH, playerPos/FIELD_WIDTH);
lcd.print('X');
delay(300);
lcd.setCursor(playerPos%FIELD_WIDTH, playerPos/FIELD_WIDTH);
lcd.print('x');
delay(300);
lcd.setCursor(playerPos%FIELD_WIDTH, playerPos/FIELD_WIDTH);
lcd.print('X');
delay(300);
lcd.setCursor(playerPos%FIELD_WIDTH, playerPos/FIELD_WIDTH);
lcd.print('x');
delay(300);
lcd.setCursor(playerPos%FIELD_WIDTH, playerPos/FIELD_WIDTH);
lcd.print('X');
delay(300);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print(«Last time:»);
lcd.print(endGameTime/1000);
lcd.print(" sec");
lcd.setCursor(0, 1);
lcd.print(«Best time:»);
lcd.print(bestGameTime/1000);
lcd.print(" sec");
}

void printGameField()
{
for (int i = 0; i < FIELD_WIDTH*2; i++)
{
lcd.setCursor(i%FIELD_WIDTH, i/FIELD_WIDTH);
lcd.print(gameFields[i]);
}
}

void pause()
{
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print(«Paused»);
lcd.setCursor(0, 1);
lcd.print(" PRESS — SELECT");
gameStatus = PAUSE;
endGameTime += (millis()-startGameTime);
}

void setup()
{
gameStatus = MAIN_MENU;
button = BUTTON_NONE;
bestGameTime = 0;

lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print(" SPACE INVADERS ");
lcd.setCursor(0, 1);
lcd.print(" RACE GAME ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" SPACE INVADERS ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" SPACE INVADERS ");
lcd.setCursor(0, 1);
lcd.print(" RACE GAME ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
delay(600);
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print(" SPACE INVADERS ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" SPACE INVADERS ");
lcd.setCursor(0, 1);
lcd.print(" RACE GAME ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" SPACE INVADERS ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
delay(600);
lcd.setCursor(0, 0);
lcd.print(" PRESS — SELECT");
lcd.setCursor(0, 1);
lcd.print(" START GAME ");
}

void loop()
{
button = getPressedButton();

switch (gameStatus)
{
case MAIN_MENU:
if (button == BUTTON_SELECT)
{
startGame();
}
break;
case IN_GAME:
gameSpeed = SPEED_NORMAL;
switch (button)
{
case BUTTON_RIGHT:
gameSpeed = SPEED_HIGH;
break;
case BUTTON_LEFT:
pause();
break;
case BUTTON_UP:
if (gameFields[0] == BAR)
{
gameOver();
} else
{
gameFields[0] = PLAYER;
if (gameFields[FIELD_WIDTH] == PLAYER)
{
gameFields[FIELD_WIDTH] = SPACE;
}
}
break;
case BUTTON_DOWN:
if (gameFields[FIELD_WIDTH] == BAR)
{
gameOver();
} else
{
gameFields[FIELD_WIDTH] = PLAYER;
if (gameFields[0] == PLAYER)
{
gameFields[0] = SPACE;
}
}
break;
}
if (millis() — lastMovementTime > gameSpeed)
{
lastMovementTime = millis();
moveGameField();
}
if (millis() — lastChangeSpeedTime > changeSpeed)
{
lastChangeSpeedTime = millis();
SPEED_NORMAL -= 40;
// Предельная скорость
if (SPEED_NORMAL < 50)
{
SPEED_NORMAL = 50;
SPEED_HIGH = 50;
}
}
if (gameStatus == IN_GAME)
printGameField();
break;
case PAUSE:
if (button == BUTTON_SELECT)
{
gameStatus = IN_GAME;
startGameTime = millis();
}
break;
case ENDING:
if (button != BUTTON_NONE)
{
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print(" PRESS — SELECT");
lcd.setCursor(0, 1);
lcd.print(" START GAME ");
gameStatus = MAIN_MENU;
}
break;
}
}



Планирую купить +25 Добавить в избранное +31 +50
+
avatar
  • Samman
  • 31 марта 2017, 13:30
0
del
+
avatar
+1
Спасибо, готово.
+
avatar
0
Такой пол года пылится в тумбочке. Всё никак не могу придумать что с ними сделать :)
Но уже задумался о метеостанции(температура, влажность и давление) + термометром вынесенным на улицу.
Как идея это вести логи на СД карту и куда-то их потом выплёвывать и обрабатывать ))) Но а по факту ещё сам не знаю что с ним делать ))) И со всем тем что прикупил ))))

Поделитесь идеями что можно придумать эдакого :)
Пока что нарыл вот это: www.youtube.com/watch?v=EzOMIqlDnWo
+
avatar
+2
Добавь простенький Ethernet Shield, Arduino и открой для себя narodmon.ru
+
avatar
0
ВИФИ 3 модуля и ЖСМ модуль тоже в шкафу лежат )))
Меня больше в данном моём проекте интересует вариант хранение данных, и анализ с прогнозами и графиками на небольшом сайте ( в идеале на базе ардуино или esp8266 )

Есть вариант сделать электронную проходную или систему прихода и ухода сотрудников по пропускам, но опять же пока что это только идеи для которых у меня в голове не сложился кубик-рубик )))
+
avatar
  • Samman
  • 31 марта 2017, 15:36
0
Вот, как вариант: hi-lab.ru/arduino-mega-server
Только не знаю как он развивается.
+
avatar
  • Piksel
  • 01 апреля 2017, 10:28
0
Сделали електронную проходную… посмотреть можно здесь
Данные пробного периода.
Обсуждение проекта здесь
+
avatar
+3
Ну в общем мне тоже было скучно и я решил сделать кусочек умного дома для коттеджа своих родаков.
Собрал 10 беспроводных термометров — гидрометров. В каждом:
1. Arduino pro mini 3.3v
2. DHT11 — влажность
3. DS18B20 — температура
4. NRF24L01 — экономичный радиопередатчик на 2.4ГГц
5. Liion батарейка на 300mah
6. Платка зарядки Liion батарейки.
Всё это живёт в боксе под 4AAA батарейки и от 1 зарядки работает около 1 года

Все датчики разбросаны по дому и раз в 5 минут передают свои показания + заряд батарейки на хаб.

Хаб это:
1. NRF24L01 с выносной антенной
2. ESP8266
3. Блок питания 220v-3.3v

Хаб принимает значения термометров и отправляет их по WiFi серверу, на котором крутится majordomo*.
C любого планшета можно открыть вебстраничку majordomo. посмотреть как дела в комнатах и установить желаемую температуру.
Majordomo по радио управляет работой газового котла.*
Также осуществляется контроль поливом и проветриванием теплицы.*
*Ещё не реализовано.

Я понимаю, что всё это можно купить и работать оно будет лучше чем у меня, но это просто увлечение такое.
+
avatar
  • DiaSan
  • 31 марта 2017, 15:51
0
2. DHT11 — влажность
3. DS18B20 — температура
Если не нужно уличное исполнение (герметичность), то можно заменить на BME280: высокая точность, измерение трех параметров (температура, влажность, давление).
4. NRF24L01
А вот NRF24L01 я так и не смог завести на расстоянии больше 2-х метров: неустойчивая связь, потери сообщений.
Модули вроде брал нормальные, конденсаторы на питание напаивал, с параметрами номера канала, скорости передачи и мощности сигнала игрался…
Сдался и ушел на модули 433 МГц.
+
avatar
+2
это только для дома. я пытался максимально удешевить устройство т.к. 10 штук вытекают в порядочную сумму, поэтому поставил старые DHT11. Исплевался на его точность и добавил в систему ds18b20. Всё уже реализовано и менять конечно не буду. но в целом да, BME280 лучше и быстрее, что важно для автономности.

С NRF я тоже долго обнимался? подбирал оптимальный электролит. Остановился на тантале 10мкф + обычный 0,1мкф. Ещё куча геморроя была с библиотеками. RF24 в интернете несколько штук ходит и не все работают адекватно.

С 433 я начинал, не понравилась дальность и потребляемая мощность. но там просче конечно. поделки можно очень быстро собирать и недалеко они хорошо работают.
+
avatar
  • robosku
  • 31 марта 2017, 16:22
+3
NRF24L01 в Китае практически все поддельные. Насчёт 433 — советую глянуть модули RFM69, их там целое семейство на любой вкус, в т.ч. и на «любую» частоту (315/433/868/915). Дальность приличная, потребляемая мощность сравнимая с nrf.

Если хочется минимум проблем с библиотеками и нужна простая сетка сенсоров на nrf/rfm — советую глянуть MySensors. Там буквально несколькими строчками кода решается любой вопрос. Есть десятки готовых примеров кода, на пальцах объяснено куда и что подключать, есть их же сайт с готовыми железными разработками — www.openhardware.io/explore

P.S. с 5-вольтовыми версиями BME280 советую быть внимательнее, люди жалуются, что находящийся в паре миллиметров от сенсора линейный стабилизатор греется и влияет на показания сенсора.
+
avatar
0
Спасибо информацию и за ссылочки. Термометры я уже собрал все и они отлично работают на расстоянии приверно 40-50 метров. Во всяком случае внутри 2-х этажного здания все пакеты передаются без потерь. С ними я закончил.

Мне теперь нужно как то majordomo освоить т.к. ESP8266 умеет передавать всё полученное от nrf24 или в majordomo или MQTT брокеру или на narodmon.ru. на narodmon.ru передалось всё просто — графики строятся и всё ок, но это не то, что я хотел. мне нужен свой локальный сервак, строящий графики, и поддерживающий с помощью котла температуру. ну и потом чего нить ещё к нему добавлю.
+
avatar
  • robosku
  • 31 марта 2017, 21:08
0
Если собрали, то MySensors уже мало поможет, но там как раз всё это есть из коробки, включая ESP8266, MQTT и работу с MajorDoMo.

У меня что-то похоже на Raspberry Pi работает. Туда подключен гейтвей MySensors, который принимает данные от сенсоров, скрипт на перле складывает их в MQTT. Ну и несколько других скриптов отправляют из MQTT в Zabbix для рисования графиков, туда же (MQTT) подключается телеграм-бот, чтобы с телефона информацию смотреть. Котёл, кстати, тоже мониторится, но не регулируется.
+
avatar
  • udavst
  • 01 апреля 2017, 08:51
0
с библиотекой RF24 и командой radio.setDataRate(RF24_250KBPS); nrf'ки охватывают всю мою квартиру с бетонными стенами.
+
avatar
  • DiaSan
  • 01 апреля 2017, 10:34
0
Дайте, пожалуйста, ссылку на библиотеку RF24. Может, она другая, попробую с ней.
+
avatar
  • udavst
  • 01 апреля 2017, 12:11
+1
Я библиотеки скачиваю прям из arduino, но версия, что у меня стоит — тут.
+
avatar
  • sir0ta
  • 31 марта 2017, 15:33
+1
За вас все уже давно сделано, просто погуглите. Но с вашим желанием экран надо будет на i2c вешать.
+
avatar
+6
Штука хорошая, но занимает слишком много пинов на ардуине. Да и сверху ничего не повесишь. Как по мне, так лучше смотреть в сторону такого же экрана но с интерфейсом I2C.
+
avatar
+1
Само-собой.
+
avatar
  • sim31r
  • 01 апреля 2017, 16:55
0
С ними несколько сложнее работать, а тут всё просто и наглядно, каждая кнопка к отдельному пину, если нужно можно увеличить число кнопок или выпять их, простор для творчества.
+
avatar
0
Это шутка первоапрельская, да?
+
avatar
  • Rokko
  • 31 марта 2017, 14:28
0
Уважаемые гуру может подскажете? Возможно ли что нить придумать чтоб с панели автомагнитолы дублировался текст на похожий экран. Нужен выносной экранчик.
Типа такой реализации.
+
avatar
+5
[sarcasm mode on]Разобраться в коде магнитолы, сваять прошивку процессора, сделать пару прототипов. На пять минут делов-то.[sarcasm mode off]
+
avatar
  • Leseg
  • 31 марта 2017, 14:42
+6
Резистор для регулировки контрастности, подсветка дубасит «на все деньги» всегда
+
avatar
+2
Если запитать её от PWM ноги ардуины то нет.
+
avatar
  • woddy
  • 31 марта 2017, 14:52
+2
Некоторые модели шилдов понимают регулировку яркости pwm pin10
+
avatar
  • skif31
  • 31 марта 2017, 16:12
0
А куда это можно применить?
+
avatar
+1
Зависит только от твоей фантазии
+
avatar
  • ksiman
  • 01 апреля 2017, 00:06
+2
Регулировка яркости подозрительно напоминает регулировку контрастности дисплея
+
avatar
0
Да, все так, опечатался.
+
avatar
  • Faster
  • 01 апреля 2017, 08:36
0
Лично я себе сделал так сказать отладочный экран, на отдельной модуль на отдельной Меге8 и графического экрана 128 на 64 а атк же микро SD cпособный рабоатть по любому интерфейсу выводить вводить и пробрасывать ком порт в БЛЮТУЗ, и соответственно все логировать, дает приемущество при отладке
+
avatar
  • rexen
  • 01 апреля 2017, 11:12
0

— покрытие на ЖК глянцевое. Проходим мимо. :)
+
avatar
  • iswan
  • 02 апреля 2017, 22:30
0
Автор, что за хрень с видео?
+
avatar
0
Видео работает исправно, как на сайте, так и на ютубе, ограничений — нет.
+
avatar
0
а о том, что без доработки на ардуино не становится- умолчали. Нужно выводы кнопок подпиливать, что бы они не упирались в разъем питания ардуины
+
avatar
0
Я это в обзоре указал, с такой проблемой не столкнулся.
+
avatar
  • demonk
  • 05 апреля 2017, 11:18
0
В обзоре не хватает хотя бы распиновки модуля, не у всех ардуина с двумя гребенками. И куда датчики то подключать? Выручает напаивание гребенок на предусмотренные на самом LCD контакты, но балбесы-производители задействованные под сам LCD выводы подписали, а оставшиеся свободными — шиш, ищите схемы.
Документация:
И на более новую версию
А модулёк полезен, если надо по быстрому проверить какой новый датчик перед подключением в схему.
+
avatar
0
Кто-нибудь скетч загружал? У меня выскакивает ошибка:

Подскажите, кто знает, пожалуйста.
+
avatar
0
Приду домой гляну, скетч 100% рабочий.
+
avatar
0
Проверь все буквы. Возможно, одна какая-то забита в русской раскладке. Чаще всего буква «с». Перебей ее в английской раскладке.
+
avatar
  • AlexH
  • 09 июня 2017, 12:36
0
Спецом зарегалса на муське.
В аналогичном. От RobotDyn косяк в плате. Зачем-то на AFREF подают напряжение. В результате, если вы надумаете работать с температурой корпуса процессора
// ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
то получите цены на дрова.
Лечится просто. Скусить по самые… эээ-корень. Ножку AREF на этом шилде.
+
avatar
0
Короче, так и не удалось загрузить скетч. Получилось вот так: geekelectronics.org/arduino/podklyuchenie-lcd-keypad-shield-k-arduino.html
Я старался, менял библиотеки, менял версии IDE…