• Auto Convert Any Excel File to SQL

    Convert XML

    Private Sub generateSQLTable()
        Dim tmpTableName As String
        Dim includeIDField As Boolean
        'Some options
        tmpTableName = InputBox("Temp Table Name:", "Temp Table Name", "ExcelTable")
        includeIDField = True
       
        
        Dim tmp As String
        Dim i As Long
        Dim j As Long
        Dim rowFrom As Long
        Dim colFrom As Long
        Dim rowTo As Long
        Dim colTo As Long
        Dim arryLen() As Long
        Dim arryType() As Integer
       
        If MsgBox("Have you selected your entire range including the header?", vbYesNo) = vbNo Then
            MsgBox ("Please select the whole range desired plus the header.")
            Exit Sub
        End If
       
        
        'collect the rows and columns
        rowFrom = Selection.Row
        colFrom = Selection.Column
        rowTo = Selection.Row + Selection.Rows.Count - 1
        colTo = Selection.Column + Selection.Columns.Count - 1
       
        'Setup lists
        ReDim arryLen(colTo) As Long
        ReDim arryType(colTo) As Integer '-1=boolean 0=int, 1=double ,2=datetime, 3= varchar
        For i = colFrom To colTo
            arryType(i) = 0
            arryLen(i) = 1
        Next
       
        'This will loop through each row and column
        'first it will look for what type of column it is
        'Second it will add it to the insert statement
        For i = rowFrom + 1 To rowTo 'skip header
            tmp = "insert into #" + tmpTableName + " values("
            For j = colFrom To colTo
                Dim vValue As Variant
                If IsError(ActiveSheet.Cells(i, j)) Then 'If the field holds an invalid value then just leave it blank
                    tmp = tmp & "'',"
                Else
                    If (IsDate(ActiveSheet.Cells(i, j)) And Not arryType(j) > 2) Then
                        'must be a date
                        arryType(j) = 2
                    ElseIf (Not IsNumeric(ActiveSheet.Cells(i, j)) And Not arryType(j) = 3) Or ActiveSheet.Cells(i, j) = "" Then ' Or ActiveSheet.Cells(i, j) = "000"
                        'must be a string
                        arryType(j) = 3
                    ElseIf IsNumeric(ActiveSheet.Cells(i, j)) And Not arryType(j) > 1 And InStr(1, ActiveSheet.Cells(i, j), ".") > 0 Then
                        'Must be double
                        arryType(j) = 1
                    ElseIf IsNumeric(ActiveSheet.Cells(i, j)) And arryType(j) < 1 Then 'And Not arryType(j) > -1 Then
                        If Len(ActiveSheet.Cells(i, j)) < 5 Then 'ActiveSheet.Cells(i, j) ' ActiveSheet.Cells(i, j) Mod 1 = 0                         If CLng(ActiveSheet.Cells(i, j)) > 1 Or CLng(ActiveSheet.Cells(i, j)) < -1 Then                             If CLng(ActiveSheet.Cells(i, j)) > 32700 Or CLng(ActiveSheet.Cells(i, j)) < -32700 Then                                 arryType(j) = 3 'its a big number just make it a string                             Else                                 arryType(j) = 0 'its an int :-)                             End If                         Else                             'do nothing it is a boolean                         End If                     Else                         arryType(j) = 3 'its too big to be a number                     End If                 End If                 If Len(ActiveSheet.Cells(i, j)) > arryLen(j) Then 'set to maximum length
                        arryLen(j) = Len(ActiveSheet.Cells(i, j))
                    End If
                    If arryType(j) = 1 Then
                        tmp = tmp & "'" & sbNum2Str(ActiveSheet.Cells(i, j)) & "',"
                    Else
                        If LCase(Trim(ActiveSheet.Cells(i, j))) = "null" Then
                            tmp = tmp & "'',"
                        Else
                            tmp = tmp & "'" & Replace(Replace(ActiveSheet.Cells(i, j), "'", "''"), vbCrLf, "") & "',"
                        End If
                    End If
                End If
            Next
            ActiveSheet.Cells(i, j + 1) = Left(tmp, Len(tmp) - 1) & ")"
        Next
       
        'This will setup the create table depening on the values
        tmp = "Create table #" + tmpTableName + "( "
        If includeIDField Then
            tmp = tmp + " ID int identity(1,1),"
        End If
        For i = colFrom To colTo
            If arryType(i) = -1 Then
                tmp = tmp + " [" + Replace(CStr(ActiveSheet.Cells(rowFrom, i)), " ", "") + "] bit,"
            ElseIf arryType(i) = 0 Then
                tmp = tmp + " [" + Replace(CStr(ActiveSheet.Cells(rowFrom, i)), " ", "") + "] int,"
            ElseIf arryType(i) = 1 Then
                tmp = tmp + " [" + Replace(CStr(ActiveSheet.Cells(rowFrom, i)), " ", "") + "] decimal(12,5),"
            ElseIf arryType(i) = 2 Then
                tmp = tmp + " [" + Replace(CStr(ActiveSheet.Cells(rowFrom, i)), " ", "") + "] datetime,"
            Else
                tmp = tmp + " [" + Replace(CStr(ActiveSheet.Cells(rowFrom, i)), " ", "") + "] varchar(" + CStr(arryLen(i)) + "),"
            End If
        Next
        ActiveSheet.Cells(rowFrom, i + 1) = Left(tmp, Len(tmp) - 1) & ")"
    End Sub
     
    Function sbNum2Str(d As Double) As String
        Dim v
        Dim lExp As Long, lLenMant As Long
        Dim sDot As String
        Dim sMant As String
        If d < 0# Then
            sbNum2Str = "-" & sbNum2Str(-d)
            Exit Function
        End If
        sDot = Application.DecimalSeparator
       
        v = Split(Format(d, "0" & sDot & String(15, "#") & "E+0"), "E")
       
        If Left(v(0), 1) = "0" Then
            sbNum2Str = "0"
            Exit Function
        End If
       
        lExp = CLng(v(1))
        v = Split(v(0), sDot)
       
        If lExp < 0 Then         sbNum2Str = "0" & sDot & String(-lExp - 1, "0") & v(0) & v(1)     Else         lLenMant = Len(v(1))         If Len(v(1)) > lExp Then
                sMant = v(0) & v(1)
                sbNum2Str = Left(sMant, lExp + 1) & sDot & Right(sMant, Len(sMant) - lExp - 1)
            Else
                sbNum2Str = v(0) & v(1) & String(lExp - lLenMant, "0")
            End If
        End If
    End Function
    


  • Poor Man’s Low Jack using Arduino and Cell Towers

    Demo Videos

    Demo 1

    Demo 2

    0 Pre-Intro

    0.1 Parts To Buy

    We tried to make this project so that anyone could replicate and use it for themselves. This system should be well setup for the DIYer. For this unit to work you must have the following:

    • Arduino Mega
    • Cell Shield
    • Sim Card with texting ability
    • Cell Phone

    The following are optional but you will have the loss of features:

    • Relay board (you can buy any 5v 4-channel relay from ebay if you want)
      • Without this item you will not be able to remote start, stop, lock, unlock your car
      • You can also get an 8-channel and a little programming on the arduino to add in roll up and down electric windows, any thing else you wish. We didn’t do this because the car we used didn’t have such cool features.
    • GPS – You do not have to buy Garmin GPS-16, we did because we already had it. Almost any GPS unit with serial communication will work great. You can use any 12v serial gps and the serial converter board, or you can buy a serial to TTL converter on ebay, or if you find a ttl GPS unit just hook up the GPS to the right ports on the Arduino Mega.
      • Without this unit you will not get the GPS location of your car
    • Android – Without the cool android + app you will have to manually send your messages to the car and read the return messages. It is pretty easy to make a message just use the table in 3.1.4.

    0.2 Possible Uses

    • Remote Start – Anyone can use it to remote start their car no matter how far away they parked and have their car warmed up by the time they get there.
    • Track your kids – you can always tell how fast your kid is traveling and where they are headed, so you always know where they are at.
    • Low Jack+ – If anyone ever steals your car simply send the kill code and the car will die right where it is at. Call the cops and meet them at your car.
    • International Car Management – Those who work internationally, such as those in the military, may be away for quite some time and need to just start the engine every few weeks to keep it well maintained. Instead of handing the neighbor kids the keys to your corvette, just start and stop the car yourself. Don’t worry about the car being left on, you can always check to make sure the car’s RPMs are at 0.

    1 Introduction

    Our senior project has been the design and implementation of a low-cost, LoJack (LoJack) type car communication system. Our goal has been to put together a two-part system that will implement the vehicle tracking ability of the original LoJack system. In addition the owner can remote start, remote door lock/unlock, and access vehicle status. All of the features are controlled and displayed from anywhere in the world via an Android (Android) phone. An Arduino (Arduino) based microcontroller provides vehicle control and car communication.
    We set the goal of having the following features working on demonstration day:
    Remote Global Positioning System (GPS) vehicle tracking using Google Maps (Google Maps) Remote engine start Remote door lock and unlock On Board Diagnostic system (OBD-II) remote data monitoring (rpm, speed, coolant temp)

    1.1 Motivation

    The motivation for re-designing a LoJack system is three-fold. First LoJack requires a monthly service fee. Poor Man’s LoJack System allows for the selection of service from any Global System for Mobile (GSM) based cellular service provider. In the United States this means using one of T-Mobile’s or AT&T’s prepaid or contract plans. As well, newer vehicles come with remote features but have a limited range of 100 feet or less. Our system is entirely cellular based. Range is only limited by the coverage of the provider for both the vehicle and Android phone. Finally, Android phones are becoming increasingly popular and using our system would eliminate the need for users to carry a remote.

     

    1.2 Technical Description

    Our embedded system for the car uses an Arduino Mega (Arduino Mega) board and an Arduino SM5100B cellular shield. The cellular shield has a TTL RS-232 interface that exposes AT modem commands to one of the RS-232 interfaces on the Arduino. From here a SIM card with service is all that is needed to enable cellular communication between an Android phone and the car based system. We use a Garmin GPS16-HVS GPS receiver to obtain the car’s position. The NMEA 2.0 location strings generated by the GPS receiver are sent to the Arduino on one of its RS-232 interfaces. The car status report is gathered by using a BR-3 OBD-II to RS-232 interface. The Arduino communicates with the car’s engine control unit using one of its RS-232 interfaces via the BR-3 interface. A voltage level conversion and bit inversion is needed for the TTL based Arduino to communicate with the RS-232 based GPS and OBD-II. We use max232 chips to handle this conversion.
    The Arduino software packages GPS data and OBD-II data then sends it to the Android via text message in a format that will be explained in a later section. The Android receives the message and displays the relevant information on the screen. Using the Android interface a user can also send commands to the car. The Android formats the command and sends it back to the Arduino via text message. The Arduino then takes the appropriate action based on the command sent to it. The Arduino uses three discrete outputs that can be triggered to lock or unlock the doors and to start or stop the engine. A current driver circuit was built to switch the relatively large currents at work in the cars electrical system. To power the Arduino we used a pre-built ATX car computer power supply. Using this device ensures there will always be stable power, even during engine start when the voltage from the car battery drops. See Figure 1.2.1 Hardware Block Diagram for a visual representation of our system.

    Figure 1.2.1 Hardware Block Diagram

    2 Hardware Design

    The bulk of our hardware design has been connecting store bought pieces into a system as well as designing and building the relay driver board.

    2.1 Relay Driver

    The purpose of this circuit is to amplify signals from the Arduino’s outputs (5V 500ma) to drive relays. As such the driver board is diode protected so that back electromagnetic flux does not ruin the driver circuitry. Attached to the driver board is a bank of three SPDT 30A automotive grade relays. The relays are used to start the car, lock the car doors, and unlock the car doors. The relay driver circuit was designed using a free design tool called Express PCB (ExpressPCB) and Express SCH (ExpressSCH). Express PCB has the option to have circuit cards built from the design for around $100. Our system uses a PCB we fabricated using the following procedure.

    1. Determine current and power requirements so we have a viable trace thickness.
    2. Design circuit using Express SCH software.
    3. Place and route traces in Express PCB software.
    4. Print final design on overhead transparency using laser printer on darkest setting.
    5. Iron printed overhead transparency on to copper base layer.
    6. Remove overhead plastic to effectively transfer printed circuit design onto copper.
    7. Touch up circuit traces that didn’t transfer well with a Sharpie magic marker.
    8. Etch circuit card 15 minutes using PCB etchant (Ferric Chloride obtainable at any electronics parts house).
    9. Dispose of waste Ferric Chloride at a hazardous waste collection center.
    10. Drill holes for components.
    11. Place and solder components. (See Figure 2.2 for component values and part numbers)
    12. Test functionality.

    The schematic (Figure 2.1.1 Current Driver Schematic) and PCB layout (Figure 2.1.2 PCB Place and Route Layout) are the final design to create the circuit. Although two PCBs were built for redundancy, the circuit proves to be reliable and remains in our design without modification.


    Figure 2.1.1 Current Driver Schematic


    Figure 2.1.2 PCB Place and Route Layout
    The gate of each Metal Oxide Field Effect Transistor (MOSFET) W1, W4, W7, and W10 are connected to discrete outputs on the Arduino Mega board. See Figure 2.2.1 Arduino Mega blue labels. The relays are then triggered in software by the Arduino. The top rail is connected to +12 volts and the bottom rail is connected to ground (see Figure 2.1.2 PCB Place and Route Layout).

    2.2 Arduino Mega Board

    From the start our design required a microcontroller with three serial ports. While the standard Arduino has one serial port, the Arduino Mega has three (see Figure 2.2.1 Arduino Mega, red labels). The Arduino Mega board we use was purchased from Liquidware (Liquidware). We use a computer and a type A-B USB cable to communicate with the Arduino Mega. The Arduino Mega can be powered by different sources but only one source should be used at a time. Figure 2.2.1 Arduino Mega green circles shows the location of three available power inputs. We use the USB power when uploading code to the Arduino and the five volt unregulated input on the pin header when operating as part of the design.

    Figure 2.2.1 Arduino Mega

    2.3 SM5100B Cellular Shield

    We based most of our design around using the SM5100B cellular shield. The SM5100B can be purchased from SparkFun.com. The GSM module on the SM5100B is quad band and will work internationally as well as state side. In the US either 800MHz (UHF) or 1900MHz (S) bands are used depending on the cellular carrier. Our system uses an Antcom L/S-Band Rod Antenna (52W-1.50-2.50P-XTB-4-0) which provides enough gain for the cellular shield to have coverage, even indoors. The cellular shield requires its own power supply and can draw upwards of 2 Amps. If there is an under voltage condition, the cell enters an indeterminate state and will requires a reset to resume function. We have to power the cellular shield directly from the power supply as the Arduino power output does not supply sufficient current. See Figure 2.3.1 SM5100B Cellular Shield yellow label for where power is connected. The printed transmit and receive pins on the cellular shield are wrong so we connect to pin two for receive and pin three for transmit on the cellular shield (Figure 2.3.1 SM5100B Cellular Shield blue label) to the Arduino Mega pin 14 for transmit and pin 15 for receive (Figure 2.2.1 Arduino Mega red label).

    Figure 2.3.1 SM5100B Cellular Shield

    2.4 OBD-II Interface

    The vehicle OBD-II port operates at +12 volt logic levels and, in the case of Ford vehicles, is pulse width modulated. We purchased a BR-3 OBD-II to RS-232 interface from OBDII Diagnostic ScanTools and code-readers (OBDDiagnostic) to allow communication between the vehicle and Arduino. A voltage level conversion and bit inversion is needed for the TTL based Arduino to communicate with the RS-232 side of the BR-3 interface. We use small MAX232 based boards that can be found on EBay (EBay) to handle this conversion. The MAX232 boards we purchased need five volt power and signal ground connections. The boards are connected per manufacturer specifications. We connected the MAX232 TTL receive pin to the Arduino Mega transmit (pin 16) and the MAX232 transmit pin to the Arduino Mega receive (pin 17). The OBD-II interface communicates with the Arduino at 19200 baud, eight stop bits, no flow control and one data bit.

    2.5 GPS Receiver

    The Garmin GPS16-HVS has an RS-232 interface with an RJ-45 Ethernet connector. Both serial lines and power are wired to this connector. A voltage level conversion and bit inversion is needed for the TTL based Arduino to communicate with the RS-232 of the Garmin GPS. We use the same MAX232 conversion board as specified in the OBD-II interface section. Before we purchased the MAX232 board we built a simple circuit to handle the conversion. See Figure 2.5.1 Simple RS232 to TTL converter. While the Garmin GPS is configured to output NMEA 2.0 strings, the code on the Arduino has safety measures built in ensuring that it only accepts the NMEA 2.0 location strings. See Table 2.5.1 – GPS Data. We connect RJ-45 pin one to vehicle +12 volts and pins two and three to ground. RJ-45 pin five (GPS transmit) is connected to the Arduino Mega pin 18(receive). An Arduino transmit connection is not needed.

    Figure 2.5.1 Simple RS232 to TTL converter

    Example Message: $GPRMC,225446,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E*68

    Table 2.5.1 – GPS Data

    2.6 Hardware Power Requirements

    Table 2.6.1 Power Requirements

    3 Software Design

    This section describes the software implementation of our LoJack system. The objective of this piece is to implement the simplest and most robust messaging system between the Arduino and Android platforms. Text messaging, though a little slow, has the greatest amount of hardware support and is a reliable transport mechanism.

    3.1 Arduino LoJack MSG Based Controller

    The message based controller for Poor Mans LoJack is the Arduino Mega. The Arduino already has some basic software uploaded to it so it can talk to the Arduino software. We used the Arduino IDE (Arduino) to write and upload the controlling program for our Arduino. (See 5.1 Arduino Code) This program handles text communication with the Android along with serial communication between the GPS and vehicle OBD-II.

    3.1.1 GPS Communication

    The code for GPS communication is basic. There is a tutorial on the arduino.cc website (Arduino) that describes the process for parsing a National Marine Electronics Association (NMEA) 2.0 GPS message. We do not listen for NMEA strings on the serial port unless the Android device sends a request for GPS update. This method ensures that we don’t flood the Arduino processor with work processing GPS data and allow it ample time for communication with the Android. When listening for NMEA strings, we check that the first 6 characters are “$GPRMC”, which identifies the string as GPS data. Next we walk through the string splitting on commas. Based on the NMEA 2.0 format in Table 2.5.1 – GPS Data we know which pieces of data to send to the Android device.

    3.1.2 Arduino Message Receive

    The cellular shield forwards all incoming text messages to the Arduino serial port. The shield sends two lines for each message it receives. The first line contains message information such as timing, sender, etc. Our code identifies the sender’s telephone number so as to send the response to the correct phone number. In this way our code is dynamic and is not hard coded to one telephone number. The second line is the message body and can only contain 160 ASCII characters. Each character of the message correlates to one specific action. All lower case letters map to relay or GPS actions. All numbers and upper case letters map to OBD-II actions. See 3.1.4 Arduino/Android Communications Protocol for details.

    3.1.3 Arduino Message Send

    The cellular shield has a TTL RS-232 interface that exposes AT modem commands to one of the three RS-232 interfaces on the Arduino. The cell shield can take 15 to 30 seconds to boot. In order for the system to boot properly every time, the Arduino waits for “Connected to Network” and “Cellular Shield Ready” messages from the cellular shield before allowing further communication to proceed. Once the messaging system is booted it is ready to send text messages corresponding to actions in our communication protocol. When an action is received the Arduino assembles all responses in a return message with a comma between each response. If the message length reaches 160 characters it will queue the message and start assembling a new message. When the response is completely written, all messages relating to the response will be sent. We have included an additional auto mode that will send text messages with GPS status at a regular interval. We were unable to make use of this feature because text messages stopped getting through to the Android after several texts were sent. We suspect the cellular provider blocked further messages as the rapid texting triggered spam guards.

    3.1.4 Arduino/Android Communications Protocol

    Table 3.1.4.1 OBDII Codes

    Table 3.1.4.2 Car Codes

    3.1.5 Relay Control

    Driving the relays is simple. A single line of code in the Arduino turns one of the discrete outputs on or off. Depending on which command has been received (lock/unlock doors, starting the car, etc.) the timing of each pin had to be established. Specifically to lock the doors an output is held high for 1000ms. To unlock the doors a different output is held high for 1000ms. To start the car we lock the doors for security, send the ignition high, wait 200ms for the car electronics to boot, pull the starter high for 1500ms, and then return the starter to low. We leave the ignition high so the car keeps running. If we want to turn off the ignition we pull the ignition low. A few precautions had to be dealt with such as not sending the start commands if the car is already running.

    3.1.6 OBD-II

    The OBD-II interface was a challenge to get working. Though the BR-3 interface that we purchased made the communication simpler there is no widely available documentation on the protocol the device uses. We used the windows software provided with the interface to log serial communication between the interface and a laptop. From the log we determined the correct data bytes to send from the Arduino. First we check if the OBD-II has been initialized. If not, we initialize it by sending a series of handshake bytes identifying the interface. The OBD-II is then ready for engine parameter queries. To request a given parameter we send handshake bytes from initialization, a PID corresponding to an engine parameter, and Cyclic Redundancy Check (CRC) Bytes. We wait for the response which is disassembled in much the same way it was formed (header, PID, Data, CRC Trailer). We then send the retrieved engine parameter data to the Android device. It turns out that generating the CRC data was quite difficult and can be looked at as the major hurdle in OBD-II communication. Sometimes the OBD-II does not respond so there is some code implemented to time out if it takes too long to respond.

    3.2 Android LoJack Controller

    Android development is accomplished in Java using Eclipse (Eclipse) IDE and the publicly available Android IDE and SDK available from Google’s website (Android). Graphical layouts are constructed using XML inside Eclipse.

    3.2.1 Creating the tabbed interface

    Using the tutorial (Android) for creating a tabbed user interface we created our base Android controller. Three tabs were created to divide the base controls. A remote tab which held the buttons to send commands to the vehicle, a map tab to display the last received location of the vehicle, and a stats tab to neatly display information regarding the vehicle. Each tab must be loaded for its contents to be updated by messages received from the vehicle.

    3.2.2 Remote Tab

    The remote tab featured four buttons. One each to lock, unlock, start and stop the car. When pressed, each button would send a text message containing the associated command (see 3.1.4 Arduino/Android Communications Protocol). Basic coding for the buttons was taken from the button tutorial available from the Android developer website (Android). Each button was made using three graphics, one each for starting state, touched and released. Each custom button had

    3.2.3 Maps Tab

    A simple tutorial on maps can be found online we used the Google Developer tutorial (Android). The maps tab consists of a map and a marker to indicate the current location of the car. This was developed using a Google Maps View and an overlay to hold the marker for the location of the car. To get the Google Map View to display properly we had to obtain an developer’s API Key from the Google website, which is custom made for each IDE based on some hash values, auto generated within Eclipse. This key is included in maps.xml.

    3.2.4 Stats Tab

    The Stats tab is the simplest tab. This is designed to display the information that is returned from the car. To make the stats tab function properly, we use the pre-made tabs interface and add several static text views that are then populated as information is received. We included a button that, when touched, sends a “get all PIDS” command to the Arduino.

    3.2.5 Sending SMS

    To send text messages we developed a SMS (short message service) sender based on an online tutorial (Lee). When called, this method sends the passed string to a hard coded phone number. In order to allow the Android app to send text messages we had to set permissions within the manifest file to permit SMS messaging.

    3.2.6 Receiving SMS

    In order to receive information from the car the program reads the incoming text messages and looks to see if they match the communication protocol. Within the manifest file we specify which class in our program is to be set as a text message receiver. This class checks to see if any given message is from the Arduino, again using a hard coded phone number. If a message originates from the vehicle, its contents are packaged and passed to the parser for further processing.

    3.2.7 Parsing

    The parser reads information from the message, delimiting on commas, and passes the data to its corresponding tab. Currently the Android application is only set up to receive information sent in response to a “get all PIDS” command.

    3.3 Software Block Diagram

    Figure 4 – Software Block Diagram

    4 Conclusion

    The Poor Man’s LoJack worked as designed. It demonstrated the ability to correctly track the current vehicle location, using the GPS and displaying it on the Android map. Using the Android anyone could easily remotely start the car, kill the car, lock and unlock the doors, and display current vehicle statistics using two different Android phones.

    4.1 Problems

    Overall the problems we encountered revolved around limitations to software or hardware that were not immediately obvious during our initial research. One that seemed obvious to onlookers and is just an accepted fact but the messaging takes a good 4-5 seconds to make a round trip. This just means that users would need to be patient when waiting for the car to react. Most other issues were caused by our unfamiliarity with the equipment (both Arduino and Android development were new to our group). Also there are probably more elegant solutions than what we used but the solutions we used worked robustly and accurately.
    It wasn’t until late in our development that we realized the command we were sending to the Arduino cell shield would cause it to crash after several texts were sent and received. A simple adjustment from using Print() rather than Println() solved this problem.
    Communication with the OBD-II was likely our most difficult obstacle to overcome. While the interface we purchased made everything a great deal easier by converting the OBD-II signals to serial communication, the communication protocol is not widely distributed. Even after finding the required protocol it took several iterations for us to find the correct handshake information required to initialize the device and to correctly compute the CRC value.
    The Android application requires communication between the various tabs and the parser. However the communication between tabs is not supported by Android. To overcome this hurdle we statically instantiate everything that needs to receive data from another portion of the program.

    4.2 Analysis

    This was a challenging and fulfilling project over all. Every element in the process seemed to work together. As always there is more that can be done to a project but the point at which we left it now should allow any DIY tinkerer to be able to replicate and have their own Poor Man’s LoJack. In that I feel this project was a major success and a benefit to the engineering community.

    4.3 Possible Future Improvements

    The communication between the Android and Arduino is currently as insecure as it gets, allowing anyone with the Arduino cell number to interface with the car. Even a mistaken text message could start the car and return general stats about the car including location. This feature could have easily been overcome by setting a list cell numbers in the Arduino’s internal memory and restricting accepted commands to that list.
    We were also unable to implement automatic updating of vehicle statistics and GPS location. Our current implementation requires manual requests for current data but with more development we could create a system for the Arduino to push updates to the Android device without requiring constant polling.

    Bibliography

    Android.com. 28 December 2011. 28 December 2011 <http://www.android.com/about/>.
    Arduino Mega. 28 December 2011. 28 December 2011 <http://www.arduino.cc/en/Main/ArduinoBoardMega>.
    Arduino.cc. 28 December 2011. 28 December 2011 <http://www.arduino.cc/>.
    Ebay. November 2011 <http://www.ebay.com>.
    “Eclipse.” Eclipse Classic, [Computer Software]. 2011.
    “ExpressPCB.” ExpressPCB, [Computer Software], 2010.
    “ExpressSCH.” ExpressPCB, [Computer Software], 2010.
    Google Maps. 2011. November 2011 <http://www.google.com/maps>.
    Lee, Wei-Meng. mobiForge. March 2009. October 2011 <http://mobiforge.com/developing/story/sms-messaging-android>.
    Liquidware. liquidware.com. June 2011 <http://www.liquidware.com>.
    LoJack.com. 28 December 2011. 28 December 2011 <http://www.lojack.com/>.
    OBDDiagnostic. November 2011 <http://www.obddiagnostics.com/>.

    5 Appendix

    5.1 Arduino Code

    /*
    Team Cool beans Car, gps, cell communications device
    AKA Low Jack :-)
    Serial1 = Cell phone
    Serial2 = Car Computer
    Serial3 = GPS
    */
    /* format for cell message
    abcdefg123 … up to 160 chars
    0-pid-05-1-Engine Cooleant Temp (a-40)
    1-pid-0C-2-rpm ((A*256)+b)/4
    2-pid-0D-1-Vehicle speed in km/h
    3-pid-0F-1-Intake Air Tempurature A-40
    4-pid-2F-1-Fuel Level Input (100*a /255)
    5-pid-46-1-Ambient Air Temperature – a-40
    6-pid-5C-1-Engine Oil Tempurature – a-40
    7-pid-
    8-pid-0D
    9-pid-0E
    A-pid-0F
    B-pid-10
    C-pid-11
    D-pid-13
    E-pid-14
    F-pid-15
    G-
    H-
    K-
    a-Get all PIDS
    b-Auto send updates
    c-End auto send updated
    g-Get GPRMC message from GPS
    k-Kill Car
    l-Lock Car
    s-Start
    u-Unlock Car
    */
    #include<string.h>
    #include<ctype.h>
    int readByte=-1;
    int ledPin=13;
    //******SMS********
    char mobilenumber[] = “##########”; //enter your sim card’s phone number
    char smsBytes[161] = “”;
    char smsCommand[8] = “+SIND: “;
    int curSMSByte=’ ‘;
    int smsCounter=0;
    int smsPassCount=0;
    int smsSplit[13];
    int gotConnect=0;
    int gotNetwork=0;
    int cellReady=0;
    int phoneLoadFailure=0;
    int waitForOK=0;
    int readNextMessage=0;
    int cellReset=50;
    // String smsWaitMessage = “”;
    String smsSendMessage = “”;
    int smsSendStatus = 0; //0=do nothing, 1=sendmessage, 2=send clear memory
    unsigned long waitForSMS; //note Millis will overflow after 50 days
    //*****GPS******
    int rxGPS = 18;
    int txGPS = 19;
    char gpsBytes[200] = “”;
    char gpsCommand[7] = “$GPRMC”; //we are polling this one :-)
    int gpsCounter=0;
    int gpsPassCount=0;
    int gpsSplit[13];
    int readGPS=1;
    //int gpsReady=0;
    //*****Car Communication****
    char carBytes[300] = “”;
    int carCounter=0;
    int carStarted=0;
    byte readCarByte=0;
    byte carReturn[4];
    //Basic Car
    int Ignition=7;
    int Starter=8;
    int DoorLock=9;
    int DoorUnlock=10;
    int carReady=0;
    int retCarValue;
    void setup()
    {
      cellReset=50;
      pinMode(cellReset,OUTPUT);
      //for USB Communcation
      Serial.begin(9600);
      //*************Cell phone************
      Serial1.begin(9600);
      pinMode(ledPin,OUTPUT);
      for (int i=0;i<161;i++){       //Setting up our sms Data
        smsBytes[i]=’ ‘;
      }
      waitForSMS=millis();
      //need to reset the phone here
      //*****GPS******
      pinMode(rxGPS, INPUT);
      pinMode(txGPS, OUTPUT);
      Serial3.begin(4800);
      for (int i=0;i<300;i++){       //Setting up our gps Data
        gpsBytes[i]=’ ‘;
      }
      readGPS=0;
      gpsCommand[0]=’$';
    gpsCommand[1]=’G';
      gpsCommand[2]=’P';
    gpsCommand[3]=’R';
      gpsCommand[4]=’M';
    gpsCommand[5]=’C';
      //****** Car ********
      //Serial.begin(9600);
      Serial2.begin(19200);
      for (int i=0;i<200;i++){       //Setting up our car Data
        carBytes[i]=’ ‘;
      }
      pinMode(Ignition,OUTPUT);
      pinMode(Starter,OUTPUT);
      pinMode(DoorLock,OUTPUT);
      pinMode(DoorUnlock,OUTPUT);
      digitalWrite(cellReset,LOW);
      delay(600);
      digitalWrite(cellReset,HIGH);
      Serial.println(“Program Started”);
    }
    void loop()
    {
      //********* Cell Phone *******
      if(Serial1.available())
      {
        readByte=Serial1.read();
        Serial.print(readByte,BYTE);
        smsBytes[smsCounter]=readByte;        // If there is serial port data, it is put in the buffer
        smsCounter++;
        if (smsCounter == 160) //we overflowed our buffer
        {
          smsCounter=0;
        }
        if (readByte==13){            // If the received byte is = to 13, end of transmission
          if(cellReady==1){
            digitalWrite(ledPin,LOW);
            if(readNextMessage==0){
              if(smsBytes[1]==’+’ && smsBytes[2]==’C’ && smsBytes[3]==’M’ && smsBytes[4]==’T’ && smsBytes[5]==’:’){
                readNextMessage=1; //the next message is the text message
                //read mobile number
                for(int i=0;i<10;i++){
                  mobilenumber[i]=smsBytes[i+10];
                }
              }
            }else{
              //Serial.print(“Got Message Will Reply to:”);
              //Serial.println(mobilenumber);
              //delay(2000);
              for(int m=0;m<smsCounter;m++)
              {
                readNextMessage=0;
                curSMSByte=smsBytes[m];
                if(curSMSByte < 96) //if less than 96 commands only for car
                {
                  switch(curSMSByte)
                  {
                  case ’0′:
                    getPID(1,12,1);
                    retCarValue = (carReturn[0] -40);
                    writeSMS(String(retCarValue));
                    break;
                  case ’1′:
                    getPID(1,12,2);
                    retCarValue = (carReturn[0] * 256 + carReturn[1])*.25;
                    writeSMS(String(retCarValue));
                    break;
                  case ’2′:
                    getPID(1,13,1);
                    retCarValue = carReturn[0];
                    writeSMS(String(retCarValue));
                    break;
                  case ’3′:
                    getPID(1,15,1);
                    retCarValue = carReturn[0];
                    writeSMS(String(retCarValue));
                    break;
                  case ’4′:
                    getPID(1,47,1);
                    retCarValue =(100*carReturn[0])/255;
                    writeSMS(String(retCarValue));
                    break;
                  case ’5′:
                    getPID(1,70,1);
                    retCarValue =(carReturn[0]-40);
                    writeSMS(String(retCarValue));
                    break;
                  case ’6′:
                    getPID(1,92,1);
                    retCarValue =(carReturn[0]-40);
                    writeSMS(String(retCarValue));
                    break;
                  }
                }else{ //if > 96 then commands for everything but car
                  switch(curSMSByte)
                  {
                  case ‘a’:
                    //a-Get all PIDS
                    //do something
                    writeSMS(“GotPids”);
                    break;
                  case ‘b’: //Auto send updates
                    writeSMS(“AutoSendSet”);
                    //do something
                    break;
                  case ‘c’: //End auto send updated
                    //do something
                    writeSMS(“AutoSendCanceled”);
                    break;
                  case ‘g’: //Get GPRMC message from GPS
                    readGPS=1;
                    break;
                  case ‘k’: //Kill Car
                    stopCar();
                    writeSMS(“Car Stopped”);
                    break;
                  case ‘l’: //Lock Car
                    lockCar();
                    writeSMS(“Doors Locked”);
                    break;
                  case ‘s’: //Start
                    startCar();
                    writeSMS(“Car Started”);
                    break;
                  case ‘u’: //Unlock Car
                    unlockCar();
                    writeSMS(“Doors Unlocked”);
                    break;
                  }
                }
              }
            }
            delay(800);
            digitalWrite(ledPin,HIGH);
          }
          if(cellReady==0){
            smsCounter=0;
            smsPassCount=0;
            for (int i=1;i<8;i++){     // Verifies if the received command starts with ???
              if (smsBytes[i]==smsCommand[i-1]){
                smsPassCount++;
              }
            }
            if(smsPassCount==7){               // If yes, smsCounter and process the data
              //Talk through USB Port
              if(smsBytes[8]==’1′)
              {
                switch(smsBytes[9])
                {
                case  13: Serial.print(“Sim inserted”);
                  cellReady=0; break;
                case  ’0′: Serial.print(“Phone Book Loaded”); break;
                case  ’1′: Serial.print(“Registered with Network”);
                  gotNetwork=1; break;
                }
              }else{
                switch(smsBytes[8])
                {
                case  ’3′: Serial.print(“Almost Ready”); break;
                case  ’4′: gotConnect=1; break;
                case  ’7′:Serial.print(“The network service is available for an emergency call”);break;
                case  ’8′: Serial.print(“Bad Return”); break;
                default: Serial.print(smsBytes[8]); break;
                }
              }
              if(gotNetwork==1 && gotConnect==1){
                //setup for calling see http://tronixstuff.files.wordpress.com/2011/01/sm5100b-at-commands.pdf for value pg 127
                Serial1.println(“AT+CMGF=1″); // set SMS mode to text
                delay(200);
                Serial1.println(“AT+CNMI=3,3,0,0″); // set module to send SMS data to serial out upon receipt
                delay(200);
                digitalWrite(ledPin,HIGH);
                cellReady=1;
              }
            }
          }
          smsCounter=0;                    // Reset the buffer
        }
      }
      //*****GPS****
      if (Serial3.available() && readGPS==1)
      {
        readByte=Serial3.read();         // Read a byte of the serial port
        gpsBytes[gpsCounter]=readByte;        // If there is serial port data, it is put in the buffer
        gpsCounter++;
        if (gpsCounter == 300) //we overflowed our buffer
        {
          gpsCounter=0;
        }
        Serial.print(readByte, BYTE);  //if you want to print all of your bytes
        if (readByte==13){            // If the received byte is = to 13, end of transmission
          gpsCounter=0;
          gpsPassCount=0;
          for (int i=1;i<7;i++){     // Verifies if the received command starts with $GPR
            if (gpsBytes[i]==gpsCommand[i-1]){
              gpsPassCount++;
            }
          }
          // if we have the first 6 chars right then must be our data
          //lets send it off to the system.
          if(gpsPassCount==6){
            String tmp = “”;
            for (int i=8;i<160;i++){
              if(gpsBytes[i] == 13){
                i=159;
              }else{
                tmp = tmp + gpsBytes[i];
              }
            }
            writeSMS(tmp);
            readGPS=0;
          }
          gpsCounter=0; // Reset the buffer
        }
      }
      //lets send a message if we have waited
      //long enough and there is a message waiting to be sent
      if(millis() > waitForSMS && smsSendStatus > 0)
      {
        sendSMS();
      }
    }
    void writeSMS(String msg)
    {
      String newMSG = smsSendMessage+msg + ‘,’;
      Serial.println(“Sending Message”);
      //the string is too long we need to send what we have
      if(newMSG.length() > 160)
      {
        //if we are already in the middle of sending message then we have to wait for it to send
        while(millis() > waitForSMS && smsSendStatus > 0)
        {
          Serial.print(‘w’,BYTE);
          delay(20);
        }
        smsSendStatus=1;
        sendSMS();
        smsSendMessage = msg + “, “;
      }else{
        smsSendStatus=1;
        smsSendMessage = newMSG;
      }
    }
    void sendSMS()
    {
      if(smsSendStatus==2){
        Serial1.println(“AT+CMGD=1,4″);// delete all SMS
        smsSendStatus=0;
      }
      if(smsSendStatus==1){
        //something
        startSMS();
        delay(500);
        Serial.print(smsSendMessage);
        Serial1.print(smsSendMessage);
        //    for (int i=0;i<160;i++){
        //      if(smsSendMessage[i] == 13){
        //        i=159;
        //      }else{
        //        Serial.print(smsSendMessage[i], BYTE);
        //        Serial1.print(smsSendMessage[i], BYTE);
        //      }
        //    }
        smsSendMessage=””;
        endSMS();
        cellWait(15000);
        smsSendStatus=2;
        /* //sleeping not working for now :-(
    if(smsBytes[1]==’W’||smsBytes[1]==’w’) //Wait
    {
    //digitalWrite(53, HIGH);
    Serial1.println(“AT+ARMSLEEP=1″);
    startSMS();
    Serial1.print(“sleeping for 30 seconds”);
    endSMS();
    delay(30000);
    Serial1.println(“AT+ARMSLEEP=0″);
    //Serial.print(“Doors Locked”);
    }*/
        //Serial1.println(“AT+CMGD=1,4″);
      }
    }
    // function to send a text message
    void startSMS()
    {
      digitalWrite(13, HIGH);
      Serial1.println(“AT+CMGF=1″); // set SMS mode to text
      Serial1.print(“AT+CMGS=”);
      Serial1.print(34,BYTE); // ASCII equivalent of “
      Serial1.print(mobilenumber);
      Serial1.println(34,BYTE);  // ASCII equivalent of “
      //delay(500); // give the module some thinking time
      //cellWait(500);
    }
    void endSMS()
    {
      Serial1.print(26,BYTE);  // ASCII equivalent of Ctrl-Z
      //delay(15000); // the SMS module needs time to return to OK status
      waitForOK=1;
      //Serial.println(“Wait OK”);
      //Serial1.println(“AT+CMGD=1,4″); // delete all SMS
      readNextMessage=0;
      digitalWrite(13, LOW);
    }
    void cellWait(int waitTime)
    {
      waitForSMS = millis() + waitTime;
    }
    void startCar()
    {
      digitalWrite(DoorLock, HIGH);
      digitalWrite(Ignition, HIGH);
      delay(100);
      digitalWrite(Starter, HIGH);
      delay(1300);
      digitalWrite(Starter, LOW);
      digitalWrite(DoorLock, LOW);
    }
    void stopCar()
    {
      digitalWrite(Ignition, LOW);
    }
    void lockCar()
    {
      digitalWrite(DoorLock, HIGH);
      delay(1500);
      digitalWrite(DoorLock, LOW);
    }
    void unlockCar()
    {
      digitalWrite(DoorUnlock, HIGH);
      delay(1500);
      digitalWrite(DoorUnlock, LOW);
    }
    /*
    //this is the communication process
    //first part is the startup and the last 2 lines are requesting pid
    tx 20
    rx FF
    tx 41 00
    rx 02 00 00
    tx 42 01 33
    rx 02 01 33
    tx 07 61 6A F1 01 0C 96 01 <- Request PID 0C = Engine RPM
    rx 08 41 6B 10 41 0C 0D 17 79 <- Response Last 2 Bytes are Engine RPM
    */
    void getPID(int mode, int PID, int numRetBytes )
    {
      int stage=0;
      int waitingforResponse=0;
      int readTo=numRetBytes + 7;
      byte inval[10] = “”;
      int carCounter=0;
      int readCount=0;
      int waitForCar = millis() + 800;
      while (carCounter < readTo && carStarted==1)
      {
        readCarByte=Serial2.read();
        Serial.print(readCarByte,HEX);
        inval[carCounter]=readCarByte;
        carCounter++;
        if(carStarted==0)
        {
          if(waitingforResponse==0){
            switch(stage){
            case 0 :sendCar(32);readTo=1;break;
            case 1 :sendCar(65);sendCar(0);readTo=3;break;
            case 2 :sendCar(66);sendCar(1);sendCar(51);readTo=3;break;
            }
            waitingforResponse=1;
          }else{
            if(carCounter==readTo){
              if(stage==3)
              {
                //car is started
                carStarted=1;
              }
              readTo=numRetBytes + 7;
              waitingforResponse=0;
              carCounter=0;
            }
          }
        }else{
          if(waitingforResponse==0){
            sendCar(7);
            sendCar(97);
            sendCar(106);
            sendCar(241);
            sendCar(1);
            sendCar(12);
            sendCar(150);
            sendCar(1);
          }
        }
        if(millis() > waitForCar){ //we have timed out on the car
          inval[6]=’E';
    inval[7]=’E';
          inval[8]=’E';
    inval[9]=’E';
          carStarted=1;
          carCounter = readTo;
        }
      }
      for(int i=0;i<numRetBytes;i++)
      {
        carReturn[i] = inval[i+6];
      }
    }
    void sendCar(int num)
    {
      Serial2.print(num,BYTE);
      Serial.print(num,HEX);
    }
    

    5.2 Android Code

    5.2.1 CarTabActivity.java

    package com.grego.tabs;
    import android.app.Activity;
    import android.app.PendingIntent;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.telephony.SmsManager;
    import android.os.Bundle;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.Toast;
    public class CarTabActivity extends Activity {
      String PHONE = “##########”; //Enter your sim card’s phone number
      public void onCreate(Bundle savedInstanceState)
      {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.buttons);
        final Button bStart = (Button) findViewById(R.id.bStart);
        bStart.setOnClickListener(new OnClickListener(){
          public void onClick(View v) {
            String message = “s”;
            sendSMS(PHONE, message);
          }
        });
        final Button bLock = (Button) findViewById(R.id.bLock);
        bLock.setOnClickListener(new OnClickListener(){
          public void onClick(View v) {
            String message = “l”;
            sendSMS(PHONE, message);
          }
        });
        final Button bUnlock = (Button) findViewById(R.id.bUnlock);
        bUnlock.setOnClickListener(new OnClickListener(){
          public void onClick(View v) {
            String message = “u”;
            sendSMS(PHONE, message);
          }
        });
        final Button bStop = (Button) findViewById(R.id.bStop);
        bStop.setOnClickListener(new OnClickListener(){
          public void onClick(View v) {
            String message = “k”;
            sendSMS(PHONE, message);
          }
        });
      }
      private void sendSMS(String phoneNumber, String message){
        String SENT = “SMS_SENT”;
        String DELIVERED = “SMS_DELIVERED”;
        PendingIntent sentPI = PendingIntent.getBroadcast(this, 0,
        new Intent(SENT), 0);
        PendingIntent deliveredPI = PendingIntent.getBroadcast(this,0,
        new Intent(DELIVERED),0);
        registerReceiver(new BroadcastReceiver(){
          @Override
          public void onReceive(Context arg0, Intent arg1){
            switch (getResultCode()) {
            case Activity.RESULT_OK:
              Toast.makeText(getBaseContext(), “SMS sent”,
              Toast.LENGTH_SHORT).show();
              break;
            case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
              Toast.makeText(getBaseContext(), “Generic failure”,
              Toast.LENGTH_SHORT).show();
              break;
            case SmsManager.RESULT_ERROR_NO_SERVICE:
              Toast.makeText(getBaseContext(), “No Service”,
              Toast.LENGTH_SHORT).show();
            case SmsManager.RESULT_ERROR_NULL_PDU:
              Toast.makeText(getBaseContext(), “Null PDU”,
              Toast.LENGTH_SHORT).show();
              break;
            case SmsManager.RESULT_ERROR_RADIO_OFF:
              Toast.makeText(getBaseContext(), “Radio off”,
              Toast.LENGTH_SHORT).show();
              break;
            }
          }
        }, new IntentFilter(SENT));
        registerReceiver(new BroadcastReceiver(){
          @Override
          public void onReceive(Context arg0, Intent arg1) {
            switch (getResultCode())
            {
            case Activity.RESULT_OK:
              Toast.makeText(getBaseContext(), “SMS delivered”,
              Toast.LENGTH_SHORT).show();
              break;
            case Activity.RESULT_CANCELED:
              Toast.makeText(getBaseContext(), “SMS not delivered”,
              Toast.LENGTH_SHORT).show();
              break;
            }
          }
        }, new IntentFilter(DELIVERED));
        SmsManager sms = SmsManager.getDefault();
        try{
          sms.sendTextMessage(phoneNumber, null, message, sentPI, deliveredPI);
        } catch(Exception e){
          Toast.makeText(CarTabActivity.this, e.toString(), Toast.LENGTH_SHORT).show();
        }
      }
    }
    

    5.2.2 MapTabActivity.java

    package com.grego.tabs;
    import java.util.List;
    import com.google.android.maps.GeoPoint;
    import com.google.android.maps.MapActivity;
    import com.google.android.maps.MapView;
    import com.google.android.maps.Overlay;
    import com.google.android.maps.OverlayItem;
    import android.content.Context;
    import android.graphics.drawable.Drawable;
    import android.os.Bundle;
    public class MapTabActivity extends MapActivity {
      static MapView mapView;
      static Drawable drawable;
      /** Called when the activity is first created. */
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.maps);
        mapView = (MapView) findViewById(R.id.mapview);
        mapView.setBuiltInZoomControls(true);
        drawable = this.getResources().getDrawable(R.drawable.carmarker);
        Parser.mapsReady = true;
        updateMap(40769232, -111846131);
      }
      public static void updateMap(int x, int y) {
        mapView.invalidate();
        List mapOverlays = mapView.getOverlays();
        mapOverlays.clear();
        Context mContext = mapView.getContext();
        MarkerOverlay itemizedoverlay = new MarkerOverlay(drawable, mContext);
        //create default marker for MEB Parking lot
        GeoPoint point = new GeoPoint(x,y);
        OverlayItem overlayitem = new OverlayItem(point, “The Car Is Here”, “Team Cool Beans”);
        itemizedoverlay.addOverlay(overlayitem);
        mapOverlays.add(itemizedoverlay);
      }
      @Override
      protected boolean isRouteDisplayed() {
        // TODO Auto-generated method stub
        return false;
      }
    }
    

    5.2.3 MarkerOverlay.java

    package com.grego.tabs;
    import java.util.ArrayList;
    import android.app.AlertDialog;
    import android.content.Context;
    import android.graphics.drawable.Drawable;
    import com.google.android.maps.ItemizedOverlay;
    import com.google.android.maps.OverlayItem;
    @SuppressWarnings(“rawtypes”)
    public class MarkerOverlay extends ItemizedOverlay {
      private ArrayList mOverlays = new ArrayList();
      private Context mContext;
      public MarkerOverlay(Drawable defaultMarker, Context context) {
        super(boundCenterBottom(defaultMarker));
        mContext = context;
      }
      @Override
      protected OverlayItem createItem(int i) {
        return mOverlays.get(i);
      }
      @Override
      public int size() {
        return mOverlays.size();
      }
      public void addOverlay(OverlayItem overlay){
        clear();
        mOverlays.add(overlay);
        populate();
      }
      public void clear() {
        mOverlays.clear();
        populate();
      }
      @Override
      protected boolean onTap(int index) {
        OverlayItem item = mOverlays.get(index);
        AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
        dialog.setTitle(item.getTitle());
        dialog.setMessage(item.getSnippet());
        dialog.show();
        return true;
      }
    }
    

    5.2.4 Parser.java

    package com.grego.tabs;
    //no info from car:
    //ect = 9, RPM = 3148, Vehicle speed = 49, intake air temp = 9
    public class Parser {
      public static boolean statsReady = false;
      public static boolean mapsReady = false;
      //create a blank constructor
      public Parser(){};
      static int parseMe(String message){
        //remove the header of the text
        String[] input = message.split(“,”, 0);
        String statMessage = “”;
        int x,y;
        float tempx, tempy;
        if(input.length > 14){
          statMessage = statMessage.concat(“Engine Coolant Temp:”.concat(input[11].concat(“\n”)));
          statMessage = statMessage.concat(“RPM:”.concat(input[12].concat(“\n”)));
          statMessage = statMessage.concat(“Vehicle speed in km/h:”.concat(input[13].concat(“\n”)));
          statMessage = statMessage.concat(“Intake Air Tempurature:”.concat(input[14].concat(“\n”)));
          //Check for valid input
          if(input[11].equals(“9″)){
            if(input[12].equals(“3148″)){
              if(input[13].equals(“49″)){
                if(input[14].equals(“9″)){
                  statMessage = “No data\n Car must be on\n”;
                }
              }
            }
          }
          //Convert GPS data
          tempx = Float.parseFloat(input[2]);
          x = 0;
          x += (int)tempx;
          x = x / 100;
          x = x * 1000000;
          tempx = tempx % 100;
          tempx = tempx / 60;
          tempx *= 1000000;
          x += (int)tempx;
          tempy = Float.parseFloat(input[4]);
          y = 0;
          y += (int)tempy;
          y = y / 100;
          y = y * 1000000;
          tempy = tempy % 100;
          tempy = tempy / 60;
          tempy *= 1000000;
          y += (int)tempy;
          if(input[3].equals(“S”)) x = -x;
          if(input[5].equals(“W”)) y = -y;
          statMessage = statMessage.concat(“x coor:”.concat(((Integer)x).toString().concat(“\n”)));
          statMessage = statMessage.concat(“y coor:”.concat(((Integer)y).toString().concat(“\n”)));
          updateMaps(x,y);
          updateStats(statMessage);
          return -1; //Success
        } else if (input.length > 0){
          return input.length;
        }
        return -2;
      }
      /* private static void buildStats(String statistics) {
    // break apart and build the stats string here
    updateStats(statistics);
    }*/
      /* private static void publishStats(String message){
    updateStats(message);
    }
    */
      private static void updateStats(String str) {
        if(statsReady) {
          //update stats tab here
          StatsTabActivity.updateStats(str);
        }
      }
      private static void updateMaps(int x, int y) {
        if(mapsReady) {
          //update maps tab here
          MapTabActivity.updateMap(x, y);
        }
      }
    }
    

    5.2.5 SmsReceiver.java

    package com.grego.tabs;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Bundle;
    import android.telephony.SmsMessage;
    import android.widget.Toast;
    public class SmsReceiver extends BroadcastReceiver{
      @Override
      public void onReceive(Context context, Intent intent) {
        //—get the SMS message passed in—
        int temp;
        String PHONE = “##########”; //enter your sim card’s phone number
        Bundle bundle = intent.getExtras();
        SmsMessage[] msgs = null;
        String str = “”;
        if (bundle != null)
        {
          //—retrieve the SMS message received—
          Object[] pdus = (Object[]) bundle.get(“pdus”);
          msgs = new SmsMessage[pdus.length];
          for (int i=0; i<msgs.length; i++){
            msgs[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
            if(!msgs[i].getOriginatingAddress().equals(PHONE)) continue;
            str += msgs[i].getMessageBody().toString();
            str += “\n”;
            //pass text into parser to be worked on
            temp = Parser.parseMe(str);
            if(temp == -2){
              Toast.makeText(context, “Error”, Toast.LENGTH_SHORT).show();
            } else if(temp != -1) {
              //Toast.makeText(context, temp, Toast.LENGTH_SHORT).show();
              Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
            }
          }
        }
      }
    }
    

    5.2.6 StatsTabActivity.java

    package com.grego.tabs;
    import android.app.Activity;
    import android.app.PendingIntent;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.content.IntentFilter;
    import android.os.Bundle;
    import android.telephony.SmsManager;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;
    public class StatsTabActivity extends Activity {
      String PHONE = “##########”; //Enter your sim cards phone number
      //static String statistics;
      static TextView topLine;
      private boolean state = true;
      public void onCreate(Bundle savedInstanceState)
      {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.stats);
        //TODO add code to send text to arduino to send stats
        final Button Refresh = (Button) findViewById(R.id.bRefresh);
        Refresh.setOnClickListener(new OnClickListener(){
          public void onClick(View v) {
            /*
    if(state){
    sendSMS(PHONE, “b”);
    } else {
    sendSMS(PHONE, “c”);
    }
    */
            sendSMS(PHONE, “a”);
            state = !state;
          }
        });
        //Tell parser it’s okay to write to the screen
        Parser.statsReady = true;
        topLine = (TextView) findViewById(R.id.topLine);
        topLine.setText(“Loading, Please Wait.”);
      }
      public void setText(String s) {
        if(s == null){
          s = “error”;
        }
        topLine = (TextView) findViewById(R.id.topLine);
        topLine.setText(s);
      }
      public static void updateStats(String stat){
        topLine.setText(stat);
      }
      private void sendSMS(String phoneNumber, String message){
        //PendingIntent pi = PendingIntent.getActivity(this, 0,
        // new Intent(this, CarTabActivity.class),0);
        String SENT = “SMS_SENT”;
        String DELIVERED = “SMS_DELIVERED”;
        PendingIntent sentPI = PendingIntent.getBroadcast(this, 0,
        new Intent(SENT), 0);
        PendingIntent deliveredPI = PendingIntent.getBroadcast(this,0,
        new Intent(DELIVERED),0);
        registerReceiver(new BroadcastReceiver(){
          @Override
          public void onReceive(Context arg0, Intent arg1){
            switch (getResultCode()) {
            case Activity.RESULT_OK:
              Toast.makeText(getBaseContext(), “SMS sent”,
              Toast.LENGTH_SHORT).show();
              break;
            case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
              Toast.makeText(getBaseContext(), “Generic failure”,
              Toast.LENGTH_SHORT).show();
              break;
            case SmsManager.RESULT_ERROR_NO_SERVICE:
              Toast.makeText(getBaseContext(), “No Service”,
              Toast.LENGTH_SHORT).show();
            case SmsManager.RESULT_ERROR_NULL_PDU:
              Toast.makeText(getBaseContext(), “Null PDU”,
              Toast.LENGTH_SHORT).show();
              break;
            case SmsManager.RESULT_ERROR_RADIO_OFF:
              Toast.makeText(getBaseContext(), “Radio off”,
              Toast.LENGTH_SHORT).show();
              break;
            }
          }
        }, new IntentFilter(SENT));
        registerReceiver(new BroadcastReceiver(){
          @Override
          public void onReceive(Context arg0, Intent arg1) {
            switch (getResultCode())
            {
            case Activity.RESULT_OK:
              Toast.makeText(getBaseContext(), “SMS delivered”,
              Toast.LENGTH_SHORT).show();
              break;
            case Activity.RESULT_CANCELED:
              Toast.makeText(getBaseContext(), “SMS not delivered”,
              Toast.LENGTH_SHORT).show();
              break;
            }
          }
        }, new IntentFilter(DELIVERED));
        SmsManager sms = SmsManager.getDefault();
        try{
          sms.sendTextMessage(phoneNumber, null, message, sentPI, deliveredPI);
        } catch(Exception e){
          Toast.makeText(StatsTabActivity.this, e.toString(), Toast.LENGTH_SHORT).show();
        }
      }
    }
    

    5.2.7 TabsGregActivity.java

    package com.grego.tabs;
    import android.app.TabActivity;
    import android.content.Intent;
    import android.content.res.Resources;
    import android.os.Bundle;
    import android.widget.TabHost;
    public class TabsGregActivity extends TabActivity {
      //create a new parser to be used throughout the program
      public static Parser parser = new Parser();
      /** Called when the activity is first created. */
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Resources res = getResources();
        TabHost tabHost = getTabHost();
        TabHost.TabSpec spec;
        Intent intent;
        intent = new Intent().setClass(this, CarTabActivity.class);
        spec = tabHost.newTabSpec(“remote”).setIndicator(“Remote”,
        res.getDrawable(R.drawable.ic_tab_car)).setContent(intent);
        tabHost.addTab(spec);
        intent = new Intent().setClass(this, MapTabActivity.class);
        spec = tabHost.newTabSpec(“map”).setIndicator(“Map”,
        res.getDrawable(R.drawable.ic_tab_map)).setContent(intent);
        tabHost.addTab(spec);
        intent = new Intent().setClass(this, StatsTabActivity.class);
        spec = tabHost.newTabSpec(“stats”).setIndicator(“Stats”,
        res.getDrawable(R.drawable.ic_tab_stats)).setContent(intent);
        tabHost.addTab(spec);
        tabHost.setCurrentTab(0);
      }
    }
    

    5.2.8 buttons.xml

    <?xml version=”1.0″ encoding=”utf-8″?>
    <TabHost
        xmlns:android=”http://schemas.android.com/apk/res/android”
      android:id=”@android:id/tabhost”
      android:layout_width=”fill_parent”
      android:layout_height=”fill_parent”
      android:padding=”0dp”>
        <LinearLayout
        android:orientation=”vertical”
        android:layout_width=”fill_parent”
        android:layout_height=”fill_parent”
        android:padding=”0dp”>
            <TabWidget
          android:id=”@android:id/tabs”
          android:layout_width=”fill_parent”
          android:layout_height=”wrap_content”
          android:padding=”0dp” />
            <Button
          android:id=”@+id/bStart”
          android:layout_width=”wrap_content”
          android:layout_height=”160px”
          android:padding=”0dp”
          android:background=”@drawable/start” />
            <Button
          android:id=”@+id/bLock”
          android:layout_width=”wrap_content”
          android:layout_height=”160px”
          android:padding=”0dp”
          android:background=”@drawable/lock” />
            <Button
          android:id=”@+id/bUnlock”
          android:layout_width=”wrap_content”
          android:layout_height=”160px”
          android:padding=”0dp”
          android:background=”@drawable/unlock” />
            <Button
          android:id=”@+id/bStop”
          android:layout_width=”wrap_content”
          android:layout_height=”160px”
          android:padding=”0dp”
          android:background=”@drawable/off” />
        </LinearLayout>
    </TabHost>
    

    5.2.9 main.xml

    <?xml version=”1.0″ encoding=”utf-8″?>
    <TabHost
        xmlns:android=”http://schemas.android.com/apk/res/android”
        android:id=”@android:id/tabhost”
        android:layout_width=”fill_parent”
        android:layout_height=”fill_parent”>
        <LinearLayout
            android:orientation=”vertical”
            android:layout_width=”fill_parent”
            android:layout_height=”fill_parent”
            android:padding=”5dp”>
            <TabWidget
                android:id=”@android:id/tabs”
                android:layout_width=”fill_parent”
                android:layout_height=”wrap_content” />
            <FrameLayout
                android:id=”@android:id/tabcontent”
                android:layout_width=”fill_parent”
                android:layout_height=”fill_parent”
                android:padding=”5dp” ></FrameLayout>
        </LinearLayout>
    </TabHost>
    

    5.2.10 maps.xml

    <?xml version=”1.0″ encoding=”utf-8″?>
    <TabHost
        xmlns:android=”http://schemas.android.com/apk/res/android”
        android:id=”@android:id/tabhost”
        android:layout_width=”fill_parent”
        android:layout_height=”fill_parent”
        android:padding=”0dp”>
        <LinearLayout
            android:orientation=”vertical”
            android:layout_width=”fill_parent”
            android:layout_height=”fill_parent”
            android:padding=”0dp”>
            <TabWidget
                android:id=”@android:id/tabs”
                android:layout_width=”fill_parent”
                android:layout_height=”wrap_content”
                android:padding=”0dp” />
            <com.google.android.maps.MapView
                xmlns:android=”http://schemas.android.com/apk/res/android”
                android:id=”@+id/mapview”
                android:layout_width=”fill_parent”
                android:layout_height=”fill_parent”
                android:clickable=”true”
                android:apiKey=”0W9nQDgG8BK2bFEhdR6cX1Xa19uQQ8Z9vH72wsQ”/>
            </LinearLayout>
        </TabHost>
    

    5.2.11 stats.xml

        <?xml version=”1.0″ encoding=”utf-8″?>
        <TabHost
            xmlns:android=”http://schemas.android.com/apk/res/android”
            android:id=”@android:id/tabhost”
            android:layout_width=”fill_parent”
            android:layout_height=”fill_parent”
            android:padding=”0dp”>
            <LinearLayout
                android:orientation=”vertical”
                android:layout_width=”fill_parent”
                android:layout_height=”fill_parent”
                android:padding=”0dp”>
                <TabWidget
                    android:id=”@android:id/tabs”
                    android:layout_width=”fill_parent”
                    android:layout_height=”wrap_content”
                    android:padding=”0dp” />
                <Button
                    android:id=”@+id/bRefresh”
                    android:layout_width=”wrap_content”
                    android:layout_height=”100px”
                    android:padding=”0dp”
                    android:background=”@drawable/refresh” />
                <TextView android:layout_height=”wrap_content” android:paddingTop=”10px” android:layout_width=”wrap_content” android:id=”@+id/topLine” android:textAppearance=”?android:attr/textAppearanceLarge” />
            </LinearLayout>
        </TabHost>
    

    5.2.12 AndroidManifest.xml

       <?xml version=”1.0″ encoding=”utf-8″?>
        <manifest
            xmlns:android=”http://schemas.android.com/apk/res/android”
        package=”com.grego.tabs”
        android:versionCode=”1″
        android:versionName=”1.0″>
            <uses-sdk android:minSdkVersion=”10″ />
            <uses-permission android:name=”android.permission.INTERNET” />
            <uses-permission android:name=”android.permission.SEND_SMS”></uses-permission>
            <uses-permission android:name=”android.permission.RECEIVE_SMS”></uses-permission>
            <application android:icon=”@drawable/icon” android:label=”@string/app_name” android:debuggable=”true” android:persistent=”false”>
                <uses-library android:name=”com.google.android.maps” />
                <activity android:name=”.TabsGregActivity”
            android:label=”@string/app_name”
            android:theme=”@android:style/Theme.NoTitleBar”
            android:screenOrientation=”portrait”>
                    <intent-filter>
                        <action android:name=”android.intent.action.MAIN” />
                        <category android:name=”android.intent.category.LAUNCHER” />
                    </intent-filter>
                </activity>
                <activity android:name=”.StatsTabActivity” android:screenOrientation=”portrait”></activity>
                <activity android:name=”.MapTabActivity” android:screenOrientation=”portrait”></activity>
                <activity android:name=”.CarTabActivity” android:screenOrientation=”portrait”></activity>
                <receiver android:name=”.SmsReceiver”>
                    <intent-filter>
                        <action android:name=“android.provider.Telephony.SMS_RECEIVED” />
                    </intent-filter>
                </receiver>
            </application>
        </manifest>
    



  • The DIY Sheldon Cooper Shirt Folder

    *all references to Sheldon Cooper, Big Bang Theory are owned by Chuck Lorre Productions.

    Things you will use:

    • Big piece of cardboard (fridge, washer, dryer, etc.)

    Tools:

    • Blade (carpet cutter, xacto, or kitchen knife)
    • Yard Stick or Measuring tape
    • Pen

    So one time I was watching the big bang theory and saw Sheldon Cooper fold his shirts using a device:

     

    tumblr_lvlrl6H1Hw1qbxt98

    So I Google’d this device and found it at www.FlipFold.com:

    flip-fold-adult-shirt-folder-2-300x249

    So I figured I could make it out of some cardboard:

    IMG_4319-300x225

    So take your piece of cardboard and cut a piece out that is 30 in x 30 in:

    Measurements-300x225

    Mark the following lines:

    measure-mark-300x225

    Cut an 1/8 in gap on the following:

    Fold-cut-300x225

    Crease the following (push a yard stick into it and bend around the yard stick):

    Fold-Crease-300x225

    How to fold the shirt

    You can use the instruction on FlipFold.com or just these (it ain’t rocket science).

    Place your shirt upside down in the middle of the shirt folder, with the folded center up:

    IMG_4320-300x225

    Fold the left side over:

    IMG_4321-300x225

    Fold the Right side over:

    IMG_4322-300x225

    Fold the top down:

    IMG_4323-300x225

    Vuala, you have a perfectly folded shirt:

    IMG_4324-300x225

    IMG_4325-300x225



  • Dynamite Christmas

    At my work it seems like it is common place to share candy for Christmas so my wife had idea for dynamite sticks (usually for valentines but hey it works).

    Time: 5 Minutes

    Things you will need:

    • Rollos (a pack from Sam’s Club or singulars)
    • Red Paper
    • Clear Tape
    • Black Pipe Cleaner (1 1/2 inch pieces)

    Tools:

    • Scissors
    • Printer

    So download the image below and print onto an 8.5 x 11 sheet of red paper.

     

    Cut out on the lines to produce 6 pieces –> Place you name on the rollo –> tape bottom onto Rollo –> tape Pipe Cleaner piece onto inside of paper — > Roll paper around rollo and tape. And that is all it takes.