Pages

Tuesday, December 17, 2013

Wireless Qi Charging Car Dock for my Nexus 5



I read the specs on the Nexus 5 as I order it and my thoughts as always go to, "What accessories can I buy/make to make all this cooler?" Then I see the wireless charging. "Sweet I want a Qi car dock so I can just slap it in there and go..." I tell myself, only to find the options are either crappy Chinese made docks that look like crud or $200+ options that aren't all that great either.

At this point my, "They don't make it... Fine I WILL!" mentality takes over and I start digging thru options of Qi charging pads. Reading tons of reviews, digging thru spec sheets, taking measurements, etc. I settle on the Nokia DT-900 charger. It has great review, is the right size, runs on 12 volts, and looks great. The only problem is it retails for north of $40 bucks most places, but oddly enough AT&T has them on sale all the time for $24, and when I ordered them they had 25% off if you ordered 3 or more accessories. So I ended up getting them for $18 a piece. about $55 for all 3 shipped. just a tad more than one other places.

Then I turned my attention to the cradle it's self. I needed something universal, strong, and that would fit with phone and charger. I found the Aduro U-GRIP Plus from Amazon for $20 It had good reviews, everything would fit, and looked the part too.

Here are some good shots of the charger torn down. You can see it has 3 pads so it is pretty much one big charger. It's hard to miss the "sweet spot" on it when it is ALL CHARGER. lol












I pulled the gripping pads out of the cradle so I could Epoxy the charger in place on just one of the expanding arms. This gave me an extra 2mm for the phone to fit in the arms a bit better as well.
















I wanted to wire it directly to the car and went over board on this part based on some other long term projects in my WRX. I brought a new power cable into the cabin directly from the battery (30amp fused at the battery) then wired it to a brand new fuse block so I can wire multiple things off this new run and not have to "Tap into" other circuits. I did tap into the ACC line so I could use a relay to make the fuse box only live when the car is on. so any of the things I wire off it won't drain the battery. I'm sorry i don't have any pics of this setup but I will work on writing that part up in another post soon.

Also one thing to note here is that the power you get from your car is NOT very "clean". While running the alternator is pumping out 13-15 volts. Second there are massive spikes from different parts turning on and off and relays that are not protected by diodes so 50-80volt spikes are not uncommon. So this should be considered or any bit of electronics wired directly to the car's "+12v" supply will not be long for this world. For the charger (and my radar detector) I built a power supply to keep it below 12v and handle the spikes.

I had some Sharp PQ12RD21's Voltage regulators sitting around so I used those along with a simple TVS circuit to handle spikes above 18 volts. (no I don't have a digital version of the schematic I will get a picture of my notebook soon) For those out there that power supply design isn't in your ball park I'd go get something simple like these maybe these Ebay 3A DC-DC Buck converters with an LM2596S These have a low drop out aswell so that when the car is in ACC and not running ever just the battery at 12ish volts would still put out around 11.5 (I'm guessing here based on the datasheet for the LM2596S) and with my setup 11.5 is still enough to power the Qi pad.

I did some testing with the pad to see what it's current draw was. I ran some tests, a full charge from 0% to 100% This took about 3 hours (about 20% long then a normal full A/C wall charger) Then another test with the phone at around 50% running GPS and Streaming Netflix over LTE. This netted a decrease in battery of about 8% over 4 hours, so that is pretty good in my book. On a recent road trip I kept switching from Torque to Waze and I ended up still charging from 60% to 100% with the screen on for the whole 6 hours, Not sure how long it took but I ended the trip with a phone at 100% :D

With no phone the DT-900 draws about 4mA on it's on and about 30mA once I added the power supply circuit

While charging it draws about 700mA when the battery is low and tapers down as the phone approaches 100% Here it is around 80% and drawing about half an Amp.

The phone is a bit offset since the charger is attached to the lower arm but it still meets up with the charging pads very well.
Here we go mounted in the car. It is rather strong, there is almost no wobble when hitting bumps or on rough roads. Over all I'm super happy with the result!








Monday, August 19, 2013

Python Matrix Keypad Package

I packaged up the Matrix Keypad code into a downloadable and installable PyPI package. Let me know if you have any issues but it seems download and install well with PIP and once you make the symlinks to the adafruit code you should be good to go.

Also if there is anyone that has a Beagle Bone that can help me port this over to it aswell I would appreciate it. I've had a few requests but I don't own one yet to do the work on. (unless someone ones to send me one :) )

https://pypi.python.org/pypi/matrix_keypad

Introduction

Python Library for Matrix Keypads. Written and tested on a Model B Raspberry Pi. Supports both a 3x4 and 4x4 keypad included
Current Version:
 v1.1.1
Project Page:Project_Page
PyPI page:PyPI_Page

Author

Prerequisites

If the I2C Port expander MCP23017 or MCP23008 is being used, the Adafruit Python library for I2C and the MCP will need to be installed.
You can clone the whole library like so:
git clone https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code.git
or the two files needed can be pulled out, Adafruit_I2C.py & Adafruit_MCP230xx.py.

Install

You can use the source from just downloading the files or Install it as a library via PIP:
pip install matrix_keypad
After the install you will need to create links to the Adafruit I2C and MCP230xx code since they are not installed as packages.:
sudo ln -s [path to Adafruit python cod]/AdafruitMCP230xx/*.py /usr/local/lib/python2.7/dist-packages/matrix_keypad
Note: you will have to change the part in the brackets and maybe the path to the where the matrix keypad package is

Files Included

README.txt
LICENSE.txt
setup.py
matrix_keypad/
    __init__.py
    matrix_keypad_RPi_GPIO.py
    matrix_keypad_MCP230xx.py
    matrix_keypad_demo.py
    matrix_keypad_demo2.py

Usage

See the demo scripts included to see this all in action.
To call the library select which one you intend to use and use the correct line:
from matrix_keypad import MCP230xx
or:
from matrix_keypad import RPi_GPIO
Then initialize and give the library a short name so it is easier to reference later. For the MCP version:
kp = MCP230xx.keypad(address = 0x21, num_gpios = 8, columnCount = 4
The variables here are the I2C address, then if you are using the MCP23017 or MCP23008 you have to put the number of GPIO pin avaialable (default is 8), Then the "columnCount" is 3 for the 4x3 keypads and 4 for the 4x4 keypads.
For the standard GPIO version you only have to reference the 'column count if you want to change it to the 4x4, it defaults at the 3x4:
kp = RPi_GPIO.keypad(ColumnCount = 4)
It is possible to just check to see if a digit is currently pressed.:
checkKeypad = kp.getKey()
Or a simple function to call the keypad library and loop through it waiting for a digit press
def digit():
    # Loop while waiting for a keypress
    digitPressed = None
    while digitPressed == None:
        digitPressed = kp.getKey()
    return digitPressed

Version History

v0.1.0:
Initial Scripts
v1.0.0:
Initial package build
v1.0.1:
Initial package build and push to PyPI
v1.0.2:
Updating the matrix_keypad_demo2.py to demo selecting the 4x4 keypad
v1.0.3:
Moved Version Log in README
Updated README Links
v1.0.4:
Updated References to include the PiLarm code as the inspiration for the "...demo2.py" code
v1.0.5:
Updates to the code in both main libs to fix some indenting and other issues from coping the code from blogger to a text file.
Updates to the keypad picking section for the constants to make it actually work
v1.0.6:
Fixes to more indenting issues. :(
v1.1.0:
Updated main libs and the demo code.
Added install directions to handle the links to the adafruit code
v1.1.1:
Updated ...demo.py and demo2.py to reflect new package name.
Updated README as well

Code References

Column and Row scanning adapted from Bandono's matrixQPI which is wiringPi based.
matrix_keypad_demo2.py is based on some work that Jeff Highsmith had done in making his PiLarm that was featured on Make.

Monday, August 5, 2013

Scrapwood Bike Stand

I had built this a while back but just found all the pictures I took during the process. I spent a ton of time measuring this all out so it would fit together tight and be very strong. It's a bit overkill but it does the job so well. This was about a half sheet of plywood left over from building the router table cabinet. I was tired of leaning my bike against things in the shop so I whipped this up.

















Yes that is Flex-seal :) lol


















Thursday, August 1, 2013

Adafruit's Simple RF receiver, Testing on an Arduino

I just picked up Adafruit's Simple RF receiver right now I was testing the Momentary type (M4) Which means that while you press the keypad button the corresponding pin on the receiver goes high. I am hoping the next time I order something from them I remember to get the latching one instead because that is what I really need. Anyway since I don't have a project for this one in mind I just wanted to do some testing... so I just plugged it directly into my Arduino( avoiding the Serial pins since I needed those to display the pin states) This is the only way I could fit it on the board without putting it on a bread board. (quick and dirty!)

The VT pin isn't of to much concern for me right now so it just tucks in between the two pin banks :)

Check out the code below. This is great but if I don't want to constantly poll for buttons I may miss one. So my intention is to order a latching version, when you press the button on the keypad the pin goes high and stays high until you press it again. My thought is that if I am powering the receiver from the arduino then I can read it maybe once a second or even less often then when a high state is detected then I can simple "reboot" the receiver by dropping the +5v pin to ground. So until I get one in I will hope it resets back to ground on all the signal pins like I need

/*--------------------------------------------------------
Adafruit's Simple RF Receiver example
http://www.adafruit.com/products/1096                                  
Author: Chris Crumpacker                               
Date: August 2013 

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
                                                           
Sketch Notes: This is set as if you put the reciever directly
onto an arduino with gnd in pin 2 and the VT pin in the 
gap between the pin banks.
--------------------------------------------------------*/

#define GROUNDPIN  2
#define POWERPIN   3

// Arduino Pin Numbers
int signalPin[4] = { 4, 5, 6, 7 };
// Reciever Pin Names
const char* pinName[] = { "D3", "D2", "D1", "D0" };
// Keyfob Pin Names
const char* buttonName[] = { "D", "C", "B", "A" };

int lastSignalState[] = { 0, 0, 0, 0 };
int signalState = 0;
float onTime = 0.00;

void setup() {
  // Set the ground pin to a Low output
  pinMode(GROUNDPIN, OUTPUT);
  digitalWrite(GROUNDPIN, LOW);
  
  // Set the +5v pin to a High output
  pinMode(POWERPIN,OUTPUT);
  digitalWrite(POWERPIN, HIGH);
  
  // Setting the signal pins as inputs
  for (int i = 0; i < 4; i++) {
    pinMode(signalPin[i], INPUT);
  }
  
  // Starting the serial interface
  Serial.begin(19200);
}

void loop(){
  // For each of the 4 pins we loop thru and check the state.
  for (int i = 0; i < 4; i++) {
    // Read the current pin
    signalState = digitalRead(signalPin[i]);
    
    if (signalState != lastSignalState[i]) {
      // if the state has changed...
      if (signalState == HIGH) {
        // ...and the current state is HIGH...
        Serial.print("Receiver Pin: ");
        Serial.println(pinName[i]);
        Serial.print("Keyfob Button: ");
        Serial.println(buttonName[i]);
        Serial.println("Switched ON");
        Serial.println("------------------------------");
        onTime = millis();
      } 
      else {
        // ...and the current State is LOW...
        Serial.print("Receiver Pin: ");
        Serial.println(pinName[i]);
        Serial.print("Keyfob Button: ");
        Serial.println(buttonName[i]);
        Serial.print("Switched OFF after ");
        Serial.print(((millis() - onTime) / 1000.00));
        Serial.println(" seconds");
        Serial.println("------------------------------");
      }
    }
    // save the current state to the last state array
    lastSignalState[i] = signalState;
  }
}

Monday, May 27, 2013

Using an Arduino board as an ISP via the ICSP header

**Moving this over from my instructables so it is also on my blog**

Here are the Gerber Files for an ATTiny84 and 85 along with the board stencils.

I won't go into how to use an Arduino as an ISP, there are plenty of instructables and other how-to's on the net for that.

http://www.instructables.com/id/Turn-Your-Arduino-Into-an-ISP/
http://www.instructables.com/id/Adding-ICSP-header-to-your-ArduinoAVR-board/
http://www.instructables.com/id/How-to-program-a-AVR-arduino-with-another-arduin/

What I needed was a simple way to program (and burn the bootloader) onto my ATtiny chips. I have made a breadboard breakout board (soon to be a different instructable) for my ATTiny84/44s as well as one for my ATTiny85/45s.



These sub-boards are for breadboard prototyping so I don't have to setup the crystal, the power, and the reset every time I want to move to a breadboard.

Step 1:




This is the ATTiny*4 Breadboard breakout. It's the board's traces are a mess I know. I made it with a sharpie marker as I didn't have access to a proper laser printer late at night when I made it. But it works like a charm.

The ICSP header breaks out the chip's MOSI, MISO, SCK, VCC, GND, and RESET pins. That is wired to a normal 6 wire cable. The other end is where the trick comes in.

Step 2:

Typically the ICSP on the arduino boards is used to program the chip on that board. So more like an "IN" as opposed to an "OUT". The reason for that is pin 5 is wired to the reset pin on that chip, and not the reset needed inside the "Arduino as ISP" sketch. I've see "how-to's" that tell you to cut those traces and rewire it. I had no interest in modifying my board permanently so I found it way easier to modify the cable.





Using an 8 pin cable I scavenged I remove the previous cable, then cut 6 wires from an old IDE ribbon cable. After that I pulled the 5th wire from the ribbon cable and made it about 3 inches longer, and did NOT put it into the punchdown for the header.

I punched the other wires in and cut them flush. I put a dab of super glue on both sides of the ribbon cable to try and prevent it from splitting down the cable any more. at the end of the reset pin (5th wire) I put a male connector on it so it would fit into the female pin 10 for programming.


Now all I need to do to program (or burn the bootloader) onto really any AVR that I have a bootloader for is attach this to the ICSP on the slave board and then the other end to my main arduino board and wire up pin 10 for reset. I am able to use the stock "Arduino as ISP" sketch with no mods to the boards or the sketch.

Monday, May 13, 2013

Using 3x4 matrix keypad with the Raspberry Pi

UPDATE 8/23/13
This page will stay since it explains the original scripts but I've made a number of updates and have packaged them up on the PyPI so they are easy to maintain for me and to distribute and install for everyone else. Let me know if there are any issues and I'll attempt to take care of them as time allows.
https://pypi.python.org/pypi/matrix_keypad
http://crumpspot.blogspot.com/p/keypad-matrix-python-package.html

Another module of my alarm interface is done. I picked up a 3x4 matrix keypad from Adafruit (http://www.adafruit.com/products/419) But couldn't find any good python code examples for it's use. I was able to find a few examples that lead me down the right path in terms of scanning rows first as inputs and then swapping pins to scan the columns. I wrote two libraries, the first just uses 7 of the Raspberry Pi's GPIO pins then I also wrote one that works for the MCP23008 chip since I'm really in a mode of saving pins when I can :) Then also just wrote a demo script to show calling the libraries and their use.

Here is the code as it stands now, I have some ideas on how to change it to be better but this is fully working for now.

UPDATE: I received a 4x4 keypad to test changes to the script and it worked like a charm once I changed the keypad and the Row and Column values like so:
KEYPAD = [
[1,2,3,"A"],
[4,5,6,"B"],
[7,8,9,"C"],
["*",0,"#","D"]
]
ROW = [7,6,5,4]
COLUMN = [3,2,1,0]


matrixKeypad_RPi_GPIO.py:


# #####################################################
# Python Library for 3x4 matrix keypad using
# 7 of the avialable GPIO pins on the Raspberry Pi. 
# 
# This could easily be expanded to handle a 4x4 but I 
# don't have one for testing. The KEYPAD constant 
# would need to be updated. Also the setting/checking
# of the colVal part would need to be expanded to 
# handle the extra column.
# 
# Written by Chris Crumpacker
# May 2013
#
# main structure is adapted from Bandono's
# matrixQPI which is wiringPi based.
# https://github.com/bandono/matrixQPi?source=cc
# #####################################################

import RPi.GPIO as GPIO

class keypad():
    # CONSTANTS   
    KEYPAD = [
    [1,2,3],
    [4,5,6],
    [7,8,9],
    ["*",0,"#"]
    ]
    
    ROW         = [18,23,24,25]
    COLUMN      = [4,17,22]
    
    def __init__(self):
        GPIO.setmode(GPIO.BCM)
    
    def getKey(self):
        
        # Set all columns as output low
        for j in range(len(self.COLUMN)):
            GPIO.setup(self.COLUMN[j], GPIO.OUT)
            GPIO.output(self.COLUMN[j], GPIO.LOW)
        
        # Set all rows as input
        for i in range(len(self.ROW)):
            GPIO.setup(self.ROW[i], GPIO.IN, pull_up_down=GPIO.PUD_UP)
        
        # Scan rows for pushed key/button
        # A valid key press should set "rowVal"  between 0 and 3.
        rowVal = -1
        for i in range(len(self.ROW)):
            tmpRead = GPIO.input(self.ROW[i])
            if tmpRead == 0:
                rowVal = i
                
        # if rowVal is not 0 thru 3 then no button was pressed and we can exit
        if rowVal <0 data-blogger-escaped-or="" data-blogger-escaped-rowval="">3:
            self.exit()
            return
        
        # Convert columns to input
        for j in range(len(self.COLUMN)):
                GPIO.setup(self.COLUMN[j], GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
        
        # Switch the i-th row found from scan to output
        GPIO.setup(self.ROW[rowVal], GPIO.OUT)
        GPIO.output(self.ROW[rowVal], GPIO.HIGH)

        # Scan columns for still-pushed key/button
        # A valid key press should set "colVal"  between 0 and 2.
        colVal = -1
        for j in range(len(self.COLUMN)):
            tmpRead = GPIO.input(self.COLUMN[j])
            if tmpRead == 1:
                colVal=j
                
        # if colVal is not 0 thru 2 then no button was pressed and we can exit
        if colVal <0 data-blogger-escaped-colval="" data-blogger-escaped-or="">2:
            self.exit()
            return

        # Return the value of the key pressed
        self.exit()
        return self.KEYPAD[rowVal][colVal]
        
    def exit(self):
        # Reinitialize all rows and columns as input at exit
        for i in range(len(self.ROW)):
                GPIO.setup(self.ROW[i], GPIO.IN, pull_up_down=GPIO.PUD_UP) 
        for j in range(len(self.COLUMN)):
                GPIO.setup(self.COLUMN[j], GPIO.IN, pull_up_down=GPIO.PUD_UP)
        
if __name__ == '__main__':
    # Initialize the keypad class
    kp = keypad()
    
    # Loop while waiting for a keypress
    digit = None
    while digit == None:
        digit = kp.getKey()
    
    # Print the result
    print digit  

matrixKeypad_MCP230xx.py

# #####################################################
# Python Library for 3x4 matrix keypad using
# the MCP23008 chip via I2C from the Raspberry Pi.
# 
# This could easily be expanded to handle a 4x4 but I 
# don't have one for testing. The KEYPAD constant 
# would need to be updated. Also the setting/checking
# of the colVal part would need to be expanded to 
# handle the extra column.
# 
# Written by Chris Crumpacker
# May 2013
#
# main structure is adapted from Bandono's
# matrixQPI which is wiringPi based.
# https://github.com/bandono/matrixQPi?source=cc
# #####################################################

from Adafruit_MCP230xx import Adafruit_MCP230XX

class keypad(Adafruit_MCP230XX):
    # Constants
    INPUT       = 0
    OUTPUT      = 1
    HIGH        = 1
    LOW         = 0
    
    KEYPAD = [
    [1,2,3],
    [4,5,6],
    [7,8,9],
    ["*",0,"#"]
    ]
    
    ROW         = [6,5,4,3]
    COLUMN      = [2,1,0]
    
    def __init__(self, address=0x21, num_gpios=8):
        
        self.mcp2 = Adafruit_MCP230XX(address, num_gpios)
        
    def getKey(self):
        
        # Set all columns as output low
        for j in range(len(self.COLUMN)):
            self.mcp2.config(self.COLUMN[j], self.mcp2.OUTPUT)
            self.mcp2.output(self.COLUMN[j], self.LOW)
        
        # Set all rows as input
        for i in range(len(self.ROW)):
            self.mcp2.config(self.ROW[i], self.mcp2.INPUT)
            self.mcp2.pullup(self.ROW[i], True)
        
        # Scan rows for pushed key/button
        # valid rowVal" should be between 0 and 3 when a key is pressed. Pre-setting it to -1
        rowVal = -1
        for i in range(len(self.ROW)):
            tmpRead = self.mcp2.input(self.ROW[i])
            if tmpRead == 0:
                rowVal = i
                
        # if rowVal is still "return" then no button was pressed and we can exit
        if rowVal == -1:
            self.exit()
            return
        
        # Convert columns to input
        for j in range(len(self.COLUMN)):
            self.mcp2.config(self.COLUMN[j], self.mcp2.INPUT)
        
        # Switch the i-th row found from scan to output
        self.mcp2.config(self.ROW[rowVal], self.mcp2.OUTPUT)
        self.mcp2.output(self.ROW[rowVal], self.HIGH)
        
        # Scan columns for still-pushed key/button
        colVal = -1
        for j in range(len(self.COLUMN)):
            tmpRead = self.mcp2.input(self.COLUMN[j])
            if tmpRead == 1:
                colVal=j
        
        if colVal == -1:
            self.exit()
            return
              
        # Return the value of the key pressed
        self.exit()   
        return self.KEYPAD[rowVal][colVal]
            
    def exit(self):
        # Reinitialize all rows and columns as input before exiting
        for i in range(len(self.ROW)):
                self.mcp2.config(self.ROW[i], self.INPUT) 
        for j in range(len(self.COLUMN)):
                self.mcp2.config(self.COLUMN[j], self.INPUT)
        
if __name__ == '__main__':
    # Initialize the keypad class
    kp = keypad()
    
    # Loop while waiting for a keypress
    r = None
    while r == None:
        r = kp.getKey()
        
    # Print the result
    print r  

Demo code, matrixKeypad_test.py

# #####################################################
# Demo script showing the use of the Python 
# matrix Keypad library for both the Raspberry Pi
# GPIO and the MSP230xx I2C Chip set.
#
# Librarys needed:
# matrixKeypad_MCP230xx.py or matrixKeypad_RPi_GPIO.py
#
# Also needed is the Adafruit python libraries for the 
# MCP230xx chips (Adafruit_MCP230xx.py) and I2C (Adafruit_I2C.py)
# 
# Written by Chris Crumpacker
# May 2013
#
# #####################################################

from matrixKeypad_MCP230xx import keypad
#from matrixKeypad_RPi_GPIO import keypad
from time import sleep

# Initialize the keypad class
kp = keypad()

def digit():
    # Loop while waiting for a keypress
    r = None
    while r == None:
        r = kp.getKey()
    return r 

print "Please enter a 4 digit code: "

# Getting digit 1, printing it, then sleep to allow the next digit press.
d1 = digit()
print d1
sleep(1)

d2 = digit()
print d2
sleep(1)

d3 = digit()
print d3
sleep(1)

d4 = digit()
print d4

# printing out the assembled 4 digit code.
print "You Entered %s%s%s%s "%(d1,d2,d3,d4) 

Wednesday, May 8, 2013

Data Table on a 20x4 LCD using python

Just a quick demo showing off some code I made to handle displaying multiple data points on a 20x4 LCD without having the update the entire screen with each refresh. This keeps the screen clean and a bit easier to read... and a ton easier to code against.



Below is the code for the demo you see in the video. The __main function is running thru some example uses, but you should be able to call this code with no changes. Also this is currently on my fork of the Adafruit python code library as it uses the underlying code from them, See my previous blog post on that. http://crumpspot.blogspot.com/2013/05/using-20x4-lcd-displays-with-mcp23017.html
#!/usr/bin/python
#----------------------------------------------------------------
# Author: Chris Crumpacker                               
# Date: May 2013
#
# Testing a data table on an 20x4 LCD, 
# using a RaspberyPi and an MCP23017 I2C port expander
# 
# Using Adafruit_CharLCD code with the I2C and MCP230xx code as well
#----------------------------------------------------------------

from time import sleep
from Adafruit_CharLCDPlate4line import Adafruit_CharLCDPlate
from LCD_EoL_HandlingAnd4LineSupport import LCD_EoL_Handling

class LCD_DataTable(Adafruit_CharLCDPlate):
    # Limited to 4 characters, 
    # position is left or right, 
    # line can be 0 thru 3
    def updateHalfLabel(self, text, position, line):
        self.clearHalfDataSet(position,line)
        if position == "left":
            lcd.setCursor(0,line)
            eol.message(text[0:4]+':')        
        elif position == "right":
            lcd.setCursor(10,line)
            eol.message('|'+text[0:4]+':')
            
    # Limited to 4 characters, 
    # position is left or right, 
    # line can be 0 thru 3
    def updateHalfValue(self, text, position, line):
        if position == "left":
            lcd.setCursor(5,line)
            eol.message("    ")
            lcd.setCursor(5,line)
            eol.message(text[0:4])        
        elif position == "right":
            lcd.setCursor(16,line)
            eol.message("    ")
            lcd.setCursor(16,line)
            eol.message(text[0:4])
    
    # Writes up to a 9 character label and value to a full line
    def updateWholeLineLabel(self, label, line):
        self.clearWholeLine(line)
        lcd.setCursor(0,line)
        eol.message(label[0:9] + ': ')
    
    # Writes up to a 9 character label and value to a full line
    def updateWholeLineValue(self, value, line):
        lcd.setCursor(10,line)
        eol.message(value[0:10])
    
    #Clears an entire line
    def clearWholeLine(self, line):
        lcd.setCursor(0,line)
        eol.message(" " * columns)
    
    #Clears an entire line
    def clearWholeLineValue(self, line):
        lcd.setCursor(10,line)
        eol.message(" " * 10)
    
    # Clears just a half data set, label and value   
    def clearHalfDataSet(self, position,line):
        if position == "left":
            lcd.setCursor(0,line)
            eol.message(" " * 10)
        elif position == "right":
            lcd.setCursor(10,line)
            eol.message(" " * 10)
    
    # Clears just the value portion for a half data set
    def clearHalfValue(self, position,line):
        if position == "left":
            lcd.setCursor(5,line)
            eol.message("    ")      
        elif position == "right":
            lcd.setCursor(16,line)
            eol.message("    ")

#----------------------------------------------------------------
# Main program, just trowing bogus data "against the wall"
#----------------------------------------------------------------
if __name__ == '__main__':
    
    #lcd size reference
    columns = 20
    rows = 4
    
    eol = LCD_EoL_Handling()
    lcd = Adafruit_CharLCDPlate()
    dt = LCD_DataTable()
    
    lcd.begin(columns, rows)
    lcd.backlight(lcd.ON)
    lcd.clear()
    
    lcd.message("20x4 Table Testing")
    sleep(2)
    
    #Filling the table with bogus info
    lcd.clear()
    dt.updateHalfLabel("Temp","left",0)
    dt.updateHalfLabel("Mode","right",0)
    dt.updateHalfLabel("Targ","left",1)
    dt.updateHalfLabel("Fan","right",1)
    dt.updateHalfValue("Cool","right",0)
    dt.updateHalfValue("75.5","left",0)
    dt.updateHalfValue("Auto","right",1)
    dt.updateHalfValue("74.0","left",1)
    dt.updateWholeLineLabel("Tempurature",2)
    dt.updateWholeLineValue("Too Hot!!!",2)
    dt.updateWholeLineLabel("Humidity",3)
    dt.updateWholeLineValue("100%!!!",3)
        
    #Start testing updating and clearing parts
    
    # Clearing entire lines
    sleep(2)
    dt.clearWholeLine(0)
    sleep(1)
    dt.clearWholeLine(3)
    sleep(1)
    
    # Repopulating the lines just cleared
    dt.updateHalfLabel("Temp","left",0)
    dt.updateHalfValue("75.3","left",0)
    dt.updateHalfLabel("Mode","right",0)
    dt.updateHalfValue("Cool","right",0)
    dt.updateWholeLineLabel("Humidity",3)
    dt.updateWholeLineValue("100%!!!",3)
    sleep(2)
    
    # Clearing the entire Data set, both Label and Value
    dt.clearHalfDataSet("left",0)
    sleep(1)
    dt.clearHalfDataSet("right",0)
    sleep(1)
    dt.clearHalfDataSet("left",1)
    sleep(1)
    dt.clearHalfDataSet("right",1)
    sleep(2)
    
    # Repopulating the half labels and values just removed
    dt.updateHalfLabel("Temp","left",0)
    dt.updateHalfLabel("Mode","right",0)
    dt.updateHalfLabel("Targ","left",1)
    dt.updateHalfLabel("Fan","right",1)
    
    dt.updateHalfValue("75.5","left",0)
    dt.updateHalfValue("Cool","right",0)
    dt.updateHalfValue("74.0","left",1)
    dt.updateHalfValue("On","right",1)
    sleep(2)
    
    # Clearing the values in the half data sets
    dt.clearHalfValue("left",0)
    sleep(1)
    dt.clearHalfValue("right",0)
    sleep(1)
    dt.clearHalfValue("left",1)
    sleep(1)
    dt.clearHalfValue("right",1)
    sleep(2)
    
    # Repopulating half data set values
    dt.updateHalfValue("74.7","left",0)
    sleep(1)
    dt.updateHalfValue("Auto","right",0)
    sleep(1)
    dt.updateHalfValue("74.0","left",1)
    sleep(1)
    dt.updateHalfValue("On","right",1)
    sleep(2)
    
    # Clearing the value on a full line entry
    dt.clearWholeLineValue(2)
    dt.clearWholeLineValue(3)
    sleep(2)
    
    # Repopulating the values that was just removed
    dt.updateWholeLineValue("Still Hot",2)
    dt.updateWholeLineValue("90%",3)

Monday, May 6, 2013

Using 20x4 LCD displays with the MCP23017 and Raspberry Pi

Adafruit sells a nice I2C connected 16x2 LCD "plate" to go on top of the RPi that also includes a few buttons. See: http://www.adafruit.com/products/1110 They also include the python library to run it and functions that are pretty easy to use. See: https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/tree/master/Adafruit_CharLCDPlate

Now that's all well and good but as part of an upcoming project I am planning I have bigger needs. First LCD size. I want to run the 20x4 size so I can display outputs on the first 3 lines and then use the fourth line for menu navigation. Second I am going to be using a 4x3 keypad, a "D-Pad" style button setup,a buzzer,  and a few status LEDs. All of this is going to require one 16 pin (28dip)digital I/O expansion chip, MCP23017 to drive the display, the d-pad, buzzers and LEDs. Then an 8pin (18dip) MCP23008 chip to handle the 4x3 keypad. On top of that it needs to be almost 70' away. 

So I used their good work in the coding department to jump off from. Obviously since the product limits to the use of the 16x2 displays due to physical space the code needed to be optimized to handle differing display sizes. I went a little beyond that and added some support for handling longer text strings that would usually overflow into the buffer, and in the case of the 4 line displays that means any text in the buffer for line one appears on line 3... :(


Running the example code provided below to demonstrate the EoL handling.

Here is the new "message" function from the Adafruit_CharLCDPlate.py script/class.  I just checked in to their Github repo. One thing to note is that this in no way changes the functionality for the normal plate. Any existing scripts will function just like before. But with the addition of handling 4 line displays seamlessly and then all of the EoL features can be added be putting a 1 or 2 in the message function call after your test to print. My github fork


    def message(self, text, limitMode = 0):
            """ Send string to LCD. Newline wraps to next line"""
            lines = str(text).split('\n')       # Split at newline(s)
            for i, line in enumerate(lines):    # For each substring...
                if i == 1:                      # If newline(s),
                    self.write(0xC0)             # set DDRAM address to 2nd line
                elif i == 2:
                    self.write(0x94)
                elif i >= 3:
                    self.write(0xD4)
                """Now depending on the limit mode set by the function call this will handle """
                lineLength = len(line)
                limit = self.numcols
                if limitMode <= 0: 
                    self.write(line, True)     
                elif lineLength >= limit and limitMode == 1:
                    '''With the limit mode set to 1 the line is truncated 
                    at the number of columns available on the display'''
                    limitedLine = line[0:self.numcols]
                    self.write(limitedLine, True)  
                elif lineLength >= limit and limitMode == 2:
                    '''With the limit mode set to 2 the line is truncated 
                    at the number of columns minus 3 to add in an elipse'''
                    limitedLine = line[0:self.numcols-3]+'...'
                    self.write(limitedLine, True)
                elif lineLength >= limit and limitMode >= 3:
                    '''Future todo, add in proper, "line after line" cariage return'''
                else:
                    self.write(line, True)

I also had to add the "self.numlines = lines" to the begin function so I could use the column count.

    def begin(self, cols, lines):
        self.currline = 0
        self.numlines = lines
        self.numcols = cols
        self.clear()

I also spent some time making an example script to leverage this new message function as well as some of the other standard built in functions. Here is my example script on github

#!/usr/bin/python

#----------------------------------------------------------------
# Author: Chris Crumpacker                               
# Date: May 2013
#
# A demo of some of the built in helper functions of 
# the Adafruit_CharLCDPlate.py and Using the EoL_HandlingAnd4LineSupport.py
# 
# Using Adafruit_CharLCD code with the I2C and MCP230xx code as well
#----------------------------------------------------------------

numcolumns = 20
numrows = 4

from time import sleep
from Adafruit_CharLCDPlate import Adafruit_CharLCDPlate

lcd = Adafruit_CharLCDPlate()

lcd.begin(numcolumns, numrows)

lcd.backlight(lcd.ON)
lcd.message("LCD 20x4\nDemonstration")
sleep(2)

while True:
    #Text on each line alone.
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.message("Line 1")
    sleep(1)
    
    lcd.clear()
    lcd.setCursor(0,1)
    lcd.message("Line 2")
    sleep(1)
    
    lcd.clear()
    lcd.setCursor(0,2)
    lcd.message("Line 3")
    sleep(1)
    
    lcd.clear()
    lcd.setCursor(0,3)
    lcd.message("Line 4")
    sleep(1)
    
    # Using the "\n" new line marker
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.message("Line 1")
    sleep(1)
    
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.message("Line 1\nLine 2")
    sleep(1)
    
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.message("Line 1\nLine 2\nLine 3")
    sleep(1)
    
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.message("Line 1\nLine 2\nLine 3\nLine 4")
    sleep(1)
        
    # Auto line limiting by length as to not overflow the display
    # This is line by line and does not to any caraige returns
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.message("This String is 33 Characters long",1)
    sleep(2)    
    
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.message("This String has elpise",2)
    sleep(2)    
    
    #Scroll text to the right
    messageToPrint = "Scrolling Right"
    i=0
    while i<20:
        lcd.clear()
        lcd.setCursor(0,0)
        suffix = " " * i
        lcd.message(suffix + messageToPrint,1)
        sleep(.25)
        i += 1
    
    # Scroll test in from the Left
    messageToPrint = "Scrolling Left"
    i=20
    while i>=0:
        lcd.clear()
        lcd.setCursor(0,0)
        suffix = " " * i
        lcd.message(suffix + messageToPrint,1)
        sleep(.25)
        i -= 1
    sleep(2)  
    
    # Printing text backwards, NOT right justified
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.message("Right to left:")
    lcd.setCursor(10,1)
    lcd.rightToLeft()
    lcd.message("Testing")
    sleep(2)
    
    # Printing normally from the middle of the line
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.message("Left to Right:")
    lcd.setCursor(10,1)
    lcd.message("Testing")
    sleep(2)
    
    # Enabling the cursor and having it blink
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.cursor()
    lcd.blink()
    lcd.message("Cursor is blinking")
    lcd.setCursor(0,1)
    sleep(3)
    lcd.noCursor()
    lcd.noBlink()
    
    # Turning the backlight off and showing a simple count down
    lcd.clear()
    lcd.setCursor(0,0)
    lcd.message("Backlight off in")
    lcd.setCursor(0,3)
    lcd.message("Back on in 3sec")
    lcd.setCursor(17,0)             #Reseting the cursor here keeps us from having to clear the screen, this over writes the previous character
    lcd.message("3")
    sleep(1)
    
    lcd.setCursor(17,0)
    lcd.message("2")
    sleep(1)
    
    lcd.setCursor(17,0)
    lcd.message("1")
    sleep(1)
    
    lcd.backlight(lcd.OFF)
    lcd.clear()
    lcd.setCursor(0,0)
    sleep(3)
    lcd.backlight(lcd.ON)
    lcd.message("Backlight on")    

Wednesday, April 17, 2013

New code for the Rear Bike Light

I spent some time last night removing all the debug stuff so it would look cleaner and be easier to follow.

Original post here: http://crumpspot.blogspot.com/2013/04/power-led-bike-tail-light-with-arduino.html
Schmatic here: https://www.circuitlab.com/circuit/b6r5h8/rear-bike-light/


/*--------------------------------------------------------
Rear Bike Light Project                                    
Author: Chris Crumpacker                               
Date: October 2012 

Copyright (c) 2012 Chris Crumpacker.  All right reserved.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.
                                                           
Sketch Notes: This version contains 6 modes of blinking with 
multiple speeds for some modes. This works off of a single 
button, with short presses the program steps thru the 
modes/speeds. When long pressed it goes into a "sleep" 
mode where the high powered LEDs are turned off. 
It also has the ablity to store the current mode so when 
leaving sleep mode or being brought back up from a power 
down it will return to the previous mode it was in. 
There is also an interal tempurature sensor that will 
put it into a hidden (at least from the mode scrolling) 
"limp home mode" during a certain temp range or evenshut the 
LEDs down if it gets any higher.
--------------------------------------------------------*/

//-------------------------
// Includes
//-------------------------
#include 
#include 

//-------------------------
// Defines
//-------------------------
#define BUTTON_PIN       11                                                  // Button
#define ledPinl          9                                                   // Left LED
#define ledPinr          10                                                  // Right LED
#define powerPin         13                                                  // Case LED to show that the circuit has power
#define tempSensorPin    A5                                                  // Analog pin the case's Temp36 sensor is on

#define LONGPRESS_LEN    10                                                  // Min numberr of loops for a long press
#define DELAY            10                                                  // Delay per loop in ms
#define CONFIG_VERSION   "rbl1"                                              // ID of the settings block
#define memoryBase       32                                                  // Tell it where to store your config data in EEPROM                                                          //Constructor for the Simple Timer

enum { EV_NONE=0, EV_SHORTPRESS, EV_LONGPRESS };

//-------------------------
// Variables
//-------------------------
boolean ok = true;                                                           // bool for the EEPROM's config setup
int configAdress = 0;

boolean currentButton = LOW;
boolean button_was_pressed = false;
int previousButton;
int button_pressed_counter = 0;
int longPress = LONGPRESS_LEN;
int buttonCount = 0;
long previousMillis = 0;

boolean fromCheckTemp = false;
long previousTempTime = 0;
float tempLimp = 100;                                                         // In degrees (f)
float tempShutdown = 120;                                                     // In degrees (f)
int checkTempInterval = 30000;                                                // In Milliseconds

float freq;
float freqInitial = .002;
float freqChange = .002;
float freqLimit = .006;

int ledStep = 255;                                                            // How much to change the dimming (PWM) between each step for steps 6 to 9 and 10 to 13

int ledState = HIGH;

// The struct for the config saved to the EEPROM
struct StoreStruct {
    char* cVersion;                                                          // This is to detect if the settings stored in the EEPROM are for this config and sketch
    int bc;
} storage = { 
    CONFIG_VERSION,                                                          // Defaults
    0
};

//-------------------------
// Setup
//-------------------------
void setup() {
  Serial.begin(9600);                                                        // Sets up the serial port and speed
  pinMode(BUTTON_PIN, INPUT);                                                // Setting the button pin to an input
  digitalWrite(BUTTON_PIN, HIGH);                                            // Setting the button with a pull-up resistor, the button when grounded or "low" will be thought of as pressed
  pinMode(powerPin, OUTPUT);                                                 // Setting the pin for the LED on the board to show that power is on or will blink if in a sleep mode
  pinMode(ledPinl, OUTPUT);                                                  // External LED control pin 1
  pinMode(ledPinr, OUTPUT);                                                  // External LED control pin 2
  EEPROM.setMemPool(memoryBase, EEPROMSizeATmega328);                        // Set memorypool base to 32, assume Atmega328
  configAdress = EEPROM.getAddress(sizeof(StoreStruct));                     // Size of config object 
  ok = loadConfig();                                                         // Loads the config, and if it loads sets the bool "ok" to true
  buttonCount = storage.bc;                                                  // Sets the variable for the buttonCount to what is brought back from the storage
  checkTemp();                                                               // Checks the intial temp at start up
  freq = setFreq();                                                          // Sets the initial Frequency for the pulsing modes
  ledStep = setLEDStep();                                                    // Sets the initial LED PWM step value for the dimming modes
  digitalWrite(powerPin, ledState);                                          // Turns on the on board LED  
}

//-------------------------
// Functions
//-------------------------

   
// Loads the Config from EEPROM
bool loadConfig() {                                             
  EEPROM.readBlock(configAdress, storage);
  return (storage.cVersion == CONFIG_VERSION);
}

// Saves Config changes to the EEPROM
void saveConfig() {                                              
   EEPROM.writeBlock(configAdress, storage);
}

//--Button Handling--
// This function determines if the button press is short or long and is made to report back to a switch case
void handle_button(int longPress, int modeType)
{
  int button_now_pressed = !digitalRead(BUTTON_PIN);                         // pin low -> pressed
  
  if (!button_now_pressed && button_was_pressed) {
//Short press
    if (button_pressed_counter < longPress) { 
        if (modeType == 1){
          /*Short press from one of the pulsing modes. This increments the button count, 
          updates the Frequency and possibly the LEDStep and also stores the new buttonCount away in the EEPROM*/
          previousButton = buttonCount;
          ++buttonCount;
          storage.bc = buttonCount;
          saveConfig();
          setFreq();
          setLEDStep();
        } else if (modeType == 2){
          /*Short press from one of the dimming PWM modes. This increments the button count, 
          updates the Frequency and the LEDStep and also stores the new buttonCount away in the EEPROM*/
          previousButton = buttonCount;
          ++buttonCount; 
          storage.bc = buttonCount;
          saveConfig(); 
          setFreq();
          setLEDStep(); 
        } else if (modeType == 3) { 
          /*Short press for the sleep modes 14 and 15. This starts us back to the first mode (0) and sets 
          the frequency and the LED step as well as store the button count to the EEPROM*/ 
          buttonCount = 0;
          storage.bc = buttonCount;                                        //Stores the buttonCount to EEPROM
          saveConfig();
          setFreq(); 
          setLEDStep();
          fromCheckTemp = false;
        }
//Long Press
    } else { 
      if (modeType == 1 || modeType == 2){
        /*Long press for any of the "awake" modes. It puts the external LEDs to sleep in mode 14 or sleep mode. 
        Also it sets the previous button so when exiting the sleep mode it knows what to go back to*/
        previousButton = buttonCount;                              
        buttonCount = 14;
      } else if (modeType == 3) {   
        /*Long press from one of the sleep modes. This sets the button count back to the previous button, 
        updates the Frequency and the LEDStep and also stores the new buttonCount away in the EEPROM*/ 
        buttonCount = previousButton;
        storage.bc = buttonCount; 
        saveConfig();
        setFreq(); 
        setLEDStep();
        fromCheckTemp = false;
      }
    }
  } 

  if (button_now_pressed){
    ++button_pressed_counter;
  } else {
    button_pressed_counter = 0;
  }
    
  button_was_pressed = button_now_pressed;
}

//Checks the tempurature inside the circuit enclosure, 
void checkTemp() {
  unsigned long currentTempTime = millis();                                  // Set the current time
  if(currentTempTime - previousTempTime > 20000){
    previousTempTime = currentTempTime;
    float Vcc = readVcc();                                                   // Calculating the Supply Voltage
    Vcc = Vcc / 1000;                                                        // Converting from mV to Volts
    int reading = analogRead(tempSensorPin);                                 // Reading the voltage from the sensor pin
      
//  Converting that reading to voltage
    float voltage = reading * Vcc;
    voltage /= 1024.0; 
    
//  Print out the temperature in Celcius
    float temperatureC = (voltage - 0.5) * 100 ;                    //converting from 10 mv per degree wit 500 mV offset to degrees ((volatge - 500mV) times 100)
    
//  Now convert to Fahrenheight
    float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
     
    if (temperatureF > tempLimp && temperatureF < tempShutdown) {
      //To Hot but not shutting down send to limp home mode
      buttonCount = 15;
      fromCheckTemp = true;
    } else if (temperatureF >= tempShutdown && buttonCount != 14) {
      //Shutting down
      buttonCount = 14;
      fromCheckTemp = true;
    } else if (fromCheckTemp) {
      fromCheckTemp = false;
      buttonCount = previousButton;
    } else {
      fromCheckTemp = false;
    }
  } else {
    //Holder for stuff to do while waiting on the time to check temp again
  }
}

//When starting up with a stored button count we need to find and set the appropriate Frequency "freq" for the button count
float setFreq() { 
  if (buttonCount == 0 || buttonCount == 3) {
    freq = freqInitial;
  } 
  else if (buttonCount == 1 || buttonCount == 4) {
    freq = freqInitial + freqChange;
  } 
  else if (buttonCount == 2 || buttonCount == 5) {
    freq = freqInitial + freqChange + freqChange;
  }
  else {
    freq = freqInitial;
  }
  return freq;
}

//When starting up with a stored button count we need to find and set the appropriate LED brightness "ledStep" for the button count
int setLEDStep() { 
  if (buttonCount == 6 || buttonCount == 10){
    ledStep = 255;
  } 
  else if (buttonCount == 7 || buttonCount == 11) {
    ledStep = 191;
  } 
  else if (buttonCount == 8 || buttonCount == 12) {
    ledStep = 127;
  } 
  else if (buttonCount == 9 || buttonCount == 13) {
    ledStep = 64;
  }
  else {
    ledStep = 255;
  }
  return ledStep;
}

//***************************//
//******Blinking Modes*******//
//***************************//

//"bothFlipFlopPulse" The LEDs pulse back and forth (3 speeds)
void bothFlipFlopPulse() {
  float ledIn;
  float ledOutL;
  float ledOutR;
  longPress = 10;
  checkTemp();
  setFreq();  
  for (ledIn = 4.712; ledIn < 10.995; ledIn = ledIn + freq)      // This sets the start of the LED (ledOutL) pulse on the sin wave to the first zero crossing 4.712 and ends it on the next 10.995.
    {
      ledOutL = sin(ledIn) * 127.5 + 127.5;                      // Making the sin wave all positive numbers and setting it to a scale of 0-255 for the PWM range
      ledOutR = 255 - (sin(ledIn) * 127.5 + 127.5);              // This inverts ledOutR so it pulses to the high as ledOutR pulses to the low
      analogWrite(ledPinl, ledOutL);
      analogWrite(ledPinr, ledOutR);
    }
    
    handle_button(longPress,1);                    // Sets the Button function to run and read the button state, then returns the event, EV_SHORT, EV_LONG, or EV_NONE    
}

//"bothPulse" Like "bothFlipFlopPulse" but with both LEDs on the same sin wave (3 speed)
void bothPulse() {
  float ledIn;
  float ledOutL;
  float ledOutR;
  longPress = 10; 
  for (ledIn = 4.712; ledIn < 10.995; ledIn = ledIn + freq)      //This sets the start of the LED (ledOutL) pulse on the sin wave to the first zero crossing 4.712 and ends it on the next 10.995.
    {
      int out = sin(ledIn) * 127.5 + 127.5;                      //Both LEDs will follow the sin wave but we also have to make it all positive numbers and set to a scale of 0-255 for the PWM range
      analogWrite(ledPinl, out);
      analogWrite(ledPinr, out);
    }
    
  handle_button(longPress,1);
}

//"bothStepped" - Both LEDs on, stepping down the brightness with each button press (4 settings from full bright to rather dim)
void bothStepped() {
  longPress = 75;
  checkTemp();
  setLEDStep();
  analogWrite(ledPinl, ledStep);                                //Turns them both on together 
  analogWrite(ledPinr, ledStep);
  
  handle_button(longPress,2);
  delay(DELAY);
}

//"singleStepped" - Just one LED on, stepping down the brightness with each button press (4 settings from full bright to rather dim)
void singleStepped(){
  longPress = 75;
  checkTemp();
  setLEDStep();
  analogWrite(ledPinl, ledStep);
  digitalWrite(ledPinr, LOW);
    
  handle_button(longPress,2);
  delay(DELAY);
}

//"sleep" external LEDs off but blink the power led on the circuit board.
void sleep() {
  longPress = 75;                     
  checkTemp();
  digitalWrite(ledPinl, LOW);                                    //Turns off both High Power LEDs
  digitalWrite(ledPinr, LOW); 
  
  // Blinks the powerPin light by checking the current time vs. the last time it went thru, once it is over the 1000 ms limit it changes the light's state
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > 1000){
    previousMillis = currentMillis;
    if (ledState == LOW){
      ledState = HIGH;
    }
    else {
      ledState = LOW;
    }
    digitalWrite(powerPin, ledState);
  }
      
  handle_button(longPress,3); 
  delay(DELAY);    
}

//"limpHome" The limp home mode, Like mode 1 the LEDs pulse back and forth, But slowly and only to half brightness.
void limpHome() {
  float ledIn;
  float ledOutL;
  float ledOutR;
  checkTemp();
  longPress = 75;
  for (ledIn = 4.712; ledIn < 10.995; ledIn = ledIn + .002)      // This sets the start of the LED (ledOutL) pulse on the sin wave to the first zero crossing 4.712 and ends it on the next 10.995.
    {
      ledOutL = sin(ledIn) * 100 + 127.5;                        // Making the sin wave all positive numbers and setting it to a scale of 0-255 for the PWM range
      ledOutR = 255 - (sin(ledIn) * 127.5 + 127.5);              // This inverts ledOutR so it pulses to the high as ledOutR pulses to the low
      analogWrite(ledPinl, ledOutL);
      analogWrite(ledPinr, ledOutR);
    }
    
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > 1000){
    previousMillis = currentMillis;
    
    if (ledState == LOW){
      ledState = HIGH;
    }
    else {
      ledState = LOW;
    }
    digitalWrite(powerPin, ledState);
  }
    
  handle_button(longPress,3);
  delay(DELAY); 
}

//-------------------------
// THE LOOP
//-------------------------
void loop(){  
  //Set what mode the LEDs are in based on how many times the button is pressed.
  while (buttonCount < 3)
  {
    bothFlipFlopPulse();
  }
  while (buttonCount >= 3 && buttonCount < 6)
  {
    bothPulse();
  }
  while (buttonCount >= 6 && buttonCount < 10)
  {
    bothStepped();
  }
  while (buttonCount >= 10 && buttonCount < 14)
  {
    singleStepped();
  }
  while (buttonCount == 14)
  {
    sleep(); 
  }
  while (buttonCount == 15)
  {
    limpHome(); 
  }
  delay(DELAY);
}