// Edit: missing printUserInfo() function added.
En dit is deel twee.
Je bent nu hopelijk een klein beetje bekend met structs.
Voor alle ingangen die je in de gaten houdt heb je een titel die je op een bepaalde plek (y/x) wilt tonen op de LCD en een status die je op een andere plek (y/x) wilt tonen. De definitie van de struct ziet er als volgt uit
/*
LCDDATA combines information for the display
This is a helper that is used in the LIGHTSTATUS and OTHERSTATUS structs
*/
struct LCDDATA
{
// title and title position
const char *title;
const byte titleY;
const byte titleX;
// state and state position
const char *txtActive;
const char *txtInactive;
const byte stateY;
const byte stateX;
// symbol index
const byte symbol;
};
Een typisch voorbeeld voor een initialisatie van een LCDDATA is bv
{"Trappe", 0, 0, "P", "Av", 8, 0, 8}
^ ^ ^ ^ ^ ^ ^ ^
| | | | | | | |
| | | | | | | +-- index van symbool
| | | | | | +----- x positie voor de status
| | | | | +-------- y positie voor de status
| | | | +--------------- inactieve status
| | | +---------------------- actieve status
| | +-------------------------- x positie titel
| +----------------------------- y positie titel
+----------------------------------------- titel
symbol waarde 255 (0xFF) is gebruikt als een uizondering; als de waarde 255 is zal er geen symbool worden geplaatst. We kunnen nu een functie schrijven die de titels op de juist plek op het scherm zet en een functie die de status voor iedere ingang op het scherm zet.
/*
show the title associated with a sense input on the lcd
In:
(title) text to display
*/
void showSenseTitle(const LCDDATA &text)
{
lcd.setCursor(text.titleY, text.titleX);
lcd.print(text.title);
}
Deze functie heeft een argument, een referentie (aangegeven met de &) naar een LCDDATA struct. Het zet de lcd cursor op de gespecificeerde titel plek en print vervolgens de titel.
De functie die de status print is praktisch hetzelfde maar met twee argumenten.
/*
show the state of the input on the lcd
In:
the relevant section that needs to be updated
the state of the sense pin
*/
void showSenseState(const LCDDATA &text, int state)
{
lcd.setCursor(text.stateY, text.stateX);
if (state == LOW)
{
lcd.print(text.txtInactive);
}
else
{
lcd.print(text.txtActive);
if (text.symbol != 255)
{
lcd.write((byte)text.symbol);
}
}
}
Omdat digitalRead() een int teruggeeft, is het tweede argument van het type int. Eerst wordt de cursor weer op de juiste plek gezet en vervolgens wordt, afhankelijk van de status, de tekst en eventueel het symbool geprint.
Nu definieren we ook een struct for de ingangs signalen. Om het iets simpeler te houden gebruik ik tweede veschillende structs, een voor de lichten (een (1) led als indicatie) en een voor de garage deur en de dieren in de tuin (2 of 3 leds).
De struct voor de lichten heeft een pin die in de gaten gehouden wordt (je originele inputPinX), een pin waar de indicatie led op is aangesloten (je originele outputPinX), informatie voor de LCD (in de vorm van LCD data) en een variabele waarin je de laatste status kunt opslaan (niet gebruikt maar handig om te hebben als je wilt reageren op de verandering van de status inplaats van op de status).
/*
LIGHTSTATUS defines the relationship between an input (sense) pin, the led indicator pin and the LCD text
*/
struct LIGHTSTATUS
{
// pins associated with light status
const byte pinSense; // the pin that senses the status
const byte pinIndicator; // the pin that controls the indicator LED
const LCDDATA lcdText; // text to display
byte previousStatus; // previous status of the pin
};
pinSense is de vervanger voor inputPinX, pinIndicator is de vervanger voor outputPinX.
We kunnen nu een array van LIGHTSTATUS struct definieren met all informatie die bij elkaar hoort
/*
The different lights that are monitored
*/
LIGHTSTATUS lightStatus[] =
{
{23, 22, {"Trappe", 0, 0, "P", "Av", 8, 0, 8}, LOW},
{25, 24, {"Garage", 11, 0, "P", "Av", 18, 0, 8}, LOW},
{27, 26, {"L.Hal", 0, 1, "P", "Av", 8, 1, 8}, LOW},
{29, 28, {"S.Hal", 11, 1, "P", "Av", 18, 1, 8}, LOW},
{31, 30, {"Korr", 0, 2, "P", "Av", 8, 2, 0}, LOW},
{33, 32, {"Vasker", 11, 2, "P", "Av", 18, 2, 8}, LOW},
};
{23, 22, {"Trappe", 0, 0, "P", "Av", 8, 0, 8}, LOW},
^^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^^ ^
|| | | | | | | | | | || |
|| | | | | | | | | | || +-- eind van array element
|| | | | | | | | | | |+-------- eind van de LCDDATA
|| | | | | | | | | | +--------- symbool dat gebruikt moet worden
|| | | | | | | | | +------------ x positie voor status
|| | | | | | | | +--------------- y positie voor status
|| | | | | | | +---------------------- de status text als de pin niet actief is
|| | | | | | +----------------------------- de status text als de pin actief is
|| | | | | +--------------------------------- x positie voor de titel
|| | | | +------------------------------------ y positie voor de titel
|| | +------------------------------------------------ de titel van wat in de gaten wordt gehouden
|| +------------------------------------------------------ begin van de LCDDATA
|| +------------------------------------------------------ de pin voor de indicator
|+---------------------------------------------------------- de pin voor de ingang
+----------------------------------------------------------- begin van array element
Nu kun je in setup() weer door alle elementen itereren en de pinnen ingang of uitgang maken en je kunt nu ook de teksten op de LCD setten
void setup()
{
...
...
// set up IO pins and show the lcd text
for (uint8_t cnt = 0; cnt < NUMELEMENTS(lightStatus); cnt++)
{
pinMode(lightStatus[cnt].pinSense, INPUT);
pinMode(lightStatus[cnt].pinIndicator, OUTPUT);
showSenseTitle(lightStatus[cnt].lcdText);
showSenseState(lightStatus[cnt].lcdText, LOW);
}
...
...
}
En als laatse kun je de ingangen in de gaten houden en op de lcd en leds de status laten zien in de *monitor()* functie
```c++
/*
monitor sense inputs and indicate on the lcd and leds
*/
void monitor()
{
// check the light sense inputs and take action
for (uint8_t cnt = 0; cnt < NUMELEMENTS(lightStatus); cnt++)
{
if (digitalRead(lightStatus[cnt].pinSense) == HIGH)
{
digitalWrite(lightStatus[cnt].pinIndicator, HIGH);
showSenseState(lightStatus[cnt].lcdText, HIGH);
}
else
{
digitalWrite(lightStatus[cnt].pinIndicator, LOW);
showSenseState(lightStatus[cnt].lcdText, LOW);
}
lightStatus[cnt].previousStatus = digitalRead(lightStatus[cnt].pinSense);
}
// check the other sense inputs (gagage door and animal)
// todo
}
De volledige code tot nu toe (garage deur en beestenboel zit hier nog niet in).
// macro to calculate number of elements in any array (char, int, struct etc.)
#define NUMELEMENTS(x) (sizeof(x)/sizeof(x[0]))
#include <IRremote.h>
#include <LiquidCrystal.h>
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = 7, en = 6, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
byte SMILEY[8] = {
B00100,
B00000,
B11111,
B00001,
B11111,
B10001,
B11111,
};
/*
LCDDATA combines information for the display
This is a helper that is used in the LIGHTSTATUS and OTHERSTATUS structs
*/
struct LCDDATA
{
// title and title position
const char *title;
const byte titleY;
const byte titleX;
// state and state position
const char *txtActive;
const char *txtInactive;
const byte stateY;
const byte stateX;
// symbol index
const byte symbol;
};
/*
LIGHTSTATUS defines the relationship between an input (sense) pin, the led indicator pin and the LCD text
*/
struct LIGHTSTATUS
{
// pins associated with light status
const byte pinSense; // the pin that senses the status
const byte pinIndicator; // the pin that controls the indicator LED
const LCDDATA lcdText; // text to display
byte previousStatus; // previous status of the pin
};
/*
The different lights that are monitored
*/
LIGHTSTATUS lightStatus[] =
{
{23, 22, {"Trappe", 0, 0, "P", "Av", 8, 0, 8}, LOW},
{25, 24, {"Garage", 11, 0, "P", "Av", 18, 0, 8}, LOW},
{27, 26, {"L.Hal", 0, 1, "P", "Av", 8, 1, 8}, LOW},
{29, 28, {"S.Hal", 11, 1, "P", "Av", 18, 1, 8}, LOW},
{31, 30, {"Korr", 0, 2, "P", "Av", 8, 2, 0}, LOW},
{33, 32, {"Vasker", 11, 2, "P", "Av", 18, 2, 8}, LOW},
};
struct OUTPUTCONTROL
{
const uint32_t code;
const byte pinLed;
};
OUTPUTCONTROL irLUT[] =
{
{0x00FF30CF, A1},
{0x00FF18E7, A2},
{0x00FF7A85, A3},
{0x00FF10EF, A4},
{0x00FF38C7, A5},
{0x00FF5AA5, A6},
{0x00FF42BD, A7},
{0x00FF4AB5, A8},
};
const byte RECV_PIN = A0;
const byte pinIRindicator = LED_BUILTIN; // was pin 13; don't use magic numbers
// variables will change:
IRrecv irrecv(RECV_PIN);
decode_results results;
// Dumps out the decode_results structure.
// Call this after IRrecv::decode()
// void * to work around compiler issue
//void dump(void *v) {
// decode_results *results = (decode_results *)v
void dump(decode_results *results) {
}
void setup()
{
// because we might immediately want to use serial, I've made this the first line
Serial.begin(9600);
// IR pin
pinMode(RECV_PIN, INPUT);
// IR indicator led
pinMode(pinIRindicator, OUTPUT);
// start the receiver
irrecv.enableIRIn();
// set up the LCD's number of columns and rows:
lcd.begin(20, 4);
printUserInfo();
lcd.clear();
// set all led pins that are controlled by the IR as output
for (uint8_t cnt = 0; cnt < NUMELEMENTS(irLUT); cnt++)
{
pinMode(irLUT[cnt].pinLed, OUTPUT);
}
// set up IO pins and show the lcd text
for (uint8_t cnt = 0; cnt < NUMELEMENTS(lightStatus); cnt++)
{
pinMode(lightStatus[cnt].pinSense, INPUT);
pinMode(lightStatus[cnt].pinIndicator, OUTPUT);
showSenseTitle(lightStatus[cnt].lcdText);
showSenseState(lightStatus[cnt].lcdText, LOW);
}
}
void loop()
{
controlOutputs();
monitor();
}
/*
monitor sense inputs and indicate on the lcd and leds
*/
void monitor()
{
// check the light sense inputs and take action
for (uint8_t cnt = 0; cnt < NUMELEMENTS(lightStatus); cnt++)
{
if (digitalRead(lightStatus[cnt].pinSense) == HIGH)
{
digitalWrite(lightStatus[cnt].pinIndicator, HIGH);
showSenseState(lightStatus[cnt].lcdText, HIGH);
}
else
{
digitalWrite(lightStatus[cnt].pinIndicator, LOW);
showSenseState(lightStatus[cnt].lcdText, LOW);
}
lightStatus[cnt].previousStatus = digitalRead(lightStatus[cnt].pinSense);
}
// check the other sense inputs (gagage door and animal)
// todo
}
/*
show the title associated with a sense input on the lcd
In:
(title) text to display
*/
void showSenseTitle(const LCDDATA &text)
{
lcd.setCursor(text.titleY, text.titleX);
lcd.print(text.title);
}
/*
show the state of the input on the lcd
In:
the relevant section that needs to be updated
the state of the sense pin
*/
void showSenseState(const LCDDATA &text, int state)
{
lcd.setCursor(text.stateY, text.stateX);
if (state == LOW)
{
lcd.print(text.txtInactive);
}
else
{
lcd.print(text.txtActive);
if (text.symbol != 255)
{
lcd.write((byte)text.symbol);
}
}
}
/*
control the output pins based on received IR code
*/
void controlOutputs()
{
static int on = LOW;
static uint32_t last = millis();
if (irrecv.decode(&results))
{
// If it's been at least 1/4 second since the last
// IR received, toggle the relay
if (millis() - last > 250)
{
on = !on;
digitalWrite(pinIRindicator, on ? HIGH : LOW);
dump(&results);
}
// find the IR code and pulse the led pin
for (uint8_t cnt = 0; cnt < NUMELEMENTS(irLUT); cnt++)
{
// if the received IR code matches
if (results.value == irLUT[cnt].code)
{
// pulse the pin
digitalWrite(irLUT[cnt].pinLed, HIGH);
delay(50);
digitalWrite(irLUT[cnt].pinLed, LOW);
}
}
last = millis();
irrecv.resume(); // Receive the next value
}
}
/*
print some user information
*/
void printUserInfo()
{
lcd.print("Riti & Jan Pronk");
delay(1000);
lcd.setCursor(0, 1);
lcd.print("Hedalsvegen 2832");
delay(1000);
lcd.setCursor(0, 2);
lcd.print("3528 Hedalen");
delay(1000);
lcd.setCursor(0, 3);
lcd.print("+47 46552668");
delay(1000);
}
Je globale variabelen zijn een beetje verplaatstst. Ik heb ook pin 13 een (logische) naam gegeven. Het is niet verstanding om magische nummers te gebruiken; als je ooit een andere pin wilt gebruiken moet je het op verschillende plaatsen aanpassen (en als je eer eentje vergeet krijg je mogelijk ongewenste resultaten en zit je een uur of meer te puzzelen waarom het niet werkt).
Misschien morgen, anders in het weekend, het laatste deel voor de garage deur en de beestenboel.