Sunday, June 11, 2017

Measuring Fidget Spinner RPM via TEK MDO4000 Oscilloscope


-----
How fast can a Fidget Spinner spin?  Well, it depends on what spins it.  Let's use compressed air.


That sounds fast, it's actually scary to hold!!!  But how fast?
-----
To make the measurement we used a simple LED as a sensor.  A bright light shines down onto the LED.  The arms of the Fidget Spinner block the light as they move over the LED creating electrical pulses. 
-----
The sensor LED is connected to a Tektronix MDO4104C to measure the pulses.  The scope (along with a few thousand other features) allows an engineer to quickly study waveform measurements with cursors.  After a little math we get our answer; well over 10,000 RPM.
That's fast!
-----

Monday, May 22, 2017

Tweeting Cat Door (now with Rasperry PI)

-----
It is hard to believe, but it was over five years ago that we created a Tweeting CatDoor using the Electric Imp.  Basic hobby IoT was much harder back then.  The Electric Imp, being a commercial grade product, was rock solid.  It only needed to reboot twice during it's entire service.  That said, a lightening strike may have gotten the best of it.
----
Basic hobby IoT is easier today thanks to project sites like HackaDay, etc.

A look into our spare parts box prompted some changes:  The Electric Imp gets replaced by a Raspberry Pi ZERO and WiFi dongle (we didn't have a spare ZERO-W laying around).  The original magnetic reed switch implementation goes solid state and gets replaced with a A3144EUA Hall Effect Sensor.  The concept is still the same as the original; a magnet is placed on the door and when Zena or Kelso use the door a magnet swings past the stationary sensor triggering a GPIO event.

As shown in the wiring diagram above, the build is simple.  The python code straightforward.

For the python code below you will need you create your own Twitter App and establish:
      - Consumer Key (API Key) and Consumer Secret (API Secret)
      - Access Token and Access Token Secret
These Keys and Tokens are, of course, changed in the python code below for obvious reasons.  Also, to save space the 'database' of tweets listed in the code is reduced.
-----
# May 2017 / WhiskeyTangoHotel.Com
# Raspberry PI ZERO for tweeting Cat Door activity
# MagSwitch is model A3144EUA Hall Effect sensor with magnet next to it
# Activate tweet loop whenever the Hall Effect Sensor changes state

import random
import time

# Set up Auth and token ID for the twitter API
from twython import Twython
# fill in your 4 keys in following variables
C_key = "ckckckckckblaghblagckckckckck"
C_secret = "cscscscscsblaghblaghblaghcscscscscs"
A_token = "atatatatatblaghblaghblagatatatatat"
A_secret = "asasasasasblaghblaghblagasasasasas"

localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
Current_day = time.strftime("%d", time.localtime()) # what day of the month is it
New_day_dawned = Current_day # 1st run after midnight send status tweet to verify program is running

###  PROGRAM VAR SETTINGS ###

# Flood control (in seconds) for debounce and not sending to many tweets
Flood_control = 180 # seconds

# Really tweet (YES) or just test/debug (NO)?
Tweet = "YES"   # YES or NO in ALL UPPERCASE

# Track how many times the door has been triggered
Counter = 0

# Set up Hall Effect Sensor
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD) # to use Raspberry Pi board pin numbers
MagSwitch = 8  # Pin from signal of hall effect sensor
#Set pin to input and set pull-up resistor to hold the pin is high
GPIO.setup(MagSwitch, GPIO.IN, pull_up_down=GPIO.PUD_UP)
print ' '
print "Program starts with Flood control of " + str(Flood_control) + " seconds."
print "Tweet set to " + Tweet
print " "

localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
print "RasPI started on " + localtime + ".  Begin the mission!!!"
MagSwitch_Current = GPIO.input(MagSwitch)
#print "MagSwitch value is: " + str(MagSwitch_Current)
print ' '
if (Tweet == "YES"):
    myTweet = Twython(C_key,C_secret,A_token,A_secret)
    localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
    myTweet.update_status(status="RasPI started at " + localtime + ".  Begin the mission!!!")
  
time.sleep(1)  # Whew... let's rest a bit..    Why?  Dunno...

while True:    # program runs 'forever'
    # First check if this is a new day.  If yes, tweet program is still active
    localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
    Current_day = time.strftime("%d", time.localtime())
    if Current_day != New_day_dawned:
        print "A new day has dawned. It is " + localtime + "."
        print ' '
        if (Tweet == "YES"):
            myTweet = Twython(C_key,C_secret,A_token,A_secret)
            localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
            myTweet.update_status(status="Hello, Twitter. Let's make the best of " + localtime)
        New_day_dawned = Current_day
                  
    # Now check to see if Sensor has changed state.  If so then Let's tweet
    if (GPIO.input(MagSwitch) != MagSwitch_Current):
        print ' '
        localtime = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())
        print "Triggered at: " + localtime
        print '-------------------------------'
      
        Counter = Counter + 1
          
        # Load the CatQuotes; then Tweet it
        CatQuote = [
        "ZERO", #this is zero and should never be called.  just here to make counting easier 'cuz i'm stupid
        "Tweet Quote 1",
        "Tweet Quote 2",
        "Tweet Quote 3",
        "Tweet Quote 4",
        "Tweet Quote 5",
        "Tweet Quote etc...",        ]
      
        Number_of_quotes =  6 - 1
        random.seed() # seed off the CPU clock
        r = random.random()              
        r = int((r * Number_of_quotes) + 1)

        print str(Counter) + ": CatQuote("+str(r)+"): " + CatQuote[r]
        print "Flood control set to " + str(Flood_control) + " seconds."
        print "Tweet set to: " + Tweet
        if (Tweet == "YES"):
            myTweet = Twython(C_key,C_secret,A_token,A_secret)
            myTweet.update_status(status=CatQuote[r])
      
        time.sleep(Flood_control)  # pause for debounce, re-read HE sensor, keeps from tweeting all the time.
      
        # Door etc has settled after the Flood Control.  Reset and wait for next toggle
        MagSwitch_Current = GPIO.input(MagSwitch)
      
        print "Flood Control PASSED."
        print " "
-----
Good luck if you decide to build your own tweeting RasPI.

Monday, March 6, 2017

Remote Control the Alexa Echo from Anywhere in the World

-----
Seems to be a common request:  "How do I control my Amazon Echo remotely?"  As to why this needs to be done is somewhat of a mystery, but evidently; done it must be...

First off; this is a hack.  Even hackers being the hacker types they are will think it's a hack and those hackers will be right.
-----
Take a look at the diagram above.  Basically what is happening is a Raspberry PI with a speaker plugged into it is commanding the Amazon Echo via a text-to-speech program.
  • The user logs into their home network via OpenVPN from anywhere in the World.
  • Navigate via VNC, etc. to a RasPI with a speaker placed next to an Amazon Echo.
  • From the RasPI command line use the 'festival' text-to-speech program to speak instructions to the Amazon Echo.
  • Amazon Echo obeys the command.
-----
Here's a quick video demo:
 -----
Others have done the hard work, but you will need to install some software:
  • If you don't have OpenVPN running, PiVPN makes this really easy on the Raspberry PI (and other platforms).  This *could* be optional if you only wanted to use the hack from inside your LAN or opened up a PORT on your router.  If you do decide to open up a PORT on your router for the whole wide world be aware of the risks.
  • Install the text-to-speech program 'festival' on the Raspberry PI with the command:
$ sudo apt-get install festival festival-freebsoft-utils 
  • Plug a speaker into the Raspberry PI and place it next to the Amazon Echo.
  • Log into that Raspberry PI and issue a 'festival' text-to-speech command to control the Amazon Echo.  For example:

$ echo  "Alexa, what time is it" | festival --tts
 or

$ echo  "Alexa, play Super Freak" | festival --tts
  •  Success! World domination!
-----

Friday, January 6, 2017

ESP8266 WiFi Garage Door Opener from any Web Browser

The 'brain' is the ESP8266 uC.  It is available with on board WiFi and plenty of I/O for smaller projects.  All this for well under $10USD with programming options for NodeMCU, MicroPython, and the Arduino IDE.
-----
There seems to be an unstoppable drive in the hacker DIY community for web based garage door openers and we were compelled to respond.  The garage door opener we have opens/shuts from a push button switch that basically creates a short to connect two terminals on the garage door opening unit.  That allows easy implementation because all that is required is a ESP8266 controlled relay wired across those two terminals to create a switch closure.

In addition to activating the door any activity is logged to a Google Sheet via the IFTTT.com Maker Channel.  This is handy to track all activation usage and ESP8266 server restarts.
-----
The main components are the ESP8266, a relay module, a BS170 N-Channel MOSFET.
 
-----
Simple.  Connect the 'stuff' as shown in the schematic:
and it will look something like this:
----
Use the Arduino IDE to load the source code below into the ESP8266 then wire the Normally Open (NO) side of relay you are controlling to the two terminals on the garage door opener that active the motor when 'shorted' together.

A few comments on the application:
  • Control works from Android, iPhone, PC, etc.  Basically any browser.  In the source code below if a device can open "http://192.168.0.28/long_confusing_URL_to_activate_relay" it will activate the garage door.
  • There is a "TEST" URL in the source code (http://192.168.0.28/) that confirms the ESP8266 is online but does not activate the door.
  • Set a static IP for the ESP8266 in your WiFi router.  Otherwise it may be assigned a different local IP if the ESP8266 or WiFi router is restarted.
  • Use long/complex URLs.  That way those that are connected to your router don't have a 'obvious' URL to activate the rig or one they can remember if you demo it.
  • We only wanted control of the door when connected to the host WiFi router locally (LAN) and not from anyplace on the planet.  If you want extended control to the WWW open a port on your router, but be aware of the concerns. We wanted to limit use only to those authorized to connect the WiFi router locally (LAN).  Plus, we didn't want to risk accidentally activating the door from a remote location.
  • The source code has separate IFTTT.com Maker Channel triggers to log events.  We could use one Maker Channel trigger and just pass different GETPOST variables.  However, creating multiple Maker Channel triggers would easily allow usage tracking on individuals by assigning each one a unique trigger name. (/ZenaActivate, /KelsoActivate, etc...)
  • Any time the door is activated or the ESP8266 is restarted (power outage, etc) a Google Sheet is updated to log the event as shown below. 
 
-----
The Arduino IDE source code is:

/*
 *  Garage Door Opener via Web Browser and log to IFTTT.com
 *  ESP8266 NodeMCU Board using Arduino IDE
 *  WhiskyTangoHotel.Com    DEC2106
 * 
 *  Blue on board LED is active LOW on GPIO2 (D4 on silkscreen)
 *  Relay to control Garage Door is active HIGH on GPIO5 (D1 on silkscreen)
 * 
 *  Opening 192.168.0.28/long_confusing_URL_to_activate_relay is called.  Every effort is made to keep the relay off 
 *  so the door does not close/activate by accident.
 * 
 *  A 'test' message is display on browser to see if server is up by calling root at:.
 *  192.168.0.28/  This WILL NOT ACTIVATE THE RELAY.  Only tests the server
 * 
 *  The 'meat' is at 192.168.0.28/long_confusing_URL_to_activate_relay. This will send a msg to the browser AND open/close the door.
 * 
 */

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

// WiFi Information
const char* ssid = "YOUR-SSID";
const char* password = "ASCII-PASSCODE-FOR-YOUR-SSID";

// IFTTT Information
String MAKER_SECRET_KEY = "xxx-yyy-zzz-123-456-789"; // your maker key here
String TRIGGER_NAME = "IFTTT_Maker_name_to_activate_relay";  // this is the Maker IFTTT trigger name for relay activation
const char* host = "maker.ifttt.com";

ESP8266WebServer server(80);

// Output pins
const int led = 2;  // Blue on board LED
const int relay = 5;  // Relay control line

int randNumber;  // Random# generated just to show a change in the screen.  Help to verify updated page call.

void handleRoot() {
  // This is called if 192.168.0.28/ is requested.  The root.
  // The 'meat' is at /long_confusing_URL_to_activate_relay.
  // This is just here to test the ESP8266 connectivity of the WiFi network without moving the relay
  // Show a message and flash the on board LED.
  randNumber = random(1, 10000);  // Random number just to show a change on the webpage at reload.
  server.send(200, "text/plain", "Testing ESP8266.  Response is: " + String(randNumber));
  digitalWrite(led, 0);  // Active LOW.  Turn On board LED On
  delay(2000);
  digitalWrite(led, 1);  // Active LOW.  Turn On board LED Off
}

void handleNotFound(){
  digitalWrite(led, 1);  // Keep the LED off.
  digitalWrite(relay, 0);  // Keep Relay OFF
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET)?"GET":"POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i=0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}

void setup(void){
  pinMode(led, OUTPUT);
  pinMode(relay, OUTPUT);
  digitalWrite(led, 1);  // LED Off
  digitalWrite(relay, 0);  // on power to relay
  Serial.begin(115200);  // serial prints to PC for debug use only
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.print("Trying to connect to ");
    Serial.print(ssid);
    Serial.print(" on ");
    Serial.print(WiFi.localIP());
    Serial.println(".");
  }
  Serial.println("");
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
 
  // Write to Google Sheet via IFTTT Maker channel that the ESP8266 has started/restarted

  // Now we trigger the IFTTT Maker Channel to update a Google sheet with the activity of the server starting/restarting
  // This can help log power outs, etc. 
  // Use WiFiClient class to create TCP connections
  WiFiClient client;
  const int httpPort = 80;
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");
    return;
  }
 
  // Create the request for IFTTT GARAGE_trigger_serverstart.  This can help log power outs, etc.
  String url = "https://maker.ifttt.com/trigger/GARAGE_trigger_serverstart/with/key/" + MAKER_SECRET_KEY;
  Serial.print("Requesting URL: ");
  Serial.println(url);
 
  // This sends the request to the IFTTT server
  client.print(String("POST ") + url + " HTTP/1.1\r\n" +
  "Host: " + host + "\r\n" +
  "Connection: close\r\n\r\n");
 
  delay(500);  // Delay to for web traffic; maybe not required.
  }

  server.on("/", handleRoot);

  server.on("/long_confusing_URL_to_activate_relay", [](){
    // This is called when 192.168.0.28/long_confusing_URL_to_activate_relay is called
    randNumber = random(1, 10000);  // Random number just to show a change on the webpage at reload.
    server.send(200, "text/plain", "Relay activated @ESP8266.  Code: " + String(randNumber));
    digitalWrite(led, 0);  // Active LOW.  Turn On board LED On
    digitalWrite(relay, 1);  // Relay ON

    // Know we trigger the IFTTT Maker Channel to update a Google sheet with the activity. 
    // Use WiFiClient class to create TCP connections
    WiFiClient client;
    const int httpPort = 80;
    if (!client.connect(host, httpPort)) {
      Serial.println("connection failed");
      return;
    }
 
    // Create the request for IFTTT
    String url = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME + "/with/key/" + MAKER_SECRET_KEY;
    Serial.print("Requesting URL: ");
    Serial.println(url);
 
    // This sends the request to the IFTTT server
    client.print(String("POST ") + url + " HTTP/1.1\r\n" +
    "Host: " + host + "\r\n" +
    "Connection: close\r\n\r\n");
   
    delay(2000);  // Delay to keep the relay closed.
    digitalWrite(led, 1);  // Active LOW.  Turn On board LED Off
    digitalWrite(relay, 0);  //Relay OFF
  });

  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void){
  server.handleClient();
}
-----

-----