понедельник, 4 декабря 2017 г.

Умная розетка

Сделал устройство включающее и выключающее 220 вольт переменного тока. Управление устройством осуществляется с компьютера через bluetooth.


Ток в розетке смертельно опасен!!! Без знания и соблюдения техники безопасности в розетку лезть не надо!

Сейчас можно купить готовое устройство. Например, Broadlink.

Есть решения дешевле. Например, Sonoff. Но здесь уже необходимы знания и опыт работы с переменным током 220 вольт.

Чем самодельное устройство лучше? Его можно сделать на любой ток, любую мощность. И оно будет работать именно так как нужно. Алгоритм, в любой момент, можно подкорректировать.

У меня включение и выключение 220 вольт осуществляется твердотельным реле


SSR-40 DA

Для этих реле есть специальные радиаторы


Radiator SSR 10A-40A


Radiator SSR 10A-100A

Без радиаторов твердотельные реле сильно греются, даже при токе в несколько ампер.

Реле управляется ардуино нано


Ардуино Нано

Для ардуино нано есть удобный сенсор шильд


Сенсор шилд

И bluetooth модуль


Bluetooth Module

Соединяется всё согласно приведенным ниже инструкциям.

Выводы 1 и 2 твердотельного реле включаются в разрыв цепи переменного тока 220 вольт. Проще говоря, один из проводов идущий к нагрузке разрезается и получившиеся выводы подключаются к разъемам 1 и 2 твердотельного реле.

Arduino Nano --- SSR
GND --- 4
D13 --- 3

Arduino Nano --- Bluetooth Module
VCC --- VCC
GND --- GND
D0 --- TXD
D1 --- RXD

Bluetooth модуль подключается к ардуино после загрузки программы в контроллер. Иначе программу загрузить не получится.

Для питания ардуино нано от сети 220 вольт переменного тока нужна специальная платка


AC-DC Power Step Down Module

Или можно взять обычную пятивольтовую зарядку


Charger

Программа для ардуино нано (C/C++)

////////////////////////
//
// Arduino
//
////////////////////////
//
// Sketch:
//

#define RELAY_PORT PORTB
#define RELAY_DDR DDRB
#define RELAY 5

volatile unsigned char data = 0;

volatile unsigned char flag = 0;

volatile unsigned char on = 0;
volatile unsigned char off = 0;

volatile unsigned char sec = 0;
volatile unsigned char min = 0;

volatile unsigned char stage = 0;

void PORT_Init( void )
{
    // Port B
    DDRB = 0b00000000;
    PORTB = 0b00000000;

    // Port C
    DDRC = 0b00000000;
    PORTC = 0b00000000;

    // Port D
    DDRD = 0b00000000;
    PORTD = 0b00000000;
}

void Relay_Init( void )
{
    RELAY_DDR |= (1<<RELAY);
    RELAY_PORT &= ~(1<<RELAY);
}

void Timer0_Init( void )
{
    // TC0 Counter Value Register
    TCNT0 = 0;
    // TC0 Control Register A
    // Bits: COM0A1 COM0A0 COM0B1 COM0B0 - - WGM01 WGM00
    TCCR0A = 0;
    // TC0 Control Register B
    // Bits: FOC0A FOC0B - - WGM02 CS02 CS01 CS00
    TCCR0B = 0; // No clock source (stopped)
    // TC0 Interrupt Mask Register
    // Bits: - - - - - OCIE0B OCIE0A TOIE0
    TIMSK0 = 0;
    // TC0 Interrupt Flag Register
    // Bits: - - - - - OCF0B OCF0A TOV0
    TIFR0 = 0;
}

void Timer1_Init( void )
{
    // TC1 Counter Value Register
    TCNT1 = 3035;
    // TC1 Control Register A
    // Bits: COM1A1 COM1A0 COM1B1 COM1B0 - - WGM11 WGM10
    TCCR1A = 0;
    // TC1 Control Register B
    // Bits: ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10
    TCCR1B = (1<<CS12); // CLK / 256
    // TC1 Control Register C
    // Bits: FOC1A FOC1B - - - - - -
    TCCR1C = 0;
    // TC1 Interrupt Mask Register
    // Bits: - - ICIE1 - - OCIE1B OCIE1A TOIE1
    TIMSK1 = (1<<TOIE1);
    // TC1 Interrupt Flag Register
    // Bits: – – ICF1 – - OCF1B OCF1A TOV1
    TIFR1 = (1<<TOV1);
}

void Timer2_Init( void )
{
    // TC2 Counter Value Register
    TCNT2 = 0;
    // TC2 Control Register A
    // COM2A1 COM2A0 COM2B1 COM2B0 - - WGM21 WGM20
    TCCR2A = 0;
    // TC2 Control Register B
    // FOC2A FOC2B - - WGM22 CS22 CS21 CS20
    TCCR2B = 0; // No clock source (stopped)
    // TC2 Interrupt Mask Register
    // - - - - - OCIE2B OCIE2A TOIE2
    TIMSK2 = 0;
    // TC2 Interrupt Flag Register
    // - - - - - OCF2B OCF2A TOV2
    TIFR2 = 0;
}

void USART_Init( void )
{
    // Set baud rate (9600 bps)
    UBRR0H = 0x00;
    UBRR0L = 0x67;

    // Enable receiver and transmitter
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);

    // Set frame format: 8 data, 1 stop bit
    UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
}

unsigned char USART_Receive( void )
{
    // Wait for data to be received
    while ( !(UCSR0A & (1<<RXC0)) ) ;

    // Get and return received data from buffer
    return UDR0;
}

void USART_Transmit( unsigned char data )
{
    // Wait for empty transmit buffer
    while ( !( UCSR0A & (1<<UDRE0)) ) ;

    // Put data into buffer, sends the data
    UDR0 = data;
}

void setup()
{
    cli();

    PORT_Init();

    Relay_Init();

    Timer0_Init();

    Timer1_Init();

    Timer2_Init();

    USART_Init();

    sei();
}

void loop()
{
    while (1)
    {
        data = USART_Receive();

        if ( data == 255 )
        {
            USART_Transmit( stage );
        }

        else if ( data == 254 )
        {
            USART_Transmit( on );
        }

        else if ( data == 253 )
        {
            USART_Transmit( off );
        }

        else if ( data == 252 )
        {
            flag = 1;
        }

        else if ( data == 251 )
        {
            flag = 0;
        }

        else if ( flag == 1 )
        {
            on = data;
        }

        else
        {
            off = data;
        }
    }
}

ISR(TIMER1_OVF_vect)
{
    TCNT1 = 3035;

    sec += 1;

    if ( sec == 60 )
    {
        min += 1;
        sec = 0;
    }

    if ( min == 240 )
    {
        min = 0;
    }

    if ( stage == 0 )
    {
        if ( on != 0 )
        {
            RELAY_PORT |= (1<<RELAY);
//            USART_Transmit( '1' ); // test
        }

        sec = 0;
        min = 0;
        stage = 1;
    }

    if ( stage == 1 && min >= on )
    {
        stage = 2;
    }

    if ( stage == 2 )
    {
        if ( off != 0 )
        {
            RELAY_PORT &= ~(1<<RELAY);
//            USART_Transmit( '0' ); // test
        }

        sec = 0;
        min = 0;
        stage = 3;
    }

    if ( stage == 3 && min >= off )
    {
        stage = 0;
    }
}

//
// End
//
////////////////////////

Программа для компьютера (python)

#!/usr/bin/env python
# coding: utf-8


import serial


port_number = 2 # COM3
baud_rate = 9600 # bps


# open port

ser = serial.Serial(port_number,
                    baud_rate,
                    bytesize = serial.EIGHTBITS,
                    parity = serial.PARITY_NONE,
                    stopbits = serial.STOPBITS_ONE,
                    timeout = 1)

print ser, '\n' # print port information


# communication

while 1 :

    tmp = raw_input('quit: ')

    if tmp :
        break

    while 1 :

        on = raw_input('on (0 ... 240): ')

        if not on :
            break

        try :
            tmp = int(on)

        except ValueError :
            continue

        if tmp < 0 :
            continue

        if tmp > 240 :
            continue

        ser.write(chr(252))
        ser.write(chr(tmp))

        break

    while 1 :

        off = raw_input('off (0 ... 240): ')

        if not off :
            break

        try :
            tmp = int(off)

        except ValueError :
            continue

        if tmp < 0 :
            continue

        if tmp > 240 :
            continue

        ser.write(chr(251))
        ser.write(chr(tmp))

        break

    while 1 :

        tmp = ser.read()

        if not tmp :
            break

    ser.write(chr(255))
    tmp = ser.read()

    if tmp :
        stage = ord(tmp)

    else :
        stage = ''
        print 'error 255'

    while 1 :

        tmp = ser.read()

        if not tmp :
            break

    ser.write(chr(254))
    on = ser.read()

    if on :
        print 'on:', ord(on),

        if stage == 0 or stage == 1 :
            print '*'

        else :
            print ''

    else :
        print 'error 254'

    while 1 :

        tmp = ser.read()

        if not tmp :
            break

    ser.write(chr(253))
    off = ser.read()

    if off :
        print 'off:', ord(off),

        if stage == 2 or stage == 3 :
            print '*'

        else :
            print ''

    else :
        print 'error 253'

    print ''


# close port

ser.close()


print ''


# C:/Python27/python.exe C:\220\console.py

Работа программы


Поясню некоторые моменты. Лучше проверять работу устройства и тестировать код подключив устройство к USB порту компьютера кабелем. А потом, когда всё проверено и настроено, можно использовать bluetooth.

Ардуино после подачи питания ожидает поступления команд по UART. Ток через реле при этом не идет. Подключенная через реле нагрузка выключена. В зависимости от полученных команд, устройство может подать питание на нагрузку или отключить питание на нагрузке. Но создавался девайс для того, чтобы включать и выключать ток через определенные интервалы времени.

Управлять устройством можно через монитор порта Arduino IDE. Только это совсем не удобно. Поэтому и была написана специальная программа для компьютера.

После запуска программа пытается открыть порт COM3. Если ничего не получается, то работа программы завершается. Если всё получается, то появляется запрос на завершение работы программы. Нажав [Enter] продолжим работу программы. Появится запрос времени работы нагрузки в минутах. Можно ввести целое число от 0 до 240. Затем нужно нажать [Enter]. И появится запрос времени в течении которого нагрузка будет отключена. Здесь всё аналогично предшествующему пункту.

Программа передает введенные данные устройству. Полученный от устройства ответ выводится на экран. Звездочка показывает текущее состояние устройства.

Если на все запросы жмакать только [Enter], то будут показаны ранее введенные параметры и текущее состояние.

Если на запрос on ввести 0, а на запрос off ввести число от 1 до 240, то устройство отключит питание на нагрузке. Ток через реле идти не будет. Это состояние будет сохранятся до введения новых команд.

Если на запрос on ввести число от 1 до 240, а на запрос off ввести 0, то устройство включит питание на нагрузке. Ток через реле будет идти. Это состояние также будет сохранятся до введения новых команд.

Если на запрос on и на запрос off ввести 0, то устройство будет зафиксировано в текущем состоянии. Я пока не решил баг это или фича, поэтому ничего и не делаю с этим.

Программа консольная, но вполне удобная. В будущем, возможно, сделаю веб-интерфейс.

Если есть вопросы по коду, то посмотрите статьи 1 и 2.