Introduction: Energy Efficient Motion Activated Street Lights

Our goal with this project was to create something that would save communities energy and financial resources. Motion activated street lights would do both these things. All across the nation energy is being wasted on street lights lighting streets that are empty. Our street light system ensures that lights are only on when needed, saving communities countless dollars. Using motion sensors, the system turns on lights only when cars are present. Also for the safety of pedestrians, we implemented an override button that turns on all lights on the street. The following steps will walk you through how we designed and built our scaled down model of the project using Vivado and a Basys 3 board.

Step 1: System Black Box

We began this project by drawing a simple black box diagram. A black box diagram simply shows the inputs and outputs our system requires to complete all necessary processes. We tried to keep our design as simple and basic as possible. Our three system inputs included a bus of motion sensors (4 for our scaled down model), a pedestrian override button, and a clock input. On the other side our single output is a bus of LED lights representing our street lights. For this model we used a scenario of 16 street lights simply because that's the max number of built in LED outputs on the Basys 3 board. Finally, using this diagram we were able to create our Vivado project, source, and constraint files with appropriate inputs and outputs.

Step 2: Components

In this step we dive deeper examining the components making up our black box diagram. Our first component is a VHDL source file containing D flip-flops. D flip-flops simply take whatever signal is being input to them from the sensors on the rising edge of clock, and latches that data in until the next rising edge. This keeps our sensitive motion sensors from causing the output LED's from "flickering". Also, we put a single D flip-flop on the button input signal to keep LED's on for about 5-7 seconds after the button is pushed. We also ran this through a clock divider.

entity clk_div2 is
Port ( clk : in std_logic; sclk : out std_logic); end clk_div2;

architecture my_clk_div of clk_div2 is constant max_count : integer := (300000000); signal tmp_clk : std_logic := '0'; begin my_div: process (clk,tmp_clk) variable div_cnt : integer := 0; begin if (rising_edge(clk)) then if (div_cnt = MAX_COUNT) then tmp_clk <= not tmp_clk; div_cnt := 0; else div_cnt := div_cnt + 1; end if; end if; sclk <= tmp_clk; end process my_div; end my_clk_div;

Our final component in this diagram is a behavioral VHDL source file containing conditionals for the outputs based off the configuration of input signals.

Step 3: D Flip-Flops

The four flip-flops attached to the input signals are essential to the functionality of our system. As said previously, with sensitive motion sensors and an override button, the flip-flops use latches to only output our input signal on the rising edge of clock. This sequential logic means our street lights can stay on for a set period of time after being triggered by a quick motion. The coding for a D-Flip Flop is pretty simple:

<p>begin<br>process (CLK) begin
    if rising_edge(CLK) then
        Q <= D;
    end if;
end process;</p>

The entire thing can be compiled into a single if statement. Once we had this piece, we created a structural VHDL source file containing all four of our necessary flip-flops:

<p>begin<br>    
DFF0: DFF port map (
        CLK => CLK,
        D => D(0),
        Q => Q(0));
        
    DFF1: DFF port map (
        CLK => CLK,
        D => D(1),
        Q => Q(1));
    
    DFF2: DFF port map (
        CLK => CLK,
        D => D(2),
        Q => Q(2));
        
    DFF3: DFF port map (
        CLK => CLK,
        D => D(3),
        Q => Q(3));    </p><p>end Behavioral;</p>

This helps keep our master structural file where we bring together all system components much more clean and organized.

Step 4: Conditionals

In order to keep our code compact and efficient we wrote all our conditionals in a single case statement. For our scaled down model, we had 16 possible LED output configurations as each motion sensor is responsible for a group of 4 LED's. :

<p>case NMS is<br>            
	    when "1111" => LED <= "0000000000000000";
            when "1110" => LED <= "0000000000001111";
            when "1101" => LED <= "0000000011110000";
            when "1100" => LED <= "0000000011111111";
            when "1011" => LED <= "0000111100000000";
            when "1010" => LED <= "0000111100001111";
            when "1001" => LED <= "0000111111110000";                                                                                                                                                                                                                                                                                                                                                                                                                                                        
            when "1000" => LED <= "0000111111111111";
            when "0111" => LED <= "1111000000000000";
            when "0110" => LED <= "1111000000001111";
            when "0101" => LED <= "1111000011110000";
            when "0100" => LED <= "1111000011111111";
            when "0011" => LED <= "1111111100000000";
            when "0010" => LED <= "1111111100001111";
            when "0001" => LED <= "1111111111110000";
            when "0000" => LED <= "1111111111111111";    
        end case;</p>

Step 5: Constraints

In order to properly state your input and outputs using Vivado, you must implement a constraint file stating all ports, buttons, LED's, and clocks being used.

set_property PACKAGE_PIN W5 [get_ports CLK]
set_property IOSTANDARD LVCMOS33 [get_ports CLK]

set_property PACKAGE_PIN U16 [get_ports {LED[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {LED[0]}] set_property PACKAGE_PIN E19 [get_ports {LED[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[1]}] set_property PACKAGE_PIN U19 [get_ports {LED[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[2]}] set_property PACKAGE_PIN V19 [get_ports {LED[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[3]}] set_property PACKAGE_PIN W18 [get_ports {LED[4]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[4]}] set_property PACKAGE_PIN U15 [get_ports {LED[5]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[5]}] set_property PACKAGE_PIN U14 [get_ports {LED[6]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[6]}] set_property PACKAGE_PIN V14 [get_ports {LED[7]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[7]}] set_property PACKAGE_PIN V13 [get_ports {LED[8]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[8]}] set_property PACKAGE_PIN V3 [get_ports {LED[9]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[9]}] set_property PACKAGE_PIN W3 [get_ports {LED[10]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[10]}] set_property PACKAGE_PIN U3 [get_ports {LED[11]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[11]}] set_property PACKAGE_PIN P3 [get_ports {LED[12]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[12]}] set_property PACKAGE_PIN N3 [get_ports {LED[13]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[13]}] set_property PACKAGE_PIN P1 [get_ports {LED[14]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[14]}] set_property PACKAGE_PIN L1 [get_ports {LED[15]}] set_property IOSTANDARD LVCMOS33 [get_ports {LED[15]}]

set_property PACKAGE_PIN U18 [get_ports BTN]
set_property IOSTANDARD LVCMOS33 [get_ports BTN]

set_property PACKAGE_PIN A14 [get_ports {MS[0]}]
set_property IOSTANDARD LVCMOS33 [get_ports {MS[0]}] set_property PACKAGE_PIN A16 [get_ports {MS[1]}] set_property IOSTANDARD LVCMOS33 [get_ports {MS[1]}] set_property PACKAGE_PIN B15 [get_ports {MS[2]}] set_property IOSTANDARD LVCMOS33 [get_ports {MS[2]}] set_property PACKAGE_PIN B16 [get_ports {MS[3]}] set_property IOSTANDARD LVCMOS33 [get_ports {MS[3]}]

Step 6: Main Source File

In this main file we bring together all component source files mentioned previously. This file functions as structural code bringing together the disparate components.

entity Master_Final_Project is
Port ( BTN : in STD_LOGIC; CLK : in STD_LOGIC; MS : in STD_LOGIC_VECTOR (3 downto 0); LED : out STD_LOGIC_VECTOR (15 downto 0)); end Master_Final_Project;

architecture Behavioral of Master_Final_Project is component final_project is Port ( --CLK : in STD_LOGIC; NMS : in STD_LOGIC_VECTOR (3 downto 0); BTN : in STD_LOGIC; --sw : in STD_LOGIC_Vector (1 downto 0); LED : out STD_LOGIC_VECTOR (15 downto 0)); end component;

component Final_DFF is Port ( CLK : in STD_LOGIC; D : in STD_LOGIC_Vector (3 downto 0); Q : out STD_LOGIC_Vector (3 downto 0)); end component;

signal DFF02proj30 : STD_LOGIC; signal DFF12proj74 : STD_LOGIC; signal DFF22proj118 : STD_LOGIC; signal DFF32proj1512 : STD_LOGIC;

begin DFF0: Final_DFF port map ( CLK => CLK, D(0) => MS(0), D(1) => MS(1), D(2) => MS(2), D(3) => MS(3), Q(0) => DFF02proj30, Q(1) => DFF12proj74, Q(2) => DFF22proj118, Q(3) => DFF32proj1512); Proj0: final_project port map ( NMS(0) => DFF02proj30, NMS(1) => DFF12proj74, NMS(2) => DFF22proj118, NMS(3) => DFF32proj1512, BTN => BTN, LED => LED); end Behavioral;

Step 7: Assembly

The hardware assembly for this project is minimal. The only required pieces are as follows:

1. Basys 3 board (1)

2. Cheap motion sensors that can be found on amazon here. (4)

3. Male-to-female leads (4)

Assembly:

1. Connect 4 male leads to PMod header JB ports 1-4 (See Figure).

2. Connect female ends to each motion sensor's output pin.

Step 8: Loading the Program

Now we are ready to load the VHDL master source file to the Basys 3 board. Make sure to run synthesis, implementation, and generate bitstream checking for any possible errors. If all run successfully, open the hardware manager and program the Basys 3 device. Your project is now complete!