#include <SdFat.h>
#include <ArduinoJson.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <Ethernet.h>
// Constant section
// SD Card
#define SPI_SPEED SD_SCK_MHZ(4)
#define CS_PIN 9
// LCD
#define I2C_ADDR 0x27
#define LCD_COLUMNS 20
#define LCD_LINES 4
//Init component's variables
SdFat sd;
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
uint8_t special_char [8] = { 0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00, 0x00 };
EthernetClient client;
// Class of the Button to store the state and react on the pressing
class Button {
public:
byte pin; // Hardware pin the button is connected to
boolean current_state; // Property to store the current state of the button (LOW/HIGH)
boolean previous_state; // Property to store previous state of the button (LOW/HIGH)
// Constructor to initialize the button and tight it to the hardware pin
Button(byte pin) {
this->pin = pin;
this->current_state = HIGH; // Default state is HIGH as internal pullup registor is used for the scheme
this->previous_state = HIGH; // Same as for the current_state
pinMode(this->pin, INPUT_PULLUP); // Assigning a hardware pin to the button
}
// Reading current state of the button and storing previous
void read_state() {
this->previous_state = this->current_state;
this->current_state = digitalRead(this->pin);
}
// "True" if the button was just pressed, if the button is continiously pressed this method returns "false"
// Must call read_state() method prior this one
boolean is_pressed() {
return this->current_state != this->previous_state && this->current_state == LOW;
}
};
Button buttons_on[] = { 17, 6, 7, 8 };
Button buttons_off[] = { 5, 4, 3, 2 };
// Config structure to store initial controller settings
struct Config {
byte mac[6];
IPAddress ip;
uint16_t port;
IPAddress point_ips[4];
byte request_period;
} config;
// Payload sent over the network, contains command and the value
struct Payload{
char header[2];
char command;
float value;
} payload;
void lcd_error_msg(char msg[]) {
lcd.setCursor(6, 0);
lcd.print("Error!!!");
lcd.setCursor(0, 1);
lcd.print(msg);
// Serial.println(msg);
//delay(200);
}
// Waiting for a cable connectino if it is not attached
void wait_for_cable() {
if (Ethernet.linkStatus() == LinkOFF) {
lcd.setCursor(0, 2);
lcd.print("Waiting for cable...");
} else return;
char sign = '/';
while (Ethernet.linkStatus() == LinkOFF) {
switch (sign) {
case '/':
sign = '-';
break;
case '-':
sign = char(0);
break;
case char(0):
sign = '|';
break;
case '|':
sign = '/';
break;
}
lcd.setCursor(9, 3);
lcd.print(sign);
// delay(200);
}
lcd.clear();
}
void setup() {
Serial.begin(9600);
// Init LCD
lcd.init();
lcd.backlight();
lcd.createChar(0, special_char);
// Init SD Card
if (!sd.begin(CS_PIN, SPI_SPEED)) {
if (sd.card()->errorCode()) {
lcd_error_msg("SD init failed");
} else if (sd.vol()->fatType() == 0) {
lcd_error_msg("No FAT16 or FAT32");
} else {
lcd_error_msg("SD error - Unknown");
}
return;
}
// Read config.json from SD card
// Expected config fields: mac, ip, port, point_ips, request_period
File json_file = sd.open("config.json");
if (json_file) {
//Reading config.json
StaticJsonDocument < JSON_OBJECT_SIZE(7) + 130 > json;
DeserializationError file_parse_error = deserializeJson(json, json_file);
// Test if parsing succeeds.
if (file_parse_error) {
lcd_error_msg("Wrong JSON format");
Serial.println(file_parse_error.f_str());
return;
}
// Getting configuration from parsed json
// parsing mac address
const char* mac = json["mac"];
char byte_char[2];
for (int i = 1; i <= 6; i++) {
byte_char[0] = mac[(i - 1) * 3];
byte_char[1] = mac[(i - 1) * 3 + 1];
if ( isHexadecimalDigit(byte_char[0]) & isHexadecimalDigit(byte_char[1]) ) {
config.mac[i - 1] = strtol(byte_char, nullptr, HEX);
} else {
lcd_error_msg("Wrong MAC address");
return;
}
}
// parsing IP address
uint8_t ip[4];
sscanf(json["ip"], "%u.%u.%u.%u", &ip[0], &ip[1], &ip[2], &ip[3]);
config.ip = IPAddress(ip);
//parsing port number
config.port = json["port"];
//parsing receivers ips
for (int i=0; i<4; i++){
sscanf(json["point_ips"][i], "%u.%u.%u.%u", &ip[0], &ip[1], &ip[2], &ip[3]);
config.point_ips[i] = IPAddress(ip);
}
} else {
lcd_error_msg("No config.json found");
return;
}
json_file.close();
// // Initit the Ethernet device
// Ethernet.begin(config.mac, config.ip);
// // Check for Ethernet hardware present
// if (Ethernet.hardwareStatus() == EthernetNoHardware) {
// lcd_error_msg("No Ethernet hardware");
// return;
// } else if (Ethernet.linkStatus() == LinkOFF) {
// lcd_error_msg("Net cable connection");
// // Waiting for the cable connection
// wait_for_cable();
// }
// Setting pin mode for buttons
lcd.setCursor(0, 0);
lcd.print("1 Off");
lcd.setCursor(0, 1);
lcd.print("2 Off");
lcd.setCursor(0, 2);
lcd.print("3 Off");
lcd.setCursor(0, 3);
lcd.print("4 Off");
}
const unsigned int response_timeout = 5 * 1000;
unsigned int response_wait;
int result;
void loop() {
for (int i = 0; i < 4; i++) {
buttons_on[i].read_state();
buttons_off[i].read_state();
}
//wait_for_cable();
for (int i = 0; i < 4; i++) {
if (buttons_on[i].is_pressed()) {
if (client.connect(config.point_ips[i], config.port) ){
// preparing payload to send to the point
strcpy(payload.header, "SU");
payload.command = 'C';
payload.value = HIGH;
// sending command to turn on relay
result = client.write(( const uint8_t *) &payload, sizeof(payload));
Serial.print("Result: ");
Serial.println(result);
if ( result > 0 ){
// response_wait = millis();
// // waiting for the repsonse
// while ( !client.available() ){
// if ( millis() - response_wait >= response_timeout ) {
// break;}
// }
// if ( client.available() ){
// client.read( (const uint8_t*) &payload, sizeof(payload) );
// }
lcd.setCursor(2, i);
lcd.print("ON ");
} else{
lcd_error_msg("Send command failed");
}
}
// lcd.setCursor(2, i);
// lcd.print("On ");
}
if (buttons_off[i].is_pressed()) {
if (client.connect(config.point_ips[i], config.port) ){
// preparing payload to send to the point
payload.command = 'C';
payload.value = LOW;
// sending command to turn on relay
result = client.write(( const uint8_t *) &payload, sizeof(payload));
Serial.print("Result: ");
Serial.println(result);
if ( result > 0 ){
lcd.setCursor(2, i);
lcd.print("OFF ");
} else{
lcd_error_msg("Send command failed");
}
}
}
}
}