Showing posts with label gpio. Show all posts
Showing posts with label gpio. Show all posts

2021-12-21

Raspberry Pi, Home Assistant controlled On Air Light

On air light


This project was to make an On Air light that would automatically come on whenever I'm in a meeting or call on my Mac. This was achieved using Home Assistant, Home Assistant Companion for Mac, Mosquitto, Raspberry Pi, 2 Relay Module and an On Air light.


Setup

Home Assistant, the Home Assistant companion and Mosquitto all need to be installed and configured to connect together. In my case I installed everything on my Mac but it's probably more common to have Home Assistant and Mosquitto co-hosted on another server. The Home Assistant companion is installed from the App Store. It adds your Mac as a Device and within it are sensors for various things including the microphone. This allows you to make automations for when the microphone is active/inactive.


In this automation the "Microphone In Use turned on" trigger is used with an MQTT action to simply publish a payload to a topic:


We're sending "flash" to the "home/onair" topic.


Wiring

The On Air light has a button which cycles through three states: ON, FLASH and OFF. I took it apart and found that it's a simple button that when pressed completes a circuit. Each momentary press/release makes and breaks the circuit. This meant it was pretty easy to achieve this same action with a GPI controlled relay. The orange and yellow wires go from the switch points to the relay module. This is wired up to the Raspberry Pi.




Code

The onair.py script uses two simple libraries to control the GPIO and to work with MQTT: RPi.GPIO and paho-mqtt. It uses the payload received on the topic to advance the on air light's state from whatever it currently is to the desired state. As there are three states: ON, FLASH and OFF, this could mean two "button presses" - to get to OFF from ON you have to go through FLASH.


That's it!

When I start a call in Teams, Zoom, Chime... etc. the "Microphone In Use turned on" trigger fires the "home/onair:FLASH" MQTT message. The Raspberry Pi receives this, advances the state to FLASH which triggers the relay and sets the On Air light flashing. When the call is over the "Microphone In Use turned off" trigger is used to set the On Air light to OFF. 

2013-03-09

Raspberry Pi midi driven solenoid bell

This is completely pointless but a bit of fun I had to share. I've been thinking about hooking my Roland TD9 v-drum kit up to a Raspberry Pi for a while for another project so I bought a really cheap Midi to USB gadget: USB Midi Cable Lead Adaptor

To my surprise this worked out-the-box, nothing to install. I made sure my Raspbian OS was up to date before I started but that was it. I have never done anything with Midi before but I knew it was a simple serial protocol and so assumed I'd be able to open some kind of tty device. In fact the ALSA driver on the OS detects it correctly as a USB-Midi input/ouput device. You can see this by running the amidi command:
pi@raspberrypi ~ $ amidi -l
Dir Device    Name
IO  hw:1,0,0  USB2.0-MIDI MIDI 1
 O  hw:1,0,1  USB2.0-MIDI MIDI 2

There are probably loads of proper Midi tools you can use since it has been discovered correctly by I just wanted to look at the raw bytes coming in. The device node that's been created in the file system can be found in /dev/snd:
pi@raspberrypi ~ $ ls /dev/snd
by-id  by-path  controlC0  controlC1  midiC1D0  pcmC0D0p  seq  timer

So all my program has to do is read bytes from /dev/snd/midiC1D0. To get this to work I didn't need to understand or decode much of the protocol - I'm basically just looking for a sequence when an "instrument" is hit. In this case it is a sequence (in hex) of 99 XX where XX is the instrument code, 26 for snare drum, 24 for kick drum etc. There's a lot more going on but I can ignore the rest apart from a useful continual pulse of FE. This is known as "Active sense" and you get this once every 300ms. I'm using this to switch the GPIO off again as you'll see in the code later. In order to ring the bell you need a quick on/off motion. You can read more about the solenoid bell in my previous post: Raspberry Pi solenoid alarm bell.

Anyway, here's a rather shakey video (sorry) and the code. Enjoy!


import RPi.GPIO as GPIO

inst = {
        '\x26':'snare',
        '\x28':'snare rim',
        '\x30':'tom1',
        '\x32':'tom1 rim',
        '\x2d':'tom2',
        '\x2f':'tom2 rim',
        '\x2b':'tom3',
        '\x3a':'tom3 rim',
        '\x24':'kick',
        '\x1a':'hi-hat rim',
        '\x2e':'hi-hat head',
        '\x2c':'hi-hat close',
        '\x31':'crash head',
        '\x37':'crash rim',
        '\x33':'ride head',
        '\x3b':'ride rim',
        '\x35':'ride bell',
}

f=open('/dev/snd/midiC1D0')

note=False

GPIO_NUM = 18
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_NUM, GPIO.OUT)

while True:
        b = f.read(1)
        if b == '\xfe':
                GPIO.output(GPIO_NUM, False)
        else:
#               if b == '\x40':
#                       print hex(ord(b))
#               else:
#                       print hex(ord(b)),
                if b == '\x99':
                        note=True
                elif note:
                        if b in inst:
                                print inst[b]
                                if b == '\x26':
                                        GPIO.output(GPIO_NUM, True)
                        else:
                                print hex(ord(b))
                        note=False

2013-02-03

Control My Pi - Easy web remote control for your Raspberry Pi projects

*** NOTE: ControlMyPi shutting down ***

I'm launching Control My Pi Beta today. In just a few lines of Python code you can create a control panel for your project accessible over the Internet. No firewall changes to make, no web servers to set up. Pick up your Raspberry Pi, take it to a friends house or work, school, a club, a coffee shop - connect it to 3G and carry it around or put it in your car - it doesn't matter where it is or what network you're on you'll be able to access your control panel on Control My Pi. It's no ordinary control panel either, every viewer establishes a "push" connection to the server so any update sent from your Raspberry Pi is pushed to every open control panel out there in the world.

If you're really proud of your control panel, and you don't mind anyone pressing buttons, you can make your panel public. It will appear as a link on the front page. (I may need to do something about this if this feature becomes very popular!) If you just want to share your panel with a few people send them a copy of your panel URL.

Instructions, FAQ and documentation are available on the web site plus some examples to get started. More write-ups of Control My Pi projects coming up soon from simple GPIO LED projects to 3G GPS bicycle telemetry systems!

I've got a couple of public examples up and running for you to take a look at. Remember to click the "Push status" button on public panels to start the connection if you want to interact with it. (This is done for you automatically on non-public panels).


Here's a video showing a control panel in action. This is taken from the easycontrol.py example available in the download at Control My Pi. In the background is a Nexus 7 tablet showing a zoom in on the control panel at Control My Pi. In the foreground you can see my Raspberry Pi connected to a breadboard with 4 GPIOs. Two for inputs from the buttons and two for outputs to the LEDs. It's a bit hard to see because of my jumper wires but when I push the hardware button the LED's state changes and this is pushed to the web page. Likewise, when I press a software button the same thing happens.



And here's the small Python script to make this happen: hardware inputs, outputs and multi-user, push status, Internet control panel!
from controlmypi import ControlMyPi
import json
import RPi.GPIO as GPIO
import time

JABBER_ID = 'me@my.jabber.domain'
JABBER_PASSWORD = 'password'
SHORT_ID = 'easy1'
FRIENDLY_NAME = 'Control my red and yellow LEDs'
PANEL_FORM = [
             [ ['L','Remote control Raspberry Pi LEDs - status pushed back to this page!'] ],
             [ ['O'] ],
             [ ['L','Yellow'],['B','18 on','on'],['B','18 off','off'],['S','GPIO18','-'] ],
             [ ['L','Red'],['B','23 on','on'],['B','23 off','off'],['S','GPIO23','-'] ],
             [ ['C'] ],
             ]

GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)
GPIO.setup(23, GPIO.OUT)
GPIO.setup(24, GPIO.IN)
GPIO.setup(25, GPIO.IN)

status = {18:False, 23:False}

def switch_led(n, state):
    if status[n] != state:
        GPIO.output(n, not state) #Low to glow!
        conn.update_status({'GPIO'+str(n): 'on' if state else 'off'})    
        status[n] = state

def on_control_message(conn, key, value):
    tokens = key.split(' ')
    number = int(tokens[0])
    state = tokens[1]
    if number in [18,23] and state in ['on','off']:
        switch_led(number, state == 'on')

def main_loop():
    switch_led(18,True)
    switch_led(23,True)
    debounced_one = True
    debounced_two = True
    while True:
        state_one = GPIO.input(24)
        state_two = GPIO.input(25)
        if state_one and debounced_one:
            switch_led(18, not status[18])
            debounced_one = False
        elif not state_one:
            debounced_one = True
            
        if state_two and debounced_two:
            switch_led(23, not status[23])
            debounced_two = False
        elif not state_two:
            debounced_two = True

        time.sleep(0.1)    


conn = ControlMyPi(JABBER_ID, JABBER_PASSWORD, SHORT_ID, FRIENDLY_NAME, PANEL_FORM, on_control_message)
if conn.start_control():
    try:
        main_loop()
    finally:
        conn.stop_control()
else:
    print("FAILED TO CONNECT")


Finally, here's a snapshot of the live dashboard of my 3G GPS bicycle telemetry system. My current position, heading, speed, height and X,Y and Z accelerations are pushed to Control My Pi and then out to anyone watching!

2012-11-15

Raspberry Pi solenoid alarm bell

Controlling components on a separate power supply from the Raspberry Pi. This project uses a PIR sensor, an IR range sensor, a solenoid, a reception bell and some bright LEDs to form a proximity alarm system.

I'm not sure if there's any real practical use for what I've made here but some of the techniques, both with the electronics and the software may be of interest. It was fun making it anyway!

Hardware:



The first video is the main event. The IR range sensor is set to ring the bell when it detects an object within 40 cm. In the second video you can see the PIR which switches on the bright LEDs when it detects motion nearby. The idea is that as you approach the lights come on and if you get really close the bell rings.

Below is a breadboard diagram and photos. There are a few things to note:

  • I'm using a split rail breadboard which means I can keep the 3.3v from the Raspberry Pi separated from the 5v from the breadboard power supply. You'll notice that the ground is common which I have achieved by bridging over the split on the ground rail.
  • I've used a rectifier diode on the fet for the solenoid to avoid reverse spike problems.
  • I adapted this Arduino PIR guide for the Raspberry Pi






The code:

The mcp3008.py unit is for talking to the analog to digital chip with the same name. I have included routines in here now for reading the raw 10-bit number, a voltage relative to 3.3V and the distance in cm from the range sensor. Read these articles for more info: Raspberry Pi hardware SPI analog inputs using the MCP3008 , Raspberry Pi distance measuring sensor with LCD output.

Bell.py is a very simple program to read the status from the sensors and activate the bell or LEDs accordingly. The main loop sleeps for 0.1 seconds each iteration so we're checking the sensor status about 10 times a second which is fast enough. Once an alarm has been activated it will not be activated again for about 3 seconds. This has two benefits. Firstly it stops the bell constantly ringing when you're within 40cm. Secondly the PIR sensor takes a couple of seconds to settle after each motion detection. During this period the sensor fluctuates between low and high so if you just have the LEDs triggered directly from the True/False state of the GPIO pin then you'll have flickering lights. An important difference in the code is that we switch the LEDs on when motion is detected and leave them on for the 3 second window. For the solenoid though, we set it high to hit the bell but then on the next iteration 0.1 seconds later we set it low again to retract the pin. This gives a nice "flick" motion onto the bell pusher and it avoids everything getting hot!

Shameless plug:
Just before you look at the code I wanted to promote an interesting project that a colleague is working on: The PiXi board.
The PiXi-200 is designed to expand the I/O capabilities of the Raspberry Pi and provide a low-cost means of introducing the user to the world of digital electronics and FPGA technology while at the same time give the 'Pi enthusiast' a little more to play with or even provide a basis for some product development.

I'm looking forward to getting my hands on this but the project needs some support to get off the ground. So please head on over to Astro Designs and register your interest.

Here's the code:


2012-07-21

Raspberry Pi 7 segment displays

This post shows how to drive two 7 segment displays from 6 gpio pins on the Raspberry Pi without a continual refresh loop. The software is simple with very low cpu usage - only processing when you want to change the displayed numbers. The electronics are pretty easy too (I'm a novice myself!) It's also a fun way to learn about BCD (Binary-coded decimal) and latches.

Hardware

To put this together I've used:
Basically you take 6 gpio pins through current limiting resistors and then on to the second breadboard. You'll see from the picture below that I have added an LED as a confidence indicator for each gpio pin. I've labelled these in the picture for the latches and data lines that then need to be wired up to the HEF4543Bs:
If you look at the data sheet for the HEF4543B you'll see that the labels I've put on this picture match. These  labels are also used in the software later.
  • LD = Latch disable (for first digit)
  • DD = 8 (BCD bit)
  • DC = 4 (BCD bit)
  • DB = 2 (BCD bit)
  • DA = 1 (BCD bit)
  • LD2 = Latch disable (for second digit)
The gpio pins for the latches are then connected directly to the two ICs, The data pins are connected in parallel to both ICs:
Now follow the data-sheets for the ICs and the displays to connect the IC output pins to the 7 segment displays. The phase input (PH) and the blanking input (BI) should both be connected to ground. I left a longer loop of wire on the blanking input as I was considering using it to blank the display but this would have used another gpio pin. Instead, if you check the data-sheet, you'll find that if you set the data to a decimal number higher than 9 it will blank the display anyway.
Note, this is all 3.3v and I've used another couple of 220ohm resistors to limit the current into the ICs.

Software

To set a number on the display you follow this sequence:
  1. Keep what's on the display now - latch disable = off
  2. Set the data pins for the required number
  3. Disable the latch to take the value to the display - latch disable = on
  4. For timing purposes wait for a short period
  5. Keep what's on the display now - latch disable = off
The Python below counts from 0 to 99 in a continuous loop as seen in the video above.
'''
Created on 7 Jul 2012

@author: Jeremy

numdisplay - counts from 0 to 99 in a loop using two 7 segment displays

Read the blog entry at http://jeremyblythe.blogspot.com for more information
'''
import time
import RPi.GPIO as GPIO


LD = 13
DD = 12
DC = 11
DB = 15
DA = 16
LD2 = 18


GPIO.setup(LD, GPIO.OUT)
GPIO.setup(DD, GPIO.OUT)
GPIO.setup(DC, GPIO.OUT)
GPIO.setup(DB, GPIO.OUT)
GPIO.setup(DA, GPIO.OUT)
GPIO.setup(LD2, GPIO.OUT)

def write_gpo(pin,state):
    GPIO.output(pin,state)

last_tens = None
    
def write(tens,units):
    global last_tens
    if last_tens != tens:
        #tens
        #keep what's on the display now - latch
        write_gpo(LD,False)
        write_digit(tens) 
        #disable the latch to take the value to the display
        write_gpo(LD,True)
        time.sleep(0.1)
        #keep what's on the display now - latch
        write_gpo(LD,False)
        last_tens = tens

    #units
    #keep what's on the display now - latch
    write_gpo(LD2,False)
    write_digit(units) 
    #disable the latch to take the value to the display
    write_gpo(LD2,True)
    time.sleep(0.1)
    #keep what's on the display now - latch
    write_gpo(LD2,False)
         
    
def write_digit(digit):    
        if digit == None:
            #blank
            write_gpo(DD,True)
            write_gpo(DB,True)
        else:
            if digit & 8 > 0:
                write_gpo(DD,True)
            else:
                write_gpo(DD,False)
            
            if digit & 4 > 0:
                write_gpo(DC,True)
            else:
                write_gpo(DC,False)
            
            if digit & 2 > 0:
                write_gpo(DB,True)
            else:
                write_gpo(DB,False)
                
            if digit & 1 > 0:
                write_gpo(DA,True)
            else:
                write_gpo(DA,False)

if __name__ == '__main__':
    t = 0
    u = 0
    while True:
        u=u+1
        if u > 9:
            t=t+1
            u = 0
        if t > 9:
            t = 0
        write(t,u)

        
On my live site (http://jerbly.uk.to/picam) you can enter a number that you want to send to the display. Here's the code to achieve that using the Flask web framework:

'''
Created on 8 Jul 2012

@author: Jeremy

numweb - writes the incoming 2 digit number to a pair of 7 segment displays

Read the blog entry at http://jeremyblythe.blogspot.com for more information
'''
import numdisplay
from datetime import datetime

from flask import Flask
app = Flask(__name__)

@app.route("/num/")
def num(number=None):
    if number == None:
        numdisplay.write(None, None)
    else:
        n = number % 100
        u = n % 10
        t = (n-u) / 10
    numdisplay.write(t, u)
    log(number)
    return "OK"

def log(number):
    with open('/tmp/numweb.log','a') as f:
        f.write('%s - %s\n' % (str(datetime.now()),number))

if __name__ == "__main__":
    app.run('0.0.0.0',8092)

This uses numdisplay.py and adds a very simple web interface. Run this up and go to http://{ip address}:8092/num/{number}. e.g. http://192.168.0.2:8092/num/12 to display 12.

Kits

I'm considering putting some kits together with the resistors, ICs and displays for people to buy if there's enough interest. It may turn out to be more cost effective if I buy some in bulk and then sell on to you. Let me know if you would be interested by leaving a comment below. Thanks.

2012-07-02

Raspberry Pi GPIO and Motion

This post shows how I've set up Motion with the Raspberry Pi GPIOs to indicate motion detection and video production on LEDs and a push button snapshot control. As part of this project I have created a Python service to allow easy scripted control of the GPIOs. This allows you to set up commands you want to run on GPIs and simple command line access to set GPOs high, low or flash. If you're new to these blog posts take a look at: Battery powered, Wireless, Motion detecting Raspberry Pi and Motion Google Drive Uploader and Emailer.

The electronics

I used the Electronic Starter Kit for Raspberry Pi plus a few extra jumper wires to give me 3 LEDs to control and two push button inputs:

The circuit uses the "low to glow" principle - set the GPO low to light the LED. I won't repeat a load of GPIO information available elsewhere, here's a very good article to read: Getting Started with Raspberry Pi GPIO and Python. I'm using 3.3v for the positive rail, pins 11, 12 and 13 for the red, yellow and green LEDs and 7 and 22 for the two buttons.

Flashing an LED from Motion

Motion has a number of configuration options which allow you to run shell commands when specific events occur. For example on_motion_detected, defined as Command to be executed when a motion frame is detected. The Motion web interface allows you to change all of these on the fly without restarting the service - great for testing. The on_motion_detected event does not have a sibling event like "on_motion_not_detected" so you can't easily switch an LED on on the first event and off on the second. Therefore you have to write a little bit of code to light the LED, wait for a short period and then switch it off. This is a common thing to want to do - a momentary indicator.

Another common thing to want to do is to flash an LED continuously, this gives you 3 indications for each LED: On, Off and Flash. But flashing can only be achieved by repeatedly setting the GPO high and low. I wanted to flash the red LED while Motion is in the "event window" this is defined by two events:
  • on_event_startCommand to be executed when an event starts. An event starts at first motion detected after a period of no motion defined by gap.
  • on_event_end: Command to be executed when an event ends after a period of no motion. The period of no motion is defined by option gap.

Taking a Motion snapshot

There are a few controls available for Motion through the action section of its web interface. The snapshot action captures a jpeg and creates a symlink to it called lastsnap.jpg. I then show this snapshot on my web site  (http://jerbly.uk.to/picam) next to the live feed. You can trigger a snapshot from the command line using curl like this:
curl http://localhost:8080/0/action/snapshot
All I needed was a way to run commands when a button is pressed. I needed to capture the GPI state going from False normally to True briefly (while the button is down) and then back to False again when it's released.
So, instead of writing more and more little scripts to handle controlling the GPIOs I wrote a service to do it for me. This allows me to echo simple instructions like "red flash" and "red high" to the service and it takes care of continuously flashing the GPO or setting it high etc.

My GPIO service

The code is available on GitHub here: Jerbly Raspberry Pi code. Download gpioservice.py and the example config file gpioservice.cfg to somewhere on your Raspberry Pi. Then, before you run it up, change the config file. This is very important as the config file determines which pins should be set as inputs and outputs and it also sets the initial mode of the pin (e.g. high). So you want to get this right before you start it up and blow something up!

The service gives you the follow features:
  • Name to pin mapping: so you can send "red flash" or "motor low" commands
  • Common modes: 
    • High: set the pin high
    • Low: set the pin low
    • Flash: flip the pin between high and low every 0.25 second
    • LowShot: set the pin low for 0.25s then leave it high
    • HighShot: set the pin high for 0.25s then leave it low
  • Commands are sent through a named pipe (fifo) which has write permissions for all, this means you don't have to be root to control the GPOs!
  • Run shell commands for button presses on GPIs
The code in gpioservice.py uses two secondary threads alongside the main thread. The main thread is a blocking reader of the named pipe, it just parses the input on the pipe and sets the mode on the GPO objects. The GPO thread sleeps for 0.25s each loop, inverts the current flash state and then runs through each GPO object calling its action method. The action method determines, based on the mode, whether to set the pin high or low. It's this that means you can have flash, lowshot and highshot. The GPI thread sleeps for 0.1s each loop and then calls the action method on all the configured GPI objects. The action method reads the input and uses a state variable to trigger the command to run when the state first changes from False to True and not to continuously call the command if the button is held down. (Other modes may be added to this in the future so you can have "button held" operations).

Setting it all up

My Motion and GPIOs Raspberry Pi is set up to do this:
  • Flash the red LED while in the "event window" (see above)
  • Light the yellow LED when Motion starts making a video
  • Light the green LED when it's finished making the movie and my Motion Google Drive Uploader and Emailer is running.
  • When the upload has finished switch off the yellow and green LEDs
  • When a Motion frame is detected briefly flash the green LED (lowshot)
  • When the first button is pressed take a snapshot
  • When the second button is pressed just lowshot the green LED for testing
Here's the gpioservice.cfg file:
[gpos]
fifo = /tmp/gpopipe

[gpo_pins]
# name = pin, initial_mode {high/low/flash}
red = 11, high
yellow = 12, high
green = 13, high

[gpi_pins]
# name = pin, command
motion_snapshot = 22, curl http://localhost:8080/0/action/snapshot
green_flash = 7, echo "green lowshot" >> /tmp/gpopipe

[options]
debug = False

Note that the fifo is configured at the top. The service takes care of creating this and setting the permissions. Then in the gpi_pins section you can see how to briefly flash the green LED when the button is pressed on pin 7.

Here's the Motion events section from my Motion config file:
# Command to be executed when an event starts. (default: none)
# An event starts at first motion detected after a period of no motion defined by gap
on_event_start echo "red flash" >> /tmp/gpopipe

# Command to be executed when an event ends after a period of no motion
# (default: none). The period of no motion is defined by option gap.
on_event_end echo "red high" >> /tmp/gpopipe

# Command to be executed when a picture (.ppm|.jpg) is saved (default: none)
# To give the filename as an argument to a command append it with %f
; on_picture_save value

# Command to be executed when a motion frame is detected (default: none)
on_motion_detected echo "green lowshot" >> /tmp/gpopipe

# Command to be executed when motion in a predefined area is detected
# Check option 'area_detect'. (default: none)
; on_area_detected value

# Command to be executed when a movie file (.mpg|.avi) is created. (default: none)
# To give the filename as an argument to a command append it with %f
on_movie_start echo "yellow low" >> /tmp/gpopipe

# Command to be executed when a movie file (.mpg|.avi) is closed. (default: none)
# To give the filename as an argument to a command append it with %f
on_movie_end /etc/motion/movie_end %f

# Command to be executed when a camera can't be opened or if it is lost
# NOTE: There is situations when motion doesn't detect a lost camera!
# It depends on the driver, some drivers don't detect a lost camera at all
# Some hang the motion thread. Some even hang the PC! (default: none)
; on_camera_lost value

Note that the on_movie_end setting calls a script that I have placed in /etc/motion to handle controlling the LEDs and uploading to Google Drive:
#!/bin/sh
echo "green low" >> /tmp/gpopipe
/root/py/jerbly/src/uploader.py /etc/motion/uploader.cfg $1
echo "green high" >> /tmp/gpopipe
echo "yellow high" >> /tmp/gpopipe

In Action

This first video shows the green lowshot by pressing the button connected to pin 7:


This second video shows the sequence when I trigger a motion event on the camera and start a recording. Note just after I remove the card from the camera there are two motion frame flashes on the green LED too.