Wiki Info
Vuoi reagire a questo messaggio? Crea un account in pochi click o accedi per continuare.
Ricerca Avanzata
Answers
Se necessiti di aiuto, clicca il pulsante "Cerco Aiuto" (disponibile anche per i non iscritti).
Ultimi argomenti attivi
» Comunicazione termine servizio Arduino BT Control
Da Admin Mar Mag 10, 2022 12:55 pm

» Robot aspirapolvere per Alexa: Dreame D9
Da Admin Mar Feb 09, 2021 9:42 am

» Come costruire un robot smart con Arduino
Da Admin Mar Nov 17, 2020 12:15 pm

» TOPPS Trading Cards Match Attax Champsion League/Europa League 2020/21
Da Admin Mar Nov 10, 2020 3:21 pm

» Guida per utilizzare Arduino BT Control v1.2
Da Admin Gio Lug 09, 2020 12:01 pm

» Recensione Umidigi A3 Pro - Smartphone sotto i 100€
Da Admin Gio Apr 04, 2019 12:22 pm

» [Arduino] aiuto programma
Da Glak Mar Mar 12, 2019 9:30 am

» ERRORE NELLA COMPILAZIONE NEXTION
Da papat Mar Feb 26, 2019 7:11 am

» Comandare la Smart TV con Alexa senza broadlink (gratis)
Da Admin Mar Feb 19, 2019 4:31 pm

I postatori più attivi del mese
Nessun utente


Timer relè con arduino

2 partecipanti

Andare in basso

Timer relè con arduino Empty Timer relè con arduino

Messaggio Da falcio1515 Dom Ott 01, 2017 12:27 pm

Spero di non dare fastidio a nessuno. Sono nuovo nel forum. Volevo chiedere se qualcuno sarebbe disposto a fare un video in ita, o perlomeno spiegarmi dettagliatamente come si fa, il timer temporizzatore che viene descritto in questo video in inglese: Arduino Timer Relay with RTC & LCD Keypad Shield. Grazie in anticipo saluti
falcio1515
falcio1515
Sono un nuovo membro
Sono un nuovo membro

Messaggi Messaggi : 5
Crediti Crediti : 7
Reputazione Reputazione : 0
Data d'iscrizione Data d'iscrizione : 30.09.17

Torna in alto Andare in basso

Timer relè con arduino Empty Re: Timer relè con arduino

Messaggio Da Admin Dom Ott 08, 2017 4:07 pm

Ciao @falcio1515,

per realizzare il progetto puoi utilizzare il codice mostrato nel video:
Codice:
// ===============================
// ATR v5
// ©️ 2016 Triyan W. Nugroho
// ===============================

#include <LiquidCrystal.h>
#include <EEPROM.h>
#include <virtuabotixRTC.h>

// relay pins
#define relay1      0
#define relay2      1
#define relay3      2
#define relay4      3
#define relay5      10
#define relay6      11
#define relay7      12
#define relay8      13

// define pins of RTC module
#define rtcVCC      A5
#define rtcGND      A4
#define rtcSCLK    A3
#define rtcIO      A2
#define rtcCE      A1

// creation of RTC object
virtuabotixRTC myRTC(rtcSCLK, rtcIO, rtcCE);

// keypad definitions
#define btnRIGHT    0
#define btnUP      1
#define btnDOWN    2
#define btnLEFT    3
#define btnSELECT  4
#define btnNONE    5

// mode definitions
#define modeSETUP  1
#define modeNORMAL  2

int mode = 1; // set default mode

// EEPROM adrressing
#define adr1ON    2
#define adr1OF    4
#define adr2ON    6
#define adr2OF    8
#define adr3ON    10
#define adr3OF    12
#define adr4ON    14
#define adr4OF    16
#define adr5ON    18
#define adr5OF    20
#define adr6ON    22
#define adr6OF    24
#define adr7ON    26
#define adr7OF    28
#define adr8ON    30
#define adr8OF    32

int eepromMin = 0;
int eepromHour = 0;

int eepromHourON = 0;
int eepromHourOF = 0;
int eepromMinON = 0;
int eepromMinOF = 0;

// pins used on LCD Keypad Shield
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int lcd_key    = 0;
int adc_key_in  = 0;

void setup() {
   // inisialisasi serial utk debugging
   //   Serial.begin(9600);

   // jadikan semua digital pin sbg output
   for (int i = 0; i <= 13; i++) {
      pinMode(i, OUTPUT);
   }

   // activate RTC module
   pinMode(rtcVCC,  OUTPUT);
   pinMode(rtcGND,  OUTPUT);
   pinMode(rtcSCLK, OUTPUT);
   pinMode(rtcIO,  OUTPUT);
   pinMode(rtcCE,  OUTPUT);
   digitalWrite(rtcVCC, HIGH);
   digitalWrite(rtcGND, LOW);

   delay(500);

   // lcd initialization
   lcd.begin(16, 2);
}

void loop() {

   lcd.setCursor(4, 0);

   // tampilkan jam:menit:detik
   displayTime();

   // set & tampilkan relay berdasarkan data eeprom
   lcd.setCursor(0, 1);
   lcd.print("1");
   relayAction(adr1ON, adr1OF, 1, relay1);
   lcd.setCursor(2, 1);
   lcd.print("2");
   relayAction(adr2ON, adr2OF, 3, relay2);
   lcd.setCursor(4, 1);
   lcd.print("3");
   relayAction(adr3ON, adr3OF, 5, relay3);
   lcd.setCursor(6, 1);
   lcd.print("4");
   relayAction(adr4ON, adr4OF, 7, relay4);
   lcd.setCursor(8, 1);
   lcd.print("5");
   relayAction(adr5ON, adr5OF, 9, relay5);
   lcd.setCursor(10, 1);
   lcd.print("6");
   relayAction(adr6ON, adr6OF, 11, relay6);
   lcd.setCursor(12, 1);
   lcd.print("7");
   relayAction(adr7ON, adr7OF, 13, relay7);
   lcd.setCursor(14, 1);
   lcd.print("8");
   relayAction(adr8ON, adr8OF, 15, relay8);

   // jika user menekan tombol SELECT, masuk menu setting

   if (read_LCD_buttons() == btnSELECT) {
      while (read_LCD_buttons() == btnSELECT);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("UP. TIME SETTING");
      lcd.setCursor(0, 1);
      lcd.print("DN. PIN SETTING");
      while (read_LCD_buttons() == btnNONE);
      if (read_LCD_buttons() == btnUP) {
         while (read_LCD_buttons() == btnUP);
         setRTC();  // jika user menekan tombol UP, masuk menu setting RTC
      }
      else if (read_LCD_buttons() == btnDOWN) {
         while (read_LCD_buttons() == btnDOWN);
         setPin();  // jika user menekan tombol DOWN, masuk menu setting PIN
      }
   }
}

void relayAction(int adrON, int adrOF, int pos, int pin) {
   myRTC.updateTime();
   int MinToday = (myRTC.hours * 60) + myRTC.minutes;
   int MinEprON = (EEPROM.read(adrON) * 60) + EEPROM.read(adrON + 1);
   int MinEprOF = (EEPROM.read(adrOF) * 60) + EEPROM.read(adrOF + 1);

   lcd.setCursor(pos, 1);
   if (MinEprON == MinEprOF) {  // jika sama berarti tidak dipake
      lcd.print(" ");
      digitalWrite(pin, LOW);
   }
   else if (MinEprON < MinEprOF) { // kondisi ON terjadi di hari yg sama
      if ((MinEprON <= MinToday) && (MinEprOF > MinToday)) {
         lcd.print("*");
         digitalWrite(pin, LOW);
      }
      else {
         lcd.print("-");
         digitalWrite(pin, HIGH);
      }
   }
   else if (MinEprON > MinEprOF) {  // kondisi ON terjadi sampai besoknya
      if ((MinEprON >= MinToday) || (MinEprOF < MinToday)) {
         lcd.print("*");
         digitalWrite(pin, LOW);
      }
      else {
         lcd.print("-");
         digitalWrite(pin, HIGH);
      }
   }
}

/* ================================================== */
/* SETUP Functions                                    */
/* ================================================== */

void setRTC() {

   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("TIME SETTING");
   delay(1000);

   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("CURRENT ");
   setupShowValue(myRTC.hours, myRTC.minutes, 0);

   lcd.setCursor(0, 1);
   lcd.print("NEW ");

   myRTC.updateTime();
   setupShowValue(myRTC.hours, myRTC.minutes, 1);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValueSetRTC(myRTC.hours, myRTC.minutes, 1);
   lcd.setCursor(0,0);
   lcd.print("SETTINGS SAVED  ");
   delay(1000);
   lcd.clear();
}

void setPin() {
   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("PIN SETTING");
   delay(1000);

   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("PIN 1 ON");
   eepromHour = EEPROM.read(adr1ON);
   eepromMin = EEPROM.read(adr1ON + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 0);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr1ON, 0);

   lcd.setCursor(0, 1);
   lcd.print("PIN 1 OFF");
   eepromHour = EEPROM.read(adr1OF);
   eepromMin = EEPROM.read(adr1OF + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 1);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr1OF, 1);

   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("PIN 2 ON");
   eepromHour = EEPROM.read(adr2ON);
   eepromMin = EEPROM.read(adr2ON + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 0);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr2ON, 0);

   lcd.setCursor(0, 1);
   lcd.print("PIN 2 OFF");
   eepromHour = EEPROM.read(adr2OF);
   eepromMin = EEPROM.read(adr2OF + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 1);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr2OF, 1);

   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("PIN 3 ON");
   eepromHour = EEPROM.read(adr3ON);
   eepromMin = EEPROM.read(adr3ON + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 0);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr3ON, 0);

   lcd.setCursor(0, 1);
   lcd.print("PIN 3 OFF");
   eepromHour = EEPROM.read(adr3OF);
   eepromMin = EEPROM.read(adr3OF + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 1);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr3OF, 1);

   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("PIN 4 ON");
   eepromHour = EEPROM.read(adr4ON);
   eepromMin = EEPROM.read(adr4ON + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 0);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr4ON, 0);

   lcd.setCursor(0, 1);
   lcd.print("PIN 4 OFF");
   eepromHour = EEPROM.read(adr4OF);
   eepromMin = EEPROM.read(adr4OF + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 1);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr4OF, 1);

   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("PIN 5 ON");
   eepromHour = EEPROM.read(adr5ON);
   eepromMin = EEPROM.read(adr5ON + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 0);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr5ON, 0);

   lcd.setCursor(0, 1);
   lcd.print("PIN 5 OFF");
   eepromHour = EEPROM.read(adr5OF);
   eepromMin = EEPROM.read(adr5OF + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 1);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr5OF, 1);

   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("PIN 6 ON");
   eepromHour = EEPROM.read(adr6ON);
   eepromMin = EEPROM.read(adr6ON + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 0);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr6ON, 0);

   lcd.setCursor(0, 1);
   lcd.print("PIN 6 OFF");
   eepromHour = EEPROM.read(adr6OF);
   eepromMin = EEPROM.read(adr6OF + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 1);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr6OF, 1);

   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("PIN 7 ON");
   eepromHour = EEPROM.read(adr7ON);
   eepromMin = EEPROM.read(adr7ON + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 0);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr7ON, 0);

   lcd.setCursor(0, 1);
   lcd.print("PIN 7 OFF");
   eepromHour = EEPROM.read(adr7OF);
   eepromMin = EEPROM.read(adr7OF + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 1);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr7OF, 1);

   lcd.clear();
   lcd.setCursor(0, 0);
   lcd.print("PIN 8 ON");
   eepromHour = EEPROM.read(adr8ON);
   eepromMin = EEPROM.read(adr8ON + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 0);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr8ON, 0);

   lcd.setCursor(0, 1);
   lcd.print("PIN 8 OFF");
   eepromHour = EEPROM.read(adr8OF);
   eepromMin = EEPROM.read(adr8OF + 1);
   if (eepromHour >= 24) eepromHour = 0;
   if (eepromMin  >= 60) eepromMin  = 0;
   setupShowValue(eepromHour, eepromMin, 1);
   while (read_LCD_buttons() == btnNONE);
   setupChooseValue(eepromHour, eepromMin, adr8OF, 1);
   lcd.clear();
}

void setupChooseValue(int HourNew, int MinNew, byte Address, byte Pos) {
   while (read_LCD_buttons() != btnSELECT) {
      if (read_LCD_buttons() == btnRIGHT) {
         if (HourNew < 23) {
            HourNew++;
         }
      }
      else if (read_LCD_buttons() == btnLEFT) {
         if (HourNew > 0) {
            HourNew--;
         }
      }
      else if (read_LCD_buttons() == btnUP) {
         if (MinNew < 59) {
            MinNew++;
         }
      }
      else if (read_LCD_buttons() == btnDOWN) {
         if (MinNew > 0) {
            MinNew--;
         }
      }
      setupShowValue(HourNew, MinNew, Pos);
      delay(150);
   }
   while (read_LCD_buttons() != btnNONE);  // tunggu sampai tombol rilis
   EEPROM.write(Address, HourNew);
   EEPROM.write(Address + 1, MinNew);
   delay(150);
}

void setupChooseValueSetRTC(int HourNew, int MinNew, byte Pos) {
   while (read_LCD_buttons() != btnSELECT) {
      if (read_LCD_buttons() == btnRIGHT) {
         if (HourNew < 23) {
            HourNew++;
         }
      }
      else if (read_LCD_buttons() == btnLEFT) {
         if (HourNew > 0) {
            HourNew--;
         }
      }
      else if (read_LCD_buttons() == btnUP) {
         if (MinNew < 59) {
            MinNew++;
         }
      }
      else if (read_LCD_buttons() == btnDOWN) {
         if (MinNew > 0) {
            MinNew--;
         }
      }
      setupShowValue(HourNew, MinNew, Pos);
      delay(150);
   }
   while (read_LCD_buttons() != btnNONE);  // tunggu sampai tombol rilis
   myRTC.setDS1302Time(00, MinNew, HourNew, 6, 10, 1, 2014);
   delay(150);
}

void setupShowValue(int Hour, int Min, int Pos) {
   lcd.setCursor(11, Pos);
   print2digits(Hour);
   lcd.print(":");
   print2digits(Min);
}

/* ================================================== */
/* LCD Functions                                      */
/* ================================================== */

int read_LCD_buttons()
{
   adc_key_in = analogRead(0);      // read the value from the sensor

   if (adc_key_in > 1000) return btnNONE;
   if (adc_key_in < 50)  return btnRIGHT; 
   if (adc_key_in < 150)  return btnUP;
   if (adc_key_in < 300)  return btnDOWN;
   if (adc_key_in < 500)  return btnLEFT;
   if (adc_key_in < 850)  return btnSELECT; 
   return btnNONE;                // when all others fail, return this.
}

void eeprom_write_int(int p_address, int p_value) {
   byte lowByte = ((p_value >> 0) & 0xFF);
   byte highByte = ((p_value >> 8) & 0xFF);

   EEPROM.write(p_address, lowByte);
   EEPROM.write(p_address + 1, highByte);
}

unsigned int eeprom_read_int(int p_address) {
   byte lowByte = EEPROM.read(p_address);
   byte highByte = EEPROM.read(p_address + 1);

   return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}

void displayTime() {
   myRTC.updateTime();
   print2digits(myRTC.hours);
   lcd.print(":");
   print2digits(myRTC.minutes);
   lcd.print(":");
   print2digits(myRTC.seconds);
}

void print2digits(int number) {
   if (number >= 0 && number < 10)
      lcd.print('0');
   lcd.print(number, DEC);
}

Per la realizzazione hai bisogno di:


A presto Ciao!
Admin
Admin
♔ Amministratore

Messaggi Messaggi : 4370
Crediti Crediti : 12173
Reputazione Reputazione : 187
Data d'iscrizione Data d'iscrizione : 08.03.11
Età Età : 26

https://wikiinfo.forumattivo.it

Torna in alto Andare in basso

Timer relè con arduino Empty Re: Timer relè con arduino

Messaggio Da falcio1515 Dom Gen 07, 2018 2:27 pm

Grazie mille. Il progetto funziona perfettamente. Peró io non mi accontento mai. Cioé, avrei bisogno di un' intermittenza regolabile da 0,1 sec a 2 sec per far andare appunto ad intermittenza un motore dc per un progettino. In pratica penso che il principio sia quello del progetto di questa pagina: http://www.maffucci.it/2010/11/17/arduino-lezione-02-facciamo-lampeggiare-un-led/ peró a me interessa che possa comandare questa intermittenza con un timer come quello della domanda precedente o con un orologio programmatore (Ecat, quartz System,...). Come posso fare a far si che questo intermittente sia compatibile con il programmatore o il modulo rtc della domanda precedente? Questo non lo so. Grazie a chi mi potrá aiutare. Saluti.
falcio1515
falcio1515
Sono un nuovo membro
Sono un nuovo membro

Messaggi Messaggi : 5
Crediti Crediti : 7
Reputazione Reputazione : 0
Data d'iscrizione Data d'iscrizione : 30.09.17

Torna in alto Andare in basso

Timer relè con arduino Empty Re: Timer relè con arduino

Messaggio Da Admin Dom Gen 07, 2018 3:15 pm

Ciao, è sufficiente modificare i valori del codice ed adattarlo. Molto probabilmente avrai bisogno della funzione delay() o meglio ancora la funzione millis().

Quest'ultima funzione aimè non è facilissima da comprendere all'inizio ma con qualche prova in un progetto magari più semplice, potrai fare sicuramente progressi Ciao!
Admin
Admin
♔ Amministratore

Messaggi Messaggi : 4370
Crediti Crediti : 12173
Reputazione Reputazione : 187
Data d'iscrizione Data d'iscrizione : 08.03.11
Età Età : 26

https://wikiinfo.forumattivo.it

Torna in alto Andare in basso

Timer relè con arduino Empty Re: Timer relè con arduino

Messaggio Da falcio1515 Lun Gen 08, 2018 2:29 pm

Grazie mille Poderak per la puntuale risposta. Però avrei ancora una domanda: avrei bisogno di avere 2 motori intermittenti indipendenti con solo una scheda arduino e poi avrei bisogno che siano alimentati a 12 v. Come faccio? Grazie.
falcio1515
falcio1515
Sono un nuovo membro
Sono un nuovo membro

Messaggi Messaggi : 5
Crediti Crediti : 7
Reputazione Reputazione : 0
Data d'iscrizione Data d'iscrizione : 30.09.17

Torna in alto Andare in basso

Timer relè con arduino Empty Re: Timer relè con arduino

Messaggio Da falcio1515 Lun Gen 08, 2018 2:30 pm

Lascia pure perdere il collegamento con l' orologio programmatore, a quello ci penso io. Grazie ancora
falcio1515
falcio1515
Sono un nuovo membro
Sono un nuovo membro

Messaggi Messaggi : 5
Crediti Crediti : 7
Reputazione Reputazione : 0
Data d'iscrizione Data d'iscrizione : 30.09.17

Torna in alto Andare in basso

Timer relè con arduino Empty Re: Timer relè con arduino

Messaggio Da falcio1515 Lun Gen 08, 2018 2:33 pm

Quindi in pratica devo avere la possibilità di regolare la frequenza di inpulso di 2 motori alimentati a 12v.
falcio1515
falcio1515
Sono un nuovo membro
Sono un nuovo membro

Messaggi Messaggi : 5
Crediti Crediti : 7
Reputazione Reputazione : 0
Data d'iscrizione Data d'iscrizione : 30.09.17

Torna in alto Andare in basso

Timer relè con arduino Empty Re: Timer relè con arduino

Messaggio Da Contenuto sponsorizzato


Contenuto sponsorizzato


Torna in alto Andare in basso

Torna in alto

- Argomenti simili

 
Permessi in questa sezione del forum:
Non puoi rispondere agli argomenti in questo forum.