├── .gitignore ├── README.md ├── arduino └── espLedDimmer │ └── espLedDimmer.ino ├── schematic └── espLedDimmer.png └── webApp └── espleddimmer ├── app.yaml ├── css └── slider.css ├── espleddimmer.py ├── index.html └── js └── app.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | espLedDimmer 2 | ============ 3 | 4 | Proof of concept demo for using a wi07c / ESP8266 WiFi module to control an LED Dimmer over the internet. 5 | 6 | http://i.imgur.com/ecIRovH.gif 7 | 8 | Web app built on Google App Engine: https://developers.google.com/appengine/ 9 | 10 | -------------------------------------------------------------------------------- /arduino/espLedDimmer/espLedDimmer.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #define SSID "linksys" 3 | #define PASS "ffffffffff" //your wep key here 4 | 5 | #define DST_IP "74.125.25.141" //appspot.google.com 6 | #define HOST "yourappspotinstance.appspot.com" 7 | 8 | SoftwareSerial dbgSerial(10, 11); // RX, TX 9 | int const led = 9; 10 | int const debugLed = 13; 11 | char myChar; 12 | 13 | void setup() 14 | { 15 | pinMode(led, OUTPUT); 16 | pinMode(debugLed, OUTPUT); 17 | 18 | //blink debugLed to indicate power up 19 | for (int i = 0; i < 15; i++) 20 | { 21 | digitalWrite(debugLed, HIGH); 22 | delay(50); 23 | digitalWrite(debugLed, LOW); 24 | delay(50); 25 | } 26 | 27 | // Open serial communications and wait for port to open: 28 | Serial.begin(115200); 29 | Serial.setTimeout(5000); 30 | 31 | //dbgSerial.begin(19200); //can't be faster than 19200 for softserial 32 | dbgSerial.begin(38400); //38400 works fine for me 33 | 34 | dbgSerial.println("ESP8266 Demo"); 35 | delay(100); 36 | 37 | //test if the module is ready 38 | Serial.println("AT+RST"); 39 | if (Serial.find("ready")) 40 | { 41 | dbgSerial.println("Module is ready"); 42 | delay(1000); 43 | //connect to the wifi 44 | boolean connected = false; 45 | for (int i = 0; i < 5; i++) 46 | { 47 | if (connectWiFi()) 48 | { 49 | connected = true; 50 | break; 51 | } 52 | } 53 | if (!connected) 54 | { 55 | //die 56 | while (1); 57 | } 58 | 59 | delay(5000); 60 | //set the single connection mode 61 | Serial.println("AT+CIPMUX=0"); 62 | } 63 | else 64 | { 65 | dbgSerial.println("Module didn't respond."); 66 | delay(100); 67 | 68 | //serial loop mode for diag 69 | while (1) { 70 | while (dbgSerial.available()) { 71 | myChar = dbgSerial.read(); 72 | Serial.print(myChar); 73 | digitalWrite(13, HIGH); 74 | delay(50); 75 | digitalWrite(13, LOW); 76 | delay(50); 77 | } 78 | 79 | while (Serial.available()) { 80 | myChar = Serial.read(); 81 | delay(25); 82 | dbgSerial.print(myChar); 83 | } 84 | } 85 | } 86 | } 87 | 88 | void loop() 89 | { 90 | String cmd = "AT+CIPSTART=\"TCP\",\""; 91 | cmd += DST_IP; 92 | cmd += "\",80"; 93 | Serial.println(cmd); 94 | dbgSerial.println(cmd); 95 | if (Serial.find("Error")) return; 96 | cmd = "GET /status HTTP/1.0\r\nHost: "; 97 | cmd += HOST; 98 | cmd += "\r\n\r\n"; 99 | 100 | Serial.print("AT+CIPSEND="); 101 | Serial.println(cmd.length()); 102 | if (Serial.find(">")) 103 | { 104 | // dbgSerial.print(">"); 105 | } 106 | else 107 | { 108 | Serial.println("AT+CIPCLOSE"); 109 | dbgSerial.println("connect timeout"); 110 | delay(1000); 111 | return; 112 | } 113 | 114 | int charCount = 0; 115 | String statusStr = ""; 116 | Serial.print(cmd); 117 | 118 | if ( Serial.find("status: ")) { 119 | char c; 120 | while (Serial.available() == 0); //wait for data 121 | while (Serial.available()) 122 | { 123 | c = Serial.read(); 124 | if (charCount < 3) 125 | statusStr += c; 126 | else if (charCount > 99) //avoid reading noise forever just in case 127 | break; 128 | 129 | charCount++; 130 | delay(50); //wait for more data. fixme: can this be smaller? 131 | } 132 | 133 | if (charCount > 0) 134 | { 135 | //Adjust the LED brightness 136 | analogWrite(led, statusStr.toInt() ); 137 | 138 | dbgSerial.print("status="); 139 | dbgSerial.println(statusStr); 140 | } 141 | } 142 | dbgSerial.println(); 143 | dbgSerial.println("===="); 144 | delay(1000); 145 | } 146 | 147 | boolean connectWiFi() 148 | { 149 | Serial.println("AT+CWMODE=1"); 150 | String cmd = "AT+CWJAP=\""; 151 | cmd += SSID; 152 | cmd += "\",\""; 153 | cmd += PASS; 154 | cmd += "\""; 155 | dbgSerial.println(cmd); 156 | Serial.println(cmd); 157 | delay(2000); 158 | if (Serial.find("OK")) 159 | { 160 | dbgSerial.println("OK, Connected to WiFi."); 161 | return true; 162 | } 163 | else 164 | { 165 | dbgSerial.println("Can not connect to the WiFi."); 166 | return false; 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /schematic/espLedDimmer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imjosh/espLedDimmer/f69d476aec003b086f2243723b87bc939a235563/schematic/espLedDimmer.png -------------------------------------------------------------------------------- /webApp/espleddimmer/app.yaml: -------------------------------------------------------------------------------- 1 | application: espleddimmer 2 | version: 1 3 | runtime: python27 4 | api_version: 1 5 | threadsafe: yes 6 | 7 | handlers: 8 | - url: /js 9 | static_dir: js 10 | 11 | - url: /css 12 | static_dir: css 13 | 14 | - url: .* 15 | script: espleddimmer.app 16 | 17 | libraries: 18 | - name: webapp2 19 | version: "2.5.2" 20 | - name: jinja2 21 | version: latest -------------------------------------------------------------------------------- /webApp/espleddimmer/css/slider.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Center the slider by wrapping it in a container 3 | */ 4 | .slider-container { 5 | text-align: center; 6 | } 7 | /* 8 | * Another container that holds the actual background of the slider, 9 | * with padding to make sure the handle doesn't go outside of the background. 10 | */ 11 | .slider-bg { 12 | background: transparent url('/static/images/slider_bg.png') 0% 0%; 13 | height: 235px; 14 | width: 98px; 15 | padding-top: 20px; 16 | padding-bottom: 7px; 17 | margin: 0 auto; 18 | } 19 | /* 20 | * The actual element holding the slide handler 21 | */ 22 | .ui-slider { 23 | height: 600px; 24 | width: 80px; 25 | // Overrides -moz-border-radius-topleft: 14px; 26 | -webkit-border-top-left-radius: 14px; 27 | -khtml-border-top-left-radius: 14px; 28 | border-top-left-radius: 14px; 29 | -moz-border-radius-topright: 14px; 30 | -webkit-border-top-right-radius: 14px; 31 | -khtml-border-top-right-radius: 14px; 32 | border-top-right-radius: 14px; 33 | -moz-border-radius-bottomleft: 14px; 34 | -webkit-border-bottom-left-radius: 14px; 35 | -khtml-border-bottom-left-radius: 14px; 36 | border-bottom-left-radius: 14px; 37 | -moz-border-radius-bottomright: 14px; 38 | -webkit-border-bottom-right-radius: 14px; 39 | -khtml-border-bottom-right-radius: 14px; 40 | border-bottom-right-radius: 14px; 41 | } 42 | .ui-slider-handle { 43 | width: 15px; 44 | height: 29px; 45 | background: transparent url('http://www.iconfinder.com/design/css/Aristo/images/slider_handles.png') 0% 0% ! important; 46 | z-index:2; 47 | // Overrides left: 5px !important; 48 | .border-radius(0 0 0 0); 49 | border: 0px !important; 50 | } 51 | .ui-slider-range { 52 | width: 15px; 53 | background: transparent url('/static/images/slider_range.png') 0% 0%; 54 | } 55 | .ui-widget-header { 56 | border: 1px solid #dddddd; 57 | background: #33CCFF url(images/ui-bg_highlight-soft_50_dddddd_1x100.png) repeat-x 50% 50%; 58 | color: #444444; 59 | font-weight: bold; 60 | -moz-border-radius: 50px; 61 | border-radius: 50px; 62 | } -------------------------------------------------------------------------------- /webApp/espleddimmer/espleddimmer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2007 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | import os 18 | import urllib 19 | import cgi 20 | import datetime 21 | 22 | from google.appengine.ext import ndb 23 | from google.appengine.api import users 24 | 25 | import jinja2 26 | import webapp2 27 | 28 | JINJA_ENVIRONMENT = jinja2.Environment( 29 | loader=jinja2.FileSystemLoader(os.path.dirname(__file__)), 30 | extensions=['jinja2.ext.autoescape'], 31 | autoescape=True) 32 | 33 | class Lamp(ndb.Model): 34 | status = ndb.IntegerProperty() 35 | 36 | myLamp = Lamp(status=10) 37 | myLamp_key = myLamp.put() 38 | 39 | class MainPage(webapp2.RequestHandler): 40 | def get(self): 41 | myLamp = myLamp_key.get() 42 | status = myLamp.status 43 | 44 | template_values = { 'status' : status } 45 | template = JINJA_ENVIRONMENT.get_template('index.html') 46 | self.response.write(template.render(template_values)) 47 | 48 | class Status(webapp2.RequestHandler): 49 | def get(self): 50 | myLamp = myLamp_key.get() 51 | self.response.out.write('status: %s' % myLamp.status) 52 | def post(self): 53 | myLamp = myLamp_key.get() 54 | myLamp.status = int(self.request.get('status')) 55 | myLamp.put() 56 | self.redirect('/') 57 | 58 | app = webapp2.WSGIApplication([ 59 | ('/', MainPage), 60 | ('/status', Status) 61 | ], debug=True) 62 | -------------------------------------------------------------------------------- /webApp/espleddimmer/index.html: -------------------------------------------------------------------------------- 1 | 2 | {% autoescape true %} 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 | 21 |
22 |
23 |
24 | 25 | 26 | {% endautoescape %} -------------------------------------------------------------------------------- /webApp/espleddimmer/js/app.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){ 2 | $('div.slider').slider({ 3 | orientation: 'vertical', 4 | value: [$("#value1").val()], 5 | min: 0, 6 | max: 255, 7 | range: 'min', 8 | animate: true, 9 | step: 1, 10 | slide: function (event, ui) { 11 | $("#value1").val(ui.value); 12 | }, 13 | stop: function (event, ui) { 14 | $(this).closest("form").submit(); 15 | } 16 | }); 17 | 18 | $("#sub, #value1").hide(); 19 | }); --------------------------------------------------------------------------------