Панель оператора (HMI) с шиной I2C для Arduino
/*
Древовидное меню, работа снопками и LCD по шине i2c
LCD подключен через плату FC-113, адрес 0x27
Клавиатура подключена через расширитель портов PCF8574P, адрес 0x20
*/
#include
#include
#define led 13 //светодиод на плате Ардуно нано; будет мигать, показывая этим, что система не зависла
#define ADDR_KBRD 0x20
#define ADDR_LCD 0x27
#define PORT_D2 2
#define PORT_D3 3
#define PORT_D4 4
#define POINT_ON_ROOT_MENU_ITEM 0 // 0/1= запретить/разрешить вывод указателя позиции(* или +) на главном экране меню
byte dio_in;
bool b;
byte i;
//bool переменные, которыми можно управлять из меню
bool BoolVal[9]={0,0,0, 0,0,0, 0,0,0};
#define ValSvet1 BoolVal[0]
#define ValSvet2 BoolVal[1]
#define ValSvet3 BoolVal[2]
#define ValRozetka1 BoolVal[3]
#define ValRozetka2 BoolVal[4]
#define ValRozetka3 BoolVal[5]
#define ValClapan1 BoolVal[6]
#define ValClapan2 BoolVal[7]
#define ValClapan3 BoolVal[8]
//
struct STRUCT_KEY{
bool StateCur; //Текущее состояние кнопки
bool StateOld; //Состояние кнопки при прошлом опросе
bool Imp; //Было нажатие кнопки (переход из 0 в 1)
};
//кнопки
STRUCT_KEY Key[5]={0,0,0,
0,0,0,
0,0,0,
0,0,0,
0,0,0
};
//---
/*Текстовые строки меню
* Допустимы теги, например:
* '#A1' bool переменная, где
* '#'- тип переменной bool,
* 'A'- адрес(HEX) переменной в массиве BoolVal,
* '1'- редактирование переменной разрешено
* при выводе текста, вместо тега автоматически подставляется значение переменной
*/
String StrNull=" "; //пустая строка
String StrRoot1="COMP-MAN.INFO";
String StrRoot2="PLC-BLOG.COM.UA";
String StrSvet= "СВЕТ"; //Свет
String StrSvet1="СВЕТ 1 #01";
String StrSvet2="СВЕТ 2 #10";
String StrSvet3="СВЕТ 3 #21";
String StrRozetka="РОЗЕТКИ"; //Розетки
String StrRozetka1="РОЗЕТКА 1 #30";
String StrRozetka2="РОЗЕТКА 2 #40";
String StrRozetka3="РОЗЕТКА 3 #50";
String StrClapan="КЛАПАНЫ"; //Клапаны
String StrClapan1="КЛАПАН 1 #60"; //
String StrClapan2="КЛАПАН 2 #70";
String StrClapan3="КЛАПАН 3 #80";
struct MENU_ITEM //Пункт меню(экран), состоит из 2 строк и координат перехода при нажатии кнопок
{
byte KeyUp; //№ пункта меню, куда переходить по кнопке "вверх"
byte KeyDwn; //№ пункта меню, куда переходить по кнопке "вниз"
byte KeyCancel; //№ пункта меню, куда переходить по кнопке "отмена"(cancel)
byte KeyEnter; //№ пункта меню, куда переходить по кнопке "ввод"(enter)
byte KeyEdit; //кнопка "edit", резерв
String *pstr1; //указатель на верхнюю строку меню(экрана)
String *pstr2; //указатель на нижнюю строку меню(экрана)
};
//
MENU_ITEM Menu[]={0,0,0,1,0, &StrRoot1,&StrRoot2, //0 Главный экран
1,8,0,2,0, &StrSvet,&StrRozetka, //1 СВЕТ
2,3,1,2,0, &StrSvet1,&StrSvet2, //2
2,4,1,3,0, &StrSvet2,&StrSvet3, //3
3,4,1,4,0, &StrSvet3,&StrNull, //4
0,0,0,0,0, &StrNull,&StrNull, //5 РЕЗЕРВ
0,0,0,0,0, &StrNull,&StrNull, //6
0,0,0,0,0, &StrNull,&StrNull, //7
1,15,0,9,0, &StrRozetka,&StrClapan, //8 РОЗЕТКИ
9,10,8,9,0, &StrRozetka1, &StrRozetka2, //9
9,11,8,10,0, &StrRozetka2, &StrRozetka3, //10
10,11,8,11,0, &StrRozetka3, &StrNull, //11
0,0,0,0,0, &StrNull,&StrNull, //12 РЕЗЕРВ
0,0,0,0,0, &StrNull,&StrNull, //13
0,0,0,0,0, &StrNull,&StrNull, //14
8,15,0,16,0, &StrClapan, &StrNull, //15 КЛАПАНЫ
16,17,15,0,0, &StrClapan1,&StrClapan2, //16
16,18,15,0,0, &StrClapan2,&StrClapan3, //17
17,18,15,0,0, &StrClapan3,&StrNull, //18
0,0,0,0,0, &StrNull,&StrNull, //19 РЕЗЕРВ
0,0,0,0,0, &StrNull,&StrNull, //20
0,0,0,0,0, &StrNull,&StrNull, //21
};
byte PosMenu=0; //позиция меню
LiquidCrystal_I2C lcd(ADDR_LCD,16,2); // Устанавливаем дисплей
//Чтение состояний кнопок
void ReadKey(byte dio_in)
{
//заполняем массив кнопок значениями их состояний
byte mask=1;
for(i=0; i<5; i++)
{
Key[i].StateCur=!(dio_in & mask);
mask=mask<<1;
Key[i].Imp=!Key[i].StateOld & Key[i].StateCur; //определяем нажатие кнопки (переход из 0 в 1)
Key[i].StateOld=Key[i].StateCur;
}
}
/*
* Перекодировка UTF-8 русских букв (только заглавных) в коды LCD
* а то Ардуино выводит их неправильно
*/
byte MasRus[33][2]= {
144, 0x41, //А
145, 0xa0,
146, 0x42,
147, 0xa1,
148, 0xe0,
149, 0x45,
129, 0xa2,
150, 0xa3,
151, 0xa4,
152, 0xa5,
153, 0xa6,
154, 0x4b,
155, 0xa7,
156, 0x4d,
157, 0x48,
158, 0x4f,
159, 0xa8,
160, 0x50,
161, 0x43,
162, 0x54,
163, 0xa9,
164, 0xaa,
165, 0x58,
166, 0xe1,
167, 0xab,
168, 0xac,
169, 0xe2,
170, 0xad,
171, 0xae,
172, 0xc4,
173, 0xaf,
174, 0xb0,
175, 0xb1 //Я
};
String RusStrLCD(String StrIn)
{
String StrOut="";
byte b1;
byte y;
byte l=StrIn.length();
for(byte i=0; i dec
byte StrHexToByte(char val)
{
byte dec=0;
switch (val) {
case '0':
dec=0;
break;
case '1':
dec=1;
break;
case '2':
dec=2;
break;
case '3':
dec=3;
break;
case '4':
dec=4;
break;
case '5':
dec=5;
break;
case '6':
dec=6;
break;
case '7':
dec=7;
break;
case '8':
dec=8;
break;
case '9':
dec=9;
break;
case 'A':
dec=10;
break;
case 'B':
dec=11;
break;
case 'C':
dec=12;
break;
case 'D':
dec=13;
break;
case 'E':
dec=14;
break;
case 'F':
dec=15;
break;
default:
dec=0;
break;
}
return dec;
}
//Вывод на экран пункта меню
void WriteLCD(byte num)
{
String str[]={"*"+*Menu[num].pstr1,*Menu[num].pstr2};
if (num==0 && POINT_ON_ROOT_MENU_ITEM==0) //на главном эркане нужно выводить указатель?
str[0].setCharAt(0,' '); //стираем указатель, если нет
//Подставляем значения переменных вместо тегов
byte NumVal;
byte l;
for(byte y=0; y<2; y++)
{
l=str[y].length();
for(i=0; i-1) //так было нажатие?
{
if (KeyImp==4) //Кнопка "Edit"
Edit(PosMenu);
PosMenu=GoMenu((KeyImp));
WriteLCD(PosMenu);
}
b=!b;
digitalWrite(led, b); //Мигаем светодиодом на Ардуино
ValToPort(); //управление выходами
delay(50);
}