Once, my girlfriend sent me this Twitter post:

…and I accepted the challenge.
However, I understood that creating an app will not be an universal solution, because she uses iPhone and as soon as I do not have an Apple Developer License (that means my app will not last on her phone longer than 48 hours), I decided to create a website, that will be available on all devices, all versions and mainly, all around the world. Here is what it turned into.
Short description
For hardware part, I have chosen Wemos D1 R2 WiFi board because it already had an integrated ESP2866 module in it.
For software, I decided to use Python web server as soon as it works with Web Sockets, and to use some simple html and js for a tiny website.
For the visible part of the process, I bought a box/lamp with replaceable letters.
The best idea to make it online was to connect it to the cloud. So this mini-server is deployed at Heroku cloud service, connected to my home WiFi and can be reached from any device with the internet connection. Last but not least, it was a perfect gift for my girlfriend 🙂
How it looks like inside



Demonstration
Code, I used
Python web server:
from flask import Flask, request, render_template from flask_sockets import Sockets import gevent import time import json ''' Setting up the webserver ''' app = Flask(__name__) app.debug = True ''' Setting up the websocket ''' sockets = Sockets(app) ''' Multiple clients handling ''' clients = list() ''' Client object ''' class Client: def __init__(self): self.queue = gevent.queue.Queue() def put(self, v): self.queue.put_nowait(v) def get(self): return self.queue.get() ''' User-defined function for sending data on the websocket to all clients ''' def send_all(msg): for client in clients: client.put(msg) ''' Reading from websocket ''' def read_ws(ws, client): while not ws.closed: gevent.sleep(0) try: msg = ws.receive() print "WS RECEIVED: %s" % msg if(msg is None): client.put(msg) else: send_all(msg) except: print "WS ERROR: read_ws exception" ''' Adding a route to the websockets server to "subscribe" clients ''' @sockets.route('/subscribe') def subscribe_socket(ws): client = Client() clients.append(client) print '# Clients: {}'.format(len(clients)) g = gevent.spawn( read_ws, ws, client) # If data is received by the queue, send it to the websocket try: while g: msg = client.get() if msg is not None: ws.send(msg) else: break except Exception as e: print "WS ERROR: %s" % e finally: ws.close() clients.remove(client) gevent.kill(g) ''' Adding a route to the webserver ''' @app.route('/') def index(): return render_template("index.html") # rendering index.html in 'templates' dir
Arduino code:
#include <ESP8266WiFi.h> #include <WebSocketsClient.h> #include <Servo.h> //Declaring servo Servo servo; // Connecting to the internet char * ssid = "Your wifi SSID"; char * password = "Your wifi password"; int servo_position = 0; // Setting up the websocket client WebSocketsClient webSocket; void setup() { pinMode(BUILTIN_LED, OUTPUT); servo.attach(D6); Serial.begin(115200); // WiFi.mode(WIFI_STA); //use this option, if you are connecting to personal hotspot WiFi.begin(ssid, password); Serial.println(""); while(WiFi.status()!=WL_CONNECTED) { Serial.print("."); delay(500); } blinkTheLed(2); servo.write(servo_position); Serial.println(""); Serial.println("Connected to WiFi."); Serial.print("IP Address: "); Serial.println(WiFi.localIP()); // webSocket.begin("172.20.10.12", 8000, "/subscribe"); //webSocket for local testing webSocket.begin("Your Heroku cloud app name.herokuapp.com", 80, "/subscribe"); webSocket.onEvent(webSocketEvent); webSocket.setReconnectInterval(10); } void loop() { webSocket.loop(); } void webSocketEvent(WStype_t type, uint8_t *payload, size_t length) { switch(type) { case WStype_DISCONNECTED: Serial.printf("Socket disconnected!n"); break; case WStype_CONNECTED: Serial.printf("Socket connected to url: %sn", payload); servo.detach(); break; case WStype_TEXT: if((char)payload[0] == '1'){ Serial.println("Received button push!"); webSocket.sendTXT("done"); notify(); } else if((char)payload[0] == '3'){ Serial.println("New connection!"); webSocket.sendTXT("connected"); } break; } } void blinkTheLed(int howManyTimes){ for(int i = 0; i < howManyTimes; i++){ digitalWrite(BUILTIN_LED, HIGH); delay(150); digitalWrite(BUILTIN_LED, LOW); delay(150); } } //Method that is called when notification arrives void notify(){ int a = 320; //blinking start interval for(int i = 0; i < 15; i++){ digitalWrite(BUILTIN_LED, HIGH); delay(a); digitalWrite(BUILTIN_LED, LOW); delay(a); a = a - 18; } digitalWrite(BUILTIN_LED, HIGH); wave(3); delay(6000); digitalWrite(BUILTIN_LED, LOW); } //Method that is used to simulate heart waving with servo void wave(int howManyTimes){ servo.attach(D6); for(int i = 0; i < howManyTimes; i++){ for (servo_position = 0; servo_position <=165; servo_position +=1){ servo.write(servo_position); delay(3); } for (servo_position=165; servo_position >= 0; servo_position -=1){ servo.write(servo_position); delay(3); } } servo.detach(); //important to use this line. Otherwise servo motor will make noise after certain time. }
Comments (2)
-
I love anything and everything that is written well… yeah you got some good content going on there for sure.
-
Normally I don’t learn post on blogs, however I would
like to say that this write-up very compelled me to check
out and do so! Your writing taste has been surprised me.
Thank you, very nice post.
Leave a reply
You must be logged in to post a comment.