At some point I’m planning on releasing the source code and the vector files for the Wordclock face. Have still some cleaning up to do. In the mean time: there are several word clock projects out there that use a Raspberry Pi and Python; just search for them.
I’ve been playing around with MicroPython and Home Assistant. MicroPython is a ‘bare-metal’-Python flavor that you can use to program ICs. Home Assistant is a home automation and home control software written in Python 3. It can be hosted on a Raspberry Pi. It enables you to connect a vast amount of different devices: lights, switches, sensors, locks etc.
This is a raw guide on how to make your own smart plug and connecting it to MQTT which is then connected to Home Assistant.
You will need a relay board (single relay), a NodeMCU board, a power adapter for 5V, a case, a button, some wire and a 1k resistor.
Connect the devices according to this schema (WARNING: Don’t do this if you’re not comfortable handling mains power!). Don’t forget earthing (it’s not on the diagram):
Flash the MicroPython firmware to the NodeMCU board. Put the ‘relay_mqtt.py‘ file on it as main.py. That way it will be run when the device boots up. Adjust the code to connect to your network and MQTT server first.
See the Home Assistant documentation for how to integrate the MQTT part with Home Assistant.
The nice thing is: you can press the button to switch the device. The status of the device will update via MQTT and Home Assistant gets a correct status update. You can of course switch the device from within Home Assistant as well.
XMPP (aka Jabber) in combination with OTR is a secure way to chat with others. There are some public servers available, but their popularity centralizes the infrastructure and leaves single points of failure. A recent example is Chaos Computer Club’s Jabber server (jabber.ccc.de) which was down for some days between Christmas and New Years 2014/2015, as a consequence of a DOS attack.
I’ve been setting up a Jabber server of my own on my Raspberry Pi. Here is how you can too:
I’ve been using the same old brushes for more than a year now on our “Profimaster Robot Model 2712”. They are a little misdesigned, as the brushes go under the wheels of the robot, which makes them tare.
More than a year ago I came across a designer lego lamp. I really liked the idea but the price tag was a little hefty. Back then the lamp cost $800 and now it is at $995. But to be honest there is no designer product like this lego lamp that really says “do it yourself” like that!
So I built one myself. I designed the basic form with Lego Digital Designer. The plan can be found here[1]. The base of the lamp uses about 800 pieces. The final build varies from the my first plan, especially in the base where I first planned to hide the foot of the old Ikea lamp. I finally just used the lamps main rod and electrical wire. The arrangement of the bricks varies to give the lamp more structural integrity and was improvised.
The parts were ordered from three different shops on BrickLink. They offered the green I wanted for the lamp at a fraction of the price of the original Lego™ store. The total price for all parts was about 60€. The lamp I had laying around was ‘free’ and the new lamp shade was about 30€. Total of about 100€ – well below the price point of the original.
[1] 2014-11-22, 18:30:
I was asked by the designer Sean Kenney, to remove the LDD plans for the lamp. He argued that the plans would hinder his ability to sell these lamps online.
Although I do not agree with Sean’s argument, that his sales might be influenced by a simple Lego™ scetch for a similar lamp, I’ve respect for the work that went into the lamps Sean designed and did take the plans down for now.
We wanted to paint one wall in our vestibule to add a little more color to the room. As we could not settle on a specific color, I thought of wallpaper. And is there any better theme than OpenStreetMap Data¹?
The theme was settled. It had to be a map, but which tiles? I thought of the beautiful pencil drawn MapBox tiles I saw some time ago:
We wanted some redish color so I had to convert the image with ImageMagick:
recently i finished my latest project: a RFID Door Opener.
besides the fun while hacking it, it improves the security of the door. since it’s not an outside door but the door to the garage, it’s construction does not allow to mount a more secure lock. so locking it from the outside when leaving is a bit bothersome.
the new auto-lock feature, which allows the door to auto-lock itself, saves some time and works around the lazy user, who wouldn’t have locked it.
additionally it is now easy to add or remove the right of access by adding or deleting the RFID’s unique number from the system. revoking someone’s right of access is far more difficult with a normal key.
below you can see the schematics of the setup. the ends marked with Ard X go to a port of the arduino, which is the brain of the lock:
edit: since i’ve been asked what transistors i used for this build i’ll tell you:
as PNP transistor i’m using a ‘mje2955T‘, as NPN i’m using a ‘bc547‘.
it’s important to have the two different transistor types NPN and PNP.
NPN is the one closer to ‘Ard6’ and the PNP is the one switching the 12V power supply for the easydriver. the most important part is that the second transistor, switching the easy driver, will only work if you’re using a PNP transistor. these transistors switch when they get pulled to ground.
/**
* door lock application (c) 2011 Florian Klien
* some code parts are borrowed from different authors ;) thx
*/
#include <NewSoftSerial.h>
#define rxPin 2
#define txPin 3
// door defs
#define DOOR_SENS 3 // analog
#define DRIVER_SWITCH 6
#define DOOR_SW 2 // analog
// motor defs
#define DIR_PIN 7
#define STEP_PIN 8
#define ledIN 5
#define ledOUT 11
NewSoftSerial rfid = NewSoftSerial( rxPin, txPin );
// The tag database consists of two parts. The first part is an array of
// tag values with each tag taking up 5 bytes. The second is a list of
// names with one name for each tag (ie: group of 5 bytes).
char* allowedTags[] = {
"AABBCCDDEE", // Tag 1
"AABBCCDDEE", // Tag 2
};
// List of names to associate with the matching tag IDs
char* tagName[] = {
"User1", // Tag 1
"User2", // Tag 2
};
// software version number:
char* software_version = "1.1";
// Check the number of tags defined
int numberOfTags = sizeof(allowedTags)/sizeof(allowedTags[0]);
int incomingByte = 0; // To store incoming serial data
boolean locked = true;
int door_open = 0; // pseudo digital
boolean prev_status = false;
boolean auto_lock = true;
//unsigned long auto_lock_time = 0;
int auto_lock_delay = 5; // in seconds
int auto_lock_switch_time = 2; // in seconds
int status_led = 0;
unsigned long status_led_time = millis();
boolean status_led_on = false;
unsigned long status_breathe_time = millis();
int breathe_delay = 10;
boolean breathe_up = true;
int breathe_i = 15;
unsigned long last_successful_rfid_read = 0;
int rfid_success_timeout = 5000; // millis
float lock_speed = 1;
/**
* Setup
*/
unsigned long time_door = millis();
unsigned long time_switch = millis();
long debounce = 500;
void setup() {
pinMode(ledIN, OUTPUT);
pinMode(ledOUT, OUTPUT);
digitalWrite(ledIN, HIGH);
digitalWrite(ledOUT, HIGH);
delay(300);
digitalWrite(ledIN, LOW);
digitalWrite(ledOUT, LOW);
pinMode(DRIVER_SWITCH, OUTPUT);
digitalWrite(DRIVER_SWITCH, LOW);
pinMode(DIR_PIN, OUTPUT);
digitalWrite(DIR_PIN, LOW);
pinMode(STEP_PIN, OUTPUT);
digitalWrite(STEP_PIN, LOW);
pinMode(DOOR_SENS,INPUT);
pinMode(DOOR_SW,INPUT);
Serial.begin(9600); // Serial port for connection to host
rfid.begin(9600); // Serial port for connection to RFID module
Serial.println("RFID reader starting up");
delay(1000);
Serial.println("done");
Serial.print("Software Version no: ");
Serial.println(software_version);
Serial.print("door locked: ");
Serial.print(locked, DEC);
Serial.print("\n");
Serial.print("door closed: ");
Serial.print(!door_open, DEC);
Serial.print("\n");
}
/**
* Loop
* non-blocking version of each function!
*/
void loop() {
readRFID();
doorSensor();
doorSwitch();
statusLed();
}
void doorSensor(){
// pseudo digital
door_open = analogRead(DOOR_SENS);
if(millis() - time_door > debounce){
if (door_open <= 500 && prev_status == false){
Serial.println("Door: opened");
prev_status = true;
locked = false;
}else if(door_open > 500 && prev_status == true){
Serial.println("Door: closed");
prev_status = false;
if(auto_lock){
Serial.println("locking door automatically...");
delay(auto_lock_delay*1000);
lock();
locked = true;
}
}
time_door = millis();
}
}
void doorSwitch(){
int dstimer = 0;
int door_switch = analogRead(DOOR_SW); // pseudo digital
if(millis() - time_switch > debounce && door_switch >= 300){
while (analogRead(DOOR_SW) >= 300) {
delay(100);
dstimer++;
}
Serial.println(door_switch,DEC);
Serial.println(dstimer,DEC);
if (dstimer < auto_lock_switch_time*10) { //button has been pressed less than 2 seconds = 1000/100
if (locked == false){
Serial.println("door locked");
locked = true;
lock();
}else if(locked == true){
Serial.println("door unlocked");
locked = false;
unlock();
}
}else {
// auto_unlock off/on
if(auto_lock == true){
Serial.println("auto_lock off");
auto_lock = false;
}else{
Serial.println("auto_lock on");
auto_lock = true;
}
analogWrite(ledIN, 0); // resetting output
}
time_switch = millis();
}
}
// breathing status led on the inside
void statusBreathe(){
if( (status_breathe_time + breathe_delay) < millis() ){
analogWrite(ledIN, breathe_i/1.5);
status_breathe_time = millis();
if (breathe_up == true){
if (breathe_i > 150) {
breathe_delay = 4;
}
if ((breathe_i > 125) && (breathe_i < 151)) {
breathe_delay = 5;
}
if (( breathe_i > 100) && (breathe_i < 126)) {
breathe_delay = 7;
}
if (( breathe_i > 75) && (breathe_i < 101)) {
breathe_delay = 10;
}
if (( breathe_i > 50) && (breathe_i < 76)) {
breathe_delay = 14;
}
if (( breathe_i > 25) && (breathe_i < 51)) {
breathe_delay = 18;
}
if (( breathe_i > 1) && (breathe_i < 26)) {
breathe_delay = 19;
}
breathe_i += 1;
if( breathe_i >= 255 ){
breathe_up = false;
}
}else{
if (breathe_i > 150) {
breathe_delay = 4;
}
if ((breathe_i > 125) && (breathe_i < 151)) {
breathe_delay = 5;
}
if (( breathe_i > 100) && (breathe_i < 126)) {
breathe_delay = 7;
}
if (( breathe_i > 75) && (breathe_i < 101)) {
breathe_delay = 10;
}
if (( breathe_i > 50) && (breathe_i < 76)) {
breathe_delay = 14;
}
if (( breathe_i > 25) && (breathe_i < 51)) {
breathe_delay = 18;
}
if (( breathe_i > 1) && (breathe_i < 26)) {
breathe_delay = 19;
}
breathe_i -= 1;
if( breathe_i <= 15 ){
breathe_up = true;
breathe_delay = 970/2;
}
}
}
}
void statusLed(){
if(auto_lock == false){
status_led = 150;
}else{
// set this to > 0 if you want the status led to blink in default mode
status_led = 0;
if(status_led == 0){
statusBreathe();
}
}
if(millis() - status_led_time >= status_led && status_led != 0){
status_led_on = !status_led_on;
digitalWrite(ledIN,status_led_on);
status_led_time = millis();
}
}
void readRFID(){
byte i = 0;
byte val = 0;
byte checksum = 0;
byte bytesRead = 0;
byte tempByte = 0;
byte tagBytes[6]; // "Unique" tags are only 5 bytes but we need an extra byte for the checksum
char tagValue[10];
if(rfid.available()>0){
if((val = rfid.read()) == 2) { // Check for header
bytesRead = 0;
while (bytesRead < 12) { // Read 10 digit code + 2 digit checksum
val = rfid.read();
Serial.print(val,BYTE);
// Append the first 10 bytes (0 to 9) to the raw tag value
if (bytesRead < 10)
{
tagValue[bytesRead] = val;
}
// Check if this is a header or stop byte before the 10 digit reading is complete
if((val == 0x0D)||(val == 0x0A)||(val == 0x03)||(val == 0x02)) {
break; // Stop reading
}
// Ascii/Hex conversion:
if ((val >= '0') && (val <= '9')) {
val = val - '0';
}
else if ((val >= 'A') && (val <= 'F')) {
val = 10 + val - 'A';
}
// Every two hex-digits, add a byte to the code:
if (bytesRead & 1 == 1) {
// Make space for this hex-digit by shifting the previous digit 4 bits to the left
tagBytes[bytesRead >> 1] = (val | (tempByte << 4));
if (bytesRead >> 1 == 5) { // If we're at the checksum byte,
checksum ^= tagBytes[bytesRead >> 1]; // Calculate the checksum... (XOR)
};
} else {
tempByte = val; // Store the first hex digit first
};
bytesRead++; // Ready to read next digit
}
// Send the result to the host connected via USB
if (bytesRead == 12) { // 12 digit read is complete
tagValue[10] = '\0'; // Null-terminate the string
Serial.print("Tag read: ");
for (i=0; i<5; i++) {
// Add a leading 0 to pad out values below 16
if (tagBytes[i] < 16) {
Serial.print("0");
}
Serial.print(tagBytes[i], HEX);
}
Serial.println();
Serial.print("Checksum: ");
Serial.print(tagBytes[5], HEX);
Serial.println(tagBytes[5] == checksum ? " -- passed." : " -- error.");
// Show the raw tag value
//Serial.print("VALUE: ");
//Serial.println(tagValue);
Serial.print("door_open: ");
Serial.println(door_open,DEC);
// Search the tag database for this particular tag
int tagId = findTag( tagValue );
// Only fire the strike plate if this tag was found in the database
if( tagId > 0 )
{
Serial.print("Authorized tag ID ");
Serial.print(tagId);
if(door_open > 500 && (last_successful_rfid_read + rfid_success_timeout) < millis() ){
Serial.print(": unlocking for ");
Serial.println(tagName[tagId - 1]); // Get the name for this tag from the database
unlock();
last_successful_rfid_read = millis();
delay(2000);
}
} else {
Serial.println("Tag not authorized");
//failSound();
for (int i=0;i<7;i++){ // FIXXME nonblocking version?
digitalWrite(ledOUT, HIGH);
digitalWrite(ledIN, HIGH);
delay(100);
digitalWrite(ledOUT, LOW);
digitalWrite(ledIN, LOW);
delay(80);
}
}
Serial.println(); // Blank separator line in output
}
bytesRead = 0;
}
}
}
/**
* Fire the relay to activate the strike plate for the configured
* number of seconds.
*/
void unlock() {
digitalWrite(ledOUT, HIGH);
digitalWrite(ledIN, HIGH);
delay(100);
// if your stepper is powerful enough you can use full speed
rotateDeg(-800, 0.6);
digitalWrite(ledIN, LOW);
digitalWrite(ledOUT, LOW);
locked = false;
}
void lock(){
digitalWrite(ledOUT, HIGH);
digitalWrite(ledIN, HIGH);
delay(100);
rotateDeg(800, 1);
digitalWrite(ledIN, LOW);
digitalWrite(ledOUT, LOW);
locked = true;
}
void rotate(int steps, float speed){
// power driver
digitalWrite(DRIVER_SWITCH,HIGH);
delay(200);
//rotate a specific number of microsteps (8 microsteps per step) - (negitive for reverse movement)
//speed is any number from .01 -> 1 with 1 being fastest - Slower is stronger
int dir = (steps > 0)? HIGH:LOW;
steps = abs(steps);
digitalWrite(DIR_PIN,dir);
float usDelay = (1/speed) * 250;
for(int i=0; i < steps; i++){
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(usDelay);
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(usDelay);
}
// unpower driver
delay(200);
digitalWrite(DRIVER_SWITCH,LOW);
}
void rotateDeg(float deg, float speed){
// power driver
digitalWrite(DRIVER_SWITCH,HIGH);
delay(200);
//rotate a specific number of degrees (negative for reverse movement)
//speed is any number from .01 -> 1 with 1 being fastest - Slower is stronger
int dir = (deg > 0)? HIGH:LOW;
digitalWrite(DIR_PIN,dir);
int steps = abs(deg)*(1/0.225);
float usDelay = (1/speed) * 250;
for(int i=0; i < steps; i++){
digitalWrite(STEP_PIN, HIGH);
delayMicroseconds(usDelay);
digitalWrite(STEP_PIN, LOW);
delayMicroseconds(usDelay);
}
// unpower driver
delay(200);
digitalWrite(DRIVER_SWITCH,LOW);
}
/**
* Search for a specific tag in the database
*/
int findTag( char tagValue[10] ) {
for (int thisCard = 0; thisCard < numberOfTags; thisCard++) {
// Check if the tag value matches this row in the tag database
if(strcmp(tagValue, allowedTags[thisCard]) == 0)
{
// The row in the database starts at 0, so add 1 to the result so
// that the card ID starts from 1 instead (0 represents "no match")
return(thisCard + 1);
}
}
// If we don't find the tag return a tag ID of 0 to show there was no match
return(0);
}