Code RGLU.ino, v0.01
//RaumGerät Light UDP v0.01
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <LiquidCrystal_I2C.h>
//IP adress and port of RG
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192, 168, 0, 42);
unsigned int port = 28001;
//IP adress and UDP port of BSB-LAN adapter
IPAddress RxIP(192, 168, 0, 88);
unsigned int RxUDPport = 28000;
const int udp_buff_size = 16;
char RxBuffer[udp_buff_size];
unsigned long t_act, t_old;
int maxtry = 5; //maximum tries for connection etc. with BSB-LAN
struct pv_Rx {
int par;
int val;
};
struct pv_RG {
double OT; //outside temperature
double RT; //room temperature
double WT; //water temperature
double RT_t; //room temperature, target
double RT_c; //room temperature, comfort
double RT_r; //room temperature, reduced
boolean presence_state;
boolean presence_error;
boolean water_state;
boolean burner_state;
};
struct pv_RG dat;
struct pv_Rx pv;
unsigned int sep_pos = 5;
unsigned int TM_size = 16;
char ps[5];
char vs[10];
boolean presence_button_pressed;
boolean button_pres_stat_act;
boolean button_pres_stat_old;
boolean isconnected;
boolean wait;
EthernetUDP Udp;
LiquidCrystal_I2C LCD(39, 20,4);
byte degree[8] = {B01110, B01010, B01110, B00000, B00000, B00000, B00000, B00000};
byte sun[8] = {0b00100, 0b10101, 0b01110, 0b11011, 0b01110, 0b10101, 0b00100, 0b00000};
byte moon[8] = {0b00111, 0b01110, 0b11100, 0b11100, 0b11100, 0b01110, 0b00111, 0b00000};
byte snow[8] = {0b01010, 0b00100, 0b10101, 0b01110, 0b10101, 0b00100, 0b01010, 0b00000};
byte flame[8] = {0b00010, 0b00100, 0b01100, 0b01110, 0b01010, 0b01010, 0b00100, 0b11011};
byte water[8] = {0b00000, 0b01110, 0b00101, 0b11111, 0b10001, 0b10000, 0b00000, 0b00000};
void requestBC() {
//sprintf(TxBuffer, "%.7s", "REQBC@1");
Udp.beginPacket(RxIP, RxUDPport);
//Udp.print(TxBuffer);
Udp.print("REQBC@0000000001");
Udp.endPacket();
}
void RxUDP() {
int RxPacket = Udp.parsePacket();
if(RxPacket) {
Udp.read(RxBuffer, udp_buff_size);
isconnected = true;
}
}
//determine presence state
void determine_presence_state() {
if (dat.RT_t == dat.RT_c) {
dat.presence_state = true;
dat.presence_error = false;
}
if (dat.RT_t == dat.RT_r) {
dat.presence_state = false;
dat.presence_error = false;
}
if (dat.RT_t != dat.RT_c && dat.RT_t != dat.RT_r)
dat.presence_error = true;
}
void get_button_state() {
button_pres_stat_act = digitalRead(8);
//Pin uses PULL UP resistance -> "not pressed" means HIGH
if (button_pres_stat_act && button_pres_stat_old) { //not pressed and not pressed before
presence_button_pressed = false; //do not detect as pressed
}
if (!button_pres_stat_act && !button_pres_stat_old) { //pressed and pressed before
presence_button_pressed = false; //do not detect as pressed
}
if (button_pres_stat_act && !button_pres_stat_old) { //not pressed and pressed before
presence_button_pressed = false; //do not detect as pressed
}
if (!button_pres_stat_act && button_pres_stat_old) { //pressed and not pressed before
presence_button_pressed = true; //detect as pressed
delay(100); //debounce
}
button_pres_stat_old = button_pres_stat_act;
}
struct pv_Rx GetParamVal() {
//all TM has to be:
//[5 character]@[10 character]
//5 character = parameter number acc. to RVS43.122, 0 at LSB if needed
//@ = separator
//10 character = value
char ps[5];
char vs[10];
int i, j;
struct pv_Rx pv;
pv.par = 0;
pv.val = 0;
//get parameter
j=0;
for(i=0;i<sep_pos; i++){
ps[j] = RxBuffer[i];
j++;
}
pv.par = atoi(ps);
//get value
j=0;
for(i=sep_pos+1;i<TM_size; i++){
vs[j] = RxBuffer[i];
j++;
}
vs[j]='\0'; //I don't know why this is necessary, maybe some char/pointer thing...
pv.val = atoi(vs);
return pv;
}
void EvalParamVal() {
pv = GetParamVal();
if (pv.par > 0) {
Serial.print(pv.par);
Serial.print("@");
Serial.println(pv.val);
}
if (pv.par == 8700) //outside temperature
dat.OT = 0.1 * pv.val;
if (pv.par == 8740) //room temperature
dat.RT = 0.1 * pv.val;
if (pv.par == 8830) //water temperature
dat.WT = 0.1 * pv.val;
if (pv.par == 8741) //room temperature, target
dat.RT_t = 0.1 * pv.val;
if (pv.par == 710) //room temperature, comfort
dat.RT_c = 0.1 * pv.val;
if (pv.par == 712) //room temperature, reduced
dat.RT_r = 0.1 * pv.val;
if (pv.par == 8300) {//burner state
//evaluate burner state
if (pv.val == 1)
dat.burner_state = true;
else
dat.burner_state = false;
}
if (pv.par == 8003) {//water heating state
//evaluate TWW state
if (pv.val == 69 || (pv.val >= 92 && pv.val <= 97))
dat.water_state = true;
else
dat.water_state = false;
}
}
void UpdateLCD(){
LCD.setCursor(0,0);
LCD.print("Au\xE2");
LCD.print("entemp: ");
LCD.print(dat.OT);
LCD.setCursor(15,0);
LCD.print(char(0));
LCD.print("C");
LCD.setCursor(0,1);
LCD.print("Raumtemp : ");
LCD.print(dat.RT);
LCD.setCursor(15,1);
LCD.print(char(0));
LCD.print("C");
LCD.setCursor(0,2);
LCD.print("TWW Temp : ");
LCD.print(dat.WT);
LCD.setCursor(15,2);
LCD.print(char(0));
LCD.print("C");
//presence state
if (dat.presence_state) {
LCD.setCursor(19,0);
LCD.print(char(1)); //sun enable
LCD.setCursor(19,1);
LCD.print(" "); //moon disable
}
else {
LCD.setCursor(19,0);
LCD.print(" "); //sun disable
LCD.setCursor(19,1);
LCD.print(char(2)); //moon enable
}
//presence error
if (dat.presence_error) {
LCD.setCursor(19,0);
LCD.print("#"); //sun disable
LCD.setCursor(19,1);
LCD.print("#"); //moon disable
}
//burner state
LCD.setCursor(19,3);
if (dat.burner_state)
LCD.print(char(4)); //flame enable
else
LCD.print(" "); //flame disable
//water state
LCD.setCursor(19,2);
if (dat.water_state)
LCD.print(char(5)); //water enable
else
LCD.print(" "); //water disable
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
pinMode(8, INPUT_PULLUP);
presence_button_pressed = false;
button_pres_stat_act = true;
button_pres_stat_old = true;
isconnected = false;
int i;
Ethernet.begin(mac,ip);
delay(500);
Udp.begin(port);
delay(500);
Serial.begin(115200);
delay(500);
LCD.init();
LCD.clear();
LCD.backlight();
LCD.createChar(0, degree);
LCD.createChar(1, sun);
LCD.createChar(2, moon);
LCD.createChar(3, snow);
LCD.createChar(4, flame);
LCD.createChar(5, water);
LCD.setCursor(0,0);
LCD.print("Connecting...");
delay(500);
for (i=0; i<=maxtry; i++) {
//request first BC to check connection
requestBC();
delay(1000);
RxUDP(); //sets "isconnected" to true if UDP packet received
Serial.println(i);
if (isconnected)
break;
}
//determine_presence_state();
LCD.setCursor(0,0);
if (!isconnected) {
LCD.print("Connection failed! ");
LCD.setCursor(0,3);
LCD.print("Error #503");
delay(2000);
}
else {
LCD.print("Connected! ");
delay(2000);
}
LCD.clear();
UpdateLCD();
t_old=millis();
}
void loop() {
int i;
//check for new UDP packets
RxUDP();
//evaluate what has been received
EvalParamVal();
//determine presence state based on room target temperature
determine_presence_state();
//check if presence button ("Präsenztaste") was pressed on RG
get_button_state();
if (presence_button_pressed) {
double RT_t_old = dat.RT_t;
LCD.setCursor(0,3);
LCD.print("Bitte warten...");
//transmit request to BSB_LAN for change of presence_state
Udp.beginPacket(RxIP, RxUDPport);
Udp.print("!PRES@0000000001");
Udp.endPacket();
wait = true;
for (i=0; i<=maxtry; i++) {
if (RT_t_old == dat.RT_t) {//if no change
//wait for RT_t to be updated
RxUDP(); //sets "isconnected" to true if UDP packet received
EvalParamVal();
delay(2000);
}
else {
wait = false;
LCD.setCursor(0,3);
LCD.print(" ");
break;
}
//Serial.println(i);
}
//invert presence state
if (!wait) {//target room teperature was changed by heating before max try was reached
dat.presence_state = !dat.presence_state;
//update LCD
LCD.clear();
LCD.setCursor(0,0);
if (dat.presence_state && !dat.presence_error) {
LCD.print("Pr\xE1");
LCD.print("senz: EIN");
LCD.setCursor(0,1);
LCD.print("Heizen auf");
LCD.setCursor(0,2);
LCD.print("Komfortsollwert");
}
if (!dat.presence_state && !dat.presence_error) {
LCD.print("Pr\xE1");
LCD.print("senz: AUS");
LCD.setCursor(0,1);
LCD.print("Heizen auf");
LCD.setCursor(0,2);
LCD.print("Reduziersollwert");
}
delay(2000);
//RxUDP(); //check for new values to make sure presence was really changed
//determine_presence_state(); //update presence state
LCD.clear();
UpdateLCD(); //show new presence state
}
else { //no answer received in time from BSB-LAN
LCD.setCursor(0,3);
LCD.print("Error #503 ");
delay(2000);
LCD.setCursor(0,3);
LCD.print(" ");
}
}
t_act = millis();
if (t_act - t_old > 5000) {
UpdateLCD();
t_old = t_act;
}
//reset Rx buffer
sprintf(RxBuffer, "%d", 0);
}
Code BSB_lan_custom.h
//RGLU, custom code for loop function
//for RGLU v0.01
//check for new UDP packets
RxUDP();
//presence state changed in RG
if (!strcmp(RxBuffer, "!PRES@0000000001")) {
Serial.println("Request to change presence status");
pv.RT_t = (int)(atof(query(8741,8741,0))*10);
pv.RT_c = (int)(atof(query(710,710,0))*10);
pv.RT_r = (int)(atof(query(712,712,0))*10);
if (pv.RT_t == pv.RT_c) { //if currently in comfort mode
Serial.println("Currently, RT_t = comfort, change to reduced / 'not present'");
set(701, "1", 1); //set heating to "reduced"
}
if (pv.RT_t == pv.RT_r) { //if currently in reduced mode
Serial.println("Currently, RT_t = reduced, change to comfort / 'present'");
set(701, "0", 1); //set heating to "comfort"
}
//delay (1);
//send new room target temperature
pv.RT_t = (int)(atof(query(8741,8741,0))*10);
//Serial.print("RT_t set to :");
//Serial.println(pv.RT_t);
Udp.beginPacket(RxIP, RxPort);
sprintf(TxBuffer, "%.5i@%.10i", 8741, pv.RT_t);
Udp.print(TxBuffer);
Udp.endPacket();
}
//broadcast request by RG
if (!strcmp(RxBuffer, "REQBC@0000000001")) {
Serial.println("BC request ... send BC data");
pv.RT_t = (int)(atof(query(8741,8741,0))*10);
pv.RT_c = (int)(atof(query(710,710,0))*10);
pv.RT_r = (int)(atof(query(712,712,0))*10);
pv.OT = (int)(atof(query(8700,8700,0))*10);
pv.RT = (int)(atof(query(8740,8740,0))*10);
pv.WT = (int)(atof(query(8830,8830,0))*10);
pv.water_state = (atoi)(query(8003,8003,0));
pv.burner_state = (atoi)(query(8300,8300,0));
Serial.println(pv.OT);
//send broadcast
TxBC();
}
//send BC data periodically every ~30 seconds
t_act = millis();
if (t_act - t_old > 30000) {
Serial.println("Send periodic BC data");
pv.RT_t = (int)(atof(query(8741,8741,0))*10);
pv.RT_c = (int)(atof(query(710,710,0))*10);
pv.RT_r = (int)(atof(query(712,712,0))*10);
pv.OT = (int)(atof(query(8700,8700,0))*10);
pv.RT = (int)(atof(query(8740,8740,0))*10);
pv.WT = (int)(atof(query(8830,8830,0))*10);
pv.water_state = (atoi)(query(8003,8003,0));
pv.burner_state = (atoi)(query(8300,8300,0));
//send broadcast
TxBC();
t_old = t_act;
}
//reset Rx buffer
sprintf(RxBuffer, "%d", 0);
Code BSB_lan_custom_global.h
//RGLU, custom code for global section
//for RGLU v0.01
#include <stdlib.h>
//UDP port of BSB-LAN
unsigned int UDPportBSB = 28000;
//IP adress and port of RG
IPAddress RxIP(192, 168, 0, 42);
unsigned int RxPort = 28001;
const int udp_buff_size = 16;
char RxBuffer[udp_buff_size];
char TxBuffer[udp_buff_size];
unsigned long t_act, t_old;
struct pv_BSB {
int OT; //outside temperature
int RT; //room temperature
int WT; //water temperature
int RT_t; //room temperature, target
int RT_c; //room temperature, comfort
int RT_r; //room temperature, reduced
int water_state;
int burner_state;
};
struct pv_BSB pv;
EthernetUDP Udp;
void RxUDP() //read UDP packet
{
int RxPacket = Udp.parsePacket();
if(RxPacket) {
Udp.read(RxBuffer, udp_buff_size);
Serial.print("RxUDP: ");
Serial.println(RxBuffer);
}
}
void TxBC(){
Serial.println("Send UDP data: ");
//send outside temperature
Udp.beginPacket(RxIP, RxPort);
sprintf(TxBuffer, "%.5i@%.10i", 8700, pv.OT);
Serial.println(TxBuffer);
Udp.print(TxBuffer);
Udp.endPacket();
delay(100);
//send room temperature
Udp.beginPacket(RxIP, RxPort);
sprintf(TxBuffer, "%.5i@%.10i", 8740, pv.RT);
Serial.println(TxBuffer);
Udp.print(TxBuffer);
Udp.endPacket();
delay(100);
//send water temperature
Udp.beginPacket(RxIP, RxPort);
sprintf(TxBuffer, "%.5i@%.10i", 8830, pv.WT);
Serial.println(TxBuffer);
Udp.print(TxBuffer);
Udp.endPacket();
delay(100);
//send burner status
Udp.beginPacket(RxIP, RxPort);
sprintf(TxBuffer, "%.5i@%.10i", 8300, pv.burner_state);
Serial.println(TxBuffer);
Udp.print(TxBuffer);
Udp.endPacket();
delay(100);
//send water status
Udp.beginPacket(RxIP, RxPort);
sprintf(TxBuffer, "%.5i@%.10i", 8003, pv.water_state);
Serial.println(TxBuffer);
Udp.print(TxBuffer);
Udp.endPacket();
delay(100);
//send room target temperature
Udp.beginPacket(RxIP, RxPort);
sprintf(TxBuffer, "%.5i@%.10i", 8741, pv.RT_t);
Serial.println(TxBuffer);
Udp.print(TxBuffer);
Udp.endPacket();
delay(100);
//send room comfort temperature
Udp.beginPacket(RxIP, RxPort);
sprintf(TxBuffer, "%.5i@%.10i", 710, pv.RT_c);
Serial.println(TxBuffer);
Udp.print(TxBuffer);
Udp.endPacket();
delay(100);
//send room reduced temperature
Udp.beginPacket(RxIP, RxPort);
sprintf(TxBuffer, "%.5i@%.10i", 712, pv.RT_r);
Serial.println(TxBuffer);
Udp.print(TxBuffer);
Udp.endPacket();
delay(100);
}
Code BSB_lan_custom_setup.h
//RGLU, custom code for setup function
//for RGLU v0.01
Udp.begin(UDPportBSB);
t_old=millis(); //counter for periodic broadcast