//This example demonstrates the ESP RainMaker with a standard Switch device. #include "RMaker.h" #include "WiFi.h" #include "WiFiProv.h" #define DEFAULT_POWER_MODE false const char *service_name = "PROV_SONOFF_DUALR3"; const char *pop = "123456"; // GPIO for push button static uint8_t gpio_reset = 0; // GPIO for switch static uint8_t gpio_switch1 = 32; static uint8_t gpio_switch2 = 33; // GPIO for virtual device static uint8_t gpio_relay1 = 27; static uint8_t gpio_relay2 = 14; /* Variable for reading pin status*/ bool switch_state_ch1 = true; bool switch_state_ch2 = true; // GPIO for link status LED static uint8_t gpio_led = 13; struct LightSwitch { const uint8_t pin; bool pressed; }; // Define the light switches for channel 1 and 2 LightSwitch switch_ch1 = {gpio_switch1, false}; LightSwitch switch_ch2 = {gpio_switch2, false}; //The framework provides some standard device types like switch, lightbulb, fan, temperature sensor. static Switch my_switch1; static Switch my_switch2; void sysProvEvent(arduino_event_t *sys_event) { switch (sys_event->event_id) { case ARDUINO_EVENT_PROV_START: #if CONFIG_IDF_TARGET_ESP32 Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on BLE\n", service_name, pop); printQR(service_name, pop, "ble"); #else Serial.printf("\nProvisioning Started with name \"%s\" and PoP \"%s\" on SoftAP\n", service_name, pop); printQR(service_name, pop, "softap"); #endif break; case ARDUINO_EVENT_WIFI_STA_CONNECTED: Serial.printf("\nConnected to Wi-Fi!\n"); digitalWrite(gpio_led, true); break; default:; } } 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(device_name, "Switch_ch1") == 0) { Serial.printf("Lightbulb = %s\n", val.val.b? "true" : "false"); if(strcmp(param_name, "Power") == 0) { Serial.printf("Received value = %s for %s - %s\n", val.val.b? "true" : "false", device_name, param_name); switch_state_ch1 = val.val.b; (switch_state_ch1 == false) ? digitalWrite(gpio_relay1, LOW) : digitalWrite(gpio_relay1, HIGH); param->updateAndReport(val); } } else if(strcmp(device_name, "Switch_ch2") == 0) { Serial.printf("Switch value = %s\n", val.val.b? "true" : "false"); if(strcmp(param_name, "Power") == 0) { Serial.printf("Received value = %s for %s - %s\n", val.val.b? "true" : "false", device_name, param_name); switch_state_ch2 = val.val.b; (switch_state_ch2 == false) ? digitalWrite(gpio_relay2, LOW) : digitalWrite(gpio_relay2, HIGH); param->updateAndReport(val); } } } void ARDUINO_ISR_ATTR isr(void* arg) { LightSwitch* s = static_cast(arg); s->pressed = true; } void setup() { uint32_t chipId = 0; Serial.begin(115200); // Configure the input GPIOs pinMode(gpio_reset, INPUT); pinMode(switch_ch1.pin, INPUT_PULLUP); attachInterruptArg(switch_ch1.pin, isr, &switch_ch1, CHANGE); pinMode(switch_ch2.pin, INPUT_PULLUP); attachInterruptArg(switch_ch2.pin, isr, &switch_ch2, CHANGE); // Set the Relays GPIOs as output mode pinMode(gpio_relay1, OUTPUT); pinMode(gpio_relay2, OUTPUT); pinMode(gpio_led, OUTPUT); // Write to the GPIOs the default state on booting digitalWrite(gpio_relay1, DEFAULT_POWER_MODE); digitalWrite(gpio_relay2, DEFAULT_POWER_MODE); digitalWrite(gpio_led, false); Node my_node; my_node = RMaker.initNode("Sonoff Dual R3"); //Initialize switch device my_switch1 = Switch("Switch_ch1", &gpio_relay1); my_switch2 = Switch("Switch_ch2", &gpio_relay2); //Standard switch device my_switch1.addCb(write_callback); my_switch2.addCb(write_callback); //Add switch device to the node my_node.addDevice(my_switch1); my_node.addDevice(my_switch2); //This is optional RMaker.enableOTA(OTA_USING_PARAMS); //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(); //Service Name for(int i=0; i<17; i=i+8) { chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i; } Serial.printf("\nChip ID: %d Service Name: %s\n", chipId, service_name); Serial.printf("\nStarting ESP-RainMaker\n"); RMaker.start(); WiFi.onEvent(sysProvEvent); #if CONFIG_IDF_TARGET_ESP32 WiFiProv.beginProvision(WIFI_PROV_SCHEME_BLE, WIFI_PROV_SCHEME_HANDLER_FREE_BTDM, WIFI_PROV_SECURITY_1, pop, service_name); #else WiFiProv.beginProvision(WIFI_PROV_SCHEME_SOFTAP, WIFI_PROV_SCHEME_HANDLER_NONE, WIFI_PROV_SECURITY_1, pop, service_name); #endif } void loop() { if (switch_ch1.pressed) { Serial.printf("Switch 1 has been changed\n"); switch_ch1.pressed = false; // Toggle switch 1 device state switch_state_ch1 = !switch_state_ch1; Serial.printf("Toggle State to %s.\n", switch_state_ch1 ? "true" : "false"); my_switch1.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, switch_state_ch1); (switch_state_ch1 == false) ? digitalWrite(gpio_relay1, LOW) : digitalWrite(gpio_relay1, HIGH); } else if (switch_ch2.pressed) { Serial.printf("Switch 2 has been changed\n"); switch_ch2.pressed = false; // Toggle switch 2 device state switch_state_ch2 = !switch_state_ch2; Serial.printf("Toggle State to %s.\n", switch_state_ch2 ? "true" : "false"); my_switch2.updateAndReportParam(ESP_RMAKER_DEF_POWER_NAME, switch_state_ch2); (switch_state_ch2 == false) ? digitalWrite(gpio_relay2, LOW) : digitalWrite(gpio_relay2, HIGH); } // Read GPIO0 (external button to reset device if(digitalRead(gpio_reset) == LOW) { //Push button pressed Serial.printf("Reset Button Pressed!\n"); // Key debounce handling delay(100); int startTime = millis(); while(digitalRead(gpio_reset) == LOW) delay(50); int endTime = millis(); if ((endTime - startTime) > 10000) { // If key pressed for more than 10secs, reset all Serial.printf("Reset to factory.\n"); RMakerFactoryReset(2); } else if ((endTime - startTime) > 3000) { Serial.printf("Reset Wi-Fi.\n"); // If key pressed for more than 3secs, but less than 10, reset Wi-Fi RMakerWiFiReset(2); } } delay(100); }