diff --git a/libraries/RainMaker/examples/RMakerSwitchArray/README.md b/libraries/RainMaker/examples/RMakerSwitchArray/README.md new file mode 100644 index 00000000000..2c486eaaf24 --- /dev/null +++ b/libraries/RainMaker/examples/RMakerSwitchArray/README.md @@ -0,0 +1,39 @@ +# ESP RainMaker 8 Switches using Array + +This example demonstrates how to build a device with 8 light switches to be used with ESP RainMaker. + + +## What to expect in this example? + +- This virtual switches will work in parallel with physical two state light switch. +- The physical switch will change the state of relay while is pressed or when released and the same will reflect on the phone app. +- After compiling and flashing the example, add your device using the [ESP RainMaker phone apps](https://rainmaker.espressif.com/docs/quick-links.html#phone-apps) by scanning the QR code. + +### Libraries dependencies + - [AceButton](https://github.com/bxparks/AceButton) + +### Output + +``` +[ 80][I][RMaker.cpp:17] event_handler(): RainMaker Initialised. +[ 96][I][WiFiProv.cpp:149] beginProvision(): Already Provisioned +[ 96][I][WiFiProv.cpp:153] beginProvision(): Attempting connect to AP: XXXXXX + +[ 199][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay1, Param Name : Power, Val : false +[ 199][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay2, Param Name : Power, Val : false +[ 207][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay3, Param Name : Power, Val : false +[ 223][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay4, Param Name : Power, Val : false +[ 232][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay5, Param Name : Power, Val : false +[ 241][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay6, Param Name : Power, Val : false +[ 251][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay7, Param Name : Power, Val : false +[ 260][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay8, Param Name : Power, Val : false +Light switch 1 released, relayState: 1 +[359585][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay1, Param Name : Power, Val : true +Light switch 1 pressed, relayState: 0 +[363695][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay1, Param Name : Power, Val : false +[390384][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay1, Param Name : Power, Val : true +[392021][I][RMakerDevice.cpp:163] updateAndReportParam(): Device : Relay1, Param Name : Power, Val : false +``` + +### Factory reset +- Press and Hold the Boot button for more than 10 seconds and then release to reset to factory defaults. diff --git a/libraries/RainMaker/examples/RMakerSwitchArray/RMakerSwitchArray.ino b/libraries/RainMaker/examples/RMakerSwitchArray/RMakerSwitchArray.ino new file mode 100644 index 00000000000..f8fa9aebd70 --- /dev/null +++ b/libraries/RainMaker/examples/RMakerSwitchArray/RMakerSwitchArray.ino @@ -0,0 +1,289 @@ +// This example demonstrates the ESP RainMaker with a standard light switch device. +#include "RMaker.h" +#include "WiFi.h" +#include "WiFiProv.h" +#include +#include + +#define NUM_RELAYS 8 + +#if CONFIG_IDF_TARGET_ESP32C3 + const byte resetPin = 9; +#else + const byte resetPin = 0; +#endif + +const char nodeName[] = "ESP32_Relay_16"; +const char *service_name = "PROV_1234"; +const char *pop = "abcd1234"; +bool firstPress = true; + +Preferences pref; + +using namespace ace_button; + +// Define the buttons in an array using the default constructor. +AceButton lightSwitch[NUM_RELAYS]; + +// The framework provides some standard device types like switch, lightbulb, fan, temperaturesensor. +static Switch myRelay[NUM_RELAYS]; + +struct INFOS +{ + const char *deviceName; + uint8_t relayPin; + bool relayState; + uint8_t switchPin; +}; + +#define INITIAL_STATE LOW // Initial state = HIGH to active LOW relay model. + +// Define the device name, relay pin, relay default status and switch pin to an array. +INFOS info[NUM_RELAYS] = { + { "Relay1", 4, INITIAL_STATE, 26}, + { "Relay2", 5, INITIAL_STATE, 27}, + { "Relay3", 18, INITIAL_STATE, 32}, + { "Relay4", 19, INITIAL_STATE, 33}, + { "Relay5", 21, INITIAL_STATE, 13}, + { "Relay6", 22, INITIAL_STATE, 12}, + { "Relay7", 23, INITIAL_STATE, 14}, + { "Relay8", 25, INITIAL_STATE, 15} +}; + +void sysProvEvent(arduino_event_t *sys_event) +{ + switch (sys_event->event_id) + { + case ARDUINO_EVENT_WIFI_STA_GOT_IP: + { + Serial.print("\nConnected IP address : "); + Serial.println(IPAddress(sys_event->event_info.got_ip.ip_info.ip.addr)); + break; + } + case ARDUINO_EVENT_WIFI_STA_DISCONNECTED: + { + Serial.println("\nDisconnected. Connecting to the AP again... "); + break; + } + case ARDUINO_EVENT_PROV_START: + { + #if CONFIG_IDF_TARGET_ESP32S2 + Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on SoftAP\n", service_name, pop); + printQR(service_name, pop, "softap"); + #else + Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on BLE\n", service_name, pop); + printQR(service_name, pop, "ble"); + #endif + break; + } + case ARDUINO_EVENT_PROV_INIT: + { + wifi_prov_mgr_disable_auto_stop(10000); + break; + } + case ARDUINO_EVENT_PROV_CRED_SUCCESS: + { + wifi_prov_mgr_stop_provisioning(); + break; + } + case ARDUINO_EVENT_PROV_CRED_RECV: + { + Serial.println("\nReceived Wi-Fi credentials"); + Serial.print("\tSSID : "); + Serial.println((const char *) sys_event->event_info.prov_cred_recv.ssid); + Serial.print("\tPassword : "); + Serial.println((char const *) sys_event->event_info.prov_cred_recv.password); + break; + } + case ARDUINO_EVENT_PROV_CRED_FAIL: + { + Serial.println("\nProvisioning failed!\nPlease reset to factory and retry provisioning\n"); + if(sys_event->event_info.prov_fail_reason == WIFI_PROV_STA_AUTH_ERROR) + { + Serial.println("\nWi-Fi AP password incorrect"); + } + else + { + Serial.println("\nWi-Fi AP not found....Add API \" nvs_flash_erase() \" before beginProvision()"); + } + break; + } + case ARDUINO_EVENT_PROV_CRED_SUCCESS: + { + Serial.println("\nProvisioning Successful"); + break; + } + case ARDUINO_EVENT_PROV_END: + { + Serial.println("\nProvisioning Ends"); + break; + } + default: + { + break; + } + } +} + +void write_callback(Device *device, Param *param, const param_val_t val, void *priv_data, write_ctx_t *ctx) +{ + const char *device_name = device->getDeviceName(); + const char *param_name = param->getParamName(); + + if (strcmp(param_name, ESP_RMAKER_DEF_POWER_NAME) == 0) + { + for (byte i = 0; i < NUM_RELAYS; i++) + { + if (strcmp(device_name, info[i].deviceName) == 0) + { + info[i].relayState = val.val.b; + digitalWrite(info[i].relayPin, info[i].relayState); + + // Publish the new state to all listeners + myRelay[i].updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, info[i].relayState); + + // Save the relay state. + pref.putBool(info[i].deviceName, info[i].relayState); + break; + } + } + } +} + +void getLastState() +{ + for (byte i = 0; i < NUM_RELAYS; i++) + { + info[i].relayState = pref.getBool(info[i].deviceName, info[i].relayState); + digitalWrite(info[i].relayPin, info[i].relayState); + myRelay[i].updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, info[i].relayState); + } +} + +void handleEvent(AceButton* button, uint8_t eventType, uint8_t buttonState) +{ + uint8_t numSwitch = button->getId(); + + switch (eventType) + { + case AceButton::kEventPressed: + { + info[numSwitch].relayState = !info[numSwitch].relayState; + Serial.print(F("Light switch ")); + Serial.print(numSwitch + 1); + Serial.print(F(" pressed, relayState: ")); + Serial.println(info[numSwitch].relayState); + digitalWrite(info[numSwitch].relayPin, info[numSwitch].relayState); + myRelay[numSwitch].updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, info[numSwitch].relayState); + pref.putBool(info[numSwitch].deviceName, info[numSwitch].relayState); + break; + } + case AceButton::kEventReleased: + { + info[numSwitch].relayState = !info[numSwitch].relayState; + Serial.print(F("Light switch ")); + Serial.print(numSwitch + 1); + Serial.print(F(" released, relayState: ")); + Serial.println(info[numSwitch].relayState); + digitalWrite(info[numSwitch].relayPin, info[numSwitch].relayState); + myRelay[numSwitch].updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, info[numSwitch].relayState); + pref.putBool(info[numSwitch].deviceName, info[numSwitch].relayState); + break; + } + } +} + +void setup() +{ + Serial.begin(115200); + + pinMode(resetPin, INPUT); + + // Starts the configuration file where the state will be saved. + pref.begin(nodeName, false); + + // Configure the ButtonConfig with the event handler, enable click and long press events. + ButtonConfig* buttonConfig = ButtonConfig::getSystemButtonConfig(); + + buttonConfig->setEventHandler(handleEvent); + buttonConfig->setFeature(ButtonConfig::kFeatureClick); + buttonConfig->setFeature(ButtonConfig::kFeatureLongPress); + + Node my_node; + my_node = RMaker.initNode(nodeName); + + for (byte i = 0; i < NUM_RELAYS; i++) + { + pinMode(info[i].relayPin, OUTPUT); + + // Turn off all relays. + digitalWrite(info[i].relayPin, info[i].relayState); + + // Initialize switch device + myRelay[i] = Switch(info[i].deviceName); + + // Specify the callback function to relays. + myRelay[i].addCb(write_callback); + + // Add switch device to the node. + my_node.addDevice(myRelay[i]); + + // Switch uses the built-in pull up resistor. + pinMode(info[i].switchPin, INPUT_PULLUP); + + // initialize the corresponding AceButton. + lightSwitch[i].init(info[i].switchPin, HIGH, i); + } + + // This is optional + RMaker.enableOTA(OTA_USING_TOPICS); + // If you want to enable scheduling, set time zone for your region using setTimeZone(). + // The list of available values are provided here https://rainmaker.espressif.com/docs/time-service.html + // RMaker.setTimeZone("Asia/Shanghai"); + // Alternatively, enable the Timezone service and let the phone apps set the appropriate timezone + RMaker.enableTZService(); + + RMaker.enableSchedule(); + + RMaker.enableScenes(); + + RMaker.start(); + + WiFi.onEvent(sysProvEvent); + #if CONFIG_IDF_TARGET_ESP32S2 + WiFiProv.beginProvision(WIFI_PROV_SCHEME_SOFTAP, WIFI_PROV_SCHEME_HANDLER_NONE, WIFI_PROV_SECURITY_1, pop, service_name); + #else + WiFiProv.beginProvision(WIFI_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_1, pop, service_name); + #endif + + // Get the last state of Relays. + getLastState(); +} + +void loop() +{ + for (int i = 0; i < NUM_RELAYS; i++) + { + lightSwitch[i].check(); + } + + // Push button pressed. + if(digitalRead(resetPin) == LOW) + { + // Key debounce handling. + delay(100); + int startTime = millis(); + while(digitalRead(resetPin) == LOW) + { + delay(50); + } + int endTime = millis(); + + // If key pressed for more than 10secs, reset all. + if ((endTime - startTime) > 10000) + { + Serial.printf("Factory reset!"); + RMakerFactoryReset(2); + } + } +}