diff --git a/Part # 2/Alex_mqtt_robodyn_dimmer/Alex_mqtt_robodyn_dimmer.ino b/Part # 2/Alex_mqtt_robodyn_dimmer/Alex_mqtt_robodyn_dimmer.ino new file mode 100644 index 0000000..5d1e47b --- /dev/null +++ b/Part # 2/Alex_mqtt_robodyn_dimmer/Alex_mqtt_robodyn_dimmer.ino @@ -0,0 +1,108 @@ +#ifdef ARDUINO_ARCH_ESP32 +#include +#else +#include +#endif +#include +#include "hassio_mqtt.h" + +boolean connectWifi(); +boolean m_light_state = false; +//callback functions +void firstLightChanged(uint8_t brightness); +WiFiClient wifiClient; +//################################################## +//################################################## +// Change this!! +const char* ssid = "Entr your wifi router's name"; +const char* password = "Enter wifi password"; +//################################################## +//################################################## +boolean wifiConnected = false; +Espalexa espalexa; +EspalexaDevice* device3; //this is optional + +void setup() +{ + Serial.begin(115200); + // Initialise wifi connection + wifiConnected = connectWifi(); + + if(wifiConnected){ + // Define your devices here. + espalexa.addDevice("office lights", firstLightChanged); //simplest definition, default state off + espalexa.begin(); + hassioMqttSetup(); + + } else + { + while (1) { + Serial.println("Cannot connect to WiFi. Please check data and reset the ESP."); + delay(2500); + } + } +} + +void loop() +{ + if(WiFi.status() != WL_CONNECTED){ + connectWifi(); + }else{ + espalexa.loop(); + hassioMqttloop(); + } + //@@@##############@@@@@@@@@@@@@###############@#@@@@@@@@@@@@@@@# + yield();//Please don't chage or remove otherwise MCU will crash + //@@@##############@@@@@@@@@@@@@###############@#@@@@@@@@@@@@@@@# +} + +//our callback functions +void firstLightChanged(uint8_t brightness) { + Serial.print("Device 1 changed to "); + //EXAMPLE + if (brightness) { + Serial.print("ON, brightness "); + Serial.println(brightness); + m_light_state = true; + publishLightState(); + publishLightBrightness(String(brightness)); + } + else { + Serial.println("OFF"); + m_light_state = false; + publishLightState(); + } +} + +// connect to wifi – returns true if successful or false if not +boolean connectWifi(){ + boolean state = true; + int i = 0; + + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + Serial.println(""); + Serial.println("Connecting to WiFi"); + + // Wait for connection + Serial.print("Connecting..."); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + if (i > 20){ + state = false; break; + } + i++; + } + Serial.println(""); + if (state){ + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + } + else { + Serial.println("Connection failed."); + } + return state; +} diff --git a/Part # 2/Alex_mqtt_robodyn_dimmer/Alex_mqtt_robodyn_dimmer.ino.nodemcu.bin b/Part # 2/Alex_mqtt_robodyn_dimmer/Alex_mqtt_robodyn_dimmer.ino.nodemcu.bin new file mode 100644 index 0000000..b321163 Binary files /dev/null and b/Part # 2/Alex_mqtt_robodyn_dimmer/Alex_mqtt_robodyn_dimmer.ino.nodemcu.bin differ diff --git a/Part # 2/Alex_mqtt_robodyn_dimmer/hassio_mqtt.h b/Part # 2/Alex_mqtt_robodyn_dimmer/hassio_mqtt.h new file mode 100644 index 0000000..3f135c7 --- /dev/null +++ b/Part # 2/Alex_mqtt_robodyn_dimmer/hassio_mqtt.h @@ -0,0 +1,127 @@ +#include +#include "robodyn_dimmer.h" +#define MQTT_VERSION MQTT_VERSION_3_1_1 +// MQTT: ID, server IP, port, username and password +const PROGMEM char* MQTT_CLIENT_ID = "Office Lights"; +const PROGMEM char* MQTT_SERVER_IP = "192.168.0.22"; +const PROGMEM uint16_t MQTT_SERVER_PORT = 1883; +const PROGMEM char* MQTT_USER = "homeassistant"; +const PROGMEM char* MQTT_PASSWORD = "welcome"; + +// MQTT: topics +const char* MQTT_LIGHT_STATE_TOPIC = "netmedias/office lights/status"; +const char* MQTT_LIGHT_COMMAND_TOPIC = "netmedias/office lights/switch"; + +const char* brightness_state_topic = "netmedias/office lights/brightness"; +const char* brightness_command_topic = "netmedias/office lights/brightness/set"; + +// payloads by default (on/off) +const char* LIGHT_ON = "ON"; +const char* LIGHT_OFF = "OFF"; + +extern boolean m_light_state;// = false; // light is turned off by default + +extern WiFiClient wifiClient; +PubSubClient client(wifiClient); + +// function called to publish the state of the light (on/off) +void publishLightState() { + if (m_light_state) { + state=1; + client.publish(MQTT_LIGHT_STATE_TOPIC, LIGHT_ON, true); + } else { + state=0; + client.publish(MQTT_LIGHT_STATE_TOPIC, LIGHT_OFF, true); + } +} + +void publishLightBrightness(String val) { + setBrightness1(val); + client.publish(brightness_state_topic, val.c_str(), true); +} +// function called to turn on/off the light +void setLightState() { + if (m_light_state) { + state=1; + Serial.println("INFO: Turn light on..."); + } else { + state=0; + Serial.println("INFO: Turn light off..."); + } +} +void setLightBrightness(int val) { + setBrightness1(String(val)); + Serial.println(val); + +} +// function called when a MQTT message arrived +void callback(char* p_topic, byte* p_payload, unsigned int p_length) { + // concat the payload into a string + String payload; + for (uint8_t i = 0; i < p_length; i++) { + payload.concat((char)p_payload[i]); + } + Serial.println(p_topic); + Serial.println(payload); + // handle message topic + if (String(MQTT_LIGHT_COMMAND_TOPIC).equals(p_topic)) { + // test if the payload is equal to "ON" or "OFF" + if (payload.equals(String(LIGHT_ON))) { + if (m_light_state != true) { + m_light_state = true; + setLightState(); + publishLightState(); + } + } else if (payload.equals(String(LIGHT_OFF))) { + if (m_light_state != false) { + m_light_state = false; + setLightState(); + publishLightState(); + } + } + }else if (String(brightness_command_topic).equals(p_topic)){ + Serial.println("Dimming ...");//payload.toInt() + state=1; + setLightBrightness(payload.toInt()); + publishLightBrightness(payload.c_str()); + client.publish(brightness_state_topic, payload.c_str(), true); + } + +} + +void reconnect() { + // Loop until we're reconnected + while (!client.connected()) { + Serial.println("INFO: Attempting MQTT connection..."); + // Attempt to connect + if (client.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASSWORD)) { + Serial.println("INFO: connected"); + // Once connected, publish an announcement... + publishLightState(); + // ... and resubscribe + client.subscribe(MQTT_LIGHT_COMMAND_TOPIC); + client.subscribe(brightness_command_topic); + } else { + Serial.print("ERROR: failed, rc="); + Serial.print(client.state()); + Serial.println("DEBUG: try again in 5 seconds"); + yield(); + } + } +} + +void hassioMqttSetup() { + dimmerSetup(); + setLightState(); + // init the MQTT connection + client.setServer(MQTT_SERVER_IP, MQTT_SERVER_PORT); + client.setCallback(callback); + +} + +void hassioMqttloop() { + if (!client.connected()) { + reconnect(); + } + client.loop(); +} diff --git a/Part # 2/Alex_mqtt_robodyn_dimmer/hw_timer.c b/Part # 2/Alex_mqtt_robodyn_dimmer/hw_timer.c new file mode 100644 index 0000000..efccef0 --- /dev/null +++ b/Part # 2/Alex_mqtt_robodyn_dimmer/hw_timer.c @@ -0,0 +1,118 @@ +/****************************************************************************** +* Copyright 2013-2014 Espressif Systems (Wuxi) +* +* FileName: hw_timer.c +* +* Description: hw_timer driver +* +* Modification history: +* 2014/5/1, v1.0 create this file. +*******************************************************************************/ +//#include "c-types.h" +typedef __SIZE_TYPE__ size_t; +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "hw_timer.h" + + +/****************************************************************************** +* FunctionName : hw_timer_arm +* Description : set a trigger timer delay for this timer. +* Parameters : uint32 val : +in autoload mode + 50 ~ 0x7fffff; for FRC1 source. + 100 ~ 0x7fffff; for NMI source. +in non autoload mode: + 10 ~ 0x7fffff; +* Returns : NONE +*******************************************************************************/ +void hw_timer_arm(u32 val) +{ + RTC_REG_WRITE(FRC1_LOAD_ADDRESS, US_TO_RTC_TIMER_TICKS(val)); +} + +static void (* user_hw_timer_cb)(void) = NULL; +/****************************************************************************** +* FunctionName : hw_timer_set_func +* Description : set the func, when trigger timer is up. +* Parameters : void (* user_hw_timer_cb_set)(void): + timer callback function, +* Returns : NONE +*******************************************************************************/ +void hw_timer_set_func(void (* user_hw_timer_cb_set)(void)) +{ + user_hw_timer_cb = user_hw_timer_cb_set; +} + +static void hw_timer_isr_cb(void) +{ + if (user_hw_timer_cb != NULL) { + (*(user_hw_timer_cb))(); + } +} + +/****************************************************************************** +* FunctionName : hw_timer_init +* Description : initilize the hardware isr timer +* Parameters : +FRC1_TIMER_SOURCE_TYPE source_type: + FRC1_SOURCE, timer use frc1 isr as isr source. + NMI_SOURCE, timer use nmi isr as isr source. +u8 req: + 0, not autoload, + 1, autoload mode, +* Returns : NONE +*******************************************************************************/ +void ICACHE_FLASH_ATTR hw_timer_init(FRC1_TIMER_SOURCE_TYPE source_type, u8 req) +{ + if (req == 1) { + RTC_REG_WRITE(FRC1_CTRL_ADDRESS, + FRC1_AUTO_LOAD | DIVDED_BY_16 | FRC1_ENABLE_TIMER | TM_EDGE_INT); + } else { + RTC_REG_WRITE(FRC1_CTRL_ADDRESS, + DIVDED_BY_16 | FRC1_ENABLE_TIMER | TM_EDGE_INT); + } + + if (source_type == NMI_SOURCE) { + ETS_FRC_TIMER1_NMI_INTR_ATTACH(hw_timer_isr_cb); + } else { + ETS_FRC_TIMER1_INTR_ATTACH(hw_timer_isr_cb, NULL); + } + + TM1_EDGE_INT_ENABLE(); + ETS_FRC1_INTR_ENABLE(); +} + +//-------------------------------Test Code Below-------------------------------------- +#if 0 +void hw_test_timer_cb(void) +{ + static uint16 j = 0; + j++; + + if ((WDEV_NOW() - tick_now2) >= 1000000) { + static u32 idx = 1; + tick_now2 = WDEV_NOW(); + os_printf("b%u:%d\n", idx++, j); + j = 0; + } + + //hw_timer_arm(50); +} + +void ICACHE_FLASH_ATTR user_init(void) +{ + hw_timer_init(FRC1_SOURCE, 1); + hw_timer_set_func(hw_test_timer_cb); + hw_timer_arm(100); +} +#endif +/* +NOTE: +1 if use nmi source, for autoload timer , the timer setting val can't be less than 100. +2 if use nmi source, this timer has highest priority, can interrupt other isr. +3 if use frc1 source, this timer can't interrupt other isr. + +*/ + diff --git a/Part # 2/Alex_mqtt_robodyn_dimmer/hw_timer.h b/Part # 2/Alex_mqtt_robodyn_dimmer/hw_timer.h new file mode 100644 index 0000000..336a744 --- /dev/null +++ b/Part # 2/Alex_mqtt_robodyn_dimmer/hw_timer.h @@ -0,0 +1,48 @@ +#ifndef HW_TIMER_H +#define HW_TIMER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define US_TO_RTC_TIMER_TICKS(t) \ + ((t) ? \ + (((t) > 0x35A) ? \ + (((t)>>2) * ((APB_CLK_FREQ>>4)/250000) + ((t)&0x3) * ((APB_CLK_FREQ>>4)/1000000)) : \ + (((t) *(APB_CLK_FREQ>>4)) / 1000000)) : \ + 0) + +#define FRC1_ENABLE_TIMER BIT7 +#define FRC1_AUTO_LOAD BIT6 + +//TIMER PREDIVED MODE +typedef enum { + DIVDED_BY_1 = 0, //timer clock + DIVDED_BY_16 = 4, //divided by 16 + DIVDED_BY_256 = 8, //divided by 256 +} TIMER_PREDIVED_MODE; + +typedef enum { //timer interrupt mode + TM_LEVEL_INT = 1, // level interrupt + TM_EDGE_INT = 0, //edge interrupt +} TIMER_INT_MODE; + +typedef enum { + FRC1_SOURCE = 0, + NMI_SOURCE = 1, +} FRC1_TIMER_SOURCE_TYPE; + + +void hw_timer_arm(u32 val); +void hw_timer_set_func(void (* user_hw_timer_cb_set)(void)); +void hw_timer_init(FRC1_TIMER_SOURCE_TYPE source_type, u8 req); +//void ICACHE_RAM_ATTR blink_gpio(void); +//volatile bool state; + + + +#ifdef __cplusplus +} +#endif + +#endif /* HW_TIMER_H */ diff --git a/Part # 2/Alex_mqtt_robodyn_dimmer/robodyn_dimmer.h b/Part # 2/Alex_mqtt_robodyn_dimmer/robodyn_dimmer.h new file mode 100644 index 0000000..62b6ebd --- /dev/null +++ b/Part # 2/Alex_mqtt_robodyn_dimmer/robodyn_dimmer.h @@ -0,0 +1,72 @@ +#include "hw_timer.h" +const byte zcPin = 12; +const byte pwmPin = 13; + +void zcDetectISR(); +void dimTimerISR(); + +byte fade = 1; +byte state = 1; +byte tarBrightness = 255; +byte curBrightness = 0; +byte zcState = 0; // 0 = ready; 1 = processing; + +void dimmerSetup() { + pinMode(zcPin, INPUT_PULLUP); + pinMode(pwmPin, OUTPUT); + attachInterrupt(zcPin, zcDetectISR, RISING); // Attach an Interupt to Pin 2 (interupt 0) for Zero Cross Detection + hw_timer_init(NMI_SOURCE, 0); + hw_timer_set_func(dimTimerISR); +} + +void setBrightness1(String val) { + tarBrightness = val.toInt(); + // Serial.println(tarBrightness); +} + + +void dimTimerISR() { + if (fade == 1) { + if (curBrightness > tarBrightness || (state == 0 && curBrightness > 0)) { + --curBrightness; + } + else if (curBrightness < tarBrightness && state == 1 && curBrightness < 255) { + ++curBrightness; + } + } + else { + if (state == 1) { + curBrightness = tarBrightness; + } + else { + curBrightness = 0; + } + } + + if (curBrightness == 0) { + state = 0; + digitalWrite(pwmPin, 0); + } + else if (curBrightness == 255) { + state = 1; + digitalWrite(pwmPin, 1); + } + else { + digitalWrite(pwmPin, 1); + } + + zcState = 0; +} + +void zcDetectISR() { + if (zcState == 0) { + zcState = 1; + + if (curBrightness < 255 && curBrightness > 0) { + digitalWrite(pwmPin, 0); + + int dimDelay = 30 * (255 - curBrightness) + 400;//400 + hw_timer_arm(dimDelay); + } + } +} diff --git a/Part # 2/circuit_diagram.jpg b/Part # 2/circuit_diagram.jpg new file mode 100644 index 0000000..7c27323 Binary files /dev/null and b/Part # 2/circuit_diagram.jpg differ