blackdog
Golden Member
Daar de mens het noodzakelijke niet kan volbrengen, streeft hij naar het overbodige (Goethe)
Hi,
Voor mijn oven projectje heb ik een simpel menutje nodig op het 16x2 LCD schermpje.
Ik dacht, laat ik een van een AI gebruik maken en wel een die voor Arduino online is gelaatst.
Dit is het verzoek dat ik heb laten uitvoeren:
Arduino code, 4 menu page, 1 button on pin-2, 4 relais output on pin 2,3,4,5, i2c LCD 16x2 display
Daar kwam het volgende uit:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Define the pins for the relays
#define RELAY1 3
#define RELAY2 4
#define RELAY3 5
#define RELAY4 6
// Button pin
#define BUTTON_PIN 2
// Create an instance of the LCD
LiquidCrystal_I2C lcd(x27, 16, 2); // Change x27 to your LCD's I2C address if necessary
// Variables for menu control
int currentMenu = ;
const int totalMenus = 4;
bool lastButtonState = HIGH;
unsigned long lastDebounceTime = ;
unsigned long debounceDelay = 50;
void setup() {
// Initialize the LCD
lcd.begin();
lcd.backlight();
// Set relay pins as OUTPUT
pinMode(RELAY1, OUTPUT);
pinMode(RELAY2, OUTPUT);
pinMode(RELAY3, OUTPUT);
pinMode(RELAY4, OUTPUT);
// Set button pin as INPUT
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Initialize relays to OFF
digitalWrite(RELAY1, LOW);
digitalWrite(RELAY2, LOW);
digitalWrite(RELAY3, LOW);
digitalWrite(RELAY4, LOW);
displayMenu();
}
void loop() {
// Read the button state
bool buttonState = digitalRead(BUTTON_PIN);
// Check if the button state has changed
if (buttonState != lastButtonState) {
lastDebounceTime = millis();
}
// If the button state has been stable for longer than the debounce delay
if ((millis() - lastDebounceTime) > debounceDelay) {
// If the button is pressed (LOW)
if (buttonState == LOW) {
currentMenu = (currentMenu + 1) % totalMenus; // Go to the next menu
displayMenu();
activateRelay();
}
}
lastButtonState = buttonState;
}
void displayMenu() {
lcd.clear();
switch (currentMenu) {
case :
lcd.print("Menu 1");
break;
case 1:
lcd.print("Menu 2");
break;
case 2:
lcd.print("Menu 3");
break;
case 3:
lcd.print("Menu 4");
break;
}
}
void activateRelay() {
// Turn off all relays first
digitalWrite(RELAY1, LOW);
digitalWrite(RELAY2, LOW);
digitalWrite(RELAY3, LOW);
digitalWrite(RELAY4, LOW);
// Activate the corresponding relay based on the current menu
switch (currentMenu) {
case :
digitalWrite(RELAY1, HIGH);
break;
case 1:
digitalWrite(RELAY2, HIGH);
break;
case 2:
digitalWrite(RELAY3, HIGH);
break;
case 3:
digitalWrite(RELAY4, HIGH);
break;
}
}
Er zitten wat fouten in en de aansturing van het LCD display wat ik gebruik kan niet met de code voor het LCD schermpje.
Een paar foutjes heb ik er uit gekregen, zoals case 0 geen waarde had.
Ik heb de code zo goed als ik kon werkend gemaakt en heb op de "relais" uitgangen voor iedere uitgang een LED en een weerstadn geplaatst.
De LED komt overeen met de menu tekst voor menu 1 t/m 4.
Wat echter niet werkt is de "Debounce Code" ik heb twee schakelaars geprobeert, ook een type waarvan ik weet dat hij netjes schakeld, ook een condensator geplaatst, maar ik krijg het niet werken.
Ook niet door het aanpassen van de debounce tijd.
Dit is de code waarbij het menu werkt, maar het debouncen niet.
#include <Wire.h>
#include "DFRobot_RGBLCD1602.h"
// #include <LiquidCrystal_I2C.h>
// Define the pins for the relays
#define RELAY1 3
#define RELAY2 4
#define RELAY3 5
#define RELAY4 6
// Button pin
#define BUTTON_PIN 2
// Create an instance of the LCD
DFRobot_RGBLCD1602 lcd(/*RGBAddr*/0x60 ,/*lcdCols*/16,/*lcdRows*/2); //16 characters and 2 lines of show
// LiquidCrystal_I2C lcd(16, 2); // Change x27 to your LCD's I2C address if necessary
// Variables for menu control
int currentMenu = 1;
const int totalMenus = 4;
bool lastButtonState = HIGH;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 30;
void setup() {
// Initialize the LCD
// lcd.begin();
// lcd.backlight();
// Setup LCD Scherm
lcd.init();
lcd.setCursor(0, 0);
lcd.setRGB(100, 0, 70);
// Set relay pins as OUTPUT
pinMode(RELAY1, OUTPUT);
pinMode(RELAY2, OUTPUT);
pinMode(RELAY3, OUTPUT);
pinMode(RELAY4, OUTPUT);
// Set button pin as INPUT
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Initialize relays to OFF
digitalWrite(RELAY1, LOW);
digitalWrite(RELAY2, LOW);
digitalWrite(RELAY3, LOW);
digitalWrite(RELAY4, LOW);
displayMenu();
}
void loop() {
// Read the button state
bool buttonState = digitalRead(BUTTON_PIN);
// Check if the button state has changed
if (buttonState != lastButtonState) {
lastDebounceTime = millis();
}
// If the button state has been stable for longer than the debounce delay
if ((millis() - lastDebounceTime) > debounceDelay) {
// If the button is pressed (LOW)
if (buttonState == LOW) {
currentMenu = (currentMenu + 1) % totalMenus; // Go to the next menu
displayMenu();
activateRelay();
}
}
lastButtonState = buttonState;
}
void displayMenu() {
lcd.clear();
switch (currentMenu) {
case 0:
lcd.print("Menu 1");
break;
case 1:
lcd.print("Menu 2");
break;
case 2:
lcd.print("Menu 3");
break;
case 3:
lcd.print("Menu 4");
break;
}
}
void activateRelay() {
// Turn off all relays first
digitalWrite(RELAY1, LOW);
digitalWrite(RELAY2, LOW);
digitalWrite(RELAY3, LOW);
digitalWrite(RELAY4, LOW);
// Activate the corresponding relay based on the current menu
switch (currentMenu) {
case 0:
digitalWrite(RELAY1, HIGH);
break;
case 1:
digitalWrite(RELAY2, HIGH);
break;
case 2:
digitalWrite(RELAY3, HIGH);
break;
case 3:
digitalWrite(RELAY4, HIGH);
break;
}
}
Het zal wel simpel zijn, maar ik zie niet hoe ik dit op kan lossen...
Dank vast en groet.
Bram
Zo te zien loopt hij direct door naar het einde van het menu, zodra de debounce is afgelopen. Er ontbreekt een test op button los.
Dat kun je op veel manieren oplossen.
Bijvoorbeeld zo:
void loop()
{
// Read the button state
bool buttonState = digitalRead(BUTTON_PIN);
if (buttonState == LOW)
{ switch(lastButtonState)
{ case 0:
{ lastDebounceTime = millis();
lastButtonState = 1;
break;
}
case 1:
{ if ((millis() - lastDebounceTime) > debounceDelay)
{ currentMenu = (currentMenu + 1) % totalMenus; // Go to the next menu
displayMenu();
activateRelay();
lastButtonState = 2;
}
break;
}
case 2:
{ break;
}
}
}
else
{ lastButtonState = 0;
}
}
[Bericht gewijzigd door deKees op (75%)]
blackdog
Golden Member
Daar de mens het noodzakelijke niet kan volbrengen, streeft hij naar het overbodige (Goethe)
Hi deKees,
Ik heb er al weer wat foutjes en aanpassingen bij.
Mijn menu begint nu netjes bij het eerste menu en ook de relais(LED) uitgangen staan nu goed bij het opstarten.
Ik heb de code voor de AI een beetje aangepast en bij die code die ik niet gebruik stond onder in de loop "delay(200);"
Met deze delay in de code werkt de menuknop normaal, maar 200mSec delay is natuurlijk niet normaal. 
Ik was al aan het zoeken hoe het goed opgelost kan worden, ik heb een simpele library die veel van drukknopjes uit handen neemt.
Alleen zal ik moeten testen of het in deze code past.
Groet,
Bram
blackdog
Golden Member
Daar de mens het noodzakelijke niet kan volbrengen, streeft hij naar het overbodige (Goethe)
blackdog
Golden Member
Daar de mens het noodzakelijke niet kan volbrengen, streeft hij naar het overbodige (Goethe)
Roches
Domoticz en ESP8266, goede combo!!!
Je zult een int van je LastButtonState moeten maken, een boolean kan niet gebruikt worden voor switch.
Dat zegt de foutmelding ook.
Arco
Special Member
Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
blackdog
Golden Member
Daar de mens het noodzakelijke niet kan volbrengen, streeft hij naar het overbodige (Goethe)
Hi,
De delay is er nu uit en dit is nu aangepast: int lastButtonState = HIGH;
En dat werkt nu zonder problemen.
Verder de kleuren van het display aangepast aan het men nummer, ieder menu een eigen kleur.
Dit alleen om te zien of het werkt, de kleur gaat de status aangeven, groen is b.v. op temperatuur en geen fouten,
Rood is dan een fout situatie en/of de oven nog niet op temperatuur, Blauw al de oven aan het opwarmen is en b.v oranje als de oven binnen 2C van de temperatuur is aangekomen.
Ik moet nog een opzetje maken hoe ik het een en ander ga uitvoeren zonder dat het een kermis attractie wordt. 
Dank in ieder geval, ik kan weer even verder met functies toevoegen.
Groet,
Bram
blackdog
Golden Member
Daar de mens het noodzakelijke niet kan volbrengen, streeft hij naar het overbodige (Goethe)
Hi,
Ik begrijp een deel nog niet van de code die deKees hier plaatste voor het debouncen en het doorlopen van de menu pagina's.
Het gaat om deze regel:
currentMenu = (currentMenu + 1) % totalMenus; // Go to the next menu
Het is mij duidelijk dat als de debounce achter de rug is, dat de conter voor het menu met 1 warde omhoog gaat of weer naar het begin.
Maar wat doet dit nu => % totalMenus
In een van mijn Arduino C boeken wordt er gesproken over een Modulo operator %
En er is wat uitleg over een deelfactor en wat de restwaarde is.
Dit kan ik niet rijmen met wat ik denk dat deze regel doet en ik denk het volgende menu item 1 t/m 4 doorlopen en dan weer na 4 doorspringen naar menu nummer 1.
Kunnen jullie wat uitleg hierover geven?
Dank en groet,
Bram
Modulo funktie is inderdaad : integer deling en alleen de rest overhouden.
Dus 'modulo 4' zorgt ervoor dat het resultaat altijd in de range 0 - 3 valt.
blackdog
Golden Member
Daar de mens het noodzakelijke niet kan volbrengen, streeft hij naar het overbodige (Goethe)
Hi deKees,
Als het currentMenu nu 4 is en totalMenus is ook 4, wat is de actie nu dat bij de volgende drukknop actie currentMenu weer 1 wordt.
switch en case
Nog iets wat ik niet zag/begreep is, switch en de case stapjes er onder.
Omdat ik op meerdere plekken "case" zag staan, dacht ik dat de case in het uitlezen van de drukknop code,
het zelfde was als die van de relais en de display functie.
De case hoort bij de switch functie, het is dus steeds een andere case, case display is niet dezelfde als relais of drukknop.
Dat is nu ook weer duidelijk, weer wat geleerd.
Als het currentMenu nu 4 is en totalMenus is ook 4, wat is de actie nu dat bij de volgende drukknop actie currentMenu weer 1 wordt.
Het currentMenu wordt nooit 4. Die heeft wel 4 standen maar loopt van 0 t/m 3.
3 + 1 = 4
4 / 4 = 1, rest 0
Dus ((3 + 1) modulo 4) = 0
En switch wordt gebruikt om een enkele variabele te testen op meerdere mogelijkheden. Dus na de switch staat de variabele die getest wordt, en dan een aparte case voor elke waarde die je wilt testen. En dan vooral niet de break vergeten aan het eind van de case-blok, anders loopt de code gewoon door in de volgende case. Soms is dat wel handig, vaak ook niet.
blackdog
Golden Member
Daar de mens het noodzakelijke niet kan volbrengen, streeft hij naar het overbodige (Goethe)
Hi,
Nog een probleempje, voor het tunen van de P.I.D. regelaar wil ik de drie waarden op een apart schermpje hebben en ik ga ze aanpassen met drie encoders.
Dit zodat ik zonder steeds de code in de software moet uploaden kan uitvinden wat goed is voor het gebruikte oventje.
Daarna kunnen de drie waarden vast in de P.I.D. software worden opgenomen.
Waar loop ik nu tegen aan.
Ik heb van DFRobot een 2x16 LCD display met een drie kleuren achtergrond verlichting, RGB LED dus.
Ik wil met een extra i2C 2x16 LCD scherm de waarden van de P.I.D. in beeld brengen.
De twee schermen hebben ieder hun eigen driver, dus daar zit denk ik geen conflict.
Maar beide schermen maken gebruik van lcd.clear, lcd.setCursor, lcd.print enz.
Hoe kan ik die twee displays uit elkaar houden wat opdrachten betreft.
Hieronder een stukje proefcode dat ik gebruikte om het extra i2c LCD scherm werkte.
/* Demonstration sketch for PCF8574T I2C LCD Backpack
Uses library from https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads GNU General Public License, version 3 (GPL-3.0) */
#include <Wire.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3f,2,1,0,4,5,6,7); // 0x27 is the I2C bus address for an unmodified backpack
void setup()
{
// activate LCD module
lcd.begin (16,2); // for 16 x 2 LCD module
lcd.setBacklightPin(3,POSITIVE);
lcd.setBacklight(HIGH);
}
void loop()
{
lcd.home (); // set cursor to 0,0
lcd.print("T1=23,6 T2=45,7");
lcd.setCursor (3,1); // go to start of 2nd line
lcd.print("Ook Leuk!");
delay(1000);
lcd.setBacklight(LOW); // Backlight off
delay(250);
lcd.setBacklight(HIGH); // Backlight on
delay(1000);
}
Hoe pas ik b.v. van de code die hierboven staat zo aan, zodat deze lcd.xxx van het hoofdscherm niet in de weg zit.
Dank en groet,
Bram
blackdog
Golden Member
Daar de mens het noodzakelijke niet kan volbrengen, streeft hij naar het overbodige (Goethe)
Hi,
Deze keer snel wat gevonden, door dit te doen LiquidCrystal_I2C "lcd2"
En verder alle data voor het tweede scherm dus lcd2 te gebruiken.
Dat ga ik er nu even inplakken en kijen of het echt werkt.
Groet,
Bram
blackdog
Golden Member
Daar de mens het noodzakelijke niet kan volbrengen, streeft hij naar het overbodige (Goethe)
Hi,
Even laten zien wat devolgende stap is in het over project.
Dat is een stukje code om de kleur van mijn DFRobot display te bepalen.
Ik wil dus kunnen opgeven dat de achtergrond kleur b.v. Rood is, dat is 255, 000, 000.
Dus ik ga een lijstje maken van een aantal kleuren die ik ga gebruiken met de daarbij behorende RGB waarden.
Dat is voor het aantal kleuren dat ik ga gebruiken eigenlijk onzin, maar je leert pas een beetje programmeren als je het ook toepast.
Dus het onderstaande heb ik in elkaar gefrut om de RGB waarde op het DFRobot display te kunnen aflezen...
De debug code van deKees om de waarde van de drie encoders in de serial monitor af te lezen staat nog steeds in in de code voor deze test is dat niet van belang
Ik gebruik een test bestand dat is meegelevert met het drie voudig i2c encoder printje, hartstikke handig trouwens dat printje.
Heb alleen nog wat goede encoders nodig, de gene die ik hier op voorraad had, was de pinning anders dan het printje gebruikte.
Vandaar de draden aan de encoders.
#include <I2C_Rotary_003.h>
#include "DFRobot_RGBLCD1602.h"
// Create an instance of the DFRobot LCD
DFRobot_RGBLCD1602 lcd(/*RGBAddr*/0x60 ,/*lcdCols*/16,/*lcdRows*/2); //16 characters and 2 lines of show
// Declare 3 rotary objects.
// - All use the same I2C address
// - But different switch nrs
I2C_Rotary Rotary_SW1(0xA0, 1); // SW1
I2C_Rotary Rotary_SW2(0xA0, 2); // SW2
I2C_Rotary Rotary_SW3(0xA0, 3); // SW3
void setup()
{
Wire.begin();
Serial.begin(38400);
// Setup DFRobot LCD
lcd.init();
lcd.setRGB(255, 255, 255);
Serial.println();
Serial.println( F("Turn a rotary an see what happens") );
Serial.println();
// Set parameters for Rotary_SW1
Rotary_SW1.m_MinValue = 0;
Rotary_SW1.m_MaxValue = 255;
Rotary_SW1.m_StepSize = 1;
Rotary_SW1.m_Value = 0;
Rotary_SW1.SetDirection(1);
Rotary_SW1.SetAccelleration(1);
// Set parameters for Rotary_SW2
Rotary_SW2.m_MinValue = 0;
Rotary_SW2.m_MaxValue = 255;
Rotary_SW2.m_StepSize = 1;
Rotary_SW2.m_Value = 0;
Rotary_SW2.SetDirection(1);
Rotary_SW2.SetAccelleration(1);
// Set parameters for Rotary_SW3
Rotary_SW3.m_MinValue = 0;
Rotary_SW3.m_MaxValue = 255;
Rotary_SW3.m_StepSize = 1;
Rotary_SW3.m_Value = 0;
Rotary_SW3.SetDirection(1);
Rotary_SW3.SetAccelleration(1);
}
void loop()
{
delay(100);
// Scan status of the rotary buttons:
byte Button1 = Rotary_SW1.Button();
byte Button2 = Rotary_SW2.Button();
byte Button3 = Rotary_SW3.Button();
// Scan status of the rotary clicks:
int Clicks1 = Rotary_SW1.Clicks();
int Clicks2 = Rotary_SW2.Clicks();
int Clicks3 = Rotary_SW3.Clicks();
lcd.clear();
// lcd.setRGB(255, 255, 255);
lcd.setCursor(2, 0);
lcd.print("NoiseAmp ASD");
lcd.setCursor(0, 1);
lcd.print("R");
lcd.print(Rotary_SW1.m_Value, 0);
lcd.setCursor(5, 1);
lcd.print("G");
lcd.print(Rotary_SW2.m_Value, 0);
lcd.setCursor(10, 1);
lcd.print("B");
lcd.print(Rotary_SW3.m_Value, 0);
lcd.setRGB(Rotary_SW1.m_Value, Rotary_SW2.m_Value, Rotary_SW3.m_Value);
// Just write the result to the serial port
if(Button1)
{ Serial.print( F("SW1 : Button = ") );
Serial.println( Button1 );
Serial.println();
}
if(Button2)
{ Serial.print( F("SW2 : Button = ") );
Serial.println( Button2 );
Serial.println();
}
if(Button3)
{ Serial.print( F("SW3 : Button = ") );
Serial.println( Button3 );
Serial.println();
}
if(Clicks1)
{ Serial.print( F("SW1 : Clicks = ") );
Serial.println( Clicks1 );
Serial.print( F("SW1 : Value = ") );
Serial.println( Rotary_SW1.m_Value);
Serial.println();
}
if(Clicks2)
{ Serial.print( F("SW2 : Clicks = ") );
Serial.println( Clicks2 );
Serial.print( F("SW2 : Value = ") );
Serial.println( Rotary_SW2.m_Value);
Serial.println();
}
if(Clicks3)
{ Serial.print( F("SW3 : Clicks = ") );
Serial.println( Clicks3 );
Serial.print( F("SW3 : Value = ") );
Serial.println( Rotary_SW3.m_Value);
Serial.println();
}
}
Hieronder is de contraptie te zien de ik heb samen gesteld uit de volgende onderdelen:
Arduino Nano op een aansluitbordje.
De 3x rotary Encoder print van deKees met bedrade encoders omdat de pinning dus anders is van deencoders die ik beschikbaar had.
En het DFRobot 2x16 LCD met RGB achtergrond verlichting.
Ik heb een beetje gespeeld met de camera om het display leesbaar te maken en de rest nog redelijk belicht is, hier onder een paar kleuren.
Oja, de encoder met de knop is een test om de dynamiek van de versnelling van de encoders te testen, dat wordt bepaald doordeze regel: Rotary_SW3.SetAccelleration(1)

.
De waarde voor oranje/rood
.
Hard rose?
.
En deze waarde komt in de buurt van wit, blauw is wat dominant in dit display.
Ik moest hiervoor wat meer ruimte verlichting aanzetten anders was de witbalans niet goed genoeg van mijn telefoon.
.
Nu kan ik weer verder met de volgende stukjes code.
Groet,
Bram

