BriComp
Well-known member
I have developed a project where I turn a valve on/off switched by a Sonoff Basic2, commanded by an ESP32-C3 over WiFi controlled by just ONE Teensy pin.
Below is a schematic picture of the bit used.
The ESP32-C3 in the form of a T-01C3 sends a control message to the Sonoff Basic2 to turn it's Relay and Led on or off.
In order to do this the Sonoff MUST have it's re-programmed to accept ESP_Now commands.
The code for this firmware is shown below. To re-program it follow the method shown here.
During operation the ESP32-C3 is sent to DeepSleep. It is woken up by the Teensy, by the Teense setting a pin low (the ESP32-C3 can use high or low). I have found by experiment that an ESP32-C3 takes circa 43.5ms to become fully compos mentis.
Therefore we can put our wakeup pin LOW for ~10ms to start the ESP32-C3 wakeup process, then decide whether we want to have the Sonoff turn it's relay (and led) on or off. If we want it off we leave the WakeUp Pin LOW for ~70ms more so that the ESP32 will have time to read the pin and determine it as a LOW and send a message to the Sonoff to turn off. If we want the Sonoff to turn on we set the Wakeup pin HIGH after the 10ms initial pulse.
Below is the software for the three devices:
Teensy Code:
Sonoff Code:
ESP32-C3 Code:
SimpleSonoff.h Code used by both ESP32-C3 and Sonoff.
Below is a schematic picture of the bit used.
The ESP32-C3 in the form of a T-01C3 sends a control message to the Sonoff Basic2 to turn it's Relay and Led on or off.
In order to do this the Sonoff MUST have it's re-programmed to accept ESP_Now commands.
The code for this firmware is shown below. To re-program it follow the method shown here.
During operation the ESP32-C3 is sent to DeepSleep. It is woken up by the Teensy, by the Teense setting a pin low (the ESP32-C3 can use high or low). I have found by experiment that an ESP32-C3 takes circa 43.5ms to become fully compos mentis.
Therefore we can put our wakeup pin LOW for ~10ms to start the ESP32-C3 wakeup process, then decide whether we want to have the Sonoff turn it's relay (and led) on or off. If we want it off we leave the WakeUp Pin LOW for ~70ms more so that the ESP32 will have time to read the pin and determine it as a LOW and send a message to the Sonoff to turn off. If we want the Sonoff to turn on we set the Wakeup pin HIGH after the 10ms initial pulse.
Below is the software for the three devices:
Teensy Code:
Code:
// Visual Micro is in vMicro>General>Tutorial Mode
//
/*
Name: Demo_Teensy_Control_Sonoff_Basic.ino
Created: 02/Jly/2023 16:06:10
Author: Robert E Bridges
*/
#define debugz
#define teensyESP32_C3_WakeUpPin 23
#define teensyEsp32WakeupLevel LOW // MUST BE THE SAME AS ESP32 LEVEL
bool sonoffOn = false;
#ifdef debug
void Delay(uint32_t howLong) {
uint32_t t = millis() + howLong;
while (millis() < t) {
if (Serial1.available()) Serial.write(Serial1.read());
}
}
#endif
void TurnSonoff(bool on) {
sonoffOn = on;
digitalWriteFast( teensyESP32_C3_WakeUpPin, teensyEsp32WakeupLevel );
#ifdef debug
if (on) Delay(70); else Delay(10);
#else
if (on) delay(70); else delay(10);
#endif
digitalWriteFast( teensyESP32_C3_WakeUpPin, !teensyEsp32WakeupLevel );
digitalWriteFast(LED_BUILTIN, on);
}
void setup()
{
#ifdef debug
Serial.begin(115200);
Serial1.begin(115200);
#endif
pinMode(LED_BUILTIN, OUTPUT);
pinMode( teensyESP32_C3_WakeUpPin, OUTPUT );
digitalWriteFast( teensyESP32_C3_WakeUpPin, !teensyEsp32WakeupLevel );
}
void loop()
{
TurnSonoff(!sonoffOn); // Will turn Sonoff On/Off every 10 seconds
#ifdef debug
Delay(10000);
#else
delay(10000);
#endif
}
Sonoff Code:
Code:
#include <ESP8266WiFi.h>
#include <espnow.h>
#include <SimpleSonoff.h>
#define printMacAddressz
#ifdef printMacAddress
#define printData
#include <HardwareSerial.h>
HardwareSerial mySerial(0);
#endif
#define sonoffRelay 12
#define sonoffLed 13
#define sonoffButton 0
uint8_t sonoffState;
bool relayState;
bool ledState;
bool errorOccured = false;
void HandleStateSettings() {
relayState = ( sonoffState & turnRelayOn );
ledState = (( sonoffState & turnLedOn ) == 0 ); // led is active low, ie low to turn it on
#ifdef printData
mySerial.print("relayState: "); mySerial.println(relayState);
mySerial.print("ledState : "); mySerial.println(ledState);
#endif
digitalWrite( sonoffRelay, relayState );
digitalWrite( sonoffLed, ledState );
}
void ToggleState() {
sonoffState = (( ~sonoffState) & 0b00000011 );
HandleStateSettings();
}
// Callback function that will be executed when data is received
void OnDataRecv(uint8_t* mac, uint8_t* incomingData, uint8_t len) {
memcpy(&myData, incomingData, sizeof(myData));
if ((myData.command & toggleState) > 0) {
ToggleState();
} else
{
HandleStateSettings();
sonoffState = myData.command;
}
}
void setup() {
#ifdef printMacAddress
mySerial.begin(115200);
mySerial.println();
mySerial.print("Sonoff(ESP8266) Board MAC Address: ");
mySerial.println(WiFi.macAddress());
#endif
pinMode( sonoffLed, OUTPUT );
pinMode( sonoffRelay, OUTPUT );
pinMode( sonoffButton, INPUT);
sonoffState = 0; // all off
HandleStateSettings();
errorOccured = true;
WiFi.disconnect();
ESP.eraseConfig();
// Set device as a Wi-Fi Station
if (WiFi.mode(WIFI_STA)) {
WiFi.disconnect();
// Init ESP-NOW
if (esp_now_init() == 0) {
// Once ESPNow is successfully Init, we will register for recv CB to
// get recv packer info
if (esp_now_set_self_role(ESP_NOW_ROLE_SLAVE) == 0) {
if (esp_now_register_recv_cb(OnDataRecv) == 0) {
errorOccured = false;
}
}
}
}
}
uint8_t count = 0;
void loop() {
if ( errorOccured){
digitalWrite(sonoffLed, LOW);
delay(1000);
digitalWrite(sonoffLed, HIGH);
delay(1000);
} else
{
count = 0;
while (digitalRead(sonoffButton) == 0 && count < 5) {
count++;
delay(35);
}
if (count >= 5) {
ToggleState();
while (digitalRead(sonoffButton) == 0) {} // wait for button to be released
}
}
}
ESP32-C3 Code:
Code:
/*
One pin from Teensy Controls a SonOff WiFi relay
*/
#include <esp_now.h>
#include <WiFi.h>
#include <esp_sleep.h>
#include <SimpleSonoff.h>
/**************************************************
* Configure the next 5 lines to suit your system *
***************************************************/
#define ESP32_C3_Wakeup_Pin 2 // The pin on ESP32_C3 which will be triggered to wake up ESP32
#define ESPWakeupLevel ESP_GPIO_WAKEUP_GPIO_LOW // MUST BE THE SAME ON TEENSY
#define timeoutTime 10000 // Timeout for sending data in ms
uint8_t broadcastAddress[] = { 0x40, 0xF5, 0x20, 0xFE, 0xA1, 0x88 }; // MAC address of Sonoff to be turned ON/OFF
#define ledPin 3
#define espDeepSleepStartFailure 8
#define espDeepSleepEnableGpioWakeupFailure 7
#define WiFiModeFailure 6
#define espNowInitFailure 5
#define espNowRegisterSendCbFailure 4
#define espNowSendFailure 3
#define espNowAddPeerFailure 2
#define notReceived 1
#define received 0
enum wakeupCauseType {
gpioFromSleep,
resetOrColdBoot
};
struct errType {
uint8_t level;
uint8_t state;
};
errType err;
esp_now_peer_info_t peerInfo;
struct_message sonoffMessage;
uint8_t whatCausedStartup;
bool turnSonoffOn;
uint32_t timeout;
// Callback when data received by sonoff
void OndataReceived(const uint8_t* mac_addr, esp_now_send_status_t sendStatus) {
err.level = sendStatus; // 0 = received
}
uint8_t DetermineWakeUp() {
uint8_t t;
esp_sleep_wakeup_cause_t wakeup_cause = esp_sleep_get_wakeup_cause();
if (wakeup_cause == ESP_SLEEP_WAKEUP_GPIO) t = gpioFromSleep; else t = resetOrColdBoot;
return t;
}
void StartWiFiAndSendMessage() {
err.level = notReceived;
err.state = 0;
if (WiFi.mode(WIFI_STA)) {
WiFi.disconnect();
if (esp_now_init() == ESP_OK) {
if (esp_now_register_send_cb(OndataReceived) == ESP_OK) {
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = commsChannel;
peerInfo.encrypt = false;
err.state = esp_now_add_peer(&peerInfo);
if (err.state == ESP_OK) {
if (turnSonoffOn == HIGH)
sonoffMessage.command = turnRelayOn + turnLedOn;
else
sonoffMessage.command = turnRelayOff + turnLedOff;
err.state = esp_now_send(broadcastAddress, (uint8_t*)&sonoffMessage, sizeof(sonoffMessage));
if (err.state == ESP_OK)
err.level = notReceived;
else
err.level = espNowSendFailure;
}
else err.level = espNowAddPeerFailure;
}
else err.level = espNowRegisterSendCbFailure;
}
else err.level = espNowInitFailure;
}
else err.level = WiFiModeFailure;
}
void setup() {
/****************************************************
** MUST READ wakeUpPin as soon as programme starts **
** to determine if turn Sonoff ON or OFF **
*****************************************************/
pinMode(ESP32_C3_Wakeup_Pin, INPUT_PULLUP);
turnSonoffOn = digitalRead(ESP32_C3_Wakeup_Pin);
whatCausedStartup = DetermineWakeUp();
if (whatCausedStartup == gpioFromSleep) { // Not a Cold Start or Reset
StartWiFiAndSendMessage(); // sets err.level and err.state in function as appropriate
}
else {
err.level = received; // Wakeup NOT from gpio, probably initial start up
}
// so go to sleep until woken up properly.
if (err.level < 2) { // = received or notReceived
if (err.level == notReceived) {
timeout = millis() + timeoutTime; // 10 seconds
while ((err.level == notReceived) && (millis() < timeout)) {}; // Wait for Sonoff to receive data
}
if (err.level == received) {
if (esp_deep_sleep_enable_gpio_wakeup((uint64_t)1 << ESP32_C3_Wakeup_Pin, ESPWakeupLevel) == ESP_OK) {
esp_deep_sleep_start();
err.level = espDeepSleepStartFailure; // SHOULD NEVER GET HERE
}
else err.level = espDeepSleepEnableGpioWakeupFailure;
}
}
pinMode(ledPin, OUTPUT);
if (err.level > 0) err.level--;
}
void FlashLed(uint8_t numTimes, uint16_t delBetween) {
for (uint8_t i = 0; i < numTimes; i++) {
digitalWrite(ledPin, HIGH);
delay(delBetween);
digitalWrite(ledPin, LOW);
delay(delBetween);
}
}
void DisplayErrorState() {
uint32_t tim = millis() + 10000; // Indicate state for 10 seconds then reboot
while (millis() < tim) { // If Error LONG ON, MEDIUM OFF followed by SHORT flashes for ERROR CODE
digitalWrite(ledPin, HIGH);
delay(500);
if (err.level > 0) delay(500);
digitalWrite(ledPin, LOW);
delay(500);
FlashLed(err.level, 200);
if (err.state > 0) {
delay(1000);
FlashLed(err.state, 200);
}
}
}
void loop() {
DisplayErrorState();
ESP.restart();
}
SimpleSonoff.h Code used by both ESP32-C3 and Sonoff.
Code:
#pragma once
#pragma pack (push, 1)
typedef struct struct_message {
uint8_t command;
} struct_message;
struct_message myData;
#define turnRelayOn 0b00000001
#define turnRelayOff 0b00000000
#define turnLedOn 0b00000010
#define turnLedOff 0b00000000
#define toggleState 0b00000100
#pragma pack (pop)
#define commsChannel 1