Inteligentný šatník: Rozdiel medzi revíziami
Zo stránky SensorWiki
Riadok 598: | Riadok 598: | ||
'''Video:''' | '''Video:''' | ||
<center><youtube>_l02MBu41n0</youtube></center> | <center><youtube>_l02MBu41n0</youtube></center> | ||
[[Category:AVR]] [[Category:MIPS]] | [[Category:AVR]] [[Category:MIPS]] |
Verzia z 22:26, 27. apríl 2024
Vypracoval: | Tomáš Reismüller |
Projekt: | Inteligentný šatník |
Študijný odbor: | Automobilová mechatronika |
Ročník: | 2. Bc. |
Zadanie
Cieľom tohto projektu bolo vytvoriť prototyp inteligentného šatníka, ktorý nám po otvorení dvierok vypíše na LCD displeji vonkajšiu teplotu aj s aktuálnou predpoveďou počasia a následne rozsvieti RGB LED diódu na farbu danú podľa teploty, napríklad: Červená = 30 °C,Modrá = -2 °C, a pod.
Literatúra:
Analýza a opis riešenia
Model inteligentného šatníku je zostrojený z troch rôznych súčiastok a to: LCD displej s radičom HD44780,Senzor otvorenia dverí MC-38A,RGB LED dióda. Tieto súčiastky sú zapojené pomocou Breadboardu do Arduina UNO R3.
Princíp fungovania je pomerne jednoduchý, keďže sa jedná len o prototyp, tak všetky funkcie sú len iba ako príklad, samotné riešenie v realite by bolo oveľa komplexnejšie. Funguje to tak, že z vnútornej strane dverí je nalepený senzor otvorenia dverí, ktorý sníma, či sú otvorené alebo zatvorené dvere. Snímanie je založené na magnete, ak je spojený, dvere sú zatvorené, ak je rozpojený, dvere sú otvorené. Následne ak otvoríme dvierka, tak sa rozpojí senzor a tým zapne LCD displej, na ktorom sa vypíše aktuálna teplota, predpoveď počasia a RGB LED dióda sa rozsvieti na farbu danú podľa teploty.
-
Senzor otvorenia dverí MC-38A.
-
RGB LED dióda.
-
LCD Displej HD44780.
Zapojenie a schéma
LCD displej sme zapojili podľa návodu z cvičenia č.11. Použili sme piny PD2,PD3,PD4 a PB1,PB2,PB3,PB4. Senzor otvorenia dverí sme zapojili do pinu PB5 a ako posledné RGB LED diódu sme zapojili do pinov PD5,PD6 a PD7. Pomohli sme si aj s Breadboardom, aby sme si uľahčili pripájanie ZEME a NAPÁJANIA 5V.
Program
Program sa skladá z hlavnej časti main.c, kde sa riadi celý systém ovládanie RGB diódy, LCD displeja a samotné fungovanie senzoru otvárania dverí. A 2 knižníc - lcd.c (Na fungovanie LCD dipleja) a uart.c (Na fungovanie sériovej komunikácie a ostatných funkcií použitých v main.c).
Ako prvé sme si zadefinovali knižnice, aby sme mohli ďalej pracovať s kódom v hlavnej časti programu. Ďalej sme už pokračovali len v hlavnej časti programu a to nasledovne: Zadefinovali sme si porty na pripojenie do Arduina. Vytvorili sme funkciu generátora náhodných čísel a následne aj časovač, ktorý umožní generátoru pomimo programu každú 1 sekundu vygenerovať 1 náhodné číslo v intervale od 1 po 6. Ako ďalšie sme si vytvorili funkciu stavy(), kde sa nachádzajú funkcie na vypisovanie na displej a riadenie RGB LED diódy. Táto funkcia sa neskôr využije v programe. Potom sme si v hlavnej funkcii main() spustili inicializáciu UART komunikácie (UART komunikáciu využívame len čisto na DEBUG na nič iné), LCD dipleja, RGB LED diódy, časovača a senzoru otvorenia dverí. A ako posledné sme do while() funkcie, ktorá prebieha neustále dokola pridali samotné riadenie celého systému inteligentného šatníka ovládané senzorom otvorenia dverí, kde sme využili aj funkciu stavy().
#define RED_PIN PD5
#define GREEN_PIN PD6
#define BLUE_PIN PD7
#define MAGNET_PIN PB5
#define LCD_CMD_DISPLAY_OFF_CURSOR_OFF 0x0C
#include <avr/io.h>
#include <avr/interrupt.h> // Pre fungovanie random generátora
#include <stdlib.h> // Pre funkcie rand() a srand()
#include <stdio.h>
#include "uart.h"
#include "lcd.h"
FILE mystdout = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE);
static volatile int randomNumber = 0;
void initRandomGenerator() { // Inicializácia generátora náhodných čísel.
srand(TCNT0);
}
int generateRandomNumber(int min, int max) { // Zadefinovanie funkcie generátora náhodých čísel.
int randomNum = rand() % (max - min + 1);
randomNum += min;
return randomNum;
}
ISR(TIMER1_OVF_vect) {
TCNT1 = 65536 - 45000; // Nastavenie timera na 1 sekundu.
randomNumber = generateRandomNumber(1, 6); // Nastavenie generátora náhodných čísel, aby generovalo len v intervale od 1 po 6.
}
void stavy() {
if (randomNumber == 1) {
lcd_command(0x80); // Nastavenie pozície kurzora na 1. riadok, 1. políčko.
lcd_puts("Teplota: 30\x01\x02\nJasno, Bez vetra");
setRGBled(255, 0, 0);
}
if (randomNumber == 2) {
lcd_command(0x80);
lcd_puts("Teplota: 25\x01\x02\nSlne\x04no, Veterno");
setRGBled(255, 165, 0);
}
if (randomNumber == 3) {
lcd_command(0x80);
lcd_puts("Teplota: 16\x01\x02\nJasno, Slne\x04no ");
setRGBled(255, 255, 0);
}
if (randomNumber == 4) {
lcd_command(0x80);
lcd_puts("Teplota: 8\x01\x02 \nPoloobla\x04no,D\x03\x05\x06");
setRGBled(102, 204, 255);
}
if (randomNumber == 5) {
lcd_command(0x80);
lcd_puts("Teplota: 2\x01\x02 \nZamra\x04\x07n\x08, D\x03\x05\x06 ");
setRGBled(153, 0, 204);
}
if (randomNumber == 6) {
lcd_command(0x80);
lcd_puts("Teplota: -5\x01\x02\nZamra\x04\x07n\x08, Sneh ");
setRGBled(0, 0, 255);
}
}
int main(void) {
printf("Spúšťam inicializáciu programu...\n");
uart_init(); // Inicializácia UARTu
initRGBled(); // Inicilizácia RGB LED diódy
stdout = &mystdout; // printf() funkcia je zapnutá
DDRB &= ~(1 << MAGNET_PIN); // Nastavenie MAGNET pinu na INPUT
PORTB |= (1 << MAGNET_PIN);
TCCR1B |= (1 << CS12) | (1 << CS10); // Prescaler nastavený na 1024
TIMSK1 |= (1 << TOIE1);
TCCR0B |= (1 << CS00);
DDRD |= (1<<LCD_EN_pin); // Pin D4 (Enable) PORTD output
DDRD |= (1<<LCD_RW_pin); // Pin D3 (RW) PORTD output
DDRD |= (1<<LCD_RS_pin); // Pin D2 (RS) PORTD output
LCD_DATA_PORT |= (1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin); // Piny 1,2,3,4, PORTB ako output (Data pre display)
lcd_init(); // Inicializácia LCD Displeja
def_spec_znaky(); // Inicializácia špeciálnych znakov na displeji
lcd_command(LCD_CMD_DISPLAY_OFF_CURSOR_OFF); // Vypnutie kurzora na displeji
printf("Inicializácia bola úspešná!\n");
sei();
while(1) {
if (PINB & (1 << MAGNET_PIN)) {
// Ak je magnet pripojený, do UARTu sa vypíše DEBUG, RGB LED dióda sa vypne a vynuluje sa LCD Displej
printf("Magnet je pripojený!\r");
setRGBled(0, 0, 0);
lcd_command(0x01);
} else {
// Ak je magnet odpojený, do UARTu sa vypíše DEBUG a spustí sa funkcia stavy().
printf("Magnet je odpojený!\r");
stavy();
}
}
return(0);
}
#include <avr/io.h>
#include <util/delay.h>
#include "uart.h"
#define RED_PIN PD5
#define GREEN_PIN PD6
#define BLUE_PIN PD7
void uart_init( void )
{
// for different BAUD rate change the project settings, or uncomment
// following two lines:
// #undef BAUD // avoid compiler warning
// #define BAUD 115200
#include <util/setbaud.h> // requires defined BAUD
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X // defined in setbaud.h
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); /* 8-bit data */
UCSR0B = _BV(RXEN0) | _BV(TXEN0); /* Enable RX and TX */
}
int uart_putc( char c, FILE *stream )
{
if (c == '\n')
uart_putc('\r',stream);
loop_until_bit_is_set(UCSR0A, UDRE0); /* Wait until data register empty. */
UDR0 = c;
return 0;
}
void uart_puts(const char *s)
{
/* toto je vasa uloha */
}
char uart_getc(void)
{
loop_until_bit_is_set(UCSR0A, RXC0); /* Wait until data exists. */
return UDR0;
}
void delay(int delay) // vlastna funkcia pre dlhsie casy
{
for (int i=1; i<=delay; i++)
_delay_ms(1);
}
// Funkcia na inicializáciu pinov
void initRGBled() {
// Nastavenie pinov ako OUTPUT
DDRD |= (1 << RED_PIN) | (1 << GREEN_PIN) | (1 << BLUE_PIN);
}
// Funkcia na nastavenie farby
void setRGBled(uint8_t r, uint8_t g, uint8_t b) {
// Nastavenie hodnôt pre červenú, zelenú a modrú LED diódu
if (r > 0) {
PORTD |= (1 << RED_PIN);
} else {
PORTD &= ~(1 << RED_PIN);
}
if (g > 0) {
PORTD |= (1 << GREEN_PIN);
} else {
PORTD &= ~(1 << GREEN_PIN);
}
if (b > 0) {
PORTD |= (1 << BLUE_PIN);
} else {
PORTD &= ~(1 << BLUE_PIN);
}
}
#define set_bit(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
#define clear_bit(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
#ifndef F_CPU
#define F_CPU 16000000UL /* Define CPU frequency here 16MHz */
#endif
#ifndef UART_H_
#define UART_H_
#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>
#define BAUD_PRESCALE (((F_CPU / (BAUDRATE * 16UL))) - 1)
void hw_init( void );
void uart_init( void );
int uart_putc( char c, FILE *stream );
void uart_puts( const char *s );
char uart_getc( void );
void initRGBled();
void setRGBled(uint8_t r, uint8_t g, uint8_t b);
int generateRandomNumber(int min, int max);
void initRandomGenerator();
void delay(int delay);
#endif /* UART_H_ */
void def_znak(unsigned char *ZnakArray,unsigned char kam) {
lcd_command(0x40|(kam << 3)); //nastavenie adresy znaku v CGRAM
for(unsigned char i = 0; i < 8; i++) lcd_data( *(ZnakArray + i));
}
/* Primitivne funkcie, nepredpoklada sa ich vyuzitie uzivatelom */
unsigned char Znak_S[8]= {0xE,0xE,0xE,0x0,0x0,0x0,0x0,0};// stupen
unsigned char Znak_SM[8]= {0x0A,0x04,0x0E,0x10,0x0E,0x01,0x1E,0};// s + mekcen
unsigned char Znak_C[8]= {0xe,0x11,0x10,0x10,0x10,0x11,0xe,0};// C
unsigned char Znak_CM[8]= {0xa,0x4,0xe,0x11,0x10,0x11,0xe,0};// c + mekcen
unsigned char Znak_DA[8]= {0x2,0x4,0xe,0x01,0x0f,0x11,0x0f,0};// Dlhe a
unsigned char Znak_Z[8]= {0xa,0x4,0x1f,0x2,0x4,0x8,0x1f,0};// z + mekcen
unsigned char Znak_D[8]= {0x5,0x5,0xc,0x14,0x14,0x14,0xc,0};// d + mekcen
unsigned char Znak_E[8]= {0x0,0x0,0xe,0x11,0x1f,0x10,0xe,0};// e
unsigned char Znak_DE[8]= {0x1,0x2,0xe,0x11,0x1f,0x10,0xe,0};// dlhe e
/* Funkcia zapise jeden bajt po SPI zbernici do zariadenia */
void def_spec_znaky(void){
// zapis vlastnych znakkov do CGRAM na poziciu 4, a 0 (8)
def_znak( Znak_S,1);// stupen
def_znak( Znak_C,2);// C
def_znak( Znak_CM,4);// c + mekcen
def_znak( Znak_DA,3);// dlhe a
def_znak( Znak_Z,5);// z + mekcen
def_znak( Znak_D,6);// d + mekcen
def_znak( Znak_E,7);// e
def_znak( Znak_DE,8);// dlhe e
// obnovenie obsahu AC. AC nastvime na 1. riadok, 1. znak
lcd_command(0x80);
}
void ini_ports(void){
/* inicializacia portov - vstupy / vystupy
oba typy displaja */
// nasledovna # riadky su spolocne pre oba typy LCD
LCD_CTRL_DDR |= (1<<LCD_EN_pin); // (Enable) output
LCD_CTRL_DDR |= (1<<LCD_RS_pin); // (RS) output
LCD_DATA_DDR |= (1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin); // Datove piny ako Output (Data pre display)
#ifndef _Shield_LCD
// klasicky LCD vyuziva aj RW pin
LCD_CTRL_DDR |= (1<<LCD_RW_pin); // (RW) output
#endif
}
void En_imp(void) {
LCD_CTRL_PORT |= (1<<LCD_EN_pin); // -> "log.1"
LCD_DELAY; // cca 400 ns
LCD_CTRL_PORT &= ~(1<<LCD_EN_pin); // -> "log.0" spolu cca 500 ns
}
void lcd_init(void)
{ // 4 bitove pripojenie display-a
ini_ports(); // inicializacia porov
_delay_ms(15);
// 1. -------------------
LCD_CTRL_PORT &= ~(1 << LCD_RS_pin); // set RS = to "log. 0" Instruction Register (IR)
#ifndef _Shield_LCD
LCD_CTRL_PORT &= ~(1 << LCD_RW_pin) ; // set R/W = to "log. 0" - Write
#endif
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 0 0 0 0 1 1 x x x x = 0x30
PORT_DATA_WR_H(0x30); // 8-bitove pripojenie
En_imp();
// 2. -------------------
// zopakujem 8-bitove pripojenie
_delay_ms(5); En_imp();
// 3. -------------------
// zopakujem 8-bitove pripojenie
_delay_ms(1); En_imp(); // - stacilo by delay 0.1ms, momentalne najkratsie nastavitelne je 1ms
// 4. -------------------
// zmenim na 4-bitove pripojenie
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 0 0 0 0 1 0 x x x x = 0x20
PORT_DATA_WR_H(0x20); // 4-bitove pripojenie
_delay_ms(1); En_imp(); // - stacilo by delay 0.04ms
// -------------------
// LCD function set mod: DL = 0 - 4-bitove data, N = 1 - 2 riadky, F = 0 - 5x7 dots
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 0 0 0 0 1 DL N F x x = 0x28
// Mozeme nasledujuci prikaz pre LCD vynechat?
_delay_ms(2);
lcd_command(0x28);
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 0 0 0 0 0 0 0 0 0 1 = 0x01 , Display clear
// Mozeme nasledujuci prikaz pre LCD vynechat?
_delay_ms(2);
lcd_command(0x01);
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 0 0 0 0 0 0 0 1 I/D S = 0x06, I/D = 1 - inkrement
// Mozeme nasledujuci prikaz pre LCD vynechat?
_delay_ms(2);
lcd_command(0x02);
// RS R/W DB7 DB6 DB5 DB4 DB3 DB2 DB1 DB0
// 0 0 0 0 0 0 1 D C B = 0x0E, D = C = 1 - Display a kurzor zapnute
// Mozeme nasledujuci prikaz pre LCD vynechat?
_delay_ms(2);
lcd_command(0x0E); // B = 0 - blikanie vypnute
}
//zapis data do Data Register
void lcd_data(unsigned char data){
while(busy_flag() & 0x80);
//while(rd_BF()); // test BF sa da realizovat pre klasicky LCD, nie Shield
LCD_CTRL_PORT |= (1<<LCD_RS_pin); // (RS = High)
#ifndef _Shield_LCD
LCD_CTRL_PORT &= ~(1<<LCD_RW_pin); // (RW = Low, write)
#endif
wr_data (data);
}
// zapis commandu do Instruction Register
void lcd_command(unsigned char command){
while(busy_flag() & 0x80);
//while(rd_BF()); // test BF sa da realizovat pre klasicky LCD, nie Shield
LCD_CTRL_PORT &= ~(1<<LCD_RS_pin); // (RS = Low)
#ifndef _Shield_LCD
LCD_CTRL_PORT &= ~(1<<LCD_RW_pin); // (RW = Low, write)
#endif
wr_data (command);
}
void wr_data(unsigned char data) {
PORT_DATA_WR_H(data); // data High nibble
En_imp();
PORT_DATA_WR_L(data); // data Low nibble
En_imp();
}
#ifdef _Shield_LCD
// namiesto testu BF "pockam".
// LCD typu Shield ma WR pripojene na GND
unsigned char busy_flag(void){
_delay_ms(2);
return(0);
}
#else
// namiesto testu BF "pockam". Vacsine prikazov tento cas vyhovuje
/*int busy_flag(void){
_delay_ms(2);
return(0);
}
*/
// klasicke pripojenie LCD umozni aj test BF
unsigned char busy_flag(void){ // rd_BF
unsigned char pom = 0;
LCD_CTRL_PORT &= ~(1<<LCD_RS_pin); // (RS = Low)
LCD_CTRL_PORT |= (1<<LCD_RW_pin); // (RW = High, read)
// port B, datove bity budu teraz input
LCD_DATA_DDR &= ~((1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin)); // Datove piny nastavime na input, (Data pre disp)
// Spravne by sa malo vycitavaj pred dobeznou hranou EN impulzu. Je tam urcity presah. Mozeme vycitat aj po dobeznej hrane.
En_imp();
pom = PORT_DATA_RD_H; // vycitam High nibble AC
En_imp();
pom |= PORT_DATA_RD_L; // vycitam Low nibble AC
// datove bity zase output
LCD_DATA_DDR |= (1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin); // Datove piny nastavime na output (Data pre disp)
//if(pom & 0x80) return 1; // display je busy
//else
return pom; // display je not busy
}
#endif
void zob_text(char *s){
register unsigned char c;
while((c = *s++)) lcd_data(c); // retazec konci "nulou"
}
/* ******************************************************** */
/* vypis retazca na poziciu, resp. podla nasledovnych */
/* formatovacich prikazov */
/* : \n - prechod na novy riadok */
/* : \f - prechod na zaciatok displeja */
/* ******************************************************** */
void lcd_puts(char *str) {
while (*str) {
if (*str == '\n') {
lcd_command(0xC0); // Presun na začiatok 2. riadka (0x80 + 0x40)
} else if (*str == '\f') {
lcd_command(0x80); // Presun na začiatok 1. riadka (displeja)
} else {
lcd_data(*str); // Vypíš znak na displej
}
str++; // Presun na ďalší znak vstupného reťazca
}
}
#ifndef _Shield_LCD
int8_t lcd_read_AC(void){ // rd_BF
char pom_AC ;
while((pom_AC = busy_flag( )) & 0x80);
// kedze po BF = 0 este cca 4us sa nezmenil obsah AC
// treba vycitat este raz
pom_AC = busy_flag( );
return pom_AC; // display not busy
}
#endif
#ifndef LCD_H_
#define LCD_H_
extern unsigned char kon_vyp;
#ifdef _Shield_LCD
#define LCD_CTRL_DDR DDRB
#define LCD_CTRL_PORT PORTB
#define LCD_DATA_DDR DDRD
#define LCD_DATA_PORT PORTD
#define LCD_RS_pin 0
#define LCD_EN_pin 1
#define LCD_D4_pin 4
#define LCD_D5_pin 5
#define LCD_D6_pin 6
#define LCD_D7_pin 7
#else
#define LCD_CTRL_DDR DDRD
#define LCD_CTRL_PORT PORTD
#define LCD_DATA_DDR DDRB
#define LCD_DATA_PORT PORTB
#define LCD_DATA_PIN PINB
#define LCD_RS_pin 2
#define LCD_RW_pin 3
#define LCD_EN_pin 4
#define LCD_D4_pin 1
#define LCD_D5_pin 2
#define LCD_D6_pin 3
#define LCD_D7_pin 4
#endif
#define NOP() asm("nop")
#define LCD_DELAY NOP();NOP();NOP();NOP();NOP();NOP();
#ifdef _Shield_LCD
#define PORT_DATA_WR_H(x) LCD_DATA_PORT &=0b00001111; LCD_DATA_PORT |= (x & 0xF0 )
#define PORT_DATA_WR_L(x) LCD_DATA_PORT &=0b00001111; LCD_DATA_PORT |= (x & 0x0F )<<4
#else
#define PORT_DATA_WR_H(x) LCD_DATA_PORT &=0b11100001; LCD_DATA_PORT |= (x & 0xF0 )>>3
#define PORT_DATA_WR_L(x) LCD_DATA_PORT &=0b11100001; LCD_DATA_PORT |= (x & 0x0F )<<1
#define PORT_DATA_RD_H ((LCD_DATA_PIN & ((1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin)))<<3)
#define PORT_DATA_RD_L ((LCD_DATA_PIN & ((1<<LCD_D4_pin)|(1<<LCD_D5_pin)|(1<<LCD_D6_pin)|(1<<LCD_D7_pin)))>>1)
#endif
#ifndef _Shield_LCD
int8_t lcd_read_AC(void);
void def_spec_znaky_AC(void);
#endif
void lcd_init(void);
void lcd_data(unsigned char );
void lcd_command(unsigned char );
void ini_ports(void);
void En_imp(void);
void wr_data (unsigned char );
unsigned char busy_flag(void);
void zob_text(char *);
void def_Clear_spec_znaky(void);
void def_znak(unsigned char *,unsigned char );
void def_spec_znaky(void);
#endif /* LCD_H_ */
Zdrojový kód: reismuller_semestralna_praca.zip
Overenie funkčnosti
Na používanie našej aplikácie stačia dve tlačítka a postup používania je opísaný v sekcii popis riešenia. Na konci uvádzame fotku záverečnej obrazovky pred resetom. Vypísaný je tu priemerný čas a najlepší čas.
Video: