WWW.DUMAIS.IO

New Home Automation systemLast edited on Aug 5, 2015

DHAS

During the past months, I've been working on my Home Automation System. I did some major refactoring and moved away from the rPi. I am now running my home automation software on a x86-64 server.

The projects is hosted on github at https://github.com/pdumais/dhas.

The system uses a modular architecture now. I like to think of DHAS as a hub for different technologies. Since my home automation system is mix of Insteon devices, ethernet relays and temperature sensors, IP phones, and more, I made a central software that can interface with all those technologies. Each module is responsible for interfacing with one technology. on github, you will find these modules under the src/module folder. Everytime I need to add a new kind of module, I just create the class and it automatically gets instanciated and used. The modules register their own REST callbacks and the REST engine being self-documenting will show the newly added functions when querying the help API. This way, the system doesn't know anything about its modules. It only knows that it is running modules. So adding new modules becomes very easy because the work is isolated from the rest of the system.

The most simple module to look at is the Weather Module.

Arduino

Since I am not using the rPi anymore, I needed to find a way to get GPIOs on the server. So I bought an Arduino Leonardo and made a very simple firmware that presents the device as a CDC device using the LUFA library. The source code for the firmware is here:

main.c

#include "usblib.h"
#include 
#include 

#define STABLE 100

uint8_t currentData = 0;

/*
Sent byte to host:
bit     Arduino pin     AVR Pin
0           2               PD1
1           4               PD4
2           7               PE6
3           8               PB4
4           12              PD6


relays:
    'a' -> arduino 10 -> PB6   ; 'a' = ON, 'A' = off
    'b' -> arduino 11 -> PB7   ; 'b' = ON, 'B' = off
to test the live stream: cat /dev/ttyACM0 | xxd -c1
*/

void setCurrentData(uint8_t data)
{
    currentData = data;
}

void sendCurrentData()
{
    SendCDCChar(currentData);
}

int main(void)
{
    uint8_t pb,pd,pe;
    uint8_t newData = 0;
    uint8_t lastData = 0;
    char stabilizerCount = -1;

    InitCDC();
    sei();

    // Set pins as pullups
    PORTB |= ((1<<4));
    PORTD |= ((1<<1)|(1<<4)|(1<<6));
    PORTE |= ((1<<6));
    // Set pins as input
    DDRB &= ~((1<<4));
    DDRD &= ~((1<<1)|(1<<4)|(1<<6));
    DDRE &= ~((1<<6));

    // set PB6 and PB6 as output
    DDRB |= (1<<6)|(1<<7);
    PORTB |= (1<<6)|(1<<7); // initially off (high = off)


    uint8_t receivedChar;
    while (1)
    {
        pb = PINB;
        pd = PIND;
        pe = PINE;

        newData = ~(((pd>>1)&1)|(((pd>>4)&1)<<1)|(((pe>>6)&1)<<2)|(((pb>>4)&1)<<3)|(((pd>>6)&1)<<4));
        newData &= 0x1F; // clear 3 top bits since we don't use them

        if (GetCDCChar(&receivedChar))
        {
            if (receivedChar == '?')
            {
                sendCurrentData();
            }
            else if (receivedChar == 'A')
            {
                PORTB |= (1<<6);
            }
            else if (receivedChar == 'B')
            {
                PORTB |= (1<<7);
            }
            else if (receivedChar == 'a')
            {
                PORTB &= ~(1<<6);
            }
            else if (receivedChar == 'b')
            {
                PORTB &= ~(1<<7);
            }
            
        }

        // debounce
        if (lastData != newData)
        {
            lastData=newData;
            stabilizerCount = STABLE;
        }
        if (stabilizerCount>0) stabilizerCount--;
        if (stabilizerCount==0)
        {
            stabilizerCount=-1;
        
            if (currentData != newData)
            {
                setCurrentData(newData);
                sendCurrentData();
            }
        }

        CDCWork();
    }
}

usblib.c

#include "usblib.h"
#include 
#include 
#include 
#include 
#include "Descriptors.h"
#include 
#include 
#include 
#include 
#include 

USB_ClassInfo_CDC_Device_t VirtualSerial_CDC_Interface =
{
.Config = {
    .ControlInterfaceNumber = INTERFACE_ID_CDC_CCI,
    .DataINEndpoint = {
        .Address = CDC_TX_EPADDR,
        .Size = CDC_TXRX_EPSIZE,
        .Banks = 1,
    },
    .DataOUTEndpoint = {
        .Address = CDC_RX_EPADDR,
        .Size = CDC_TXRX_EPSIZE,
        .Banks = 1,
    },
    .NotificationEndpoint = {
        .Address = CDC_NOTIFICATION_EPADDR,
        .Size = CDC_NOTIFICATION_EPSIZE,
        .Banks = 1,
    },
},
};

void CDCWork()
{
    CDC_Device_USBTask(&VirtualSerial_CDC_Interface);
    USB_USBTask();
}

uint8_t GetCDCChar(uint8_t* data)
{
    int16_t r = CDC_Device_ReceiveByte(&VirtualSerial_CDC_Interface);
    if (r >= 0)
    {
        *data = r;
        return 1;
    }
    return 0;
}

void SendCDCChar(uint8_t data)
{
    Endpoint_SelectEndpoint(VirtualSerial_CDC_Interface.Config.DataINEndpoint.Address);
    CDC_Device_SendByte(&VirtualSerial_CDC_Interface, data);
}

void InitCDC()
{
    MCUSR &= ~(1 << WDRF);
    wdt_disable();
    clock_prescale_set(clock_div_1);
    USB_Init();
}

void EVENT_USB_Device_Connect(void)
{
}

void EVENT_USB_Device_Disconnect(void)
{
}

void EVENT_USB_Device_ConfigurationChanged(void)
{
    bool ConfigSuccess = true;
    ConfigSuccess &= CDC_Device_ConfigureEndpoints(&VirtualSerial_CDC_Interface);
}

void EVENT_USB_Device_ControlRequest(void)
{
    CDC_Device_ProcessControlRequest(&VirtualSerial_CDC_Interface);
}

void EVENT_CDC_Device_LineEncodingChanged(USB_ClassInfo_CDC_Device_t* const CDCInterfaceInfo)
{
}