Showing posts with label GPIO. Show all posts
Showing posts with label GPIO. Show all posts

Monday, December 3, 2018

Internet Time Morse Code Clock

-----
There are plenty of ways to irritate your XYL with an obsession of ham radio; but if you feel you need just one more then keep reading.   The black box above is actually a clock.  It's a cool clock if you know Morse Code (or CW), but it is worthless if you don't.
-----
The brains of the Morse Code clock is an ESP8266 with on board WiFi.  At power up, the rig connects to WiFi and fetches the time from the internet (so always accurate).  On first run the 'clock' sends out some Self Test tones after getting the time.  These Self Test tones can be something like a callsign, but it could be anything or even nothing.  The pace (or WPM) of the Morse Code is adjustable by a variable in the source code.  In the example videos it is 10WPM as calculated by "The Paris Standard".

From that point on the time of day (localized to your time zone) is output to a speaker and LED tower in Morse Code (24 hour format) as HHMM.  A calm quite house is filled with dit-dah tones and flashing light.
----
The build is not that difficult as shown in the schematic below.  On the bench it looks something like this:
-----
A metal box found around the shack was repurposed to clean up the look.  This vid shows the end result.  At power up the LED tower flashes until it gets the time from the internet.  Then the Self Test tones and LED flashes happen.   After that, only the time is "announced" when the red button is pressed.
It all came out pretty nice.
-----
If you decide to duplicate the build here is the simple schematic:
 -----
And the Arduino/ESP8266 IDE Source Code:
 _______________________________________________________________
/*
 *  NOV2018
 *  CW "Talking Clock"
 *  WhiskeyTangoHotel.Com
 *
 * Get Internet time.
 * Upon a button press, convert the Internet time to defined local time zone.
 * Parse the Internet time to HHMM (24 hr format)
 * Convert the HHMM time to Morse Code (CW) dits and dahs
 * Play the Morse Code converted times over a speaker and a LED.
 *
 * Hold the button down for over a second and get a random letter or number played.
 *
 * At startup a self test sends a callsign (or user define msg) to
 * the speaker in CW.
 *
 * BTW, CW is fun to learn.
 *  --... / ...--   //  --... / ...--
 *
*/

#include <time.h>

// Set up or the Wireless.  We wil need this to get the internet time
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

// WiFi Connection Information
const char* ssid = "Virus-2.4";         // PRIVATE: Enter your personal SSID setup information. *PRIVATE*
const char* password = "zenakelsocats"; // PRIVATE: Enter your personal PASSWORD setup information. *PRIVATE*
ESP8266WebServer server(80);

// Define and set up some variables
int timezone = 18;  // Chicago = 18
int dst = 0;        // 0 or 1.  1 for DST or "Summer Adjusted Time"
String Callsign = "W1AW";   // ALL CAPS!!!  Used for self test 'beeps' at startup.  Could be anything alphanumic you like
String Time_substring;   // Used for parsing
String Parsedtime; // Feteched internet time boiled down to HHMM in 24 hr format.
String Dit_or_Dah;  // Output a dit or dah to the speaker.  Used when parsing a character

int Pitch = 600;      // Normally around 600Hz.  This this the pitch frequency of the CW tones
float WPM = 12;       // How fast the letters are sent to the speaker using the "Paris Standard" in Words per Minute (8-25; depending on skill level)
//  Paris Standard formula: Tmsec = (1200 / WPM) WPM is the desired speed in words-per-minute. T is the dit time in mSecs
float Tmsec = 1200 / WPM;
float Dit = Tmsec;
float intercharacter_spacing = Dit;
float Dah = Tmsec * 3;
float interword_spacing = Dah;
int Random_CW;  // Random number generated to select a random character
String Random_CW_Character;  // This is the random character sent to speaker/LED

// Load the CW database 'dits' and 'dahs' for each Alphanumeric. Boring....
String A = ".-";;
String B = "-...";
String C = "-.-.";
String D = "-..";
String E = ".";
String F = "..-.";
String G = "--.";
String H = "....";
String I = "..";
String J = ".---";
String K = "-.-";
String L = ".-..";
String M = "--";
String N = "-.";
String O = "---";
String P = ".--.";
String Q = "--.-";
String R = ".-.";
String S = "...";
String T = "-";
String U = "..-";
String V = "...-";
String W = ".--";
String X = "-..-";
String Y = "-.--";
String Z = "--..";
String zero = "-----";
String one = ".----";
String two = "..---";
String three = "...--";
String four = "....-";
String five = ".....";
String six = "-....";
String seven = "--...";
String eight = "---..";
String nine = "----.";

// Output pins
const int led = 2;  // NEGATIVE Logic (LOW is ON) Blue on board LED is on PIN2 for this NoderMCU 1.0 ESP8266.
const int speaker = 14;  // tone outputs to the EXTERNALLY amplified speaker to pin 14
int time_buttonPin = 4;  // When button pressed CW the time to the speaker.  pin 4
boolean buttonState = LOW;  // for the time_buttonPin status

void setup(void){  // This is run once.
  Serial.begin(115200);  // turn on the serial monitor for debug
  //Serial.setDebugOutput(true);  // Turn on the Serial Debugger if desired

  //Define the I/O
  pinMode(led, OUTPUT);  // set up the onboard Blue LED as an output.
  pinMode(time_buttonPin, INPUT); // Input for the button switch.  if HIGH (pressed) play CW the time to the speaker

  // Connect WiFi.  Is the WiFi working?
  WiFi.begin(ssid, password);
  Serial.println("");
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print("Trying to connect to ");
    Serial.print(ssid);
    Serial.print(" on ");
    Serial.println(WiFi.localIP());
    for (int x = 0; x < 20; x++) { //
      digitalWrite(led, !digitalRead(led));  // toggle state of the on board blue LED. Shows program is trying to WiFi connect
      delay(50); 
    } // endfor WiFi blink connect
  }
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.println(WiFi.localIP());

  //Send tones for the variable Callsign to the speaker as a self test.
  Serial.println("Sending " + Callsign + " as Self Test......");
  int L = Callsign.length();
  for (int i=0; i<L; i++) {  // Start Self Test Convert the time numbers to dit and dahs
    Time_substring = Callsign.substring(i, i+1);
    if (Time_substring == "0") { Time_substring = zero; }
    if (Time_substring == "1") { Time_substring = one; }
    if (Time_substring == "2") { Time_substring = two; }
    if (Time_substring == "3") { Time_substring = three; }
    if (Time_substring == "4") { Time_substring = four; }
    if (Time_substring == "5") { Time_substring = five; }
    if (Time_substring == "6") { Time_substring = six; }
    if (Time_substring == "7") { Time_substring = seven; }
    if (Time_substring == "8") { Time_substring = eight; }
    if (Time_substring == "9") { Time_substring = nine; }
    if (Time_substring == "A") { Time_substring = A; }
    if (Time_substring == "B") { Time_substring = B; }
    if (Time_substring == "C") { Time_substring = C; }
    if (Time_substring == "D") { Time_substring = D; }
    if (Time_substring == "E") { Time_substring = E; }
    if (Time_substring == "F") { Time_substring = F; }
    if (Time_substring == "G") { Time_substring = G; }
    if (Time_substring == "H") { Time_substring = H; }
    if (Time_substring == "I") { Time_substring = I; }
    if (Time_substring == "J") { Time_substring = J; }
    if (Time_substring == "K") { Time_substring = K; }
    if (Time_substring == "L") { Time_substring = L; }
    if (Time_substring == "M") { Time_substring = M; }
    if (Time_substring == "N") { Time_substring = N; }
    if (Time_substring == "O") { Time_substring = O; }
    if (Time_substring == "P") { Time_substring = P; }
    if (Time_substring == "Q") { Time_substring = Q; }
    if (Time_substring == "R") { Time_substring = R; }
    if (Time_substring == "S") { Time_substring = S; }
    if (Time_substring == "T") { Time_substring = T; }
    if (Time_substring == "U") { Time_substring = U; }
    if (Time_substring == "V") { Time_substring = V; }
    if (Time_substring == "W") { Time_substring = W; }
    if (Time_substring == "X") { Time_substring = X; }
    if (Time_substring == "Y") { Time_substring = Y; }
    if (Time_substring == "Z") { Time_substring = Z; }  

    // Now send the dit/dahs for the Self Test Callsign Character  
    int CW_length = Time_substring.length();

    for (int j=0; j<CW_length; j++)   {  // Send tones and blink LED in CW
      digitalWrite(led , HIGH); // NEGATIVE LOGIC.  Make sure the onboard LED is off
      Dit_or_Dah = Time_substring.substring(j, j+1);
      //Serial.println(Dit_or_Dah);
    
      if (Dit_or_Dah == ".") {   // process a Dit
        digitalWrite(led , LOW);
        //tone(speaker, Pitch, Dit); // Comment for LED only; no BEEPS on Self Test
        delay(Dit);
        digitalWrite(led , HIGH);
      }  // end if process a Dit

      if (Dit_or_Dah == "-") {   // process a Dah
        digitalWrite(led , LOW);
        // tone(speaker, Pitch, Dah); // Comment for LED only; no BEEPS on Self Test
        delay(Dah);
        digitalWrite(led , HIGH);
      }  // end if process a Dah

      delay(intercharacter_spacing);   //  a delay of "1 Dit" between dit/dahs in a character    
    }  // send for loop dit/dahs for selected time number
    delay (interword_spacing);   // done with sending character, so a longer "3 * Dit" delay"   
  } // end for loop on the Self Test

  // Do we want summertime or standard time?  Default is standard time dst = 0.  1 for DST or "Summer Adjusted Time"
  int buttonState = digitalRead(time_buttonPin);
  if (buttonState == HIGH) {   // if the button is pushed after the self test; then set to Summer Time; dst = 1
    tone(speaker, Pitch, Dit); // Quick 'dit' to say "I'm in Summer Time mode"
    dst = 1;
  }
  
  // Get the current time from "THE INTERNET! and adjust for timezone and dst"
  configTime((timezone + dst) * 3600, 0, "pool.ntp.org", "time.nist.gov");
  Serial.println("\nWaiting for time");
  while (!time(nullptr)) {
    Serial.print(".");
    delay(100);
  }

  Serial.println("Self test completes!!!");
  digitalWrite(led , HIGH);  // turn off the LED
  Serial.println("");

  delay(1000);

} // End void setup loop

void loop(void){  // Loop forever waiting for button push.  If pushed CW the local time as HHMM in CW and go back to waiting for that button
  int buttonState = digitalRead(time_buttonPin);   // check if the pushbutton is pressed. 
    if (buttonState == HIGH) {   // button is pressed, CW time to speaker or random character?
      delay(1000);
      int buttonState = digitalRead(time_buttonPin);   // check if the pushbutton is STILL pressed.
      if (buttonState == HIGH)   { // if the button is still pressed after the delay then rnd character, not time.
        digitalWrite(led , LOW); // NEGATIVE LOGIC.  Blink the LED to show RND char mode entered
        delay(150);
        digitalWrite(led , HIGH); // NEGATIVE LOGIC.  Make sure the  LED is off
        delay(1000);
        do {
          Random_CW = random(48,90); //48 is 0.  57 is 9.  A is 65.  Z is 90        
        } while (Random_CW > 57 and Random_CW < 65);   // if the random# did not map to a letter or number then try again.
        char Random_CW_Character = Random_CW;
        Serial.print("Random CW Character is: ");
        Serial.println(Random_CW_Character);
      
        Time_substring = Random_CW_Character;
        if (Time_substring == "0") { Time_substring = zero; }
        if (Time_substring == "1") { Time_substring = one; }
        if (Time_substring == "2") { Time_substring = two; }
        if (Time_substring == "3") { Time_substring = three; }
        if (Time_substring == "4") { Time_substring = four; }
        if (Time_substring == "5") { Time_substring = five; }
        if (Time_substring == "6") { Time_substring = six; }
        if (Time_substring == "7") { Time_substring = seven; }
        if (Time_substring == "8") { Time_substring = eight; }
        if (Time_substring == "9") { Time_substring = nine; }
        if (Time_substring == "A") { Time_substring = A; }
        if (Time_substring == "B") { Time_substring = B; }
        if (Time_substring == "C") { Time_substring = C; }
        if (Time_substring == "D") { Time_substring = D; }
        if (Time_substring == "E") { Time_substring = E; }
        if (Time_substring == "F") { Time_substring = F; }
        if (Time_substring == "G") { Time_substring = G; }
        if (Time_substring == "H") { Time_substring = H; }
        if (Time_substring == "I") { Time_substring = I; }
        if (Time_substring == "J") { Time_substring = J; }
        if (Time_substring == "K") { Time_substring = K; }
        if (Time_substring == "L") { Time_substring = L; }
        if (Time_substring == "M") { Time_substring = M; }
        if (Time_substring == "N") { Time_substring = N; }
        if (Time_substring == "O") { Time_substring = O; }
        if (Time_substring == "P") { Time_substring = P; }
        if (Time_substring == "Q") { Time_substring = Q; }
        if (Time_substring == "R") { Time_substring = R; }
        if (Time_substring == "S") { Time_substring = S; }
        if (Time_substring == "T") { Time_substring = T; }
        if (Time_substring == "U") { Time_substring = U; }
        if (Time_substring == "V") { Time_substring = V; }
        if (Time_substring == "W") { Time_substring = W; }
        if (Time_substring == "X") { Time_substring = X; }
        if (Time_substring == "Y") { Time_substring = Y; }
        if (Time_substring == "Z") { Time_substring = Z; } 

        int CW_length = Time_substring.length();
      
        for (int j=0; j<CW_length; j++)   {  // Send tones and blink LED in CW
          digitalWrite(led , HIGH); // NEGATIVE LOGIC.  Make sure the onboard LED is off
          Dit_or_Dah = Time_substring.substring(j, j+1);
          //Serial.println(Dit_or_Dah);
        
          if (Dit_or_Dah == ".") {   // process a Dit
            digitalWrite(led , LOW);
            tone(speaker, Pitch, Dit);
            delay(Dit);
            digitalWrite(led , HIGH);
          }  // end if process a Dit
  
          if (Dit_or_Dah == "-") {   // process a Dah
            digitalWrite(led , LOW);
            tone(speaker, Pitch, Dah);
            delay(Dah);
            digitalWrite(led , HIGH);
          }  // end if process a Dah
  
          delay(intercharacter_spacing);   //  a delay of "1 Dit" between dit/dahs in a character    
        }  // send for loop dit/dahs for selected time number
       
      }  // end of if the button is still pressed after the delay then rnd character, not time.
      else { // random character code above; time code below
        time_t now = time(nullptr);
        Serial.println("---------------------------------------");
        Serial.print("Internet time adjusted to tone zone: ");
        Serial.println(ctime(&now));
        Parsedtime = (ctime(&now));
    
        // Build the Parsed time from the internet time. The time is 24 hr format and always 4 characters in HHMM
        Parsedtime = Parsedtime.substring(11,12) + Parsedtime.substring(12,13) + Parsedtime.substring(14,15) + Parsedtime.substring(15,16);
        Serial.print("Parsedtime as HHMM is: ");
        Serial.println(Parsedtime);    
      
        for (int i=0; i<4; i++) {  // Convert the time numbers to dit and dahs. Always 4 characters in HHMM
          Time_substring = Parsedtime.substring(i, i+1);
          Serial.print("Currently sending Parsedtime character: ");
          Serial.println(Time_substring);
        
          if (Time_substring == "0") { Time_substring = zero; }
          if (Time_substring == "1") { Time_substring = one; }
          if (Time_substring == "2") { Time_substring = two; }
          if (Time_substring == "3") { Time_substring = three; }
          if (Time_substring == "4") { Time_substring = four; }
          if (Time_substring == "5") { Time_substring = five; }
          if (Time_substring == "6") { Time_substring = six; }
          if (Time_substring == "7") { Time_substring = seven; }
          if (Time_substring == "8") { Time_substring = eight; }
          if (Time_substring == "9") { Time_substring = nine; }
    
          // Now send the dit/dahs for the time number processed      
          int CW_length = Time_substring.length();
    
          for (int j=0; j<CW_length; j++)   {  // Send tones and blink LED in CW
            digitalWrite(led , HIGH); // NEGATIVE LOGIC.  Make sure the onboard LED is off
            Dit_or_Dah = Time_substring.substring(j, j+1);
                    
            if (Dit_or_Dah == ".") {   // process a Dit
              digitalWrite(led , LOW);
              tone(speaker, Pitch, Dit);
              delay(Dit);
              digitalWrite(led , HIGH);
            }  // end if process a Dit
    
            if (Dit_or_Dah == "-") {   // process a Dah
              digitalWrite(led , LOW);
              tone(speaker, Pitch, Dah);
              delay(Dah);
              digitalWrite(led , HIGH);
            }  // end if process a Dah
    
            delay(intercharacter_spacing);   //          
          }  // send for loop dit/dahs for selected time number  
          delay (interword_spacing);       
        } // end for loop Convert the HHMM time numbers to dit and dahs
    
        Serial.println("Sending time tones completed!!!");
        delay(1000);  // Delay for a sec just beacause...
      }  //  If checking for long press for RND character
    }  // End button press if
}  // End of Loop forever waiting for button push.
________________________________________________________
-----
Thanks for stopping by and 73!

Monday, September 4, 2017

Arduino / MOSFET Controlled High Bright LED Motorcycle Light

In our modern world, high bright LEDs can be purchased cheaply to provide low current draw auxiliary lighting to any vehicle.  Then it is only a simple matter of connecting the LED to 12VDC and head down the road enjoying your new found wattage.  However, there is no fun in the easy way.  Follow the instructions below to build your own custom high bright LED driver.
-----
OBJECTIVE: A high bright LED for installation on a Kawasaki KLR650 motorcycle with six programmable modes:
-----
To keep the project simple, an Ardunio Nano is used to control the Gate Voltage (Vgs) of a MOSFET.  When Vgs is HIGH the MOSFET switches on and the high bright LED turns on.  By using Pulse Width Modulation (PWM) on the Arduino the brightness of the high bright LED can be easily controlled.
-----
You will need the parts in the schematic.  All are readily available from Amazon or a host of electronic parts distributors.  Of course, the 12VDC power source is provided by the vehicle. 
---
On the bench the rig will look and function something like this:
 ----
The schematic leaves out some details that could be helpful.  To control the MODE selection and the STROBE effect we used a handlebar mounted switch. I also drilled a small hole to mount the STATUS LED into the handlebar switch.  With coded flashes, the STATUS LED lets the rider know the current mode of the rig.  Pressing the GREEN button selects the next mode.  Move the turn signal switch to "R" to turn on the STROBE effect.  The image below provides some real world connection details.  If you get the same handlebar switch we recommend buzzing out the wires first to verify them because the unit came with no diagram and low cost manufactures often change these details.
----
After you use the Arduino IDE to download the source code provided below into your Ardunio Nano you will need to mount to high bright LED.  We got a mounting bracket from eBay.  In the end it came out pretty nice.
-----
Here is a video of the rig running through all the modes.  The quick strobe at the beginning is a wake up self test.  The high bright LED swamps out the camera light sensor and doesn't provide a good indication of the actual effect.  One note:  It turns out Apple has a patent on the Breathing LED pattern (well, for a sleep indicator anyway).  Use the "breathing LED" portion of the code with caution or, I guess,  risk a cease and desist order.
-----
Here is the Arduino source code for the rig.  Good luck!
//------------------------------------------------------------------------
/*
KLR650 Aux LED Driver
July 2019
WhiskeyTangoHotel.Com
Ardunio Nano (should work with other Arduinos)

Program controls a IRF510 MOSFET to drive a High Bright LED Array
(I recommend a heat sink on the MOSFET, but it should be fine without it)
A button switch selects between modes
A switch sets the LED to fast strobe mode
*/

// MODES:
// 0 = OFF, Bright Examples (vars set below):
// 1 = Low% Bright
// 2 = Higher% Bright
// 3 = FULL BRIGHT
// 4 = Breath

int Number_of_Modes = 4;  // Needed for program control.
int LED_Mode = 1;  // LED_Mode sets the startup condition and is changed with button pushes in the main loop.
int Status_LED = 3;   // PWM output pin for the LED status light.
int FET_Drive = 5;  // PWM output pin to drive the gate on the MOSFET.
int mode_buttonPin = 4;  // Mode changing push button (wired to GND via 10K)
int strobe_buttonPin = 8;  // Mode changing push button (wired to GND via 10K)
boolean buttonState = LOW;  // for the mode_ and strobe_ detect

// Percent brightness of the 3 modes.  Example:  35% = .35, 100% = 1.00
// LED_Mode = 0 is OFF
float MODE1_Bright = .20;
float MODE2_Bright = .50;
// LED_Mode = 3 is FULL Bright: digitalWrite(Status_LED, 255);  // 255 turns LED FULL BRIGHT
// LED_Mode = 4 is Breath

int Breath_Rate = 15;  //Higher is faster
int Breath_Max_Bright = 170;
int Breath_Min_Bright = 20;
int Flash_delay = 50;  // sets delay for strobe mode and self test (50 is a good start)

void setup()
{
  // Serial commands are for debugging
  //Serial.begin(9600);
  //Serial.print(LED_Mode);
  //Serial.println();
  pinMode(Status_LED, OUTPUT);      // sets the digital pins as output
  pinMode(FET_Drive, OUTPUT);
  pinMode(mode_buttonPin, INPUT); // initialize the mode pushbutton pin as an input.  If HIGH change modes
  pinMode(strobe_buttonPin, INPUT); // if HIGH quick stobe/flash mode

  digitalWrite(Status_LED, 0);  // 0 turns the LED OFF
  digitalWrite(FET_Drive, 0);  

  // Self test loop.  Quick flash the status and high bright LED
  for (int i = 0; i <= 4; i++) {
    digitalWrite(Status_LED, 255);  // 255 turns LED FULL BRIGHT
    digitalWrite(FET_Drive, 255);
    delay(Flash_delay);   
    digitalWrite(Status_LED, 0);  // 0 turns the LED OFF
    digitalWrite(FET_Drive, 0);
    delay(Flash_delay);
  } // endSelf Test Loop
}   //end Setup

void stobe_on_button () {  // strobe/flash the LED when HIGH
  int buttonState = digitalRead(strobe_buttonPin);   // check if the pushbutton is pressed.
  if (buttonState == HIGH) {   // button is pressed, strobe the LEDs
    Serial.println(buttonState);
    digitalWrite(Status_LED, 255);  // 255 turns LED FULL BRIGHT
    digitalWrite(FET_Drive, 255);
    delay(Flash_delay);
   
    digitalWrite(Status_LED, 0);  // 0 turns the LED OFF
    digitalWrite(FET_Drive, 0);
    delay(Flash_delay);
    stobe_on_button ();  // check to see if strobe button is still pressed HIGH
  } // endif strobe_button HIGH 
}  // end of strobe_button function

void mode_change_button() {    // function to dectect the mode_buttonPin press to change modes (increment the state machine)
  int buttonState = digitalRead(mode_buttonPin);   // check if the pushbutton is pressed.
  if (buttonState == HIGH) {   // button is pressed, advance to next LED_Mode
    digitalWrite(Status_LED, 255);  // 255 turns LED FULL BRIGHT
    for (int x = 0; x < 25; x++) { // Quick flash the Status LED to confirm button press.
      digitalWrite(Status_LED, !digitalRead(Status_LED));  // toggle state of the on board blue LED. Shows program is running
      delay(100);  
    }  // endfor quick toogle Blue LED
     
    if (LED_Mode >= Number_of_Modes)  {  //  reset state machine to zer0 to recycle LED_Modes
      LED_Mode = 0;
    }
    else {
    LED_Mode = LED_Mode + 1;
    } 

  if (digitalRead(strobe_buttonPin) == HIGH) {
    LED_Mode = 0;  // this is a fast cycle to OFF.  The flash button AND the mode button are HIGH
  }
 
  }  // endif state_mode button pressed
}  // end of mode_change_button function

void loop()  {
  //Serial.print(LED_Mode);
  Serial.println(buttonState);
  Serial.println();

  // LED_Mode - OFF, no lights
  if (LED_Mode == 0) {
    analogWrite(Status_LED, 0);  // 0 turns the LED OFF
    analogWrite(FET_Drive, 0);   // 0 turns the MOSFET
  } // endif LED_Mode = 0

  // LED_Mode - Percent Brightness
  if (LED_Mode == 1) {
    analogWrite(FET_Drive, (255 *  MODE1_Bright));   // Percent Brightness = MODE1_Bright
    for (int x = 0; x < LED_Mode; x++) { // Flash rate Status LED to match mode
      digitalWrite(Status_LED, 255);  // 255 turns LED FULL BRIGHT
      delay(100);      // endfor quick toogle Blue LED
      digitalWrite(Status_LED, 0);  // 0 turns LED OFF
      delay(100);      // endfor quick toogle Blue LED
    } //for match Status_LED with mode
    delay(600);
  } // endif LED_Mode = 1

    // LED_Mode - Percent Brightness
  if (LED_Mode == 2) {
    analogWrite(FET_Drive, (255 *  MODE2_Bright));   // Percent Brightness = MODE2_Bright
    for (int x = 0; x < LED_Mode; x++) { // Flash rate Status LED to match mode
      digitalWrite(Status_LED, 255);  // 255 turns LED FULL BRIGHT
      delay(100);      // endfor quick toogle Blue LED
      digitalWrite(Status_LED, 0);  // 0 turns LED OFF
      delay(100);      // endfor quick toogle Blue LED
    } //for match Status_LED with mode
    delay(600);
  } // endif LED_Mode = 2

  // LED_Mode - Full Brightness
  if (LED_Mode == 3) {
    digitalWrite(FET_Drive, 255);   // FULL BRIGHT
    for (int x = 0; x < LED_Mode; x++) { // Flash rate Status LED to match mode
      digitalWrite(Status_LED, 255);  // 255 turns LED FULL BRIGHT
      delay(100);      // endfor quick toogle Blue LED
      digitalWrite(Status_LED, 0);  // 0 turns LED OFF
      delay(100);      // endfor quick toogle Blue LED
    } //for match Status_LED with mode
    delay(600);
  } // endif LED_Mode = 3
 

  // LED_Mode - LED BREATHING
  if (LED_Mode == 4) {
    // Ramp up the brighness of the LED
    for (int i = Breath_Min_Bright; i <= Breath_Max_Bright; i++) {
      analogWrite(Status_LED, i);
      analogWrite(FET_Drive, i);
      float Breath_Delay = (exp(sin(i/2000.0*PI*10)) - 0.36787944)*108.0;
      delay(Breath_Delay/Breath_Rate);
      if (buttonState == HIGH) {   // check if the pushbutton is pressed.
        i = Breath_Max_Bright;
      }
    } 

    mode_change_button();  // check to see if mode_change button is pressed
    stobe_on_button ();   // check to see if strobe_button is pressed
   
    // Ramp down the brightness of the LED
    for (int i = Breath_Max_Bright; i >= Breath_Min_Bright; i--) {
      analogWrite(Status_LED, i);
      analogWrite(FET_Drive, i);
      float Breath_Delay = (exp(sin(i/2000.0*PI*10)) - 0.36787944)*108.0;
      delay(Breath_Delay/Breath_Rate);
      if (buttonState == HIGH) {   // check if the pushbutton is pressed.
        i = Breath_Min_Bright;
      }
    }
  } // endif LED_Mode 4

  mode_change_button();  // check to see if mode_change button is pressed
  stobe_on_button ();   // check to see if strobe_button is pressed

}
-----

Thursday, August 24, 2017

ToF Laser to Monitor Cat Food Levels


 -----
We live in great times.  There was once a day when the only way to determine if the cat feeder needed more kibble was to actually look at it with your own eyes like some Neanderthal.  Thankfully technology and the magical world of IoT has changed all of that.
----
For this project we use a WiFi enable ESP8266 and a STMicro VL53L0X ToF Sensor mounted to a breakout board.  If you don't find a need to measure feline food consumption the project still provides Arduino code that can be extremely useful for your other IoT projects:
  • Logging data to a Google Drive Spreadsheet (via IFTTT)
  • Logging data to AT&T's M2X machine to machine servers (think nice graphs)
  • Sending SMSs to your mobile device (via IFTTT)
All of the above services are free but; of course, you will need to establish an account if you do not have on.
----
What's happening?
The ESP8266 runs in Arduino mode (source code below) in an endless loop.  Every hour it polls the VL53L0X ToF Sensor mounted on the lid of the cat food feeder.
-----
Since we know how many centimeters the food is from the ToF sensor at full and at empty we are able to scale those values and report/log "percent full" status.  Those values are posted to a Google Drive Spreadsheet and to AT&T's M2X machine to machine servers for logging.  If the food level is considered CRITICALLY LOW a SMS message goes out to our mobile device.  Just to increase the geek factor, CRITICALLY LOW alerts are also displayed on our Pebble watch.
----
The Google Drive Spreadsheet looks like this:
-----
Here is the bad ass AT&T M2X machine to machine server graph (note the increase after we filled the feeder):
-----
The IFTTT SMS alerts are sent if the rig determines food levels critically low:

-----
So..... How's it done?  You will need these:
Easy; hook it up like this:
 And it will look something like this:
-----
 Mount it to the cat feeder and you end up with this:

-----
Now all that is left is to copy/paste the code below into the Arduino IDE.  Upload it to the ESP8266 and your cats will never go hungry again!
-----
/*
 *  AUG2017
 *  STMicro VL53L0X ToF Sensor for
 *  Cat Food Level Monitoring
 *  WhiskeyTangoHotel.Com
 * 
 *  Logs % full values to:
 *    Google Drive (as an appending spreadsheet)
 *    AT&T M2X for historical graphing
 *    Send SMS to cell phone if level condition is RED/CRITICAL
 * 
 *  uC setting for Ardunio IDE
 *  NoderMCU 1.0 (ESP-12E Module), 80MHz, 921600, 4M (3M SPIFFS)
 * 
*/

// For the STMicro VL53L0X ToF Sensor
// I2C SDA to ESP8266 Pin D2.  SCL to ESP8266 Pin D1
#include "Adafruit_VL53L0X.h"   // Thanks again ADAFRUIT!!!
Adafruit_VL53L0X lox = Adafruit_VL53L0X();

// For the Wireless
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>

// WiFi Connection Information
const char* ssid = "YourNetworkHere";    // PRIVATE: Enter your personal setup information.
const char* password = "YourNetworkPasswordHere"; // PRIVATE: Enter your personal setup information.
ESP8266WebServer server(80);

// IFTTT Information for WebHook widget
String MAKER_SECRET_KEY = "YourIFTTTCodeHere"; // PRIVATE: Enter your personal setup information. Your IFTTT Webhook key here
String TRIGGER_NAME_google_drive = "Cat_Food";  // this is the Maker IFTTT trigger name for google drive spreadsheet logging
String TRIGGER_NAME_M2X = "cat_food_mx";   // this is the Maker IFTTT trigger name for M2X logging
String TRIGGER_NAME_SMS = "CriticalFoodLevel_SMS";  // this is the Maker IFTTT trigger name to send SMS if low is level is CRITICAL.
const char* host = "maker.ifttt.com";
String url_google_drive;  // url that gets built for the IFTTT Webhook logging to google drive spreadsheet
String url_M2X;   // url that gets built for the IFTTT Webhook logging to AT&T M2X service
String url_SMS;   // url that gets built for the IFTTT Webhook sending SMS if food level is critical

// Define and set up some variables
float Range_inches;  // How far the sensor is from the food at time of reading.  Sensor is on roof of feeder.
float Min_level = 5.0;   // Distance in inches before low food alarm level.  Food is far from sensor on feeder roof
float Max_level = 0.5;   // Distance in inches from sensor for full feeder level.  Food is close to sensor on feeder roof
float Percent_full;  // How full in xx.x% is the food based on the Min/Max_levels defined above
float Caution_alarm = 35.0;  // at xx.x% food level is considered low.  String Status YELLOW
float Critical_alarm = 25.0;  // at xx.x% food level is considered critically low. String Status RED
String Status = "***_Starting_with_Caution_at:_" + String(Caution_alarm) + "%_and_CRITICAL_at:_" + String(Critical_alarm) + "%";  // Update to Out of Range, NORMAL, LOW, CRITICAL, etc. "spaces" will error IFTTT Webhook; use "_"
int Run_number;  // how many times the sensor has been read

// Output pins
const int led = 2;  // Blue on board LED is on PIN2 for this NoderMCU 1.0 ESP8266.  Blink it between reads

// Program control variables
int Seconds_between_posts = 60 * 60;  // how often to post the results of the sensor read. NOT EXACT due to post delays, Sensor reads, LED flashing, etc.
int logging = 1; // If 1 then log to cloud.  Any other value (0) turns it off.  ESP8266 "Start/Restart" message is always logged. 


void setup(void){  // This is run once.
  pinMode(led, OUTPUT);  // set up the onbaord LED pin as an output. 
  Serial.begin(115200);  // turn on the serial monitor for debug

  // wait until serial port opens for native USB devices
  while (! Serial) {
    delay(1);
  }

  // Is the ToF sensor connecting via I2C?
  Serial.println("STMicro VL53L0X test");
  if (!lox.begin()) {
    Serial.println(F("Failed to boot VL53L0X!!!"));
    while(1);
  }
  // power
  Serial.println(F("VL53L0X Passed... \n\n"));
 
  // Is the WiFi working?
  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.print("Connected to ");
  Serial.println(ssid);
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
    Serial.println(" ");
  }

  // Use WiFiClient class to create TCP connections for WiFi logging
  WiFiClient client;
  const int httpPort = 80;
  if (!client.connect(host, httpPort)) {
    Serial.println("connection failed");  // Boo!!!
    return;
  }

  server.begin();
  Serial.println("HTTP server started");  // Woo Hoo!!!

  // Write to Google Sheet via IFTTT Maker channel that the ESP8266 has started/restarted
  // Trigger the IFTTT Webhook Channel to update a Google sheet with the activity of the server starting/restarting
  // This can help log power outs, etc.  For first run we defined String Status for identify a startup condition.
  // Create the request for IFTTT google drive
  url_google_drive = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_google_drive + "/with/key/" + MAKER_SECRET_KEY + "?value1=" + String(Status);
  Serial.println("Status: " + Status);
  Serial.println(" ");
   
  // This sends the request to the IFTTT server
  client.print(String("POST ") + url_google_drive + " HTTP/1.1\r\n" +
  "Host: " + host + "\r\n" +
  "Connection: close\r\n\r\n"); 
  delay(500);  // Delay for web traffic; maybe not required. 
}

void loop(void){
  // Loop forever.  Read the sensor and post based on the delay values set above.
  // The blue onboard LED will blink between ToF reads.
 
  // Read the ToF.  Distance in mm  returned. 
  for (int x = 0; x < 10; x++) { // Quick toggle blue on  board LED to show measurement being taken.
    digitalWrite(led, !digitalRead(led));  // toggle state of the on board blue LED. Shows program is running
    delay(100);    }  // endfor quick toogle Blue LED
  VL53L0X_RangingMeasurementData_t measure;

  Serial.println("-------------------------------------");
  Serial.println("Reading a measurement... ");
  lox.rangingTest(&measure, false);
  // Convert to inches because the USA, for some reason, doesn't want to adopt the metric system...
  Range_inches = measure.RangeMilliMeter/25.4;
  Run_number = Run_number + 1;

  // Scale and normalize the Min Max levels to 0% to 100%.  Clip the range in case of a the Max fill limit was exceeded or a misread.
  Percent_full = ((Range_inches - Min_level) / (Max_level - Min_level)) * 100.0;
  if (Percent_full > 100) {
    Percent_full = 100.00;
  }
  if (Percent_full < 0) {
    Percent_full = 0.0;
  }

  //Serial.println(String(Range_inches));  // Debug use
     
  // Is the ToF Sensor reading 'anything' for a distance?
  if (Range_inches > 100 ) {  // Something's weird.  Ranging error.  The ToF sensor is NOT over 100 inches fron the food. EVER!!!
      Status = "Run:" + String(Run_number) + "__ERROR:_***_Out_of_Range_***";
  } else {  // ToF made a successful reading so log the food level   
    if (Percent_full >= Caution_alarm) {  // above CAUTION LEVEL, All's good
      Status = "Run:" + String(Run_number) + "___GREEN---GOOD";
    } // endif GREEN---GOOD
 
    if (Percent_full < Caution_alarm && Percent_full > Critical_alarm) {  //  CAUTION Zone, YELLOW---REFILL_SOON
      Status = "Run:" + String(Run_number) + "___~~~YELLOW---REFILL_SOON~~~";
    } // end if YELLOW---REFILL_SOON"
 
    if (Percent_full <= Critical_alarm) {  //  CAUTION Zone, RED---REFILL_ASAP"
      Status = "Run:" + String(Run_number)+ "___!!!_RED---REFILL_ASAP_!!!";
      if (logging == 1) { // is logging turned on? Maninly for debug...  Typically would be set = 1
        // Set up IFTTT Webhook Channel to send the SMS. 
        // Use WiFiClient class to create TCP connections for IFTT SMS
        WiFiClient client;
        const int httpPort = 80;
        if (!client.connect(host, httpPort)) {
          Serial.println("connection failed");
          return;
        }     
        url_SMS = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_SMS + "/with/key/" + MAKER_SECRET_KEY+ "?value1=" + String(Percent_full);
        Serial.println("Critical Level: Sending SMS with payload:.");
        Serial.println(url_SMS);
        Serial.println(" ");
        client.print(String("POST ") + url_SMS + " HTTP/1.1\r\n" +
        "Host: " + host + "\r\n" +
        "Connection: close\r\n\r\n");
        //Serial.println("GOOGLE DRIVE URL:");
        //Serial.println(url_google_drive);
        delay(500);   // pause for webservices   
      }         
    } // endif RED---REFILL_ASAP
     
  }

  // Serial print to the monitor for debug
  Serial.println(String(Range_inches) + " inches down / " + String(Percent_full) + "% full");
  Serial.println("Status: " + Status);
  // Create the request for IFTTT Google Drive and M2X updates
  url_google_drive = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_google_drive + "/with/key/" + MAKER_SECRET_KEY + "?value1=" + String(Percent_full) + "%" + "&value2=" + Status;
  url_M2X = "https://maker.ifttt.com/trigger/" + TRIGGER_NAME_M2X + "/with/key/" + MAKER_SECRET_KEY + "?value1=" + String(Percent_full);
 
  // This sends the request to the IFTTT server.
  //Serial.println("Requesting URL...");   // debug
  //Serial.println(url);   // debug
  if (logging == 1) { // is logging turned on? Maninly for debug...  Typically would be set = 1
    //Serial.println("Logging is ON."); 
    // Set up IFTTT Webhook Channel to update a Google sheet with the activity. 
    // Use WiFiClient class to create TCP connections for IFTT Webhook logging
    WiFiClient client;
    const int httpPort = 80;
    if (!client.connect(host, httpPort)) {
      Serial.println("connection failed");
      return;
    }     
    client.print(String("POST ") + url_google_drive + " HTTP/1.1\r\n" +
    "Host: " + host + "\r\n" +
    "Connection: close\r\n\r\n");
    Serial.println(" ");
    Serial.println("IFTTT url payload to Google Drive:");
    Serial.println(url_google_drive);
    Serial.println(" ");
    delay(500);   // pause for webservices
 
    // Set up IFTTT Maker Channel to update a AT&T M2X Server with the activity. 
    // Use WiFiClient class to create TCP connections for IFTT Webhook logging
    //WiFiClient client;
    //const int httpPort = 80;
    if (!client.connect(host, httpPort)) {
      Serial.println("connection failed");
      return;     }     
    client.print(String("POST ") + url_M2X + " HTTP/1.1\r\n" +
    "Host: " + host + "\r\n" +
    "Connection: close\r\n\r\n");
    Serial.println("IFTTT url payload for M2X:");
    Serial.println(url_M2X);
   
  } else {
     Serial.println("Logging is OFF.");
  } // endif/else logging 
 
  for (int x = 0; x < Seconds_between_posts; x++) { // Delay for next measurement.
    digitalWrite(led, !digitalRead(led));  // toggle state of the on board blue LED. Shows program is running
    delay(1000);   } // endfor delay for read measurement
}
-----
Thanks and check out our other stuff!!!

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.

Tuesday, October 25, 2016

Prime Numbers in a Box

-----
How many times have you needed the next prime number in a sequence and, like some animal, had to go to a printed table to look it up.  Well, those days are over.
----
A prime number is any positive whole number that can only get evenly divided only by 1 and itself.  Primes are used in many applications; a popular use being for encryption and cryptography.  Demonstrated here is another use for prime numbers.  That is making use of an older/slower Raspberry PI and a few parts to nerd up the decor of any room.

A few Raspberry PI skills learned will be:
     - writing to text files
     - reading from text files
     - driving a low cost I2C LCD display
     - driving a relay via a transistor
     - simple graceful shutdown method for the RasPI with a button and a JST connector.
-----
 
The project has an entertaining audio effect if you are into numbers.  Primes go on forever and ever; infinitely large.  The smallest numerical difference between two primes is 2 (example: 7-5=2).  What is interesting is the distance (difference) between two consecutive primes stays relatively low as the primes become very large.  Press a button and Primes in a Box gives a audible (relay click) signal for each non prime as it waits to display the next found prime.  If you enjoy mathematics you may find this oddly relaxing.
-----
The python source is pretty straight forward.  On button press a pointer to a file containing the first few million primes is indexed and displayed on the LCD.  A 5VDC relay clicks to represent the non primes in between.  The rig runs via USB power and the last found prime is always saved.  A handy 'shutdown' button is incorporated to allow the Raspbery PI project to be powered down gracefully if it needs to be moved.
-----
You'll need a Raspberry PI, 5VDC relay, PN2222A transistor, 16x2 I2C LCD, two resistors, and two normally open button switches.  A project box holds it all together.  Connect it all up like this:
Once on the breadboard it will look a bit like this:
 -----
Set up the RasPI to run the python code below at reboot (use @reboot in the sudo crontab).
Note, two files are expected to be found in the working directory:
     - prime_list.txt (List of prime numbers in order. One per line. As many as you like)
     - high_prime.txt (Holds the highest prime found in case of a restart/reboot)

#!/usr/bin/python

#
#  WhiskeyTangoHotel.Com
#  OCT 2016
#  
#  Program reads a file and displays prime # on LCD
#  On button press find next prime #.  Click a relay for each non prime
#  Write new highest prime to file to save if restarting the program
#
#  Gives an audible (relay click) representation of the distance between primes
#
#  Expects program to be in: /home/pi/RasPI/Programs-RasPI/Prime_Relay/
#
#  Leverages LCD script using I2C backpack for 16x2 and 20x4 screens.
#  Thanks to Matt Hawkins  http://www.raspberrypi-spy.co.uk/
#
#--------------------------------------

import smbus  # for LCD I2C display
import time # fpr sleep and pause delays
import os # this is for the shutdown button press
import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD) # to use Raspberry Pi board pin numbers
# set up GPIO output channel
Relay_Pin = 11 # pin to drive the relay clicks via 2222a transistor
GPIO.setup(Relay_Pin, GPIO.OUT)
Relay_delay = .1  # time delay between relay clicks

Switch = 8  # Button switch to move to next prime number
GPIO.setup(Switch, GPIO.IN)

reboot_pin = 26 #  Push this button and the RasPI shuts down gracefully
#Set pin to input and set pull-up resistor to hold the pin is high
GPIO.setup(reboot_pin, GPIO.IN, pull_up_down=GPIO.PUD_UP)

#######################################
### LCD DRIVER FUNCTIONS START HERE ###
#######################################

# Define some device parameters
I2C_ADDR  = 0x27 # I2C device address
LCD_WIDTH = 16   # Maximum characters per line

# Define some device constants
LCD_CHR = 1 # Mode - Sending data
LCD_CMD = 0 # Mode - Sending command

LCD_LINE_1 = 0x80 # LCD RAM address for the 1st line
LCD_LINE_2 = 0xC0 # LCD RAM address for the 2nd line
LCD_LINE_3 = 0x94 # LCD RAM address for the 3rd line
LCD_LINE_4 = 0xD4 # LCD RAM address for the 4th line

LCD_BACKLIGHT  = 0x08  # On
#LCD_BACKLIGHT = 0x00  # Off

ENABLE = 0b00000100 # Enable bit

# Timing constants
E_PULSE = 0.0005
E_DELAY = 0.0005

#Open I2C interface
#bus = smbus.SMBus(0)  # Rev 1 Pi uses 0
bus = smbus.SMBus(1) # Rev 2 Pi uses 1 RasPI(Bob)

def lcd_init():
 # Initialise display
 lcd_byte(0x33,LCD_CMD) # 110011 Initialise
 lcd_byte(0x32,LCD_CMD) # 110010 Initialise
 lcd_byte(0x06,LCD_CMD) # 000110 Cursor move direction
 lcd_byte(0x0C,LCD_CMD) # 001100 Display On,Cursor Off, Blink Off 
 lcd_byte(0x28,LCD_CMD) # 101000 Data length, number of lines, font size
 lcd_byte(0x01,LCD_CMD) # 000001 Clear display
 time.sleep(E_DELAY)

def lcd_byte(bits, mode):
 # Send byte to data pins
 # bits = the data
 # mode = 1 for data
 #        0 for command

 bits_high = mode | (bits & 0xF0) | LCD_BACKLIGHT
 bits_low = mode | ((bits<<4) & 0xF0) | LCD_BACKLIGHT

 # High bits
 bus.write_byte(I2C_ADDR, bits_high)
 lcd_toggle_enable(bits_high)

 # Low bits
 bus.write_byte(I2C_ADDR, bits_low)
 lcd_toggle_enable(bits_low)

def lcd_toggle_enable(bits):
 # Toggle enable
 time.sleep(E_DELAY)
 bus.write_byte(I2C_ADDR, (bits | ENABLE))
 time.sleep(E_PULSE)
 bus.write_byte(I2C_ADDR,(bits & ~ENABLE))
 time.sleep(E_DELAY)

def lcd_string(message,line):
 # Send string to display
 message = message.ljust(LCD_WIDTH," ")
 lcd_byte(line, LCD_CMD)
 for i in range(LCD_WIDTH):
  lcd_byte(ord(message[i]),LCD_CHR)
  
#######################################
### LCD DRIVER FUNCTIONS END HERE  ###
#######################################

# Highest calulated prime is stored in a file.  Get that number
highprime = open('/home/pi/RasPI/Programs-RasPI/Prime_Relay/high_prime.txt','r')
storedprime = (highprime.readline())
storedprime_val = int(storedprime)
print ' '
print "Recalled highest prime from file is: " + storedprime
print ' '

#Put status on LCD
lcd_init()
lcd_string("Init to last",LCD_LINE_1)
lcd_string("prime: " + str(storedprime_val),LCD_LINE_2)
highprime.close

def main():
 # Main program block 
 
 # Relay test clicks
 for i in range(1, 3):  
  GPIO.output(Relay_Pin,GPIO.HIGH) # Close relay
  time.sleep(Relay_delay) 
  GPIO.output(Relay_Pin,GPIO.LOW) # Open relay
  time.sleep(Relay_delay)  
 
 # Set the pointer to the correct place in the file that holds the prime list
 # prime_list.txt contains primes to 1,299,709
 primefile = open('/home/pi/RasPI/Programs-RasPI/Prime_Relay/prime_list.txt','r')
 currentprime_val = 0

 while storedprime_val > currentprime_val:
  currentprime = (primefile.readline())
  currentprime_val = int(currentprime)
  
 # Update the LCD
 lcd_string("PRESS for next",LCD_LINE_1)
 lcd_string("prime: " + str(currentprime_val),LCD_LINE_2)
 time.sleep(0.5)
 
 # Relay test clicks
 for i in range(1, 3): 
  GPIO.output(Relay_Pin,GPIO.HIGH) # Close relay
  time.sleep(Relay_delay) 
  GPIO.output(Relay_Pin,GPIO.LOW) # Open relay
  time.sleep(Relay_delay) 
 
 while True:
  
  if (GPIO.input(reboot_pin) == 0):
   # Update the LCD
   lcd_string("Primes in a Box",LCD_LINE_1)
   lcd_string("shutting down!!!" + str(currentprime_val),LCD_LINE_2)
   time.sleep(5)
   #Send command to system to shutdown
   os.system("sudo shutdown -h now")
  
  if (GPIO.input(Switch) == 0):
   # Update the LCD
   lcd_string("Calculating next",LCD_LINE_1)
   lcd_string("prime: " + str(currentprime_val),LCD_LINE_2)
  
   oldprime = currentprime_val
   currentprime = (primefile.readline())
   currentprime_val = int(currentprime)
   deltaprime = currentprime_val - oldprime 
   
   for i in range(1, deltaprime+1):  # click relay for each non prime between primes
    #print i
    GPIO.output(Relay_Pin,GPIO.HIGH) # Close relay
    time.sleep(Relay_delay) 
    GPIO.output(Relay_Pin,GPIO.LOW) # Open relay
    time.sleep(Relay_delay)   

   #Write the prime to a file so it can be recalled at prog start
   hp = open("/home/pi/RasPI/Programs-RasPI/Prime_Relay/high_prime.txt","w")
   hp.write(str(currentprime_val))
   
   print "CURRENT " +  str(currentprime_val)
   print "    OLD " + str(oldprime)
   print '------- '
   print "  DELTA " +  str(deltaprime)
   #print i
   print ' '

   # Update the LCD
   lcd_string("PRESS for next",LCD_LINE_1)
   lcd_string("prime: " + str(currentprime_val),LCD_LINE_2)

   hp.close
   time.sleep(.250)

if __name__ == '__main__':

 try:
  main()
 except KeyboardInterrupt:
  pass
 finally:
  lcd_byte(0x01, LCD_CMD)
----
Thanks for the visit and happy prime numbering!

Sunday, September 18, 2016

Keep Tabs on the Devices Connected to Your LAN

-----
THE PROBLEM:
Like most people, we have tons of things connected via WiFi to our router.  Be it AppleTVs, Rasberry PIs, Amazon Echos, weather stations, overhead airplane trackers, security cameras, etc. etc. etc....  The problem arises when you think and expect a device to "work" and it doesn't.  Is it the device? Is it the service being accessed? VNC server not running?  Most of the time "all is well", but if it isn't it can be frustrating.  Who knew the security camera was down?  Why can't I VNC into that Raspberry PI?  Most of us have been there and this project helps solve the problem.
------
PROBLEM SOLVED:
We had in the 'warehouse'  a Raspberry PI Zero (mainly because it was cheap so why not) and a BLINKT (who doesn't like flashy light things) doing nothing and just waiting for a project.  Setting up the gear to monitor our LAN seemed perfect for the pair.

The solution is pretty straight forward: Get the RaspBerry PI Zero on your LAN via WiFi.  Plug in the BLINKT module.  Every thirty seconds or so send a ping to an IP of a device on your network.
If the device IP ping is good then
     - flash the BLINKT green.
If the device IP ping is bad then
     - retest the device just to make certain the failure wasn't an anomaly.
     - two failed pings back to back then flash the BLINKT red and log the failure to a Google Drive Sheet.
-----
RESULTS:

The rig works perfectly.  We can check the Google Drive Sheet or the BLINKT quickly see if anything on the LAN is down.  We use IFTTT.com to log the failures to the Google Drive Sheet.  We can just as easily use IFTTT.com to send out a text message, post a Tweet, call our phone, or a host of other IFTTT.com supported actions.  In short, it is pretty darn useful.

Here is what a sample output to the Google Drive Sheet looks like:
-----
SOURCE:
You will need an IFTTT.com and Google logons if you want to log ping status to a Google Drive Sheet.  Both are useful for a few hundred reasons, so if you don't have one then get one - they are free.

#!/usr/bin/env python

# LocalPing Testing/Tracking
# RasPI Zero with BLINKT module attached
# BLINKT scans back/forth with delay in mS = to CPU % use (45% = ,45 Secs)
#
# After a set delay (var = Ping_test_how_often)
# Checks if tracked IPs are up on the LAN. 
#      - If device ping passes, flash all BLINKT LEDs green.
#            - however, if there was a ping fail in the last xx (var = Ping_check) flash 1st LED as RED and rest as GREEN.
#      - If device ping Fails two pings back to back:
#            - use IFTTT Maker Channel to log the failed ping to a Google Drive sheet
#            - write the falied ping information to the terminal
#
# WhiskeyTangoHotel.Com - AUG2016

import os # allows for the ping calls
import time # for sleeps and localtime prints
import random # for random number generation
import psutil # for ping system calls
from blinkt import set_pixel, show, set_brightness, clear # https://shop.pimoroni.com/products/blinkt

# Set up for IFTTT triggering
import requests # needed to get https request to IFTTT web page
MAKER_SECRET_KEY = "xxxyyyzzz1234561890"  #this is your IFTTT Maker Channel secret key

PingSiteIP = [1,2,3,4,5,6,7,8,9,10,11]   # holds the IP or DNS name of the site being pinged
PingSiteName = [1,2,3,4,5,6,7,8,9,10,11]   # Friendly/Human name for the device

# Identify IPs that you want to track on your LAN
# PingSiteName[] can be anything. It is the "human" name for the IP
# Enter the 'x' part of the IP; the last number of the IP address without the '.'
IP_Prefix = "192.168.0."  # typically 192.168.0. or 192.168.1.  DON'T FORGET THE LAST '.'
PingSiteIP[1] = "11"
PingSiteName[1] = "RasPI(Upper)"
PingSiteIP[2] = "14"
PingSiteName[2] = "RasPI(Lower)"
PingSiteIP[3] = "20"
PingSiteName[3] = "Security(NW)"
PingSiteIP[4] = "23"
PingSiteName[4] = "Security(Drive)"
PingSiteIP[5] = "32"
PingSiteName[5] = "Security(SE)"
PingSiteIP[6] = "13"
PingSiteName[6] = "ElectricImp"

PingSiteIP[7] = "27)"
PingSiteName[7] = "RasPI(Airplanes)"
PinsSiteIP[8] = "49"
PingSiteName[8] = "RasPI(Weather)"
PingSiteIP[9] = "45"
PingSiteName[9] = "Printer(HP)"

Num_ping_sites = 9   # Make sure this matches the above number of site entries

Single_test_site = 1 # Must be 1; I did not want to start at array 0
Flash_on = .05       # Secs to keep ping status LEDs on
Flash_off = .2       # Secs to keep ping status LEDs off
Failed_ping = 0      # FLAG for if there is a failed ping in the last Ping_check trys then turn one green light red
Second_try_delay = 20    # seconds to wait to config falied ping on device (Yellow LEDs scan during this delay)
Ping_check = 15         # see comment above for Failed_Ping
Ping_check_counter = Ping_check # Counts Ping_check, decrements and resets at Zero
Ping_test_how_often = 10 # 10 is ~30 secs depending on delay from CPU load. Dont ping test on every cpu speed check
Every_x_times = Ping_test_how_often # decrementing counter for the Ping_test_how_often var
set_brightness(0.05) # 0.05 is lowest usable dim.  1.0 is full bright (the BLINKT is *really* bright if you want!)
LED_Delay = 0        # Controls the LED to LED scan speed. This is adjusted in the program after reading CPU % load
r_largest_wanted_value = 10  # these vars control the max amount of R, G, or B you desire. Also helps with more dimming.
g_largest_wanted_value = 10  # during the CPU load status LED back and forth scan on the BLINKT
b_largest_wanted_value = 255 # For example: 10, 10, 255 would mean favor BLUE hues.

Current_day = time.strftime("%d", time.localtime()) # what day of the month is it
New_day_dawned = Current_day # 1st run after midnight update the Google Sheet. Use to verify the prog has not stopped running
Failed_since_midnight = 0

print " "
print "Writing header information to Google Sheet..."
#Triggers the IFTTT Maker channel "local_ping" to create an information header
url = "https://maker.ifttt.com/trigger/local_ping/with/key/" + MAKER_SECRET_KEY + "?value1=" + "*************************"
res = requests.get(url)
#print str(res) + " is web request result. "   #used only for debug

print "Testing LEDs..."
print "---------------------------------------------"
#LED test and some delay for the IFTTT calls
for j in range (0,8):
    set_pixel(j, 30, 0, 0)
show()
time.sleep(Flash_on * j)
for j in range (0,8):
    set_pixel(j, 0, 30, 0)
show()
time.sleep(Flash_on * j)
for j in range (0,8):
    set_pixel(j, 0, 0, 30)
show()
time.sleep(Flash_on * j)   

for sheet_header in range (1, Num_ping_sites + 1):
    #this triggers the IFTTT Maker channel "local_ping" to update the google sheet.
    url = "https://maker.ifttt.com/trigger/local_ping/with/key/" + MAKER_SECRET_KEY + "?value1=" + "Tracking " + PingSiteName[sheet_header] + "&value2=" + IP_Prefix + str(PingSiteIP[sheet_header])
    res = requests.get(url)
    #print str(res) + " is web request result. "   #used only for debug
    print str(sheet_header) + ") " + PingSiteName[sheet_header] + " at " + IP_Prefix + str(PingSiteIP[sheet_header])
   
    #LED test and some delay for the IFTTT calls
    for j in range (0,8):
        set_pixel(j, 30, 0, 0)
    show()
    time.sleep(Flash_on * j)
    for j in range (0,8):
        set_pixel(j, 0, 30, 0)
    show()
    time.sleep(Flash_on * j)
    for j in range (0,8):
        set_pixel(j, 0, 0, 30)
    show()
    time.sleep(Flash_on * j)       

clear() # all LEDS off
   
url = "https://maker.ifttt.com/trigger/local_ping/with/key/" + MAKER_SECRET_KEY + "?value1=" + "*************************"
res = requests.get(url)
#print str(res) + " is web request result. "   #used only for debug
print " "
print "Done with test!!!"
localtime = time.asctime( time.localtime(time.time()) )
print localtime + ": Tracking begins for the devices listed..."
print " "

time.sleep(1) # let CPU% settle from prog load

while True:
    #LED delay is established randomly
    #random.seed() # seed off the CPU clock
    #r = random.random()
    #LED_Delay = (int((r * 10) + 1)) / 100.0
   
    #LED delay is equal to %CPU usage in mSec (45% = .45 Secs)
    LED_Delay = psutil.cpu_percent() / 100.0

    random.seed() # seed off the CPU clock
    r = random.random()
    red = int((r * r_largest_wanted_value) + 1)
   
    random.seed() # seed off the CPU clock
    r = random.random()
    green = int((r * g_largest_wanted_value) + 1)

    random.seed() # seed off the CPU clock
    r = random.random()
    blue = int((r * b_largest_wanted_value) + 1)   
   
    for i in range(8):       
        set_pixel(i , red, green, blue)
        show()
        time.sleep(LED_Delay)
        clear()

    #LED delay is established randomly
    #random.seed() # seed off the CPU clock
    #r = random.random()
    #LED_Delay = (int((r * 10) + 1)) / 100.0
   
    #LED delay is equal to %CPU usage in mSec (45% = .45mS)
    LED_Delay = psutil.cpu_percent() / 100.0

    random.seed() # seed off the CPU clock
    r = random.random()
    red = int((r * r_largest_wanted_value) + 1)
   
    random.seed() # seed off the CPU clock
    r = random.random()
    green = int((r * g_largest_wanted_value) + 1)

    random.seed() # seed off the CPU clock
    r = random.random()
    blue = int((r * b_largest_wanted_value) + 1)       

    for i in range(7, -1, -1):
        set_pixel(i , red, green, blue)
        show()
        time.sleep(LED_Delay)
        clear()
       
    # Local ping to see if all is up
    # But we don't want to check on every run so:
   
    if Every_x_times == 1: #  var is decremented in the else.  Check the ping
        Every_x_times = Ping_test_how_often
        if Single_test_site == Num_ping_sites:
            Single_test_site = 1
        else:   
            Single_test_site = Single_test_site + 1           
   
        if Ping_check_counter == 0:
            Ping_check_counter = Ping_check
            Failed_ping = 0
        else:
            Ping_check_counter = Ping_check_counter -1       
                   
        hostname = IP_Prefix + str(PingSiteIP[Single_test_site]) # OK, now the have selected one of our ping test sites
        # Ping test IP with os.system command
        response = os.system("ping -c 1 " + hostname + " >/dev/null")
        #print PingSiteName[Single_test_site] + " at IP " + str(PingSiteIP[Single_test_site])
        if response == 0:
            for    i in range (0,10): #flash all green
                for j in range (0,8):
                    if j ==0 and Failed_ping == 1:
                        set_pixel(j, 30, 0, 0)   # red pixel to show a recent failed ping
                    else:
                        set_pixel(j, 0, 30, 0)
                show()
                time.sleep(Flash_on)
                for j in range (0,8):
                    set_pixel(j, 0, 0, 0)
                show()
                time.sleep(Flash_off)               
        else: #response != 0:
            # Ping failed, but wait 5 seconds and try again.
            # Walk yellow LEDs to show re-test and to delay some before double checking the device ping
            for i in range(8):       
                set_pixel(i , 30, 30, 0)  # YELLOW LED walk
                show()
                time.sleep(Second_try_delay/16.0)     
                   
            clear()
            for i in range(7, -1, -1):
                set_pixel(i , 30, 30, 0)
                show()
                time.sleep(Second_try_delay/16.0)
                   
            response = os.system("ping -c 1 " + hostname + " >/dev/null") # let's check again to make certain the ping really failed.
            if response != 0:                                             # it take two back to back failed pings to consider that device down.
                Failed_since_midnight = Failed_since_midnight + 1  # resetable counted tracks the days failed ping count
               
                localtime = time.asctime( time.localtime(time.time()) )
                print localtime + ": Failed ping " + str(Failed_since_midnight) + " from " + PingSiteName[Single_test_site] + " at " + IP_Prefix + str(PingSiteIP[Single_test_site])
               
                #this triggers the IFTTT Maker channel "local_ping" to update the google sheet.
                url = "https://maker.ifttt.com/trigger/local_ping/with/key/" + MAKER_SECRET_KEY + "?value1=" + "FailCount[" + str(Failed_since_midnight) + "]:" + PingSiteName[Single_test_site] + "&value2=" + IP_Prefix + str(PingSiteIP[Single_test_site])
                res = requests.get(url)
                #print str(res) + " is web request result. "   #used only for debug
               
                for    i in range (0,10): #flash all red
                    Failed_ping = 1
                    for j in range (0,8):
                        set_pixel(j, 30, 0, 0)
                    show()
                    time.sleep(Flash_on)
                    for j in range (0,8):
                        set_pixel(j, 0, 0, 0)
                        show()
                    time.sleep(Flash_off)
    else: # if Every_x_times == 0:
        Every_x_times = Every_x_times - 1
       
    # Last thing is check if this is a new day.  If yes, this write status to Google sheet
    Current_day = time.strftime("%d", time.localtime())
    if Current_day != New_day_dawned:
        url = "https://maker.ifttt.com/trigger/local_ping/with/key/" + MAKER_SECRET_KEY + "?value1=" + ">>> Failed since midnight: " + str(Failed_since_midnight)
        res = requests.get(url)
        localtime = time.asctime( time.localtime(time.time()) )
        print " "
        print localtime + " >>> Failed since midnight: " + str(Failed_since_midnight)
        print " "
        time.sleep(1)
        Failed_since_midnight = 0
        New_day_dawned = Current_day       

   
-----
Give it a try.  It was been very very useful here.