Air conditioner on/off control (raspberry pi)


#1

About This Project

Well first I did this project because I was looking for a new thing to do with my daughter :slight_smile:.

The projects that work well right now for a father/daughter cooperation are small but still interesting for daddy while ideally using a lot of components daughter can play with. Also with a minimum of code because this daddy needs to do alone at night as it doesn’t attract attention as much.

Use-case

Even for toy projects required to have a valid use-case and for this one it was for mommy too start the air conditioner in the bus while going home.

Step 1 - Carrier frequency

This AC is controller by an IR remote so we first had to get the carrier frequency which we managed to do here using an IR led in a received kinda mode.

You can’t see it on the scope now but the received signal was not even above the noise level which was pretty high on the 20 seconds setup but we knew what we were looking for and we managed to find a pretty convincing 38 khz.

Step 2 - The circuit

Infrared transmitter

The plan was to use python as much as possible coupled with lirc.

I looked around and it seems that people are modulating in the kernel driver but I was pretty convinced to do a clean 38khz using a 555 so that’s what we did. I must say I’m not much of an analog guy but if a couple resistors can buy me a clean non jittery signal I’m in.

So basically we go from pin 18 of the the raspberry pi to the reset pin of the 555. The output pin of the 555 controls a nice big n-channel mosfet which drives the ir led at just under 200 mA.

I then setup the lirc driver to disable carrier control.

I chose pin 18 because I though that if the 555 idea would fail me, I would use the raspberry pi hardware pwm instead.

In the end, that gave us a nice clean ir signal.

Infrared receiver

We kinda had to also have a receiver to allow us to spy on the remote.

To do this we hooked up a vishay ir receiver to pin 17 of the raspberry pi.

Lirc

So we used lirc userspace programs to record the on of the off commands.

We had to fall back to the raw mode as the remote is almost using the NEC protocol but also not. The raw mode did a good job of dealing with the non standard bits of the communication.

I can’t remember but this shows either the on or the off command. You can see it uses 2 ir transmissions for a single button press.

We mapped ON to KEY_POWER and OFF to KEY_POWER2 and that is about it.

Step 3 - Code

Cayenne works really well for our father daughter projects as almost no code is required.

I take the time to wish you guys the best of luck. The idea behind the service is really good and even though we are not talking about apple’s like resources here, it’s working pretty well.

Note (Si7021)

We ended up adding an Si7021 as a second phase and you’ll see that in the code

So the whole thing looks like this:

import cayenne.client
import time
import subprocess
import smbus2 as smbus

from enum import Enum
from si7021 import Si7021

MQTT_USERNAME  = ""
MQTT_PASSWORD  = ""
MQTT_CLIENT_ID = ""

def on_message(message):
    global push_status
    global ac_state

    # print("message received: " + str(message))
    if message.channel == 4:
        if message.value == "1":
            print("AC START")
            subprocess.run(["irsend", "SEND_ONCE", "ac", "KEY_POWER"])
            ac_state = State.on
        elif message.value == "0":
            print("AC STOP")
            subprocess.run(["irsend", "SEND_ONCE", "ac", "KEY_POWER2"])
            ac_state = State.off

        push_status = True
    
client = cayenne.client.CayenneMQTTClient()
client.on_message = on_message
#client.begin(MQTT_USERNAME, MQTT_PASSWORD, MQTT_CLIENT_ID)
# For a secure connection use port 8883 when calling client.begin:
client.begin(MQTT_USERNAME, MQTT_PASSWORD, MQTT_CLIENT_ID, port=8883)


State = Enum('State', 'on off')

push_status = False
ac_state = None

bus = smbus.SMBus(1)
sensor = Si7021(bus, hold_master_mode=False)

timestamp = 0

while True:
    client.loop()

    if push_status:
        if ac_state == State.on:
            # print("State ON")
            client.virtualWrite(1, 1, "digital_sensor", "d")
        elif ac_state == State.off:
            # print("State OFF")
            client.virtualWrite(1, 0, "digital_sensor", "d")

        push_status = False

    if (time.time() > timestamp + 30):
        client.virtualWrite(2, sensor.temperature, "temp", "c")
        client.virtualWrite(3, sensor.relative_humidity, "rel_hum", "p")

        timestamp = time.time()

We control that with a fancy systemd service which ensures the network is operational before starting the script among other things.

Side note on the Si7021

Daddy had quite a bit of problem making the Si7021 work with the raspberry pi.

I didn’t go to deep in the issue but it seems that the raspberry pi doesn’t handle the i2c clock stretching too well and this is what this lib uses.

I’m trying to have this upstream right now but to make it work I had to modify the Si7021 to also support getting the humidity level not using the mode which uses clock stretching.

To do this I also had to modify a bit the smbus library to handle the non smbus required transfer in a nice and loosely coupled way.

So I need this accepted first:

Then that:

Once this is done, the library should work with the raspberry pi out of the box with:

pip install smbus2
pip install Si7021

Video

It ended up working quite nicely and you can see the response time is really impressive. I must say it’s orders of magnitude above my expectations.

I goes from app to the server (websocket?), then MQTT to raspberry pi, then python spawns the lirc process, then kernel, then IR.

Just writing this I can’t believe it work :stuck_out_tongue:


History not working?
#2

Thanks for posting! Very detailed.


#3

Nice project @francoisgervais. I had also tried to do something similar using IRremote library but never got it working with new LG air-condition as AC remote use longer codes because there is no feedback. So thanks for the solution of using Lirc. will give it a try this week.