$20 Door Chime

Built a $20 door chime using a zigbee radio from a Cree Connected Bulb ($15) and a Chinese Arduino ($5).

The code is just a combination of the State Change and toneMelody example sketches. If anyone wants it I can post exactly what I was using, and I’ll probably make up a circuit diagram so it can be easily replicated by someone w/o electronics experience.

7 Likes

ToDo list:
Swap out piezo for small speaker
Replace the Arduino Mega with a nano or pro mini
Change melody to something cooler, maybe Zelda theme
Build it all into a small enclosure with 5v USB power plug

I might also do a battery powered version that runs on AAs if I can think of a good use case. What else could we use this for?

Very nice @drbudro , can’t wait to build something with hackable devices, it’s really cheap compare to ST arduino shield.

2 Likes

Really, really cool @drburns
Might be my first hacking project with SmartThings. An affordable and reliable door chime would be a great addition to our available devices.

Thanks and well done! Looking forward to seeing more details and future iterations!

@drbudro

So, I have followed your lead and ripped apart a Cree Connected Bulb to get at the Zigbee board. I have it powered via a 3.3V source on Pin 2, with Ground on Pin 1 per your instructions. When I check the voltage from pin 4, and get 3.3V with the “bulb” on in SmartThings. So far, so good. When I check the voltage from Pin 3 (PWM) I get 0V with the bulb on and the dimmer at 99%. With the bulb on and the dimmer at 7%, I get ~3.2V.

So, this leads me to my question… How do you have the wiring, shown in your video, configured for Pin 3 (PWM) to change the illumination of the LED? Are your two LEDs wired identically, or opposite of one another?

The one thing I was really hoping for was that the Cree bulb sent out some sort of ZIgbee signal when it powers on. The GE bulbs do this. It would have been nice to simply power the Zigbee board off, and then on to let ST know that something has happned. This would provide bi-directional communications. Oh well! I guess we can’t have everything for $15!

Thanks,

Dan

1 Like

Good catch, I was also seeing the bulb dim as I moved the ST brightness up to 99%. On pin 3 I actually had to reverse the polarity of the LED. From the zigbee board I went to the resistor and then to the cathode side of the LED. The anode is plugged into the 3.3v rail coming from the arduino (so yeah, opposite the wiring for the LED on pin 4).

1 Like

Yeah, I saw the updated device type for the GE bulbs that detect status when first powered on but to my knowledge the Cree bulbs don’t do this. I’m not sure if that was just left out of the device type out if the Cree hardware functions differently.

I originally tried hacking up aGE Link bulb but all the components are potted in silicone and looked like a nightmare to work on. The only wire that was easily reachable was the LED power and it was coming in at 25vDC.

I noticed that the official SmartThings Cree Bulb Device Type code has the same bug as the GE Link Bulb code. Neither can properly dim under 7% properly. I fixed this on my GE Link bulbs by using a Community written Device Type. So, I figured why not try the same change on the Cree Bulb Device Type. The fix works great and now I can adjust the PWM output down less than 7% properly. I am working on a sketch for an Arduino Nano to monitor both the On/Off signal, as well as properly measure the PWM value (from 0 to 100). This will allow a user to interpret the 0 to 100 value range as special commands (e.g. perhaps a motor speed, or a relay number to turn on, or a temperature target, or which doorbell chime to play, or whatever one can think of!) I’ll post my sketch as an example once I finish cleaning it up a little more.

Here is the improved Cree Bulb Device Type groovy code:

/**
 *  Cree Bulb
 *
 *  Copyright 2014 SmartThings
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 *
 *	Change History
 *  
 *  Change 1:	2015-04-04 (ogiewon with credit to jscgs350, Sticks18's work on GE Link Bulb)
 *				Added fix for dimming below 7%
 */
 
metadata {
	definition (name: "Cree Bulb", namespace: "smartthings", author: "SmartThings") {

		capability "Actuator"
        capability "Configuration"
		capability "Refresh"
		capability "Switch"
		capability "Switch Level"

		fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0000,0019"
	}

	// simulator metadata
	simulator {
		// status messages
		status "on": "on/off: 1"
		status "off": "on/off: 0"

		// reply messages
		reply "zcl on-off on": "on/off: 1"
		reply "zcl on-off off": "on/off: 0"
	}

	// UI tile definitions
	tiles {
		standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
			state "off", label: '${name}', action: "switch.on", icon: "st.switches.light.off", backgroundColor: "#ffffff"
			state "on", label: '${name}', action: "switch.off", icon: "st.switches.light.on", backgroundColor: "#79b821"
		}
		standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
			state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
		}
		controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 3, inactiveLabel: false) {
			state "level", action:"switch level.setLevel"
		}
		valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") {
			state "level", label: 'Level ${currentValue}%'
		}
		

		main(["switch"])
		details(["switch", "level", "levelSliderControl", "refresh"])
	}
}

// Parse incoming device messages to generate events

def parse(String description) {
	log.trace description
	if (description?.startsWith("catchall:")) {
		def msg = zigbee.parse(description)
		log.trace msg
		log.trace "data: $msg.data"
        
        if(description?.endsWith("0100") ||description?.endsWith("1001"))
        {
        	def result = createEvent(name: "switch", value: "on")
            log.debug "Parse returned ${result?.descriptionText}"
            return result
        }
        
        if(description?.endsWith("0000") || description?.endsWith("1000"))
        {
        	def result = createEvent(name: "switch", value: "off")
            log.debug "Parse returned ${result?.descriptionText}"
            return result
        }
	}
    
    
   if (description?.startsWith("read attr")) {
   		
        log.debug description[-2..-1]
        def i = Math.round(convertHexToInt(description[-2..-1]) / 256 * 100 )
        
		sendEvent( name: "level", value: i )
    }
    
	
}

def on() {
	log.debug "on()"
	sendEvent(name: "switch", value: "on")
    
    "st cmd 0x${device.deviceNetworkId} ${endpointId} 6 1 {}"
    }

def off() {
	log.debug "off()"
	sendEvent(name: "switch", value: "off")
    
	"st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {}"
 
}

def refresh() {
    // Schedule poll every 1 min
    //schedule("0 */1 * * * ?", poll)
    //poll()
    
    [
	"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
    "st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0"
    ]
}

def setLevel(value) {
	log.trace "setLevel($value)"
	def cmds = []

	if (value == 0) {
		sendEvent(name: "switch", value: "off")
		cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 6 0 {0000 0000}"
	}
	else if (device.latestValue("switch") == "off") {
		sendEvent(name: "switch", value: "on")
	}

	sendEvent(name: "level", value: value)
    
    //Fix the "Less than 7%" dimming bug.  Code taken directly from the GE Link LED Bulb code
	//def level = new BigInteger(Math.round(value * 255 / 100).toString()).toString(16)
    value = (value * 255 / 100)
    def level = hex(value);
    cmds << "st cmd 0x${device.deviceNetworkId} ${endpointId} 8 4 {${level} 0000}"

	//log.debug cmds
	cmds
}

def configure() {

	String zigbeeId = swapEndianHex(device.hub.zigbeeId)
	log.debug "Confuguring Reporting and Bindings."
	def configCmds = [	
  
        //Switch Reporting
        "zcl global send-me-a-report 6 0 0x10 0 3600 {01}", "delay 500",
        "send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1000",
        
        //Level Control Reporting
        "zcl global send-me-a-report 8 0 0x20 5 3600 {0010}", "delay 200",
        "send 0x${device.deviceNetworkId} ${endpointId} 1", "delay 1500",
        
        "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 6 {${device.zigbeeId}} {}", "delay 1000",
		"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 8 {${device.zigbeeId}} {}", "delay 500",
	]
    return configCmds + refresh() // send refresh cmds as part of config
}

def uninstalled() {

	log.debug "uninstalled()"
		
	response("zcl rftd")
 
}

private getEndpointId() {
	new BigInteger(device.endpointId, 16).toString()
}



private hex(value, width=2) {
	def s = new BigInteger(Math.round(value).toString()).toString(16)
	while (s.size() < width) {
		s = "0" + s
	}
	s
}

private Integer convertHexToInt(hex) {
	Integer.parseInt(hex,16)
}

private String swapEndianHex(String hex) {
    reverseArray(hex.decodeHex()).encodeHex()
}

private byte[] reverseArray(byte[] array) {
    int i = 0;
    int j = array.length - 1;
    byte tmp;
    while (j > i) {
        tmp = array[j];
        array[j] = array[i];
        array[i] = tmp;
        j--;
        i++;
    }
    return array
}
2 Likes

Next on my list was going to be exactly that. I was hoping to use different dimmer values to play chimes or even pre recorded messages depending on the door that is opened. I’m not sure what kind of “resolution” we’d be able to get between ST and the voltages on pin 3, but I’d be happy to get at least 10 distinct values with high accuracy.

1 Like

Here’s another project that makes use of the Cree Bulb’s Zigbee board.

2 Likes

I would appreciate anything you can give me on this project; components, scripts, diagrams, etc… I’m new to this type of project but want to learn and I need a few door chimes, LOL.

1 Like

ogiewon has the groovy code above, but if you want just enough to get started with an on/off/dimmable light, here’s what you need:

Cree Connected Bulb $15

If you’re just getting started out with electronics, I’d recommend getting a kit so you have assorted LEDs, resistors, potentiometers, and a breadboard with jumper wires.

This is a basic kit for only $10, but it doesn’t come with a buzzer. You could spend $20 and get a bigger kit that comes with an arduino and some additional stuff to play with like an IR remote and LED matrix.

Arduino (I went with a cheap Chinese nano clone) $2.50

5v Piezo Buzzer 20 for $2.00, or pull one off an old PC

LEDs and Resistors (if you don’t end up buying a kit)

This is exactly what I had wired up in my demo and allows you to play the chime and have the lights come on:

The code is just two sample sketches; the state change sketch to detect when pin4 on the zigbee board goes high, and the chime sketch to output a PWM signal to the buzzer:

/*

Quick sketch as PoC for viability of using Cree Connected LED's zigbee board to drive other devices (a speaker in this case).
Modified by Buddy Crotty - March 2015
version 0.1.2

Completely copied from Tom Igoe's example sketches: 

  State change detection (edge detection)
  toneMelody

This example code is in the public domain.

 http://arduino.cc/en/Tutorial/ButtonStateChange
 http://arduino.cc/en/Tutorial/Tone

Zigbee board from Cree Connected LED
   pin 1 ground 
   pin 2 3.3Vin
   pin 3 PWM Out
   pin 4 Digital Out
   
 circuit:
   * 8-ohm speaker on digital pin 8
   * Pin 2 connected to zigbee board pin 4
 */

#include <Tone.h>
#include "pitches.h"

// this constant won't change:
const int buttonPin = 2;    // the pin that the pushbutton is attached to
const int speakerPin = 8;       // the pin that the speaker is attached to

// Variables will change:
int buttonPushCounter = 0;   // counter for the number of button presses
int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button


// notes in the melody (7nationArmy):
int melody[] = {
  NOTE_GS2, NOTE_GS2, NOTE_B2, NOTE_GS2, NOTE_FS2, NOTE_E2, NOTE_DS2
};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
  4, 8, 8, 8, 8, 3.5, 3
};

void setup() {
  // initialize the button pin as a input:
  pinMode(buttonPin, INPUT);
  // initialize the LED as an output:
  pinMode(speakerPin, OUTPUT);
}


void loop() {
  // read the pushbutton input pin:
  buttonState = digitalRead(buttonPin);

  // compare the buttonState to its previous state
  if (buttonState != lastButtonState) {
    // if the state has changed, increment the counter
    if (buttonState == HIGH) {
      // if the current state is HIGH then the button
      // wend from off to on:
      buttonPushCounter++;
      
      //play tone
      // iterate over the notes of the melody:
    for (int thisNote = 0; thisNote < 8; thisNote++) {

    // to calculate the note duration, take one second
    // divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 10000 / noteDurations[thisNote];
    tone(speakerPin, melody[thisNote], noteDuration);

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * .32;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    noTone(speakerPin);
    }
    }
    else {
      // if the current state is LOW then the button
      // went from on to off:
      Serial.println("off");
    }
    // Delay a little bit to avoid bouncing
    delay(50);
  }
  // save the current state as the last state,
  //for next time through the loop
  lastButtonState = buttonState;


  // turns on the Chime every two state changes (only when the door opens)
  // the modulo function gives you the remainder of
  // the division of two numbers:
  if (buttonPushCounter % 2 == 0) {
    digitalWrite(speakerPin, HIGH);
  } else {
    digitalWrite(speakerPin, LOW);
  }

}
3 Likes

Has anyone tried hacking the ZigBee module out of a GE Link bulb?

Should be nearly entirely the same, no? Pricing is about the same these days, so I don’t know if there is a preferred vendor to use for this hack…

The GE Link bulb’s zigbee board is “potted” with a difficult to remove material. This makes it nearly impossible to remove without damaging it.

2 Likes

Can confirm, I had actually tried going this route initially with no luck.

1 Like

I wanted to see if batteries can be used to power the cree bulb guts that you pulled out.
I wanted to basically do the same thing you did but I wanted to use an ESP2866 vs a full fledged Arduino.
I am looking to build a door bell that can light up as well as play sounds.
I want to use low power z-wave to listen for the doorbell being pushed when it is, to power on the ESP8266 which would be in deep sleep mode to perform the other needed functions of the sound playing and the light lighting up. The ESP8266 would also be battery powered, but it would only be powered on when the z-wave cree module receives an ‘on’ instruction.

1 Like

Did you find a solution to using battery power? I’m wanting to connect the hacked board to a lantern and wireless candles. Any advice?

How do I remove the board from the larger Cree connected board just heat up the wider and remove? Sorry this is my very first project.

Great work!

I am new to the Arduino environment, but have some programming experience. I cannot get this sketch to compile. I continue to get the errors listed below. I’m not too sure how to decipher. I have copied this sketch from different sources only to get the same errors.

Any help would be most appreciated…

************* Copied Error listing ***********
Arduino: 1.8.9 (Windows 10), Board: “Arduino Nano, ATmega328P (Old Bootloader)”

Build options changed, rebuilding all
C:\Users\JTR\Documents\Arduino\My Sketches\CreeLightbulbNotify\CreeLightbulbNotify.ino:49:1: warning: narrowing conversion of ‘3.5e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]

};

^

C:\Users\JTR\Documents\Arduino\My Sketches\CreeLightbulbNotify\CreeLightbulbNotify.ino: In function ‘loop’:

C:\Users\JTR\Documents\Arduino\My Sketches\CreeLightbulbNotify\CreeLightbulbNotify.ino:78:54: warning: iteration 7 invokes undefined behavior [-Waggressive-loop-optimizations]

 int noteDuration = 10000 / noteDurations[thisNote];

                                                  ^

C:\Users\JTR\Documents\Arduino\My Sketches\CreeLightbulbNotify\CreeLightbulbNotify.ino:73:37: note: containing loop

 for (int thisNote = 0; thisNote < 8; thisNote++) {

                                 ^

Tone.cpp.o (symbol from plugin): In function `timer0_pin_port’:

(.text+0x0): multiple definition of `timer0_pin_port’

libraries\Tone-master\Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here

Tone.cpp.o (symbol from plugin): In function `timer0_pin_port’:

(.text+0x0): multiple definition of `timer0_pin_mask’

libraries\Tone-master\Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here

Tone.cpp.o (symbol from plugin): In function `timer0_pin_port’:

(.text+0x0): multiple definition of `timer1_pin_port’

libraries\Tone-master\Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here

Tone.cpp.o (symbol from plugin): In function `timer0_pin_port’:

(.text+0x0): multiple definition of `timer1_pin_mask’

libraries\Tone-master\Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here

Tone.cpp.o (symbol from plugin): In function `timer0_pin_port’:

(.text+0x0): multiple definition of `timer2_pin_port’

libraries\Tone-master\Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here

Tone.cpp.o (symbol from plugin): In function `timer0_pin_port’:

(.text+0x0): multiple definition of `timer2_pin_mask’

libraries\Tone-master\Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here

Tone.cpp.o (symbol from plugin): In function `timer0_pin_port’:

(.text+0x0): multiple definition of `timer0_toggle_count’

libraries\Tone-master\Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here

Tone.cpp.o (symbol from plugin): In function `timer0_pin_port’:

(.text+0x0): multiple definition of `timer1_toggle_count’

libraries\Tone-master\Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here

Tone.cpp.o (symbol from plugin): In function `timer0_pin_port’:

(.text+0x0): multiple definition of `timer2_toggle_count’

libraries\Tone-master\Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here

Tone.cpp.o (symbol from plugin): In function `timer0_pin_port’:

(.text+0x0): multiple definition of `__vector_7’

libraries\Tone-master\Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\main.cpp: In function ‘main’:

C:\Users\JTR\Documents\Arduino\My Sketches\CreeLightbulbNotify\CreeLightbulbNotify.ino:78:54: warning: iteration 7 invokes undefined behavior [-Waggressive-loop-optimizations]

 int noteDuration = 10000 / noteDurations[thisNote];

                                                  ^

C:\Users\JTR\Documents\Arduino\My Sketches\CreeLightbulbNotify\CreeLightbulbNotify.ino:73:37: note: containing loop

 for (int thisNote = 0; thisNote < 8; thisNote++) {

                                 ^

collect2.exe: error: ld returned 1 exit status

exit status 1
Error compiling for board Arduino Nano.

This report would have more information with
“Show verbose output during compilation”
option enabled in File -> Preferences.