===== Эксперимент 54. Управление кладовщиком =====
Научим кладовщика двигаться при нажатии на кнопки "вверх", "вниз", "влево" и "вправо".
Подключим кнопки к микроконтроллеру. Три кнопки подтянуты к земле, а одна к питанию. Это сделано специально, чтобы обеспечить условия для правильной загрузки микроконтроллера. Микроконтроллер при включении проверяет состояние некоторых выводов и на основании этого принимает решение о режиме работы — выполнение программы или переход в режим перепрошивки. Для нормального режима работы необходимо, чтобы уровень сигнала на выводе 15 был низким, а на 2 и 0 был высоким. Чтобы соблюсти эти условия и пришлось подключить кнопки по-разному.
==== Схема эксперимента ====
{{ :products:esp-iot:exp27_mont.png?direct&600 |}}
//Рисунок 1. Монтажная схема эксперимента с 8 выводами//
{{ :products:esp-iot:exp27_mont_11pin.png?direct&600 |}}
//Рисунок 2. Монтажная схема эксперимента с 11 выводами//
==== Программный код эксперимента ====
#include
#include
#include "LittleFS_ImageReader.h"
#define PIN_CS 2
#define PIN_DC 4
#define PIN_RST 5
Adafruit_ST7735 tft = Adafruit_ST7735(PIN_CS, PIN_DC, PIN_RST);
LittleFS_ImageReader reader;
bool Map[10][8] = {
{1,1,0,1,1,1,0,1},
{0,1,1,1,1,1,1,0},
{1,1,0,0,0,1,1,1},
{0,1,0,1,0,1,0,1},
{0,1,0,0,0,1,0,1},
{1,1,1,1,0,0,0,1},
{1,0,0,0,0,0,0,1},
{1,0,0,0,1,0,0,1},
{1,0,0,0,1,1,1,1},
{1,1,1,1,1,0,0,0}
};
struct Pos {
int x = 0;
int y = 0;
bool operator == (const Pos &pos) const {
return x == pos.x && y == pos.y;
}
};
class Box {
private:
Adafruit_ST7735 *tft_ptr;
LittleFS_ImageReader *reader_ptr;
Pos pos;
String picture = "/box.bmp";
String picture_on_gate = "/boxngate.bmp";
bool on_gate = false;
public:
Box(Adafruit_ST7735 *_tft_ptr, LittleFS_ImageReader *_reader_ptr, int x, int y) {
tft_ptr = _tft_ptr;
reader_ptr = _reader_ptr;
pos.x = x;
pos.y = y;
}
void draw() {
if (on_gate) reader_ptr->drawBMP(picture_on_gate, *tft_ptr, pos.x * 16, pos.y * 16);
else reader_ptr->drawBMP(picture, *tft_ptr, pos.x * 16, pos.y * 16);
}
void setOnGate(bool state) {
on_gate = state;
}
bool getOnGate() const {
return on_gate;
}
Pos getPos() const {
return pos;
}
void setPos(Pos _pos) {
pos.x = _pos.x;
pos.y = _pos.y;
draw();
}
};
class Gate {
private:
Adafruit_ST7735 *tft_ptr;
LittleFS_ImageReader *reader_ptr;
Pos pos;
String picture = "/gate.bmp";
public:
Gate(Adafruit_ST7735 *_tft_ptr, LittleFS_ImageReader *_reader_ptr, int x, int y) {
tft_ptr = _tft_ptr;
reader_ptr = _reader_ptr;
pos.x = x;
pos.y = y;
}
void draw() const {
reader_ptr->drawBMP(picture, *tft_ptr, pos.x * 16, pos.y * 16);
}
Pos getPos() const {
return pos;
}
};
class Man {
private:
Adafruit_ST7735 *tft_ptr;
LittleFS_ImageReader *reader_ptr;
Pos pos;
String picture = "/man.bmp";
public:
Man(Adafruit_ST7735 *_tft_ptr, LittleFS_ImageReader *_reader_ptr, int x, int y) {
Serial.println("Man constructor");
tft_ptr = _tft_ptr;
reader_ptr = _reader_ptr;
pos.x = x;
pos.y = y;
}
void draw() const {
reader_ptr->drawBMP(picture, *tft_ptr, pos.x * 16, pos.y * 16);
}
Pos getPos() const {
return pos;
}
void setPos(Pos _pos) {
tft_ptr->fillRect(pos.x * 16, pos.y * 16, 16, 16, ST77XX_BLACK);
pos.x = _pos.x;
pos.y = _pos.y;
draw();
}
};
class Button {
private:
int pin;
bool pressState;
bool oldState;
public:
Button(int _pin, bool _pressState) {
pin = _pin;
setPinMode();
pressState = _pressState;
oldState = not _pressState;
}
bool onPress() {
bool state = digitalRead(pin);
if (state != oldState){
oldState = state;
if (state == pressState) return true;
}
return false;
}
void setPinMode() const {
pinMode(pin, INPUT);
}
};
const int boxes_number = 3;
Box boxes[boxes_number] = {
{&tft, &reader, 3, 4},
{&tft, &reader, 4, 6},
{&tft, &reader, 2, 7},
};
const int gates_number = 3;
Gate gates[gates_number] = {
{&tft, &reader, 6, 3},
{&tft, &reader, 6, 4},
{&tft, &reader, 6, 5},
};
Man man(&tft, &reader, 5, 6);
Button btn_up(16, HIGH);
Button btn_down(15, HIGH);
Button btn_left(12, HIGH);
Button btn_right(0, LOW);
void setup() {
Serial.begin(9600);
Serial.println();
Serial.println("Setup");
LittleFS.begin();
tft.initR(INITR_BLACKTAB);
tft.setRotation(2);
tft.fillScreen(ST77XX_BLACK);
for (int y = 0; y < 10; y++) {
for (int x = 0; x < 10; x++) {
if (Map[y][x]) reader.drawBMP("/brick.bmp", tft, x * 16, y * 16);
}
}
for (int i = 0; i < boxes_number; i++) {
boxes[i].draw();
}
for (int i = 0; i < gates_number; i++) {
gates[i].draw();
}
man.draw();
btn_left.setPinMode();
}
void loop() {
Pos man_pos = man.getPos();
if (btn_up.onPress()) man.setPos({man_pos.x, man_pos.y - 1});
if (btn_down.onPress()) man.setPos({man_pos.x, man_pos.y + 1});
if (btn_left.onPress()) man.setPos({man_pos.x - 1, man_pos.y});
if (btn_right.onPress()) man.setPos({man_pos.x + 1, man_pos.y});
}
В код прошлого эксперимента мы добавили класс кнопки, который уже использовали ранее, например в проекте секундомера. Объявили 4 кнопки, на выводах 16, 15, 12 и 0:
Button btn_up(16, HIGH);
Button btn_down(15, HIGH);
Button btn_left(12, HIGH);
Button btn_right(0, LOW);
В класс Button мы добавили метод ''setPinMode()'' и вызываем его для кнопки на 12 пине, хотя он уже вызывался в конструкторе. Это необходимо, так как метод дисплея ''initR()'' вызывает переопределение режима пина 12.
btn_left.setPinMode();
В бесконечном цикле постоянно мониторим события нажатия на кнопки:
Pos man_pos = man.getPos();
if (btn_up.onPress()) man.setPos({man_pos.x, man_pos.y - 1});
if (btn_down.onPress()) man.setPos({man_pos.x, man_pos.y + 1});
if (btn_left.onPress()) man.setPos({man_pos.x - 1, man_pos.y});
if (btn_right.onPress()) man.setPos({man_pos.x + 1, man_pos.y});
При нажатии на кнопку "вверх" уменьшаем координату Y и задаем новую координату кладовщику. Аналогичные действия производим при нажатии на другие кнопки. Теперь наш кладовщик может двигаться по экрану. Но сейчас нет никаких ограничений — он свободно проходит сквозь стены и ящики, никак на них не влияя. Теперь самое время реализовать логику игры, описать условия и взаимодействия. Этим займемся в следующем эксперименте.