├── BoardSettings.png ├── README.md ├── SonoffV1.0.png └── firmware └── SonoffWebserver ├── Sonoff.h ├── SonoffWebserver.ino ├── relay.h └── switch.h /BoardSettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImUrlaub/Sonoff/86f92e77348ab0ad6d2850b0f1e893c5d54b2413/BoardSettings.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sonoff 2 | Alternative firmware for the itead Sonoff based on the Arduino port for the ESP8266 3 | 4 | ## Features 5 | ### Common 6 | #### Relay control with eco mode to reduce power consumption 7 | Relays need less current one they are fully switched on. PWM is used here to reduce the current after a period of time which is needed to turn the relay on. If you experience switching off of the relay if it should stay on the reduction parameter has to be increased. please see Sonoff.h and relay.h for further info. 8 | 9 | **If you have pets at home** please be aware that they might hear the PWM frequnecy of currently 25kHz. Please set the reduction paramter to 1.0, which switches off the eco mode but will keep your pets at peace. 10 | #### Switch reading with debouncing and events 11 | ### Sonoff Webserver 12 | * Control the relay through the webpage found under http://sonoff.local 13 | 14 | ## Settings for the Arduino IDE 15 | 16 | First I had a strange behavior: 17 | * Serial working 18 | * GPIO working 19 | * **and Wifi was not connecting**. 20 | 21 | Thanks to Leonzio who posted here http://tech.scargill.net/itead-slampher-and-sonoff/#comment-13634 I figured out that I also had the wrong flash size set. 22 | 23 | Flash size to set for a generic module in the Arduino IDE is 512k for the V1.0 hardware. Even if the chip says 1M bit. 24 | 25 | Also be aware that OTA is not support with that little flash. Tried it but it will **not** advertise itself and never shows up in the list of OTA devices. 26 | 27 | Here the working setting for the Arduino ESP8622 2.0 IDE 28 | 29 | ![Board settings](BoardSettings.png) 30 | 31 | ## Helpfull links 32 | * https://github.com/esp8266/Arduino 33 | * http://www.esp8266.com/wiki/doku.php?id=esp8266_gpio_pin_allocations 34 | * http://wiki.iteadstudio.com/images/6/6b/Sonoff_schmatic.pdf 35 | * http://www.john-lassen.de/index.php/projects/esp-8266-arduino-ide-webconfig 36 | * https://github.com/bblanchon/ArduinoJson 37 | * https://github.com/knolleary/pubsubclient 38 | -------------------------------------------------------------------------------- /SonoffV1.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ImUrlaub/Sonoff/86f92e77348ab0ad6d2850b0f1e893c5d54b2413/SonoffV1.0.png -------------------------------------------------------------------------------- /firmware/SonoffWebserver/Sonoff.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef Sonoff_h 3 | #define Sonoff_h 4 | 5 | #include "relay.h" 6 | #include "switch.h" 7 | 8 | static const uint8_t RELAY = 12; // MTDI 9 | static const uint8_t SWITCH = 0; // GPIO0 10 | static const uint8_t LED = 13; // MTCK 11 | 12 | class Sonoff 13 | { 14 | 15 | private: 16 | 17 | 18 | 19 | public: 20 | Relay relay = Relay( RELAY, 0.4, 500, 25000); 21 | Switch sw = Switch( SWITCH, 2, 5); 22 | 23 | Sonoff(void) { 24 | 25 | } 26 | 27 | void loop() { 28 | relay.loop(); 29 | sw.loop(); 30 | } 31 | 32 | }; 33 | #endif /* Sonoff_h */ 34 | 35 | -------------------------------------------------------------------------------- /firmware/SonoffWebserver/SonoffWebserver.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 ImUrlaub 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without modification, 6 | * are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, this 12 | * list of conditions and the following disclaimer in the documentation and/or 13 | * other materials provided with the distribution. 14 | * 15 | * * Neither the name of Majenko Technologies nor the names of its 16 | * contributors may be used to endorse or promote products derived from 17 | * this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "Sonoff.h" 37 | 38 | const char *ssid = "ssid"; 39 | const char *password = "pass"; 40 | 41 | ESP8266WebServer server ( 80 ); 42 | 43 | Sonoff sonoff; 44 | 45 | const int led = LED; 46 | 47 | void handleRoot() { 48 | 49 | char temp[400]; 50 | int sec = millis() / 1000; 51 | int min = sec / 60; 52 | int hr = min / 60; 53 | 54 | 55 | for ( uint8_t i = 0; i < server.args(); i++ ) { 56 | if(server.argName(i) == String("relay")){ 57 | if(server.arg(i) == String("on")){ 58 | sonoff.relay.on(); 59 | digitalWrite ( led, 0 ); 60 | } 61 | else if(server.arg(i) == String("off")){ 62 | sonoff.relay.off(); 63 | digitalWrite ( led, 1 ); 64 | } 65 | } 66 | } 67 | char *r; 68 | if(sonoff.relay.isOn()) r = "on"; 69 | else r = "off"; 70 | 71 | snprintf ( temp, 400, 72 | 73 | "\ 74 | \ 75 | \ 76 | Sonoff Demo\ 77 | \ 80 | \ 81 | \ 82 |

Hello from Sonoff!

\ 83 |

Uptime: %02d:%02d:%02d

\ 84 |

Relay: %s

\ 85 | Relay on
\ 86 | Relay off
\ 87 | \ 88 | ", 89 | 90 | hr, min % 60, sec % 60, r 91 | ); 92 | server.send ( 200, "text/html", temp ); 93 | } 94 | 95 | void handleNotFound() { 96 | String message = "File Not Found\n\n"; 97 | message += "URI: "; 98 | message += server.uri(); 99 | message += "\nMethod: "; 100 | message += ( server.method() == HTTP_GET ) ? "GET" : "POST"; 101 | message += "\nArguments: "; 102 | message += server.args(); 103 | message += "\n"; 104 | 105 | for ( uint8_t i = 0; i < server.args(); i++ ) { 106 | message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n"; 107 | } 108 | 109 | server.send ( 404, "text/plain", message ); 110 | } 111 | 112 | void setup ( void ) { 113 | pinMode ( led, OUTPUT ); 114 | digitalWrite ( led, 1 ); 115 | Serial.begin ( 115200 ); 116 | WiFi.begin ( ssid, password ); 117 | Serial.println ( "" ); 118 | 119 | // Wait for connection 120 | while ( WiFi.status() != WL_CONNECTED ) { 121 | delay ( 500 ); 122 | Serial.print ( "." ); 123 | } 124 | 125 | Serial.println ( "" ); 126 | Serial.print ( "Connected to " ); 127 | Serial.println ( ssid ); 128 | Serial.print ( "IP address: " ); 129 | Serial.println ( WiFi.localIP() ); 130 | 131 | if ( MDNS.begin ( "Sonoff" ) ) { 132 | Serial.println ( "MDNS responder started" ); 133 | } 134 | 135 | server.on ( "/", handleRoot ); 136 | server.on ( "/on", []() { 137 | sonoff.relay.on(); 138 | digitalWrite ( led, 0 ); 139 | server.send ( 200, "text/plain", "on" ); 140 | } ); 141 | server.on ( "/off", []() { 142 | sonoff.relay.off(); 143 | digitalWrite ( led, 1 ); 144 | server.send ( 200, "text/plain", "off" ); 145 | } ); 146 | server.on ( "/state", []() { 147 | if(sonoff.relay.isOn()) server.send ( 200, "text/plain", "on" ); 148 | else server.send ( 200, "text/plain", "off" ); 149 | } ); 150 | server.onNotFound ( handleNotFound ); 151 | server.begin(); 152 | Serial.println ( "HTTP server started" ); 153 | } 154 | 155 | void loop ( void ) { 156 | server.handleClient(); 157 | sonoff.loop(); 158 | if(sonoff.sw.getEvent() == SWITCH_EVENT_ON){ 159 | if(sonoff.relay.isOn()){ 160 | sonoff.relay.off(); 161 | digitalWrite ( led, 1 ); 162 | } 163 | else { 164 | sonoff.relay.on(); 165 | digitalWrite ( led, 0 ); 166 | } 167 | } 168 | } 169 | 170 | 171 | -------------------------------------------------------------------------------- /firmware/SonoffWebserver/relay.h: -------------------------------------------------------------------------------- 1 | #ifndef relay_h 2 | #define relay_h 3 | 4 | class Relay 5 | { 6 | private: 7 | 8 | unsigned long time_to_reduce_current; 9 | unsigned long millis_till_reduction; 10 | uint16_t reduced_current; 11 | 12 | uint8_t pin; 13 | 14 | static const uint8_t RELAY_STATE_OFF = 0; 15 | static const uint8_t RELAY_STATE_ON_WAIT_TO_REDUCE = 1; 16 | static const uint8_t RELAY_STATE_ON_REDUCED = 1; 17 | uint8_t state = RELAY_STATE_OFF; 18 | 19 | public: 20 | 21 | Relay(uint8_t pin = 0, float reduction = 0.4, unsigned long millis_till_reduction = 500, unsigned long analog_write_frequency = 10000) { 22 | setReduction(reduction); 23 | 24 | setMillisTillReduction(millis_till_reduction); 25 | 26 | analogWriteFreq(analog_write_frequency); 27 | 28 | this->pin = pin; 29 | pinMode ( pin, OUTPUT ); 30 | } 31 | 32 | void on() { 33 | switch (state) { 34 | case RELAY_STATE_OFF: 35 | analogWrite(pin, PWMRANGE); 36 | time_to_reduce_current = millis() + millis_till_reduction; 37 | state = RELAY_STATE_ON_WAIT_TO_REDUCE; 38 | break; 39 | } 40 | } 41 | 42 | void off() { 43 | analogWrite(pin, 0); 44 | state = RELAY_STATE_OFF; 45 | } 46 | 47 | bool isOn() { 48 | return state != RELAY_STATE_OFF; 49 | } 50 | 51 | void setReduction(float reduction) { 52 | if ( reduction > 1.0 ) reduction = 1.0; 53 | else if ( reduction < 0.0 ) reduction = 0.0; 54 | reduced_current = (PWMRANGE * 1.0) * reduction; 55 | } 56 | 57 | void setMillisTillReduction(unsigned long millis_till_reduction) { 58 | this->millis_till_reduction = millis_till_reduction; 59 | } 60 | 61 | void loop() { 62 | switch (state) { 63 | case RELAY_STATE_ON_WAIT_TO_REDUCE: 64 | if (millis() > time_to_reduce_current) { 65 | analogWrite(pin, reduced_current); 66 | state = RELAY_STATE_ON_REDUCED; 67 | } 68 | break; 69 | } 70 | } 71 | 72 | }; 73 | #endif /* relay_h */ 74 | 75 | -------------------------------------------------------------------------------- /firmware/SonoffWebserver/switch.h: -------------------------------------------------------------------------------- 1 | #ifndef switch_h 2 | #define switch_h 3 | 4 | static const uint8_t SWITCH_EVENT_IDLE = 1; 5 | static const uint8_t SWITCH_EVENT_OFF = 1; 6 | static const uint8_t SWITCH_EVENT_ON = 2; 7 | 8 | /* 9 | We will check the switch every debounce_intervall_ms. 10 | If the logic value stays debounce_count times at a differnt level than the current one 11 | an event is triggered and the new state is stored. 12 | */ 13 | 14 | class Switch 15 | { 16 | 17 | private: 18 | 19 | static const uint8_t SWITCH_STATE_OFF = 0; 20 | static const uint8_t SWITCH_STATE_ON = 1; 21 | uint8_t state = SWITCH_STATE_OFF; 22 | 23 | unsigned long debounce_intervall_ms; 24 | unsigned long time_to_check_switch; 25 | 26 | bool polarity; 27 | 28 | uint8_t debounce_counter; 29 | uint8_t debounce_count; 30 | 31 | uint8_t pin; 32 | 33 | uint8_t event = SWITCH_EVENT_IDLE; 34 | 35 | public: 36 | 37 | Switch(uint8_t pin = 0, bool polarity = false, uint8_t debounce_count = 3, unsigned long debounce_intervall_ms = 5) { 38 | this->pin = pin; 39 | pinMode ( pin, INPUT ); 40 | 41 | this->polarity = polarity; 42 | 43 | debounce_counter = 0; 44 | this->debounce_count = debounce_count; 45 | this->debounce_intervall_ms = debounce_intervall_ms; 46 | time_to_check_switch = millis() + debounce_intervall_ms; 47 | 48 | state = SWITCH_EVENT_OFF; 49 | } 50 | 51 | uint8_t getEvent(void) { 52 | uint8_t e = event; 53 | event = SWITCH_EVENT_IDLE; 54 | return e; 55 | } 56 | 57 | bool isOn() { 58 | return state == SWITCH_STATE_ON; 59 | } 60 | 61 | void loop() { 62 | if (millis() > time_to_check_switch) { 63 | 64 | time_to_check_switch = millis() + debounce_intervall_ms; 65 | 66 | switch (state) { 67 | 68 | case SWITCH_STATE_OFF: 69 | if ((digitalRead(pin) == HIGH && polarity == true) || (digitalRead(pin) == LOW && polarity == false) ) { 70 | debounce_counter++; 71 | if (debounce_counter >= debounce_count) { 72 | debounce_counter = 0; 73 | state = SWITCH_STATE_ON; 74 | event = SWITCH_EVENT_ON; 75 | } 76 | } 77 | else { 78 | debounce_counter = 0; 79 | } 80 | break; 81 | 82 | case SWITCH_STATE_ON: 83 | 84 | if ((digitalRead(pin) == LOW && polarity == true) || (digitalRead(pin) == HIGH && polarity == false) ) { 85 | debounce_counter++; 86 | if (debounce_counter >= debounce_count) { 87 | debounce_counter = 0; 88 | state = SWITCH_STATE_OFF; 89 | event = SWITCH_EVENT_OFF; 90 | } 91 | } 92 | else { 93 | debounce_counter = 0; 94 | } 95 | break; 96 | 97 | } 98 | } 99 | } 100 | }; 101 | 102 | #endif /* switch_h */ 103 | 104 | --------------------------------------------------------------------------------