diff --git a/.github/workflows/arduino-build.yml b/.github/workflows/arduino-build.yml new file mode 100644 index 0000000..b7e4a42 --- /dev/null +++ b/.github/workflows/arduino-build.yml @@ -0,0 +1,38 @@ +name: Arduino Build CI + +on: + push: + branches: [ "**" ] + pull_request: + branches: [ "**" ] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + sketch: + - path: ac_control/ir_sender + name: ir_sender + - path: ac_control/ir_receiver + name: ir_receiver + profile: + - uno + - esp8266 + - esp32 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Arduino CLI + uses: arduino/setup-arduino-cli@v1 + + - name: Compile sketch with profile ${{ matrix.profile }} + run: | + arduino-cli compile --profile ${{ matrix.profile }} ${{ matrix.sketch.path }} + + - name: Build summary + run: | + echo "✅ Successfully built ${{ matrix.sketch.name }} for profile ${{ matrix.profile }}" diff --git a/ac_control/ir_receiver/ir_receiver.ino b/ac_control/ir_receiver/ir_receiver.ino index 0f208fc..bc74b1f 100644 --- a/ac_control/ir_receiver/ir_receiver.ino +++ b/ac_control/ir_receiver/ir_receiver.ino @@ -1,73 +1,43 @@ -#define sprint Serial.print +#define sprint Serial.print #define sprintln Serial.println -#include -int RECV_PIN = 11; - - -IRrecv irrecv(RECV_PIN); +#include // IRremote 4.4.1 from Library Manager - -decode_results results; +int RECV_PIN = 11; +unsigned long timeBegin; int c = 1; - unsigned long timeBegin; + void setup() { Serial.begin(9600); - irrecv.enableIRIn(); // Start the receiver + IrReceiver.begin(RECV_PIN, ENABLE_LED_FEEDBACK); // Start the receiver timeBegin = micros(); - + Serial.println(F("Ready to receive IR signals")); } void loop() { + if (IrReceiver.decode()) { + // Print counter + sprintln(c); + c++; + + // Use built-in print functions from IRremote library + IrReceiver.printIRResultShort(&Serial); + Serial.println(); + // Print as C array for use in sender + IrReceiver.printIRSendUsage(&Serial); - if (irrecv.decode(&results)) { - dump(&results); -// ======== - Serial.println(results.value, HEX); - Serial.print("rawlen "); - Serial.println(results.rawlen); - Serial.print("decode type "); - Serial.println(results.decode_type); + // Additional details + Serial.print("Protocol: "); + Serial.println(IrReceiver.decodedIRData.protocol); + Serial.print("Decoded value: 0x"); + Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX); unsigned long timeEnd = micros(); unsigned long duration = timeEnd - timeBegin; double averageDuration = (double)duration / 1000.0; Serial.println(averageDuration); -// if(results.value==0xA1026EFF) { -// Serial.println("turning off"); -// } - irrecv.resume(); // Receive the next value + IrReceiver.resume(); // Receive the next value } } - -void dump(decode_results *results) { - int count = results->rawlen; - sprintln(c); - c++; - sprintln("For IR Scope: "); - for (int i = 1; i < count; i++) { - sprint("0x"); - sprint((unsigned int)results->rawbuf[i], HEX); - sprint(" "); - } - - sprintln(""); - sprintln("For Arduino sketch: "); - sprint("unsigned int raw["); - sprint(count, DEC); - sprint("] = {"); - for (int i = 1; i < count; i++) { - sprint("0x"); - sprint((unsigned int)results->rawbuf[i], HEX); - sprint(","); - } - sprint("};"); - sprintln(""); - sprint("irsend.sendRaw(raw,"); - sprint(count, DEC); - sprint(",38);"); - sprintln(""); - sprintln(""); -} diff --git a/ac_control/ir_receiver/sketch.yaml b/ac_control/ir_receiver/sketch.yaml new file mode 100644 index 0000000..8978cfe --- /dev/null +++ b/ac_control/ir_receiver/sketch.yaml @@ -0,0 +1,21 @@ +profiles: + uno: + fqbn: arduino:avr:uno + platforms: + - platform: arduino:avr (1.8.6) + libraries: + - IRremote (4.4.1) + esp8266: + fqbn: esp8266:esp8266:nodemcuv2 + platforms: + - platform: esp8266:esp8266 (3.1.2) + platform_index_url: https://arduino.esp8266.com/stable/package_esp8266com_index.json + libraries: + - IRremote (4.4.1) + esp32: + fqbn: esp32:esp32:esp32 + platforms: + - platform: esp32:esp32 (3.3.2) + platform_index_url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + libraries: + - IRremote (4.4.1) diff --git a/ac_control/ir_sender/ir_sender.ino b/ac_control/ir_sender/ir_sender.ino index 8e85fca..f2b69d2 100644 --- a/ac_control/ir_sender/ir_sender.ino +++ b/ac_control/ir_sender/ir_sender.ino @@ -1,7 +1,11 @@ -#include // IRremote 4.1.2 from Library Manager +#include // IRremote 4.4.1 from Library Manager -unsigned long period = 7200000; -unsigned long last_trigger = 0; +// PIR motion sensor configuration +#define PIR_PIN 2 // GPIO pin connected to PIR sensor output +#define MOTION_TIMEOUT 3600000 // 1 hour in milliseconds (60 * 60 * 1000) + +unsigned long last_motion_time = 0; +bool ac_is_off = false; // Track whether we've already sent the off command //4467 4347 608 1542 609 493 582 1568 582 493 582 493 582 493 582 493 582 1568 582 1568 583 493 582 493 582 1570 581 1570 581 495 580 1571 580 1571 580 495 580 1571 580 1571 580 495 580 1571 580 1571 580 495 581 1571 579 1571 580 1571 580 1571 580 1571 580 1571 580 1571 580 1571 580 1571 580 1572 579 1571 580 1571 580 1571 580 1571 580 1572 579 1571 580 1572 579 496 579 1572 579 1572 579 1572 579 496 579 1572 579 1572 579 1572 580 5167 4439 4377 579 496 579 1572 579 497 578 1572 579 1572 579 1572 579 1573 578 497 578 497 578 1573 578 1573 578 497 578 497 578 1573 578 497 578 497 578 1573 578 497 578 497 578 1573 578 497 578 498 577 1573 578 498 577 498 577 498 577 498 577 498 577 498 577 498 577 499 576 522 553 522 554 522 553 522 553 522 553 522 553 522 553 522 553 522 553 1598 553 522 553 522 554 522 553 1598 553 522 553 522 553 522 553 uint16_t on_code[199] = {0x1173, 0x10fb, 0x260, 0x606, 0x261, 0x1ed, 0x246, 0x620, 0x246, 0x1ed, 0x246, 0x1ed, 0x246, 0x1ed, 0x246, 0x1ed, 0x246, 0x620, 0x246, 0x620, 0x247, 0x1ed, 0x246, 0x1ed, 0x246, 0x622, 0x245, 0x622, 0x245, 0x1ef, 0x244, 0x623, 0x244, 0x623, 0x244, 0x1ef, 0x244, 0x623, 0x244, 0x623, 0x244, 0x1ef, 0x244, 0x623, 0x244, 0x623, 0x244, 0x1ef, 0x245, 0x623, 0x243, 0x623, 0x244, 0x623, 0x244, 0x623, 0x244, 0x623, 0x244, 0x623, 0x244, 0x623, 0x244, 0x623, 0x244, 0x623, 0x244, 0x624, 0x243, 0x623, 0x244, 0x623, 0x244, 0x623, 0x244, 0x623, 0x244, 0x624, 0x243, 0x623, 0x244, 0x624, 0x243, 0x1f0, 0x243, 0x624, 0x243, 0x624, 0x243, 0x624, 0x243, 0x1f0, 0x243, 0x624, 0x243, 0x624, 0x243, 0x624, 0x244, 0x142f, 0x1157, 0x1119, 0x243, 0x1f0, 0x243, 0x624, 0x243, 0x1f1, 0x242, 0x624, 0x243, 0x624, 0x243, 0x624, 0x243, 0x625, 0x242, 0x1f1, 0x242, 0x1f1, 0x242, 0x625, 0x242, 0x625, 0x242, 0x1f1, 0x242, 0x1f1, 0x242, 0x625, 0x242, 0x1f1, 0x242, 0x1f1, 0x242, 0x625, 0x242, 0x1f1, 0x242, 0x1f1, 0x242, 0x625, 0x242, 0x1f1, 0x242, 0x1f2, 0x241, 0x625, 0x242, 0x1f2, 0x241, 0x1f2, 0x241, 0x1f2, 0x241, 0x1f2, 0x241, 0x1f2, 0x241, 0x1f2, 0x241, 0x1f2, 0x241, 0x1f3, 0x240, 0x20a, 0x229, 0x20a, 0x22a, 0x20a, 0x229, 0x20a, 0x229, 0x20a, 0x229, 0x20a, 0x229, 0x20a, 0x229, 0x20a, 0x229, 0x20a, 0x229, 0x63e, 0x229, 0x20a, 0x229, 0x20a, 0x22a, 0x20a, 0x229, 0x63e, 0x229, 0x20a, 0x229, 0x20a, 0x229, 0x20a, 0x229}; @@ -12,15 +16,52 @@ uint16_t temp_code[199] = {0x1113, 0x1176, 0x207, 0x655, 0x207, 0x20b, 0x207, 0x uint16_t off_code[] = {0x2301, 0x11a8, 0x27e, 0x237, 0x27f, 0x684, 0x27f, 0x685, 0x27f, 0x239, 0x27d, 0x684, 0x27f, 0x686, 0x27e, 0x237, 0x27f, 0x237, 0x27f, 0x685, 0x27f, 0x685, 0x27e, 0x238, 0x27e, 0x239, 0x27d, 0x239, 0x27d, 0x238, 0x27e, 0x238, 0x27e, 0x239, 0x27d, 0x238, 0x27e, 0x23a, 0x27c, 0x237, 0x27f, 0x238, 0x27e, 0x238, 0x27e, 0x684, 0x27f, 0x236, 0x280, 0x239, 0x27c, 0x238, 0x27e, 0x237, 0x27f, 0x687, 0x27d, 0x684, 0x27f, 0x685, 0x27f, 0x237, 0x27f, 0x685, 0x27e, 0x236, 0x280, 0x237, 0x27f, 0x685, 0x27e, 0x253, 0x263, 0x4e21, 0x27f, 0x236, 0x27f, 0x237, 0x27f, 0x235, 0x281, 0x253, 0x263, 0x235, 0x281, 0x236, 0x280, 0x238, 0x27f, 0x236, 0x280, 0x237, 0x27f, 0x236, 0x280, 0x236, 0x280, 0x236, 0x27f, 0x236, 0x280, 0x236, 0x280, 0x236, 0x27f, 0x683, 0x280, 0x235, 0x281, 0x237, 0x27f, 0x236, 0x280, 0x236, 0x280, 0x238, 0x27f, 0x237, 0x27f, 0x238, 0x27e, 0x235, 0x281, 0x235, 0x281, 0x235, 0x281, 0x237, 0x27f, 0x237, 0x27f, 0x684, 0x27f, 0x685, 0x27f, 0x687, 0x27c, 0x237, 0x27f, 0x9c48, 0x2302, 0x11aa, 0x27d, 0x237, 0x27f, 0x685, 0x27f, 0x684, 0x27f, 0x235, 0x281, 0x684, 0x280, 0x685, 0x27e, 0x237, 0x27f, 0x238, 0x27e, 0x684, 0x27f, 0x682, 0x281, 0x237, 0x27f, 0x237, 0x27f, 0x236, 0x27f, 0x236, 0x280, 0x237, 0x27f, 0x238, 0x27e, 0x237, 0x27f, 0x236, 0x280, 0x237, 0x27f, 0x237, 0x27f, 0x236, 0x280, 0x684, 0x27f, 0x237, 0x27f, 0x237, 0x27f, 0x236, 0x280, 0x236, 0x280, 0x682, 0x281, 0x685, 0x27e, 0x236, 0x280, 0x684, 0x27f, 0x685, 0x27e, 0x237, 0x27f, 0x237, 0x27f, 0x686, 0x27e, 0x237, 0x27f, 0x4e25, 0x27e, 0x23a, 0x27c, 0x237, 0x27f, 0x238, 0x27e, 0x237, 0x27f, 0x239, 0x27d, 0x239, 0x27d, 0x238, 0x27e, 0x236, 0x280, 0x238, 0x27d, 0x237, 0x27f, 0x237, 0x27f, 0x237, 0x27f, 0x239, 0x27d, 0x236, 0x280, 0x238, 0x27e, 0x237, 0x27e, 0x236, 0x280, 0x237, 0x27e, 0x237, 0x27f, 0x236, 0x280, 0x237, 0x27f, 0x238, 0x27e, 0x237, 0x27f, 0x238, 0x27e, 0x237, 0x27f, 0x237, 0x27f, 0x237, 0x27f, 0x238, 0x27e, 0x685, 0x27e, 0x685, 0x27f, 0x685, 0x27f, 0x687, 0x27c, 0x9c4c, 0x2301, 0x11a7, 0x27e, 0x237, 0x27e, 0x685, 0x27f, 0x685, 0x27e, 0x238, 0x27e, 0x684, 0x280, 0x686, 0x27d, 0x239, 0x27d, 0x238, 0x27e, 0x6a1, 0x261, 0x685, 0x27e, 0x237, 0x27f, 0x237, 0x27f, 0x236, 0x280, 0x238, 0x27e, 0x236, 0x280, 0x237, 0x27f, 0x238, 0x27e, 0x237, 0x27f, 0x238, 0x27e, 0x237, 0x27f, 0x237, 0x27f, 0x685, 0x27f, 0x237, 0x27f, 0x237, 0x27f, 0x237, 0x27f, 0x236, 0x280, 0x684, 0x27f, 0x684, 0x27f, 0x684, 0x27f, 0x683, 0x280, 0x685, 0x27e, 0x238, 0x27e, 0x236, 0x27f, 0x685, 0x27f, 0x238, 0x27e, 0x4e21, 0x280, 0x236, 0x280, 0x235, 0x281, 0x237, 0x27f, 0x237, 0x27f, 0x236, 0x280, 0x236, 0x280, 0x237, 0x27f, 0x237, 0x27f, 0x238, 0x27e, 0x237, 0x27f, 0x237, 0x27f, 0x237, 0x27f, 0x237, 0x280, 0x237, 0x27f, 0x236, 0x280, 0x235, 0x280, 0x237, 0x27f, 0x236, 0x280, 0x236, 0x280, 0x236, 0x280, 0x685, 0x27f, 0x238, 0x27e, 0x684, 0x280, 0x683, 0x280, 0x237, 0x27f, 0x237, 0x27f, 0x236, 0x280, 0x237, 0x27f, 0x236, 0x280, 0x236, 0x280, 0x685, 0x27e, 0x684, 0x27f, 0x9c4c, 0x22ff, 0x11a6, 0x280, 0x237, 0x27f, 0x236, 0x280, 0x237, 0x27f, 0x238, 0x27e, 0x237, 0x27f, 0x239, 0x27d, 0x23a, 0x27c, 0x237, 0x27f, 0x237, 0x27f, 0x236, 0x280, 0x238, 0x27e, 0x237, 0x27f, 0x238, 0x27e, 0x237, 0x27f, 0x237, 0x27f, 0x236, 0x280, 0x237, 0x27e, 0x239, 0x27d, 0x238, 0x27e, 0x238, 0x27e, 0x237, 0x27f, 0x238, 0x27e, 0x236, 0x280, 0x237, 0x27f, 0x237, 0x27f, 0x237, 0x27f, 0x236, 0x280, 0x684, 0x280, 0x236, 0x280, 0x684, 0x280, 0x237, 0x27f, 0x686, 0x27e, 0x236, 0x280, 0x684, 0x27f, 0x235, 0x281, 0x4e20, 0x280, 0x237, 0x27f, 0x238, 0x27e, 0x236, 0x280, 0x238, 0x27e, 0x237, 0x27f, 0x237, 0x27f, 0x237, 0x27f, 0x237, 0x27f, 0x238, 0x27e, 0x237, 0x27f, 0x238, 0x27e, 0x235, 0x281, 0x237, 0x27f, 0x237, 0x27f, 0x239, 0x27d, 0x253, 0x262, 0x235, 0x280, 0x238, 0x27e, 0x238, 0x27d, 0x237, 0x27e, 0x236, 0x280, 0x237, 0x27f, 0x237, 0x27f, 0x237, 0x27f, 0x238, 0x27e, 0x236, 0x280, 0x238, 0x27e, 0x239, 0x27d, 0x237, 0x27e, 0x687, 0x27c, 0x237, 0x27e, 0x238, 0x27e}; void setup() { pinMode (1, OUTPUT); - Serial.begin(9600); // does serial need to be open? + pinMode (PIR_PIN, INPUT); + Serial.begin(9600); IrSender.begin(1); + + // Initialize with current time - assume there's motion at startup + last_motion_time = millis(); + + // Send initial off code as before IrSender.sendRaw(off_code, sizeof(off_code)/sizeof(off_code[0]), 38); + ac_is_off = true; + + Serial.println("AC IR Sender with PIR Motion Sensor initialized"); + Serial.print("Motion timeout: "); + Serial.print(MOTION_TIMEOUT / 1000); + Serial.println(" seconds"); } void loop() { - // due to unsigned math being modulo, this period math still works when it wraps around after 50 days - if ((unsigned long)(millis() - last_trigger) > period) { + // Read PIR sensor state (HIGH = motion detected, LOW = no motion) + int motion_detected = digitalRead(PIR_PIN); + + // If motion is detected, update the last motion time and reset AC off flag + if (motion_detected == HIGH) { + last_motion_time = millis(); + if (ac_is_off) { + Serial.println("Motion detected! AC can be turned off again after timeout."); + ac_is_off = false; // Reset flag so we can turn off AC again after timeout + } + } + + // Check if no motion timeout has been reached + // Due to unsigned math being modulo, this still works when millis() wraps around after ~50 days + unsigned long time_since_motion = (unsigned long)(millis() - last_motion_time); + + if (time_since_motion > MOTION_TIMEOUT && !ac_is_off) { + // No motion for 1 hour and we haven't sent the off code yet + Serial.print("No motion for "); + Serial.print(MOTION_TIMEOUT / 1000); + Serial.println(" seconds. Sending AC OFF command."); + IrSender.sendRaw(off_code, sizeof(off_code)/sizeof(off_code[0]), 38); - last_trigger = millis(); + ac_is_off = true; + + Serial.println("AC OFF command sent."); } + + // Small delay to avoid excessive polling + delay(100); } diff --git a/ac_control/ir_sender/sketch.yaml b/ac_control/ir_sender/sketch.yaml new file mode 100644 index 0000000..8978cfe --- /dev/null +++ b/ac_control/ir_sender/sketch.yaml @@ -0,0 +1,21 @@ +profiles: + uno: + fqbn: arduino:avr:uno + platforms: + - platform: arduino:avr (1.8.6) + libraries: + - IRremote (4.4.1) + esp8266: + fqbn: esp8266:esp8266:nodemcuv2 + platforms: + - platform: esp8266:esp8266 (3.1.2) + platform_index_url: https://arduino.esp8266.com/stable/package_esp8266com_index.json + libraries: + - IRremote (4.4.1) + esp32: + fqbn: esp32:esp32:esp32 + platforms: + - platform: esp32:esp32 (3.3.2) + platform_index_url: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json + libraries: + - IRremote (4.4.1)